devstuff 1.16.0__tar.gz → 1.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {devstuff-1.16.0 → devstuff-1.17.0}/.github/workflows/test-installs.yml +1 -0
  2. {devstuff-1.16.0 → devstuff-1.17.0}/CHANGELOG.md +7 -0
  3. {devstuff-1.16.0 → devstuff-1.17.0}/CLAUDE.md +88 -2
  4. {devstuff-1.16.0 → devstuff-1.17.0}/PKG-INFO +109 -2
  5. {devstuff-1.16.0 → devstuff-1.17.0}/README.md +108 -1
  6. {devstuff-1.16.0 → devstuff-1.17.0}/docs/specs/README.md +1 -0
  7. devstuff-1.17.0/docs/specs/starship-config/development-plan.md +74 -0
  8. devstuff-1.17.0/docs/specs/starship-config/specifications.md +164 -0
  9. devstuff-1.17.0/docs/specs/starship-config/stack-decisions.md +187 -0
  10. {devstuff-1.16.0 → devstuff-1.17.0}/pyproject.toml +1 -1
  11. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/cli.py +3 -0
  12. devstuff-1.17.0/src/dev_setup/commands/configure_cmd.py +123 -0
  13. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/help_cmd.py +2 -0
  14. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/install_cmd.py +19 -1
  15. devstuff-1.17.0/src/dev_setup/configure/__init__.py +56 -0
  16. devstuff-1.17.0/src/dev_setup/configure/starship/__init__.py +1 -0
  17. devstuff-1.17.0/src/dev_setup/configure/starship/fonts.py +47 -0
  18. devstuff-1.17.0/src/dev_setup/configure/starship/model.py +850 -0
  19. devstuff-1.17.0/src/dev_setup/configure/starship/preview.py +157 -0
  20. devstuff-1.17.0/src/dev_setup/configure/starship/render.py +268 -0
  21. devstuff-1.17.0/src/dev_setup/configure/starship/wizard.py +396 -0
  22. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/tools.yaml +48 -0
  23. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/ui.py +4 -2
  24. devstuff-1.17.0/tests/test_configure_starship.py +682 -0
  25. {devstuff-1.16.0 → devstuff-1.17.0}/uv.lock +1 -1
  26. {devstuff-1.16.0 → devstuff-1.17.0}/.claude/skills/run-stuff/SKILL.md +0 -0
  27. {devstuff-1.16.0 → devstuff-1.17.0}/.claude/skills/run-stuff/smoke.sh +0 -0
  28. {devstuff-1.16.0 → devstuff-1.17.0}/.github/workflows/bump.yml +0 -0
  29. {devstuff-1.16.0 → devstuff-1.17.0}/.github/workflows/ci.yml +0 -0
  30. {devstuff-1.16.0 → devstuff-1.17.0}/.github/workflows/publish.yml +0 -0
  31. {devstuff-1.16.0 → devstuff-1.17.0}/.gitignore +0 -0
  32. {devstuff-1.16.0 → devstuff-1.17.0}/dev/Dockerfile +0 -0
  33. {devstuff-1.16.0 → devstuff-1.17.0}/dev/Dockerfile.ci +0 -0
  34. {devstuff-1.16.0 → devstuff-1.17.0}/dev/Makefile +0 -0
  35. {devstuff-1.16.0 → devstuff-1.17.0}/dev/docker-compose.yml +0 -0
  36. {devstuff-1.16.0 → devstuff-1.17.0}/dev-setup +0 -0
  37. {devstuff-1.16.0 → devstuff-1.17.0}/docs/specs/agent/development-plan.md +0 -0
  38. {devstuff-1.16.0 → devstuff-1.17.0}/docs/specs/agent/specifications.md +0 -0
  39. {devstuff-1.16.0 → devstuff-1.17.0}/docs/specs/agent/stack-decisions.md +0 -0
  40. {devstuff-1.16.0 → devstuff-1.17.0}/install.sh +0 -0
  41. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/__init__.py +0 -0
  42. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/__main__.py +0 -0
  43. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/__init__.py +0 -0
  44. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/approval.py +0 -0
  45. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/bridges.py +0 -0
  46. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/catalog.py +0 -0
  47. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/completion.py +0 -0
  48. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/config.py +0 -0
  49. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/loop.py +0 -0
  50. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/ollama.py +0 -0
  51. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/preflight.py +0 -0
  52. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/primitives.py +0 -0
  53. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/registry.py +0 -0
  54. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/sandbox.py +0 -0
  55. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/session.py +0 -0
  56. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/transcript.py +0 -0
  57. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent/wizard.py +0 -0
  58. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent_tools.schema.json +0 -0
  59. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/agent_tools.yaml +0 -0
  60. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/base.py +0 -0
  61. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/catalog.py +0 -0
  62. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/__init__.py +0 -0
  63. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/add_cmd.py +0 -0
  64. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/agent_cmd.py +0 -0
  65. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/catalog_cmd.py +0 -0
  66. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/delete_cmd.py +0 -0
  67. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/docs_cmd.py +0 -0
  68. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/functions_cmd.py +0 -0
  69. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/list_cmd.py +0 -0
  70. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/remove_cmd.py +0 -0
  71. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/run_cmd.py +0 -0
  72. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/skills_cmd.py +0 -0
  73. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/commands/update_cmd.py +0 -0
  74. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/function_runner.py +0 -0
  75. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/functions.schema.json +0 -0
  76. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/functions.yaml +0 -0
  77. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/functions_catalog.py +0 -0
  78. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/functions_registry.py +0 -0
  79. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/generic.py +0 -0
  80. {devstuff-1.16.0 → devstuff-1.17.0}/src/dev_setup/registry.py +0 -0
  81. {devstuff-1.16.0 → devstuff-1.17.0}/tests/__init__.py +0 -0
  82. {devstuff-1.16.0 → devstuff-1.17.0}/tests/integration/__init__.py +0 -0
  83. {devstuff-1.16.0 → devstuff-1.17.0}/tests/integration/conftest.py +0 -0
  84. {devstuff-1.16.0 → devstuff-1.17.0}/tests/integration/test_agent_smoke.py +0 -0
  85. {devstuff-1.16.0 → devstuff-1.17.0}/tests/integration/test_agent_tui.py +0 -0
  86. {devstuff-1.16.0 → devstuff-1.17.0}/tests/integration/test_tools.py +0 -0
  87. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent.py +0 -0
  88. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent_completion.py +0 -0
  89. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent_loop.py +0 -0
  90. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent_sandbox.py +0 -0
  91. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent_tools.py +0 -0
  92. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_agent_wizard.py +0 -0
  93. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_catalog.py +0 -0
  94. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_functions.py +0 -0
  95. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_generic.py +0 -0
  96. {devstuff-1.16.0 → devstuff-1.17.0}/tests/test_registry.py +0 -0
@@ -61,6 +61,7 @@ jobs:
61
61
  - java
62
62
  - lazygit
63
63
  - mkcert
64
+ - nerd-font
64
65
  - nvm
65
66
  - php
66
67
  - pi # installs nvm first via its declared `requires`
@@ -1,3 +1,10 @@
1
+ ## v1.17.0 (2026-07-31)
2
+
3
+ ### Feat
4
+
5
+ - starship config font validation and install
6
+ - **configure**: add a starship prompt setup wizard with live preview
7
+
1
8
  ## v1.16.0 (2026-07-24)
2
9
 
3
10
  ### Feat
@@ -21,8 +21,10 @@ uv run pytest tests/test_catalog.py::test_user_catalog_overrides_bundled_tool_in
21
21
  ```
22
22
 
23
23
  `pip` is not available in this dev environment — `uv pip install -e .` / `uv run` are the way in.
24
- Interactive commands (`install`, `add`, `remove`, `delete`) open questionary/click prompts and
25
- hang in non-TTY shells; don't try to drive them from a piped/non-interactive shell.
24
+ Interactive commands (`install`, `add`, `remove`, `delete`, `configure`) open questionary/click
25
+ prompts and hang in non-TTY shells; don't try to drive them from a piped/non-interactive shell.
26
+ To exercise one end-to-end anyway, fork a pty (`pty.fork()`) and write keystrokes to the fd —
27
+ that is how the starship wizard's full flow was verified.
26
28
 
27
29
  ### Integration tests (real installs, Docker-isolated)
28
30
 
@@ -60,6 +62,7 @@ src/dev_setup/
60
62
  ├── generic.py # GenericTool — the ONE engine that implements every install type
61
63
  ├── tools.yaml # Bundled built-in catalog (core/tools/languages categories)
62
64
  ├── ui.py # Rich console + questionary wrappers (spinners, prompts, styled output)
65
+ ├── configure/ # Per-tool setup wizards (see "Configurators" below)
63
66
  └── commands/ # One Click command per file: list, install, remove, add, delete, docs, catalog
64
67
  ```
65
68
 
@@ -167,6 +170,83 @@ non-interactive `bash <tmpfile>`, so `~/.bashrc`/nvm's shell init never runs on
167
170
  Not yet built: an `add` wizard and `catalog import`/`export` for functions, analogous to the
168
171
  ones tools already have.
169
172
 
173
+ ## Configurators (`configure/`) — tool-specific wizards, deliberately *not* catalog-driven
174
+
175
+ `src/dev_setup/configure/` holds per-tool setup wizards (`devstuff configure <tool>`), registered
176
+ in a `CONFIGURATORS` dict keyed by catalog tool key. `starship` is the first one:
177
+ `configure/starship/{model,render,preview,wizard}.py`.
178
+
179
+ **Why this one breaks the YAML-catalog rule.** Installation generalises into ~7 mechanisms, which
180
+ is what makes `GenericTool` possible. Configuration does not: starship's config is a TOML file of
181
+ format strings, palette tables and powerline transition glyphs; another tool's would be shell
182
+ exports or `git config` calls. Expressing this wizard in YAML would need conditional questions, an
183
+ ordered section model with colour roles, and a template language — a programming language spelled
184
+ in YAML. So configurators are Python, dispatched from a dict, the same shape as `_INSTALLERS` in
185
+ `generic.py`. Full reasoning in `docs/specs/starship-config/stack-decisions.md` (SD-1).
186
+
187
+ A configurator that finds a missing *prerequisite* should offer to install it through
188
+ `install_cmd.install_by_key(key)` rather than telling the user to run a second command — that is
189
+ what the starship wizard does for `nerd-font`.
190
+
191
+ **Adding a configurator** — a new module plus one dict entry. The module must expose
192
+ `run(*, target: Path | None = None)` (returns `None` if the user cancelled; writes nothing until
193
+ they confirm) and `config_path() -> Path`. Everything generic — the picker, install-state check,
194
+ `--list`/`--path`/`--output`, and the post-install offer in `install_cmd.py` — reads the registry,
195
+ so none of it needs touching. There is deliberately **no `configure:` field in `tools.yaml`**: user
196
+ catalogs are strictly validated, so a catalog naming a nonexistent configurator would add a
197
+ load-time failure mode for nothing.
198
+
199
+ **Within the starship configurator, `model.py` is the data and everything else reads it.**
200
+ `SECTIONS` (ordered — declaration order *is* prompt order), `PALETTES` and `PRESETS` drive both the
201
+ TOML emitter and the offline preview, which is what stops those two from drifting. Adding a section
202
+ or palette is one entry; no other file changes.
203
+
204
+ **Things learned from the real binary — don't "simplify" these away:**
205
+ - `symbol` is emitted for every section whose body contains `$symbol`, *including as an empty
206
+ string*. Omit it and starship's built-in Nerd Font glyph leaks into the `plain` preset. The
207
+ converse is unit-tested: a section carrying an icon its body can't render is a bug (that's why
208
+ `directory`, which has no `symbol` key at all, has no icon).
209
+ - Not every module spells the colour key `style` — hence `Section.style_key` (`username` wants
210
+ `style_user`). `battery` is deliberately absent: it accepts neither `style` nor `symbol` (both
211
+ live in its `[[battery.display]]` array), so it would cost three quirk fields for one section.
212
+ - `kubernetes`, `time`, `azure`, `status` and `shlvl` ship *disabled*; listing them in `format`
213
+ isn't enough, they need `disabled = false`. `os` and `git_metrics` are absent for the same
214
+ reason `battery` is: `os` takes its symbol from an `[os.symbols]` distro table, `git_metrics`
215
+ has `added_style`/`deleted_style` instead of `style`.
216
+ - Format strings are emitted as TOML **literal** strings (`'…'`) so starship's `$`, `[`, and `\[`
217
+ grammar needs no escaping. The multi-line top-level `format` is the one basic string, and it
218
+ contains no backslashes of its own beyond the line continuations.
219
+ - Powerline transitions are emitted per **run of consecutive sections sharing a palette role**, not
220
+ per section — otherwise two adjacent language segments draw an arrow between two identical
221
+ backgrounds. The three powerline presets differ *only* in the four glyphs in their `Powerline`
222
+ record (`POWERLINES` in `model.py`); nothing in the emitter knows which variant it is drawing.
223
+ - Any literal text the emitter writes into a format string is escaped with `_escape_format()`.
224
+ This is not theoretical: `success_symbol = '[$](bold fg:ok)'` made starship read `$` as the start
225
+ of a variable name, so the `plain` preset shipped with *no prompt symbol at all* until it was
226
+ caught by sweeping every combination and asserting empty stderr.
227
+ - A custom module (`custom.compose`) is a normal `Section`, but its dotted key has to be written
228
+ `${custom.compose}` in `format` — hence `Section.ref`. It also runs a shell command on **every**
229
+ prompt, so it carries a `when` guard, and anything needing the network is disqualified.
230
+ - Symbols for new sections come from `starship print-config --default` — the module's own shipped
231
+ glyph — rather than being picked from a Nerd Font chart that can't be verified without the font.
232
+ - The sample project must never gain a `bun.lock`: starship's `nodejs` module lists it as a
233
+ *negative* detector, so it silently switches the Node section off in previews.
234
+ - The live preview runs `starship prompt` with `STARSHIP_SHELL=nu`, not `bash`: for bash, starship
235
+ wraps escapes in readline's `\[`/`\]` markers, which are invisible inside a `PS1` and print
236
+ literally anywhere else. It also overrides `PWD` (starship prefers it over the real cwd) and
237
+ makes a second `--right` call, since `right_format` is not part of the left prompt.
238
+ - Every preview failure path returns `None` and degrades to the offline renderer. A preview must
239
+ never be able to end the wizard.
240
+ - The Nerd Font gate (`configure/starship/fonts.py`) is allowed to answer "don't know": without
241
+ fontconfig there is nothing to enumerate, and `None` means *stay silent* rather than warn. It
242
+ also refuses to install over SSH — the glyphs are drawn by the client's terminal — and always
243
+ says that installing a font does not repoint the terminal at it. The install itself goes through
244
+ the ordinary catalog path (`nerd-font` in `tools.yaml` → `install_cmd.install_by_key`), never a
245
+ private download (SD-10).
246
+
247
+ Not yet built: configurators for anything other than starship, and round-tripping an existing
248
+ hand-edited config back into wizard state (the timestamped backup is the safety net instead).
249
+
170
250
  ## Specs (`docs/specs/`)
171
251
 
172
252
  Design documents live in `docs/specs/<feature>/` — `specifications.md` (numbered, testable
@@ -258,6 +338,12 @@ Not yet built: an `add` wizard for agent tools, and `catalog import`/`export` fo
258
338
  - **No new runtime dependencies for the agent.** The Ollama transport is stdlib `urllib` against
259
339
  `/api/chat`; the REPL uses `prompt_toolkit`, already vendored via questionary. devstuff is a
260
340
  globally installed CLI, so every dependency is a cost paid by users who never run `agent`.
341
+ - **Configuration is Python, installation is YAML.** The no-per-tool-code rule covers *install
342
+ mechanisms*, which generalise; per-tool config formats don't. `configure/` is a registry of
343
+ Python wizards on purpose — see the section above before trying to fold it into `tools.yaml`.
344
+ - **A config wizard previews with the real binary, not an approximation.** `starship prompt`
345
+ against a temp config in a throwaway project is the source of truth; the offline renderer is a
346
+ labelled fallback for when the tool isn't installed (and is what unit tests exercise).
261
347
  - **Functions get a parallel catalog/registry instead of extending `GenericTool`.** The
262
348
  schemas diverge enough (no `requires` inference, a `params` list, no install/remove
263
349
  lifecycle) that folding them into the tool catalog would be lossy; some duplication with
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devstuff
3
- Version: 1.16.0
3
+ Version: 1.17.0
4
4
  Summary: Development environment setup CLI for Linux
5
5
  Project-URL: Repository, https://github.com/thesawdawg/dev-setup-py
6
6
  Author-email: Sawyer <sawyerksu@gmail.com>
@@ -188,6 +188,105 @@ for confirmation before doing this.
188
188
 
189
189
  ---
190
190
 
191
+ ### `configure`
192
+
193
+ Set up an installed tool through a guided wizard, previewing the result before anything is
194
+ written. Installing a tool that has a wizard also offers to run it there and then.
195
+
196
+ ```bash
197
+ devstuff configure # pick from the tools that have a wizard
198
+ devstuff configure starship # run starship's prompt wizard
199
+ devstuff configure --list # which tools have a wizard
200
+ devstuff configure starship --path # print the config file path
201
+ devstuff configure starship --output /tmp/try.toml # write elsewhere, leave the live config alone
202
+ ```
203
+
204
+ | Tool | What it configures |
205
+ |------|-------------------|
206
+ | `starship` | Prompt style, colour palette, which sections appear, and layout |
207
+
208
+ #### Starship prompt wizard
209
+
210
+ Four questions, with the prompt re-rendered after each one:
211
+
212
+ | Step | Options |
213
+ |------|---------|
214
+ | **Style** | `Plain text` · `Bracketed segments` (both ASCII, any font) · `Icons` · `Icons, bracketed` (Nerd Font glyphs) · `Powerline`, `Powerline, rounded`, `Powerline, slanted` (solid colour bars) |
215
+ | **Palette** | `Terminal colours` (inherits your terminal theme) · Catppuccin Mocha · Nord · Gruvbox Dark · Tokyo Night · Dracula · Rosé Pine · Catppuccin Latte (light) |
216
+ | **Sections** | Grouped checkbox over ~35 modules — see the table below |
217
+ | **Layout** | Single line · Two lines · Two lines with shell info right-aligned (needs zsh/fish/nushell — bash has no right prompt) |
218
+
219
+ | Group | Sections |
220
+ |-------|----------|
221
+ | **Context** | username, hostname (over SSH), container (Docker/toolbox/distrobox) |
222
+ | **Location** | current directory |
223
+ | **Git** | branch, commit hash (when detached), status (dirty/ahead/behind), operation in progress |
224
+ | **Languages** | Node.js, Deno, Bun, Python, Rust, Go, Java, PHP, Ruby, Elixir, .NET, package version |
225
+ | **Infrastructure** | Docker context, **Docker Compose project**, Kubernetes context, AWS profile, Google Cloud project, Azure subscription, Terraform workspace, Nix shell |
226
+ | **Shell** | command duration, exit code of the last command, background jobs, nested shell depth, clock |
227
+
228
+ The review menu adds two content toggles: the blank line between prompts, and whether language
229
+ sections show **version numbers** or just the runtime's symbol.
230
+
231
+ If you pick a style that needs a Nerd Font and there isn't one on the machine, the wizard says
232
+ so in the style list itself (`Needs a Nerd Font — none installed here.`) and offers to install
233
+ `nerd-font` right there — once per run, whether you say yes or no. Two cases it handles rather
234
+ than pretending otherwise: over SSH it points you at [nerdfonts.com](https://www.nerdfonts.com/)
235
+ instead, because the glyphs are drawn by the terminal on *your* machine, not the one you're
236
+ configuring; and after installing it reminds you to actually select the font in your terminal's
237
+ settings, which is a preference no shell can change for you. Without `fontconfig` there is no way
238
+ to enumerate fonts, so the wizard says nothing rather than guessing.
239
+
240
+ The Compose section is a starship [custom module](https://starship.rs/config/#custom-commands)
241
+ rather than a built-in one — it reports the project name `docker compose` in that directory would
242
+ actually use (`$COMPOSE_PROJECT_NAME`, else a top-level `name:` in the compose file, else the
243
+ lowercased directory name), and it only runs at all when there is a compose file next to you.
244
+
245
+ The preview is the real thing: the candidate config is written to a temp file and rendered by
246
+ your installed `starship` binary inside a throwaway sample project (a git repo with language
247
+ marker files), so what you see is the bytes your prompt will produce. Without starship installed
248
+ the wizard still works and falls back to a labelled approximation.
249
+
250
+ A review menu then lets you revisit any answer, dump the generated TOML, or save. On save:
251
+
252
+ - Written to `$STARSHIP_CONFIG`, or `~/.config/starship.toml`.
253
+ - Any existing file is copied to `starship.toml.bak.<timestamp>` first — and if it wasn't written
254
+ by this wizard, you're asked before it's replaced.
255
+ - If `~/.bashrc` has no starship hook, you're offered one (using the same marker
256
+ `devstuff install starship` uses, so `devstuff remove starship` still cleans up).
257
+
258
+ The output is meant to be hand-edited afterwards: it's commented, ordered, and colours are
259
+ referenced through nine semantic palette roles (`dir`, `git`, `lang`, `infra`, `shell`, `ok`,
260
+ `err`, `muted`, `bar_text`), so retheming everything is nine edits in one table.
261
+
262
+ ```toml
263
+ # Starship prompt configuration
264
+ # Generated by `devstuff configure starship` — edit freely, or re-run the wizard.
265
+ # Style: Powerline · Palette: Nord · Layout: Two lines
266
+ # Module reference: https://starship.rs/config/
267
+ "$schema" = 'https://starship.rs/config-schema.json'
268
+
269
+ add_newline = true
270
+ palette = 'nord'
271
+
272
+ format = """
273
+ [](fg:dir)\
274
+ $directory\
275
+ [](fg:dir bg:git)\
276
+ $git_branch\
277
+ [](fg:git)\
278
+ $line_break\
279
+ $character"""
280
+
281
+ # Semantic colour roles — swap these to retheme every section at once.
282
+ [palettes.nord]
283
+ dir = '#81a1c1'
284
+ git = '#a3be8c'
285
+ # …
286
+ ```
287
+
288
+ ---
289
+
191
290
  ### `add`
192
291
 
193
292
  Guided wizard to register a new custom package. Supports six install types:
@@ -590,12 +689,13 @@ Optional utilities you may want on some machines.
590
689
  | `gh` | GitHub CLI | GitHub's official CLI | `gh --help` |
591
690
  | `htop` | htop | Interactive process and resource monitor | `man htop` |
592
691
  | `mkcert` | mkcert | Zero-config local HTTPS certificates | `mkcert --help` |
692
+ | `nerd-font` | JetBrainsMono Nerd Font | Patched font supplying the icons Starship and other CLI tools draw | `fc-list \| grep -i "nerd font"` |
593
693
  | `ollama` | Ollama | Run large language models locally | `ollama --help` |
594
694
  | `php` | PHP 8.4 | PHP 8.4 + common extensions via ondrej/php PPA | `php --help` |
595
695
  | `pi` | Pi Coding Agent | AI coding agent npm package | `pi --help` |
596
696
  | `pre-commit` | pre-commit | Git hook manager for automated code quality checks | `pre-commit --help` |
597
697
  | `saml2aws` | saml2aws | SAML → AWS STS credentials CLI (Versent) | `saml2aws --help` |
598
- | `starship` | Starship | Fast, cross-shell customizable prompt | `starship --help` |
698
+ | `starship` | Starship | Fast, cross-shell customizable prompt (`devstuff configure starship`) | `starship --help` |
599
699
  | `yq` | yq | Portable command-line YAML/JSON/XML processor | `yq --help` |
600
700
 
601
701
  ### Languages
@@ -740,6 +840,13 @@ dev-setup-py/
740
840
  ├── functions_registry.py # Loads bundled + user YAML into the live function registry
741
841
  ├── function_runner.py # Param resolution + script/eval/bashrc rendering & execution
742
842
  ├── functions.yaml # Bundled built-in function catalog
843
+ ├── configure/ # Per-tool setup wizards (see "configure" above)
844
+ │ ├── __init__.py # CONFIGURATORS registry: tool key -> wizard module
845
+ │ └── starship/
846
+ │ ├── model.py # presets, palettes, sections, StarshipConfig
847
+ │ ├── render.py # starship.toml emitter + offline preview renderer
848
+ │ ├── preview.py # live preview via `starship prompt` in a sample project
849
+ │ └── wizard.py # the interactive flow, backup + save
743
850
  ├── agent_tools.yaml # Bundled agent tool catalog
744
851
  ├── agent/ # Local-model agent (see "Agent" above)
745
852
  │ ├── config.py # agent.yaml load/validate/save
@@ -161,6 +161,105 @@ for confirmation before doing this.
161
161
 
162
162
  ---
163
163
 
164
+ ### `configure`
165
+
166
+ Set up an installed tool through a guided wizard, previewing the result before anything is
167
+ written. Installing a tool that has a wizard also offers to run it there and then.
168
+
169
+ ```bash
170
+ devstuff configure # pick from the tools that have a wizard
171
+ devstuff configure starship # run starship's prompt wizard
172
+ devstuff configure --list # which tools have a wizard
173
+ devstuff configure starship --path # print the config file path
174
+ devstuff configure starship --output /tmp/try.toml # write elsewhere, leave the live config alone
175
+ ```
176
+
177
+ | Tool | What it configures |
178
+ |------|-------------------|
179
+ | `starship` | Prompt style, colour palette, which sections appear, and layout |
180
+
181
+ #### Starship prompt wizard
182
+
183
+ Four questions, with the prompt re-rendered after each one:
184
+
185
+ | Step | Options |
186
+ |------|---------|
187
+ | **Style** | `Plain text` · `Bracketed segments` (both ASCII, any font) · `Icons` · `Icons, bracketed` (Nerd Font glyphs) · `Powerline`, `Powerline, rounded`, `Powerline, slanted` (solid colour bars) |
188
+ | **Palette** | `Terminal colours` (inherits your terminal theme) · Catppuccin Mocha · Nord · Gruvbox Dark · Tokyo Night · Dracula · Rosé Pine · Catppuccin Latte (light) |
189
+ | **Sections** | Grouped checkbox over ~35 modules — see the table below |
190
+ | **Layout** | Single line · Two lines · Two lines with shell info right-aligned (needs zsh/fish/nushell — bash has no right prompt) |
191
+
192
+ | Group | Sections |
193
+ |-------|----------|
194
+ | **Context** | username, hostname (over SSH), container (Docker/toolbox/distrobox) |
195
+ | **Location** | current directory |
196
+ | **Git** | branch, commit hash (when detached), status (dirty/ahead/behind), operation in progress |
197
+ | **Languages** | Node.js, Deno, Bun, Python, Rust, Go, Java, PHP, Ruby, Elixir, .NET, package version |
198
+ | **Infrastructure** | Docker context, **Docker Compose project**, Kubernetes context, AWS profile, Google Cloud project, Azure subscription, Terraform workspace, Nix shell |
199
+ | **Shell** | command duration, exit code of the last command, background jobs, nested shell depth, clock |
200
+
201
+ The review menu adds two content toggles: the blank line between prompts, and whether language
202
+ sections show **version numbers** or just the runtime's symbol.
203
+
204
+ If you pick a style that needs a Nerd Font and there isn't one on the machine, the wizard says
205
+ so in the style list itself (`Needs a Nerd Font — none installed here.`) and offers to install
206
+ `nerd-font` right there — once per run, whether you say yes or no. Two cases it handles rather
207
+ than pretending otherwise: over SSH it points you at [nerdfonts.com](https://www.nerdfonts.com/)
208
+ instead, because the glyphs are drawn by the terminal on *your* machine, not the one you're
209
+ configuring; and after installing it reminds you to actually select the font in your terminal's
210
+ settings, which is a preference no shell can change for you. Without `fontconfig` there is no way
211
+ to enumerate fonts, so the wizard says nothing rather than guessing.
212
+
213
+ The Compose section is a starship [custom module](https://starship.rs/config/#custom-commands)
214
+ rather than a built-in one — it reports the project name `docker compose` in that directory would
215
+ actually use (`$COMPOSE_PROJECT_NAME`, else a top-level `name:` in the compose file, else the
216
+ lowercased directory name), and it only runs at all when there is a compose file next to you.
217
+
218
+ The preview is the real thing: the candidate config is written to a temp file and rendered by
219
+ your installed `starship` binary inside a throwaway sample project (a git repo with language
220
+ marker files), so what you see is the bytes your prompt will produce. Without starship installed
221
+ the wizard still works and falls back to a labelled approximation.
222
+
223
+ A review menu then lets you revisit any answer, dump the generated TOML, or save. On save:
224
+
225
+ - Written to `$STARSHIP_CONFIG`, or `~/.config/starship.toml`.
226
+ - Any existing file is copied to `starship.toml.bak.<timestamp>` first — and if it wasn't written
227
+ by this wizard, you're asked before it's replaced.
228
+ - If `~/.bashrc` has no starship hook, you're offered one (using the same marker
229
+ `devstuff install starship` uses, so `devstuff remove starship` still cleans up).
230
+
231
+ The output is meant to be hand-edited afterwards: it's commented, ordered, and colours are
232
+ referenced through nine semantic palette roles (`dir`, `git`, `lang`, `infra`, `shell`, `ok`,
233
+ `err`, `muted`, `bar_text`), so retheming everything is nine edits in one table.
234
+
235
+ ```toml
236
+ # Starship prompt configuration
237
+ # Generated by `devstuff configure starship` — edit freely, or re-run the wizard.
238
+ # Style: Powerline · Palette: Nord · Layout: Two lines
239
+ # Module reference: https://starship.rs/config/
240
+ "$schema" = 'https://starship.rs/config-schema.json'
241
+
242
+ add_newline = true
243
+ palette = 'nord'
244
+
245
+ format = """
246
+ [](fg:dir)\
247
+ $directory\
248
+ [](fg:dir bg:git)\
249
+ $git_branch\
250
+ [](fg:git)\
251
+ $line_break\
252
+ $character"""
253
+
254
+ # Semantic colour roles — swap these to retheme every section at once.
255
+ [palettes.nord]
256
+ dir = '#81a1c1'
257
+ git = '#a3be8c'
258
+ # …
259
+ ```
260
+
261
+ ---
262
+
164
263
  ### `add`
165
264
 
166
265
  Guided wizard to register a new custom package. Supports six install types:
@@ -563,12 +662,13 @@ Optional utilities you may want on some machines.
563
662
  | `gh` | GitHub CLI | GitHub's official CLI | `gh --help` |
564
663
  | `htop` | htop | Interactive process and resource monitor | `man htop` |
565
664
  | `mkcert` | mkcert | Zero-config local HTTPS certificates | `mkcert --help` |
665
+ | `nerd-font` | JetBrainsMono Nerd Font | Patched font supplying the icons Starship and other CLI tools draw | `fc-list \| grep -i "nerd font"` |
566
666
  | `ollama` | Ollama | Run large language models locally | `ollama --help` |
567
667
  | `php` | PHP 8.4 | PHP 8.4 + common extensions via ondrej/php PPA | `php --help` |
568
668
  | `pi` | Pi Coding Agent | AI coding agent npm package | `pi --help` |
569
669
  | `pre-commit` | pre-commit | Git hook manager for automated code quality checks | `pre-commit --help` |
570
670
  | `saml2aws` | saml2aws | SAML → AWS STS credentials CLI (Versent) | `saml2aws --help` |
571
- | `starship` | Starship | Fast, cross-shell customizable prompt | `starship --help` |
671
+ | `starship` | Starship | Fast, cross-shell customizable prompt (`devstuff configure starship`) | `starship --help` |
572
672
  | `yq` | yq | Portable command-line YAML/JSON/XML processor | `yq --help` |
573
673
 
574
674
  ### Languages
@@ -713,6 +813,13 @@ dev-setup-py/
713
813
  ├── functions_registry.py # Loads bundled + user YAML into the live function registry
714
814
  ├── function_runner.py # Param resolution + script/eval/bashrc rendering & execution
715
815
  ├── functions.yaml # Bundled built-in function catalog
816
+ ├── configure/ # Per-tool setup wizards (see "configure" above)
817
+ │ ├── __init__.py # CONFIGURATORS registry: tool key -> wizard module
818
+ │ └── starship/
819
+ │ ├── model.py # presets, palettes, sections, StarshipConfig
820
+ │ ├── render.py # starship.toml emitter + offline preview renderer
821
+ │ ├── preview.py # live preview via `starship prompt` in a sample project
822
+ │ └── wizard.py # the interactive flow, backup + save
716
823
  ├── agent_tools.yaml # Bundled agent tool catalog
717
824
  ├── agent/ # Local-model agent (see "Agent" above)
718
825
  │ ├── config.py # agent.yaml load/validate/save
@@ -39,3 +39,4 @@ They are most useful when they capture:
39
39
  | Feature | Status |
40
40
  |---------|--------|
41
41
  | [`agent/`](agent/) — `devstuff agent`, the local-model agent | Complete (v1) |
42
+ | [`starship-config/`](starship-config/) — `devstuff configure starship`, the prompt wizard | Complete (v1) |
@@ -0,0 +1,74 @@
1
+ # Development Plan: `devstuff configure starship`
2
+
3
+ **Date:** 2026-07-30
4
+ **Status:** Milestones 1–6 complete
5
+
6
+ ---
7
+
8
+ ## Milestones
9
+
10
+ | # | Milestone | Deliverable | Done when |
11
+ |---|-----------|-------------|-----------|
12
+ | 1 | Data model | `configure/starship/model.py` — `Preset`, `Palette`, `Section`, `StarshipConfig`, the ordered `SECTIONS` tuple | Defaults produce a coherent config with no I/O |
13
+ | 2 | TOML emitter | `configure/starship/render.py` `to_toml()` | Output parses under `tomllib` for every preset × palette × layout combination, and `starship print-config` accepts it |
14
+ | 3 | Preview | `configure/starship/preview.py` (live, via `starship prompt` in a sample project) + `render.sample_markup()` (offline fallback) | Live path prints ANSI for a real config; offline path renders every selected section with starship absent |
15
+ | 4 | Wizard + command | `configure/starship/wizard.py`, `commands/configure_cmd.py`, registry in `configure/__init__.py`, CLI wiring | `devstuff configure starship` walks the steps, previews after each, saves with a backup |
16
+ | 5 | Integration + docs | post-install offer in `install_cmd.py`, help text, README, CLAUDE.md, this spec | `devstuff install starship` offers the wizard; docs describe how to add a configurator |
17
+ | 6 | Preset + section expansion (v2) | `POWERLINES` glyph sets, bracketed presets, three more palettes, `show_versions`, custom modules (`custom.compose`) and 10 further sections | Every preset × palette × layout × versions combination is accepted by the real binary with **no stderr output**, and the compose command resolves the same name Compose would |
18
+
19
+ ## Testing Strategy
20
+
21
+ `tests/test_configure_starship.py` — unit only, no starship required (NFR-5):
22
+
23
+ - **Emitter validity:** every `preset × palette × layout` combination parses under `tomllib`;
24
+ spot-check that `format`, `palette`, `add_newline` and the expected module tables exist.
25
+ - **Section selection:** unselected modules get no table and never appear in `format`; selected
26
+ ones appear in canonical order regardless of the order the checkbox returned them in.
27
+ - **Symbol handling:** `plain` preset emits `symbol = ''` for icon-bearing sections (regression
28
+ guard for starship's default glyph leaking through); `icons`/`powerline` emit the Nerd Font one.
29
+ - **Powerline runs:** consecutive same-role sections produce one bar run — the transition count
30
+ equals the number of role changes, not the number of sections.
31
+ - **Defaults on by default:** `disabled = false` is emitted for `kubernetes`/`time` when selected
32
+ (FR-16) and never for modules starship already enables.
33
+ - **Layout:** `two_line`/`two_line_right` place `$line_break` before `$character`; `single` does
34
+ not; `two_line_right` moves Shell-group sections into `right_format` and out of `format`.
35
+ - **Offline renderer:** `sample_markup()` mentions every selected section's sample value and is
36
+ valid Rich markup (rendered through a `Console` without raising).
37
+ - **Save path:** `save()` honours `STARSHIP_CONFIG`, backs an existing file up to a timestamped
38
+ name, and leaves the backup byte-identical to the original.
39
+ - **Wizard flow:** scripted `ui.*` prompts (the `FakePrompts` pattern from `test_agent_wizard.py`)
40
+ drive a full run and assert the written file reflects every choice; a cancel writes nothing.
41
+ - **Registry:** `configure.get()` / `keys()` resolve starship and reject unknown keys.
42
+
43
+ - **Presets as data:** every preset's `powerline` is one of the declared glyph sets; a preset with
44
+ no bars emits none of those glyphs anywhere in the file; each powerline preset's own four glyphs
45
+ appear in the expected counts (one cap, one transition per role change, one closing separator).
46
+ - **Brackets / versions:** bracketed presets wrap every body except the one that brackets itself;
47
+ hiding versions drops `$version` and the symbol's trailing space, and is recorded in the header.
48
+ - **Custom modules:** `${custom.compose}` is braced in `format`, the table is nested under
49
+ `custom`, and the shell script round-trips byte for byte through the multi-line literal string.
50
+ - **Prompt symbol escaping:** the plain presets emit `[\$]`, not `[$]` (FR-20).
51
+
52
+ Verification against the real binary (not in CI, which has no starship — run from the repo when
53
+ touching the emitter):
54
+
55
+ ```python
56
+ # for every preset × palette × layout × show_versions, with every section selected:
57
+ # starship print-config, starship prompt, starship prompt --right
58
+ # assert returncode == 0 and stderr == "" # a *warning* is a failure here
59
+ ```
60
+
61
+ That sweep is 1008 invocations and is how the `character`-module bug in §6a was found; a config
62
+ that merely parses is not enough, since starship degrades unknown keys and bad format strings to
63
+ a stderr warning and an empty render. Plus a visual check of the live preview per preset.
64
+
65
+ ## Risks
66
+
67
+ | Risk | Mitigation |
68
+ |------|------------|
69
+ | `starship prompt` flags differ across versions (`--terminal-width`, `--jobs`) | Try the full flag set, retry with a minimal one, then give up to the offline renderer. Never fatal (NFR-4). |
70
+ | Nerd Font glyphs wrong or absent in the user's terminal | Glyphs are defined as explicit `\uXXXX` escapes with the Nerd Font name in a comment, so they are reviewable; the `plain` preset is the escape hatch and the two Nerd Font presets say so at the point of choosing. |
71
+ | Preview subprocess hangs | 5 s timeout; timeout is treated as a failed render. |
72
+ | Overwriting a hand-tuned `starship.toml` | Timestamped backup before write, path reported; the wizard warns when replacing a file it did not generate (no devstuff header). |
73
+ | Sample project creation fails (no `git`) | Wrapped in try/except; git sections then simply do not render in the live preview, which is the same degradation as running outside a repo. |
74
+ | Emitter and offline renderer drifting apart | Both consume the same `SECTIONS`/`PALETTES` tables; neither hard-codes a module list. |