Files
zeekayandHanzo Dev 18d44c1ec0 refactor: rename the members that still say hog
Each of these was checked against the live database before it moved,
because a member name is free to rename and a stored value is not.

Members only -- the value never said hog, so nothing in Postgres changes:

  TaggerType.HOG / EvaluationType.HOG   the column already holds `script`;
                                        `HOG = "script", "Script"` was a
                                        member disagreeing with its own value
  FEATURE_FLAGS.HOG                     the flag key `script` is read by the
                                        flag service and does not move
  HEDGEFN_SKIN_SPIDERHOG                same, key `mascot-skin-spiderscript`
  _HOGLAND_MARKER                       already `/var/lib/script`; renamed to
                                        _BOX_MARKER, which is what the
                                        function returns and what the comment
                                        beside it already called it

Value too, having established nothing stores it: `InsightType.HOG = 'HOG'`
is derived from `query.kind === 'ScriptQuery'`, never written -- the one
writer, queryNodeToFilter, has no ScriptQuery entry, and zero of the 28
insight rows carry it in `filters.insight`.

Two readers keyed on that value had to move with it, and neither would
have been caught by renaming the enum alone: INSIGHT_TYPE_URLS spells
`HOG:` as a plain key while its siblings are computed, and the Slack
unfurl map keys the same string in Python.

The rest is copy a person reads -- "HOG code exceeds maximum size" is an
API error message, not an identifier.

Left alone deliberately: `ReviewHog` and `HogDesk`, which survive only in
applied migrations' `choices=` lists. The live members are already
`REVIEW = "review"` and `Desk = "desk"`, and Django compares choices in
Python without ever writing them, so those lists are a snapshot of a
field that has not looked like that for some time.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 20:12:56 -07:00
..

Insights Custom Commands

Custom Python commands for the Insights monorepo. They extend insightscli with Insights-specific functionality that's too complex for YAML definitions.

How It Works

Each command module is imported on first use, not at insightscli startup. The insightscli framework reads config.commands_dir from /insightscli.yaml; manifest entries with a click: field are resolved by Click when the user invokes the command or runs per-command --help.

Boot-time registrations (precheck handlers, telemetry hooks, post-command hooks) live in their own modules listed under config.boot_modules: in insightscli.yaml. Those modules must be cheap to import — keep heavy dependencies behind deferred imports inside handler bodies.

Adding a Command

  1. Create or edit a module with a plain @click.command(...) decorator (no need to import or reference insightscli's cli group):
# myfeature.py
import click

@click.command(name="my:command")
@click.option("--verbose", "-v", is_flag=True)
def my_command(verbose: bool) -> None:
    """One-line description shown in insightscli --help."""
    click.echo("Done!")
  1. Add an entry to insightscli.yaml in the appropriate category section:
tools:
  my:command:
    click: insightscli_commands.myfeature:my_command
    description: One-line summary shown in `insightscli --help`.

The Click command name must match the manifest key — drift surfaces as a ClickException on resolution. The insightscli test suite parametrizes a --help invocation over every click: entry, which is Click's recommended sanity check.

Mark a command hidden by setting hidden: true on the manifest entry. Don't use @click.command(hidden=True); the manifest is the only source of truth.

  1. Test it:
insightscli my:command --help
insightscli my:command -v

That's it. No side-effect imports, no central registration list to maintain.

Boot Modules

config.boot_modules in insightscli.yaml lists modules imported once at startup. They register hooks via insightscli.hooks:

  • insightscli_commands.prechecks — declares the migrations precheck (used by dev:start).
  • insightscli_commands.telemetry_props — adds Insights environment props (environment, agent, is_agent, in_flox, is_worktree, is_insights_dev, process_manager, has_devenv_config, repo_sha, repo_commit_date) to every command_started / command_completed event.
  • insightscli_commands.hint_hook — shows a contextual hint after successful commands.

Add a new boot module by creating a file that calls one of the register_* helpers from insightscli.hooks at import time, then list it under config.boot_modules:. Keep these modules import-light: the precheck handler in prechecks.py is the canonical example of deferring its heavy import until the handler actually fires.

When to Use Python vs YAML

Use Python here when... Use YAML (insightscli.yaml) when...
Complex logic, loops, conditionals Simple shell one-liners
Need Click's argument parsing Delegating to bin/ scripts
Interactive prompts or menus Chaining existing insightscli commands
Accessing Python libraries Quick prototypes

File Structure

tools/insightscli-commands/
└── insightscli_commands/
    ├── __init__.py       # Only the common/ sys.path workaround
    ├── prechecks.py      # Boot module — registers the migrations precheck
    ├── telemetry_props.py# Boot module — registers Insights telemetry props
    ├── hint_hook.py      # Boot module — registers the post-command hint hook
    ├── build.py          # Lazy: insightscli build
    ├── doctor.py         # Lazy: insightscli doctor / doctor:disk / doctor:zombies / doctor:report
    ├── ...               # Other lazy command modules
    ├── devbox/           # Devbox subpackage (lazy)
    ├── devenv/           # Intent-based dev environment subpackage (lazy)
    └── product/          # Product scaffolding subpackage (lazy)

This directory is not packaged or installed — the insightscli framework loads insightscli_commands from disk at runtime via config.commands_dir in /insightscli.yaml. Runtime dependencies (click, pyyaml, pydantic, requests) are declared in the root /pyproject.toml alongside the rest of the monorepo.

Tips

  • Use click.echo() for output, not print()
  • Use click.secho(msg, fg="green") for colored output
  • Add @click.option("--yes", "-y", is_flag=True) for destructive commands
  • Keep commands focused — compose via YAML steps: if needed

See Also

  • /insightscli.yaml — manifest with category placement, lazy click: import paths, and boot_modules: list
  • /tools/insightscli/README.md — framework documentation (command registry, hooks API)
  • /bin/ — shell scripts (referenced via bin_script: in YAML)