Status: implemented (features 1–6), owner: Tom Sapletta, created: 2026-05-22.
This is the design contract for the next round of koru wizard improvements.
Six features in scope, ordered by user-visible impact:
- Bilingual side-by-side labels (foundation — affects all other features)
- Per-option help dictionary
[?] --quickzero-prompt mode- Post-creation guidance (next-3-steps panel)
- Strategy templates marketplace (
--template,--strategies URL) --guibrowser front-end
The IDE-install-proposal feature is out of scope for this iteration (kept
in backlog as wizard-ide-install-spec.md).
- Backwards compatibility: existing
koru wizardcalls keep working with no flag changes. New flags are additive. strategies.jsonschema version stays 1; new fields are optional and default-tolerant (older trees still load).- No new required dependencies.
--guiadds an optional dep groupwizard-gui(FastAPI + uvicorn already used elsewhere in koru, so re-use). - The decision tree is the single source of truth. CLI, GUI, and
--quickall read the sameStrategyTree. - Localisation: every user-facing string must accept
{pl, en}dict form with the existing_pick_localizedfallback.
Remove the first friction point: today users may not know whether they want PL or EN, and the choice is sticky for the whole session. Show both languages in one label.
Before:
[1] Architektura projektu
After (when bilingual mode active):
[1] Architektura projektu · Project architecture
No schema change. The renderer concatenates pl + · + en when both
exist and --bilingual (or KORU_WIZARD_BILINGUAL=1) is set, or when
strategies.json declares "bilingual_default": true.
--bilingual/--no-bilingualflag, defaults to off for now.- Env override:
KORU_WIZARD_BILINGUAL=1. --language=pl,en(comma-separated) becomes the explicit form; CLI sniffs the locale (LANG) to auto-enable when bothpl_PLand English texts are present.
- New
tree._pick_localized_multi(value, langs: list[str]) -> strthat joins with·and dedupes when both translations are identical. load_tree(..., language=...)acceptsstr | list[str].cli.pyparses comma-separated--language.
test_tree_pick_localized_multi_joins_with_separatortest_tree_load_supports_language_listtest_cli_bilingual_flag_renders_both_languages(uses ScriptedPrompter + capturesseen_prompts).
- Existing single-language tests still pass.
koru wizard --bilingual --detect-onlyis a no-op for IDE list (only affects tree labels).- Token
·(U+00B7) is configurable viaKORU_WIZARD_BILINGUAL_SEPARATOR.
~80 lines, 1 commit.
Non-tech users see "CQRS+ES" or "Hexagonal architecture" and bounce. Add a 1-2 sentence explanation per option, available on-demand without leaving the prompt.
Co Cię najbardziej interesuje?
[1] Architektura projektu [?]
[2] Frontend / UI / UX [?]
…
[?] = wyjaśnij wszystkie opcje
> ?2
Frontend / UI / UX
──────────────────
Wygląd i interakcje aplikacji widoczne dla użytkownika końcowego.
Obejmuje design system, accessibility, performance renderingu.
> 2 ← user then picks
strategies.json gains optional help per option/ticket:
{"id": "cqrs_es", "label": {"pl": "..."}, "ticket": "tpl_cqrs_es",
"help": {"pl": "...", "en": "..."}}Backwards compatible: missing help shows (brak opisu / no description).
- User types
?Nto see help for option N, prompt re-displays. ?alone lists help for all options.?qquits help and returns to prompt.
TreeOptiongainshelp: str(already localised at load time).StdinPrompter.ask_choiceparses?…prefix before regular answers.- GUI mode (feature 6) renders help as expandable info-icon next to each option.
test_tree_option_help_loaded_when_presenttest_stdin_prompter_question_mark_shows_help(uses io.StringIO with scripted answers?1\n?\n1\n).test_walk_help_does_not_advance_path.
- Backwards-compatible: existing strategies.json without
helploads fine. - Pressing
?never ends the wizard. - Default
strategies.jsonships withhelpfor ALL 26 ticket templates.
~150 lines, 1 commit. Includes content writing for help strings.
Single-keystroke first-time success. User installs koru, runs
koru wizard --quick, gets a sensible first ticket without ANY prompts.
quality → cc_refactor — every project benefits, requires no domain
knowledge, produces actionable backlog.
Override in strategies.json:
"quick_default": {
"path": ["quality", "cc_refactor"],
"description_pl": "Domyślny pierwszy ticket: redukcja CC w 5 hotspotach.",
"description_en": "Default first ticket: reduce CC in 5 hotspots."
}$ koru wizard --quick
✓ koru wizard --quick
Project : /home/tom/myproj (auto-picked: shell cwd)
Strategy : quality → cc_refactor (default for --quick)
Ticket : PLF-001 — Quality: redukcja CC w hotspotach
Next : run `koru` to see the LLM brief.
(re-run with `koru wizard` to pick a different strategy interactively)
--quickimplies: skip IDE prompt, skip project prompt (use cwd or--project), followquick_default.path, create ticket, print summary.--quick --strategy <path.dotted>overrides default (e.g.--quick --strategy architecture.cqrs_es).
cli.run_wizard(quick=True, strategy_path=None)follows the JSON-encoded path without invoking the prompter.- Walks tree node-by-node; raises clear error when path is invalid.
test_quick_mode_creates_ticket_without_promptstest_quick_mode_with_custom_strategy_pathtest_quick_mode_falls_back_when_path_missing.
- Zero stdin reads in
--quickmode (verifiable by feedingStringIO("")). - Exits 0 even when project has no
.planfileyet (creates it).
~120 lines, 1 commit.
After ticket creation, user knows EXACTLY which command to run next. Today's output ends at "Created PLF-001"; user might think they're done.
✓ koru wizard finished
IDE : Cursor
Project : /home/tom/myproj
Strategy : quality → cc_refactor
Ticket : PLF-001 — Quality: redukcja CC w hotspotach
Co teraz / What's next:
1. `koru` — wyświetli brief LLM do wklejenia w Cursor/Cascade
2. Otwórz Cursor i napisz: "Pracuj nad ticketem PLF-001"
3. `koru --queue --loop` — agent sam przepracuje kolejkę
Edytuj strategie w: /home/tom/.config/koru/strategies.json
(skopiuj `koru wizard --print-strategies > my.json` żeby zacząć)
strategies.json.tickets[*].next_steps: localised list, optional. Falls
back to a global default in strategies.json.defaults.next_steps.
TicketTemplate.next_steps: tuple[str, ...]_emit_humanrenders the panel.- GUI mode shows it as a card.
test_ticket_template_loads_next_stepstest_post_creation_panel_contains_stepstest_post_creation_falls_back_to_default_steps.
- Steps must be commands or single sentences, not paragraphs.
- If
--no-create, panel still prints "would create" preview.
~100 lines + content writing, 1 commit.
Curated starting points for common project shapes (web app, ML research, CLI tool, library). Power users + non-tech users both benefit.
$ koru wizard --list-templates
Built-in templates:
default — General-purpose decision tree (7 root branches)
web-app — Frontend + Backend + DevOps for SPAs
ml-research — Experiments, reproducibility, data quality
cli-tool — Distribution, packaging, semver, docs
library — API stability, type stubs, examples, CI
$ koru wizard --template web-app
[wizard walks the web-app tree]
$ koru wizard --strategies https://raw.githubusercontent.com/.../tree.json
[fetches over HTTPS, caches in ~/.cache/koru/wizard/]
src/koru/wizard/templates/default.json— symlink/copy of current strategies.jsonsrc/koru/wizard/templates/web-app.jsonsrc/koru/wizard/templates/ml-research.jsonsrc/koru/wizard/templates/cli-tool.jsonsrc/koru/wizard/templates/library.jsonsrc/koru/wizard/templates/registry.json— mapsname -> path + description
--template <name>— load from packaged templates.--list-templates— print registry.--strategies <path|url>— already supported, extend to accept URLs (https only) with 1MB max size, 5s timeout, SHA-256 cache key.
templates.pymodule:list_templates() -> list[TemplateInfo],resolve_template(name_or_url) -> Path.- URL fetch behind small abstraction so tests can stub.
test_list_templates_returns_packaged_settest_resolve_template_by_nametest_resolve_template_rejects_non_https_urltest_resolve_template_caches_remote(mock requests).
--templateand--strategiesare mutually exclusive (parse-time error).- HTTPS fetch is opt-in (allow-list to known hosts? or always require
--allow-remoteflag? decision: require--allow-remotefor security).
~250 lines + 4 template JSONs, 1 commit. Content authoring is the biggest chunk.
Zero-terminal experience. User runs koru wizard --gui, browser opens at
http://localhost:8765/wizard, they click through, ticket gets created.
- CLI starts FastAPI + uvicorn on 127.0.0.1 free port.
- Opens
webbrowser.open()automatically (skip with--no-browser). - Single-page app:
- Step 1: IDE picker (cards with logos, "running" badge).
- Step 2: Project picker (cards with path + source label).
- Step 3: Strategy walker (one node per page, large buttons,
[?]becomes a tooltip / expandable card). - Step 4: Confirmation + post-creation panel (feature 4).
- After ticket creation, server shows "OK" and shuts down on next request
(
/doneendpoint).
- FastAPI (already in
optional-dependencies.api). - Jinja2 templates (single file).
- No JS framework — vanilla JS for
fetchcalls + form switching. - Tailwind via CDN (offline fallback: bundled minimal CSS).
src/koru/wizard/gui/app.py— FastAPI app factory.src/koru/wizard/gui/templates/wizard.html— single template.src/koru/wizard/gui/static/wizard.js+wizard.css.
- Server keeps an in-memory
WizardSessionkeyed by random UUID stored in a cookie. Single-user, localhost-only, no auth needed. - Session expires after 30 min idle.
--gui— start GUI server, open browser.--gui --port 0— random port (default).--gui --no-browser— print URL only.--gui --bind 127.0.0.1— non-configurable for security (localhost only).
- Bind 127.0.0.1 only. Refuse 0.0.0.0 even with explicit override.
- CSRF token on all POST forms.
- No file upload, no shell exec endpoints, no escape from project path.
test_gui_app_factory_serves_step1test_gui_app_walks_tree_via_posttest_gui_app_creates_ticket_at_final_step- Use
httpx.AsyncClientagainst the FastAPI app — no real network.
koru wizard --gui --no-browserprints URL within 2s on cold start.- Closing the browser tab doesn't leave a zombie server (auto-shutdown on
/doneor 30-min idle timeout). - Works offline (no CDN required for core flow).
~600 lines (FastAPI app + HTML + CSS + JS), 1 commit. The biggest item in the spec — likely best split into a follow-up branch after features 1-5 ship.
1. bilingual ─┐
├──► 4. post-create panel (uses bilingual)
2. help [?] ─┤ │
│ ▼
├──► 6. GUI (consumes 1, 2, 3, 4)
3. --quick ──┤ ▲
│ │
5. templates ┘────────┘
Recommended order:
- Bilingual (foundation)
- Help dictionary (~150 lines, immediately useful)
--quick(~120 lines, zero-friction onboarding)- Post-creation panel (~100 lines, completes happy path)
- Templates (~250 lines, power feature)
- GUI (~600 lines, separate branch —
feature/wizard-gui)
Each step is independently shippable and tested.
- Do we want analytics (anonymous, local-only) of which paths users pick? Useful for tuning the default tree, but adds privacy surface. Recommendation: defer to a later spec.
- Should
koru autoautomatically launchkoru wizard --guion first-run? Recommendation: only if--guiships and stdin is TTY +DISPLAYis set; otherwise keep current text hint. --templateover HTTPS — should we sign templates? Recommendation: v1 =--allow-remoteflag + 1MB cap + display SHA-256. v2 = signature if community demand appears.
- Code + tests in same commit; coverage ≥ 90% for new modules.
ruff check src/koru/wizard tests/test_wizard_*.pypasses.koru wizard --detect-only --format jsonschema stays backwards-compat.- README.md "Quick start" section updated.
- CHANGELOG.md entry.
- No new required dependencies (optional groups OK).