diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index efd6a52..0798d7f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -4,14 +4,14 @@ "name": "Kennet Kusk" }, "metadata": { - "description": "Pure-Rust Clean Architecture workflow. Bootstrap axum+sqlx+dioxus+tokio, plan with PRDs, implement with parallel agents, audit on CI.", - "version": "3.9.0" + "description": "Pure-Rust Clean Architecture workflow. Six commands (start, fix, plan, ship, review, install-ci) for axum + sqlx + Dioxus 0.7+ + tokio. Always-latest deps, CI audit gate, anti-slop enforced.", + "version": "4.0.0" }, "plugins": [ { "name": "code", "source": "./code-et-implementer", - "description": "Bootstrap pure-Rust Clean Architecture projects. Plan with LSP, implement with parallel agents, enforce anti-slop on CI." + "description": "Pure-Rust Clean Architecture workflow. /code:start scaffolds axum+sqlx+Dioxus, /code:plan synthesises PRD + tasks, /code:ship runs parallel agents and audits, /code:review is the pre-merge gate." } ] } diff --git a/.claude/code-et-tasks.json b/.claude/code-et-tasks.json new file mode 100644 index 0000000..4dbf4aa --- /dev/null +++ b/.claude/code-et-tasks.json @@ -0,0 +1,171 @@ +{ + "task_list_id": "code-et-tasks", + "prd": "plans/2026-05-06-v3.10-audit-command.md", + "branch": "feature/v3.10-audit-command", + "created": "2026-05-06", + "tasks": [ + { + "id": "1", + "title": "Correct PRD stage list to match shipped CI yaml", + "status": "pending", + "blocked_by": [], + "metadata": { + "verification": "grep -E 'udeps|clippy.toml thresholds|clippy -W dead_code' plans/2026-05-06-v3.10-audit-command.md && exit 1 || exit 0", + "files": [ + "plans/2026-05-06-v3.10-audit-command.md:1", + "code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml:45" + ], + "expected_outcome": "PRD's stage references describe the actual yaml stages (fmt, clippy -D warnings, layer-deps, machete, audit, deny, nextest). US-9 (nightly udeps fallback) reframed as a generic missing-tool fallback for cargo-machete/audit/deny. The grep above returns no hits.", + "rationale": "PRD was drafted from CHANGELOG preview prose, not from the merged yaml. The yaml has no udeps, no standalone clippy.toml threshold step, and no clippy -W dead_code — but does have fmt and nextest tests. Every downstream task anchors to this stage list, so the spec must match reality before T2 builds the parser. Replaces the 4-stage prose in the PRD's Solution paragraph and US-9's nightly-udeps wording.", + "user_story": "chore:spec drift between PRD and shipped v3.9.0 yaml must be fixed before implementation", + "layer": "chore" + } + }, + { + "id": "2", + "title": "Audit foundation: command, yaml-parsed runner, report writer, Cargo.toml guard, portability", + "status": "pending", + "blocked_by": ["1"], + "metadata": { + "verification": "bash code-et-implementer/scripts/audit.sh code-et-implementer/templates/rust/dioxus-fullstack && test -f code-et-implementer/templates/rust/dioxus-fullstack/.claude/audit-*.md && bash code-et-implementer/tests/audit-skips-non-rust.sh && bash code-et-implementer/tests/audit-stage-list-matches-yaml.sh && ! grep -riE '(visma|emerging-tech)' code-et-implementer/scripts/audit*.sh code-et-implementer/commands/audit.md", + "files": [ + "code-et-implementer/commands/audit.md:1", + "code-et-implementer/scripts/audit.sh:1", + "code-et-implementer/scripts/audit-stages.sh:1", + "code-et-implementer/scripts/audit-report.sh:1", + "code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml:30", + "code-et-implementer/templates/rust/dioxus-fullstack/Cargo.toml:1", + "code-et-implementer/tests/audit-skips-non-rust.sh:1", + "code-et-implementer/tests/audit-stage-list-matches-yaml.sh:1" + ], + "expected_outcome": "/code:audit command exists. Running it in a Rust workspace executes every step from code-et-audit.yml in declared order, exits non-zero on any stage failure, writes .claude/audit-.md with severity-grouped findings and path:line citations, and exits 0 with a 'not a Rust workspace' message when no Cargo.toml is found at any depth. No org-specific tokens (visma|emerging-tech) anywhere in the new code. This task ships ONE bundled slice that satisfies five PRD stories: US-1 (full pipeline), US-6 (Cargo.toml guard), US-7 (report format), US-8 (yaml as source of truth), US-11 (portability). On completion, flip ALL five boxes: `- [ ] US-1`, `- [ ] US-6`, `- [ ] US-7`, `- [ ] US-8`, `- [ ] US-11` to ticked in the PRD — the metadata.user_story field carries only US-1 because the schema is single-valued, but the slice covers all five. Stage all five edits in the same commit as the code.", + "rationale": "The yaml is the single source of truth (PRD design choice). Parsing it at runtime — instead of hardcoding the stage list — is what makes local and CI un-driftable. This task ships the minimum viable slice: command definition + parser + runner + report writer + workspace guard + portability discipline, end-to-end testable against the bundled v3.9.0 fixture. Splitting these five concerns into separate tasks would create artificial seams (a parser with no runner, a runner with no report) — they only make sense shipped together. Everything else (--fast, --stage, --review, hooks) is a flag or wiring on top of this foundation. Code-et itself is not Rust — `layer: chore` since the audit lives in plugin tooling, not a Rust workspace.", + "user_story": "US-1", + "layer": "chore" + } + }, + { + "id": "3", + "title": "Add --fast and --stage flags to audit runner", + "status": "pending", + "blocked_by": ["2"], + "metadata": { + "verification": "bash code-et-implementer/scripts/audit.sh --fast code-et-implementer/templates/rust/dioxus-fullstack && bash code-et-implementer/scripts/audit.sh --stage 3 code-et-implementer/templates/rust/dioxus-fullstack && bash code-et-implementer/scripts/audit.sh --stage 99 code-et-implementer/templates/rust/dioxus-fullstack; test $? -ne 0", + "files": [ + "code-et-implementer/scripts/audit.sh:1", + "code-et-implementer/commands/audit.md:1" + ], + "expected_outcome": "--fast runs only stages 1-2 from the parsed yaml (fmt + clippy). --stage for valid n runs only that stage. Invalid n exits non-zero with a usage message listing valid stage numbers and names.", + "rationale": "These flags are the contract that lets verify-gate (T5) and implement.md (T4) call the audit cheaply on the hot path without running deny/audit/machete every time. Splitting them out from T2 keeps T2's slice focused on 'does the full pipeline work end-to-end'; this task's slice is 'does the runner respect mode flags'. Both flags share the same flag-parsing block in audit.sh, so they belong in one task.", + "user_story": "US-4", + "layer": "chore" + } + }, + { + "id": "4", + "title": "Wire audit --fast into implement.md skill chain after simplify", + "status": "pending", + "blocked_by": ["3"], + "metadata": { + "verification": "grep -A1 'Skill(\"simplify\")' code-et-implementer/commands/implement.md | grep -q 'audit.*--fast' && bash code-et-implementer/tests/implement-chain-halts-on-audit-failure.sh", + "files": [ + "code-et-implementer/commands/implement.md:83", + "code-et-implementer/tests/implement-chain-halts-on-audit-failure.sh:1" + ], + "expected_outcome": "After the orchestrator's Skill(\"simplify\") call on implement.md:83, a Skill(\"audit\", \"--fast\") call runs. A test fixture with a known clippy regression triggers a halt of the implement chain with the audit report path surfaced in the output.", + "rationale": "Implement.md's current chain ends at simplify with no static-analysis pass. Inserting --fast audit (stages 1-2 only) keeps the implement loop fast while catching style and lint regressions before merge. The end-to-end test exercises the full chain against a regression fixture, which is what makes this a vertical slice rather than a one-line edit.", + "user_story": "US-2", + "layer": "chore" + } + }, + { + "id": "5", + "title": "Wire audit --fast into verify-gate.sh, guarded by Cargo.toml presence", + "status": "pending", + "blocked_by": ["3"], + "metadata": { + "verification": "bash code-et-implementer/tests/verify-gate-runs-audit-on-rust.sh && bash code-et-implementer/tests/verify-gate-skips-audit-on-non-rust.sh", + "files": [ + "code-et-implementer/scripts/verify-gate.sh:1", + "code-et-implementer/tests/verify-gate-runs-audit-on-rust.sh:1", + "code-et-implementer/tests/verify-gate-skips-audit-on-non-rust.sh:1" + ], + "expected_outcome": "verify-gate.sh invokes audit --fast when Cargo.toml exists at repo root and skips silently when it doesn't. Both branches covered by integration tests.", + "rationale": "verify-gate.sh is currently 5 lines: detect tests, run them. Adding the audit hook at the SubagentStop boundary catches regressions one step earlier than the implement-chain hook (T4) and is cheap (--fast = 2 stages). Cargo.toml guard makes the hook safe on non-Rust repos — code-et is general-purpose (used across Kennet's repos, not Visma-only) so the hook must no-op cleanly on TS/Python projects.", + "user_story": "US-3", + "layer": "chore" + } + }, + { + "id": "6", + "title": "Add --review flag chaining engineering plugin's code-review skill", + "status": "pending", + "blocked_by": ["2"], + "metadata": { + "verification": "bash code-et-implementer/tests/audit-review-with-plugin.sh && bash code-et-implementer/tests/audit-review-without-plugin.sh", + "files": [ + "code-et-implementer/scripts/audit.sh:1", + "code-et-implementer/scripts/audit-report.sh:1", + "code-et-implementer/tests/audit-review-with-plugin.sh:1", + "code-et-implementer/tests/audit-review-without-plugin.sh:1" + ], + "expected_outcome": "/code:audit --review runs the static stages, then invokes engineering plugin's code-review skill against `git diff ..HEAD`. Combined output written to the audit report under a 'Review' section. Without the engineering plugin installed, exits non-zero with a hint to install it.", + "rationale": "Static analysis catches mechanical issues; code-review prose catches design issues. Chaining the two through one command means the developer runs `/code:audit --review` once before pushing instead of switching skills. This is also code-et's first cross-plugin dependency — the absence-detection branch is what makes the dependency soft.", + "user_story": "US-5", + "layer": "chore" + } + }, + { + "id": "7", + "title": "Tool-presence guards: WARN-and-skip when machete/audit/deny missing", + "status": "pending", + "blocked_by": ["2"], + "metadata": { + "verification": "bash code-et-implementer/tests/audit-skips-missing-tools.sh", + "files": [ + "code-et-implementer/scripts/audit.sh:1", + "code-et-implementer/tests/audit-skips-missing-tools.sh:1" + ], + "expected_outcome": "If cargo-machete, cargo-audit, or cargo-deny is not on PATH, the audit emits a WARNING for the missing stage, skips it, and continues with the remaining stages. The WARNING is recorded in the report's findings list at LOW severity. Exit code is 0 if no other stage fails.", + "rationale": "PRD's US-9 was written assuming `cargo +nightly udeps` was a stage; the actual yaml uses machete/audit/deny instead. T1 corrects the PRD; this task implements the spirit of US-9 — graceful degradation when local tooling diverges from CI's fully-provisioned environment — against the real toolset. Without this, a fresh dev machine gets non-zero exit for missing tools that CI installs automatically, which makes the local audit useless on day one.", + "user_story": "US-9", + "layer": "chore" + } + }, + { + "id": "8", + "title": "CRITICAL-first summary line in report and terminal", + "status": "pending", + "blocked_by": ["2"], + "metadata": { + "verification": "bash code-et-implementer/tests/audit-summary-line-on-failure.sh", + "files": [ + "code-et-implementer/scripts/audit-report.sh:1", + "code-et-implementer/scripts/audit.sh:1", + "code-et-implementer/tests/audit-summary-line-on-failure.sh:1" + ], + "expected_outcome": "When the audit reports any non-LOW finding, the first line of .claude/audit-.md is a one-sentence summary of the highest-severity finding plus a remediation hint, and the same line is echoed to stderr.", + "rationale": "Report files can be long. The 'first thing the user reads' must be 'what's the worst thing and how do I fix it'. This is a UX primitive that pays off every failed run. Splits cleanly from T2 because T2 ships the report writer's structure; this task adds the summary-line layer on top.", + "user_story": "US-12", + "layer": "chore" + } + }, + { + "id": "9", + "title": "code-et CI smoke job: run audit against bundled fixture", + "status": "pending", + "blocked_by": ["2", "3"], + "metadata": { + "verification": "test -f .github/workflows/audit-smoke.yml && grep -q 'templates/rust/dioxus-fullstack' .github/workflows/audit-smoke.yml", + "files": [ + ".github/workflows/audit-smoke.yml:1", + "code-et-implementer/templates/rust/dioxus-fullstack/Cargo.toml:1" + ], + "expected_outcome": "Every PR to code-et runs /code:audit against templates/rust/dioxus-fullstack/. Job exits 0 on a clean fixture. The fixture doubles as the audit's regression suite.", + "rationale": "The bundled fixture is already the v3.9.0 gate's smoke target — making it the audit's smoke target too means one fixture, one set of expectations, no drift between the templated CI gate and the local command that mirrors it. Adds the first .github/workflows/ entry to code-et root (currently empty), so this is also where future plugin-level CI lands.", + "user_story": "US-10", + "layer": "chore" + } + } + ] +} diff --git a/.github/workflows/audit-smoke.yml b/.github/workflows/audit-smoke.yml new file mode 100644 index 0000000..6a358d8 --- /dev/null +++ b/.github/workflows/audit-smoke.yml @@ -0,0 +1,71 @@ +name: audit-smoke + +# Runs /code:audit's local script against the bundled Rust fixture on every PR. +# The fixture (code-et-implementer/templates/rust/dioxus-fullstack/) doubles as +# the audit's regression suite — if the audit can't pass cleanly here, neither +# can the v3.9.0 CI gate it mirrors. Mirrors the toolchain setup from +# code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml. + +on: + pull_request: + push: + branches: [main] + +jobs: + audit-fixture: + runs-on: ubuntu-latest + + env: + CARGO_TERM_COLOR: always + FIXTURE: code-et-implementer/templates/rust/dioxus-fullstack + + steps: + - uses: actions/checkout@v4 + + - name: install GTK + WebKit + xdo system libs (dioxus-desktop) + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libgtk-3-dev libglib2.0-dev libsoup-3.0-dev \ + libjavascriptcoregtk-4.1-dev libwebkit2gtk-4.1-dev \ + libxdo-dev + + - name: install rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: code-et-implementer/templates/rust/dioxus-fullstack + + - name: install cargo-nextest + uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest + + - name: install cargo-machete + uses: taiki-e/install-action@v2 + with: + tool: cargo-machete + + - name: install cargo-audit + uses: taiki-e/install-action@v2 + with: + tool: cargo-audit + + - name: install cargo-deny + uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + + - name: run audit against fixture + run: bash code-et-implementer/scripts/audit.sh code-et-implementer/templates/rust/dioxus-fullstack + + - name: upload audit report + if: always() + uses: actions/upload-artifact@v4 + with: + name: audit-report + path: code-et-implementer/templates/rust/dioxus-fullstack/.claude/audit-*.md + if-no-files-found: ignore diff --git a/CHANGELOG.md b/CHANGELOG.md index b98c027..f35815b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,80 @@ All notable changes to the code-et plugin will be documented in this file. +## [4.0.0] - 2026-05-06 + +**Breaking restructure** — the plugin is now pure-Rust only and condenses to six commands. Existing PRDs and tasks under `plans/` and `.claude/.json` continue to work, but the entry-point command names have changed. See the migration table below. + +### Changed — six commands, two lanes + +The v3.x command set (`/code:bootstrap`, `/code:go`, `/code:grill`, `/code:prd`, `/code:plan-issue`, `/code:implement`, `/code:audit`, `/code:install-ci`) collapses to: + +| v3.x | v4.0 | Why | +|---|---|---| +| `/code:bootstrap` | `/code:start` | Same behavior; renamed for "what you do day one". Now runs `cargo update` post-scaffold so every dep is at its latest semver-compatible patch. | +| `/code:go` | `/code:fix` | Same behavior; renamed for "what you do when something is broken". Pure-Rust now — no more "Rust projects only" conditionals. | +| `/code:grill` + `/code:prd` + `/code:plan-issue` | `/code:plan` | One extended turn (refined brief → PRD on disk → vertical-slice tasks) with three checkpoints. PRD lands on disk before task decomposition starts so you can interrupt and edit. Includes inline anti-slop self-critique before `TaskCreate`. | +| `/code:implement` | `/code:ship` | Same parallel-worktree dispatch logic, but the post-merge audit is now built in. On CRITICAL/HIGH findings, dispatches **one** auto-retry fix-pass; if still failing, halts and surfaces the highest finding. No infinite loops. | +| `/code:audit` (top-level) | `just audit` (in scaffolded projects) and the inner step of `/code:ship` | The standalone `/code:audit` command is gone. The `audit.sh` script and the CI workflow are unchanged — they run via `just audit`, the `SubagentStop` hook, and `/code:ship`'s tail step. | +| (none) | `/code:review` | New — pre-merge gate that runs the full audit + diff review against `..HEAD`. Delegates to engineering plugin's `code-review` skill if installed; falls back to a 5-area inline checklist (layer compliance, anti-slop, test coverage, security, slice integrity). | +| `/code:install-ci` | `/code:install-ci` | Unchanged — retrofit the audit gate onto an existing Rust repo. | + +### Changed — plugin posture + +- **Pure-Rust only.** Dropped all "if Rust" / "if TS legacy" conditionals from commands, CLAUDE.md, and metadata. Every project that uses code-et follows the four-crate Clean Architecture workspace. +- **Always-latest deps.** `/code:start` runs `cargo update` post-scaffold. Caret pins (`dioxus = "0.7"`, `axum = "0.8"`, `sqlx = "0.8"`, `tokio = "1"`, `tower = "0.5"`) deliver latest minor/patch automatically. Major bumps documented as a manual `cargo upgrade` flow. +- **README cut from 605 to 203 lines.** Single page: workflow diagram, six commands, install, prereqs, doctrine links, migration table. The "Building a Plugin from Scratch" section is removed — it belonged in a separate guide, not the user-facing README. +- **CLAUDE.md trimmed** — dropped TS-legacy paragraphs and "if Rust" conditionals; everything assumes the four-crate stack. + +### Changed — `/code:ship` adds an auto-retry contract + +After all task subagents land and merge, `/code:ship`: +1. Runs `Skill("simplify")` for changed-code refactor pass. +2. Runs `bash audit.sh` (full seven-stage gate). +3. On CRITICAL/HIGH, dispatches **one** fix-pass subagent on the feature branch (no worktree isolation, since the swarm already merged), then re-audits. +4. After 1 retry, halts and surfaces the highest finding. No infinite loops. + +This makes `/code:ship` self-healing for routine misses (a missed `cargo fmt`, a single clippy warning, a layer slip) without burning tokens on stuck audits. + +### Changed — `/code:plan` adds inline anti-slop self-critique + +Before `TaskCreate`, `/code:plan` walks the drafted task list and **rejects** any task that: +- Touches only one layer (split or merge into a vertical slice). +- Has rationale "because the PRD says so" (restate the underlying constraint). +- Adds a duplicate utility instead of extracting (Rule of Three triggers refactor in the same task). +- Adds defensive validation between trusted modules (`interface↔application` is the only validation boundary). +- Adds a mirror test (assert observable behaviour, not implementation calls). +- Has no test for at least one acceptance criterion. + +The full list lives in `code-et-implementer/docs/anti-slop.md`. The inline summary keeps the model focused at plan time without an extra file read. + +### Added — deploy & upload via scripts + +- **`scripts/deploy.sh`** in every scaffolded project — single entry point for shipping the server. Pre-flight (clean tree + audit) → build container → run migrations → roll out → smoke check. Host-specific lines (`gcloud run deploy`, `kubectl set image`, `flyctl deploy`) are `# TODO:` placeholders to fill in once per project. +- **`scripts/upload.sh`** — single entry point for uploading static artifacts (web bundle / desktop binaries / mobile builds) to CDN / object store / app store. Same discipline as deploy. +- **`just deploy `** and **`just upload `** justfile targets wrap the scripts. +- **CLAUDE.md template** encodes the rule: never deploy/upload via raw `cargo`/`docker`/`gcloud`/`gsutil` commands. If you find yourself typing the underlying command, add the missing step to the script instead. + +### Unchanged + +- **Doctrine** (`docs/architecture.md`, `docs/anti-slop.md`, `docs/testing.md`) — already lean and sharp. +- **Hooks** (`PermissionRequest`, `SubagentStop`, `SessionStart`, `TaskCreated`, `TaskCompleted`, `PreCompact`) — net-positive, low cost. +- **Templates** (`templates/rust/dioxus-fullstack/`, `templates/shared/`) — same scaffold; `/code:start` now runs `cargo update` after copying. +- **CI gate** (`code-et-audit.yml`, `layer-deps-validator.sh`) — unchanged. `audit.sh` parses the yaml at runtime so local and CI cannot drift. + +### Migration + +| If you used v3.x for | In v4.0 do | +|---|---| +| Bootstrap | `/code:start ` | +| Single bug | `/code:fix ""` | +| Feature (full lane) | `/code:plan ""` → `/code:ship` → `/code:review` | +| Pre-merge audit | `/code:review` (or `just audit` for static-only) | +| Standalone audit | `bash scripts/audit.sh` or `just audit` (no slash command) | +| CI retrofit | `/code:install-ci` (unchanged) | + +PRD files under `plans/YYYY-MM-DD-.md` continue to work with `/code:plan` (Phase 3 takes an existing PRD and jumps straight to task decomposition). + ## [3.9.0] - 2026-05-06 ### Added — Pure-Rust Clean Architecture diff --git a/FILE-REFERENCE.md b/FILE-REFERENCE.md index 2758101..c74e667 100644 --- a/FILE-REFERENCE.md +++ b/FILE-REFERENCE.md @@ -1,7 +1,7 @@ # FILE-REFERENCE.md -Map of every component in the code-et plugin. Used by `/code:go` for intake scoping -and as primary context for `/code:plan-issue` (so plans can skip a full codebase sweep). +Map of every component in the code-et plugin. Used by `/code:fix` for intake scoping +and as primary context for `/code:plan` (so plans can skip a full codebase sweep). ## Project Overview @@ -12,15 +12,16 @@ and as primary context for `/code:plan-issue` (so plans can skip a full codebase --- -## Plugin — Commands +## Plugin — Commands (v4.0) | Command | Skill name | File | Description | |---------|------------|------|-------------| -| `/code:grill` | `code:grill` | `code-et-implementer/commands/grill.md` | Interrogate a rough idea into a refined brief | -| `/code:prd` | `code:prd` | `code-et-implementer/commands/prd.md` | Synthesise brief into `plans/YYYY-MM-DD-.md` (US-N / AC-N.M) | -| `/code:go` | `code:go` | `code-et-implementer/commands/go.md` | Feature/bug intake — scope work, auto-generate this file | -| `/code:plan-issue` | `code:plan-issue` | `code-et-implementer/commands/plan-issue.md` | Research codebase with LSP, create implementation tasks | -| `/code:implement` | `code:implement` | `code-et-implementer/commands/implement.md` | Dispatch each task via `Agent(isolation: "worktree")`, parallel where independent | +| `/code:start` | `code:start` | `code-et-implementer/commands/start.md` | Scaffold a new pure-Rust full-stack project; runs `cargo update` post-scaffold | +| `/code:install-ci` | `code:install-ci` | `code-et-implementer/commands/install-ci.md` | Retrofit the CI audit gate onto an existing Rust repo | +| `/code:fix` | `code:fix` | `code-et-implementer/commands/fix.md` | Single-bug intake → Task Brief; user implements directly | +| `/code:plan` | `code:plan` | `code-et-implementer/commands/plan.md` | One extended turn: refined brief → PRD on disk → vertical-slice tasks (3 checkpoints) | +| `/code:ship` | `code:ship` | `code-et-implementer/commands/ship.md` | Parallel worktree agents + post-merge audit + 1-pass auto-retry on CRITICAL/HIGH | +| `/code:review` | `code:review` | `code-et-implementer/commands/review.md` | Pre-merge gate — full audit + diff review (delegates to engineering plugin) | ## Plugin — Hooks @@ -59,9 +60,9 @@ and as primary context for `/code:plan-issue` (so plans can skip a full codebase | File | Description | |------|-------------| -| `README.md` | Plugin documentation, workflow diagrams, install instructions | +| `README.md` | Plugin documentation, workflow diagrams, install instructions (≤210 lines) | | `CHANGELOG.md` | Version history — leading entry must match `plugin.json` version | -| `package.json` | npm metadata (Next.js scaffold; not the plugin's version) | +| `.claude-plugin/marketplace.json` | Marketplace manifest (points at `code-et-implementer/`) | | `.claude/settings.json` | Project-level Claude Code settings | | `.claude/settings.local.json` | Local-only Claude Code settings | | `plans/` | PRDs and plan artifacts (`YYYY-MM-DD-.md`) | @@ -94,17 +95,17 @@ A regression in `auto-approve-readonly.sh` blocks every read permission prompt; | Never force push | Rebase locally, push normally | | Never bump only `CHANGELOG.md` without `plugin.json` | The 3.7.0 release shipped with manifest stuck on 3.6.1 — installs reported the old version | | Never call `/ultraplan` via `Skill("ultraplan", …)` | It's a built-in Claude Code command, not a callable skill (3.6.1 fix) | -| Never `git worktree add` from inside `/code:implement` | Use `Agent(isolation: "worktree")` so the subagent forks cleanly | +| Never `git worktree add` from inside `/code:ship` | Use `Agent(isolation: "worktree")` so the subagent forks cleanly | | Never have a subagent merge its own branch | The subagent has no view of the parent feature branch — orchestrator merges | -| Never serialize independent `/code:implement` tasks | Dispatch them in a single message with multiple `Agent` calls | +| Never serialize independent `/code:ship` tasks | Dispatch them in a single message with multiple `Agent` calls | ## Module Invariants | Module | Invariant | |--------|-----------| -| `commands/*.md` | Frontmatter must declare `effort`; coding/research commands use `xhigh` (4.7 default), `/code:grill` is the documented exception (`high`) | -| `commands/implement.md` | Each task → one `Agent(isolation: "worktree")` call; orchestrator owns merge + worktree cleanup, subagent does not | -| `commands/plan-issue.md` | Every task's `metadata.rationale` is mandatory; subagents start cold and need the *why* | +| `commands/*.md` | Frontmatter must declare `effort`; commands default to `xhigh` (4.7 default); `/code:install-ci` and `/code:review` are the documented exceptions (`high`) | +| `commands/ship.md` | Each task → one `Agent(isolation: "worktree")` call; orchestrator owns merge + worktree cleanup, subagent does not. Post-merge audit auto-retries once on CRITICAL/HIGH. | +| `commands/plan.md` | Every task's `metadata.rationale` is mandatory; subagents start cold and need the *why*. PRD lands on disk before task decomposition (Phase 2 → Phase 3 checkpoint). | | `hooks/hooks.json` | All script paths use `${CLAUDE_PLUGIN_ROOT}` — never hard-code `code-et-implementer/scripts/...`. Add hooks only when (a) no CLAUDE.md instruction can replace them and (b) the per-event token cost is justified by the value. | | `FILE-REFERENCE.md` | Refresh only after a PR merges to main with structural changes (`*/page.tsx`, `*/route.ts`, `*/layout.tsx`, new top-level apps/packages). Do not edit on a feature branch — it tracks merged state. | | `scripts/*.sh` | Must exit 0 on the no-op path; non-zero exit blocks the tool/event they hook into | diff --git a/README.md b/README.md index 45496b3..6b14620 100644 --- a/README.md +++ b/README.md @@ -1,605 +1,219 @@ # code-et -Pure-Rust Clean Architecture workflow powered by Claude Code + plugins. +Pure-Rust Clean Architecture workflow for Claude Code. **One stack, six commands, no slop.** -Task-driven development for Rust full-stack projects (`axum + sqlx + Dioxus 0.7+ + tokio`). Bootstraps new projects with the Dependency Rule enforced at the Cargo level. Ships a CI gate (`.github/workflows/code-et-audit.yml`) that fails PRs on slop. Doctrine in [`code-et-implementer/docs/`](code-et-implementer/docs/) covers architecture, anti-slop, and per-layer testing. +Stack: `axum + sqlx + Dioxus 0.7+ + tokio`. One frontend codebase renders to web, desktop, and mobile. Database: SQLite for local, Postgres on GCP Cloud SQL for prod. Always-latest semver-compatible deps via post-scaffold `cargo update`. -> **When to use code-et:** Single bugs benefit from `/code:go`'s scoping (intake → Task Brief, you implement). Complex features with 3+ tasks, dependencies, or parallel execution get the full PRD-driven lane. New projects start with `/code:bootstrap`. For 1-2 file changes you already understand, direct prompting works fine. +> v4.0.0 is a deliberate rewrite. The plugin is biased toward starting fresh: `/code:start` scaffolds the four-crate workspace; the rest of the commands keep you in flow. -## What's new in v3.9.0 +## The six commands -- **`/code:bootstrap`** — scaffold a pure-Rust project (4-crate Clean Architecture workspace + Dioxus 0.7+ for web/desktop/mobile + sqlx for SQLite/Postgres + axum for the server). -- **`/code:install-ci`** — drop the audit GitHub workflow + layer validator into an existing Rust repo. -- **CLAUDE.md as control plane** — `code-et-implementer/CLAUDE.md` §"Clean Architecture (Rust)" tells Claude when to invoke the engineering plugin's skills (`code-review`, `tech-debt`, `testing-strategy`, `system-design`) with Clean Architecture context. -- **Doctrine** — [`docs/architecture.md`](code-et-implementer/docs/architecture.md), [`docs/anti-slop.md`](code-et-implementer/docs/anti-slop.md), [`docs/testing.md`](code-et-implementer/docs/testing.md). Loaded on demand by the skills. -- **Companion plugins**: hard-depend on `knowledge-work-plugins/engineering` and `rust-analyzer-lsp` (replaces the old `typescript-lsp` recommendation). -- v3.10.0 preview: `/code:audit` will mirror the CI gate locally for fast feedback. +| Command | What it does | +|---|---| +| `/code:start ` | Scaffold a new pure-Rust full-stack project — 4 crates (`domain`, `application`, `infrastructure`, `interface`), 4 apps (`server`, `web`, `desktop`, `mobile`), CI gate, latest deps. | +| `/code:install-ci` | Drop the audit GitHub workflow + layer-deps validator into an existing Rust repo. | +| `/code:fix ""` | Single-bug intake → Task Brief (with per-file `Layer`). You implement directly. | +| `/code:plan ""` | One extended turn: refined brief → PRD on disk → vertical-slice tasks. Three checkpoints — interrupt at any. | +| `/code:ship` | Execute pending tasks in parallel worktree-isolated agents → audit → 1 auto-retry on CRITICAL/HIGH. | +| `/code:review` | Pre-merge gate: full local audit + diff review. Delegates to engineering plugin's `code-review` skill. | -## Workflows +That's it. `/commit-push-pr` (from the `commit-commands` plugin) ships the PR. -code-et ships **two distinct lanes**. They share no midpoint — `/code:go` does NOT chain into `/code:plan-issue` or `/code:implement`. The user reads the brief and decides next steps. +## Workflow -### Bug lane — single fix, scope-and-go - -``` -/code:go → (user implements directly) → /commit-push-pr - intake 1-3 file edits, no swarm PR ``` +NEW PROJECT (one-time) + /code:start myapp [--db sqlite|postgres] [--targets web,desktop,mobile,server] + ↓ + cd myapp && cp .env.example .env && just db-migrate && just run-server -`/code:go` produces a Task Brief and stops. Most bugs are 1-3 file edits — no orchestration needed. If the work spans multiple coherent vertical slices, it's a feature in disguise — write a PRD instead. - -### Feature lane — PRD-driven, vertically sliced +DAILY -``` -/code:grill → /code:prd → /code:plan-issue → /code:implement → /commit-push-pr - interview PRD file vertical slices parallel agents PR (+ /ultrareview hint) + Bug? /code:fix "..." → user implements 1-3 files → /commit-push-pr + Feature? /code:plan "..." → /code:ship → /code:review → /commit-push-pr + ↓ + audit auto-runs + ↓ + ✓ green → ready to push + ✗ red → 1 retry → if still red, surface findings ``` -- `/code:grill` runs a one-question-at-a-time interview, refusing to converge until scope, constraints, and success criteria are explicit. -- `/code:prd` writes the PRD to `plans/YYYY-MM-DD-.md` with `US-N` / `AC-N.M` checkboxes. -- `/code:plan-issue` is **PRD-only**. It decomposes into **vertical slices** — each task implements UI ↔ logic ↔ API ↔ DB end-to-end and is testable as a unit. When a slice supersedes existing code, deletion of the old code is part of the same commit (no parallel utilities, no `// TODO: remove old X`). -- `/code:implement` prefixes each commit with `US-N:` / `AC-N.M:` / `chore:` and ticks the PRD checkbox for that story. +Two lanes. No mid-points. `/code:fix` does **not** chain into `/code:plan` or `/code:ship` — bugs that span vertical slices are features in disguise; write a PRD. -> Tip: for richer upstream planning, run `/ultraplan` (built-in Claude Code command, research preview) before `/code:plan-issue` and commit its output to `plans/`. +## Architecture (enforced) -### PRD file convention +The four-crate workspace **is** the architecture. Imports point inward; the `Cargo.toml` deps enforce it. -- Path: `plans/YYYY-MM-DD-.md` where `` = current branch with `feature/`, `fix/`, or `chore/` prefix stripped. -- Structure: problem, goals, user stories `US-N`, acceptance criteria `AC-N.M`, open questions, story checklist. -- Most recent date wins when multiple match the branch slug. -- Resolved by `scripts/resolve-prd.sh`; consumed by SessionStart, PreCompact, TaskCreated, and `/code:implement`. +``` +crates/ + domain/ Entities, value objects, errors. Pure logic. NO workspace deps. + application/ Use cases + ports (traits). Orchestrates domain. + infrastructure/ Adapters: sqlx repos, HTTP clients, secrets. Implements ports. + interface/ axum handlers + Dioxus components. Composition lives in apps/. -### Dependencies +apps/ + server/ axum + dioxus-fullstack SSR (always present) + web/ dioxus-web (WASM) + desktop/ dioxus-desktop + mobile/ dioxus-mobile (best-effort) +``` -- `/ultrareview` is suggested (not required) after PR creation. -- `/ultraplan` is optional — a built-in Claude Code command (research preview) you can run manually upstream of `/code:plan-issue`. +Each `apps//main.rs` is the **composition root** — the only place that instantiates concrete `infrastructure` types and wires them into `interface` ports. -### Git Branch Flow +Doctrine lives in [`code-et-implementer/docs/`](code-et-implementer/docs/): -``` - main ---------.---------------*--- - \ / - feature/x -*--* - ^ ^ - task commits -``` +- [`architecture.md`](code-et-implementer/docs/architecture.md) — Clean Architecture, dependency rule, secrets, security checklist. +- [`anti-slop.md`](code-et-implementer/docs/anti-slop.md) — 4 elements (dead code, duplication, complexity, drift) + 5 categories + 6 hard rules. +- [`testing.md`](code-et-implementer/docs/testing.md) — per-layer test matrix, mirror-test ban, contract tests at boundaries. -### How the Plugins Work Together +## CI gate -**Bug lane** — single fix, no orchestration: +`.github/workflows/code-et-audit.yml` runs on every PR + push to main: -``` - YOU: "fix the broken submit button" - │ - ▼ - /code:go ─── scopes ────────▶ identifies app, screen, files - │ generates/updates FILE-REFERENCE.md - ▼ Task Brief (stops here) - (you implement directly — 1-3 file edits) - │ - ▼ code ready - /commit-push-pr ─── runs ───▶ git commit + push + gh pr create -``` +1. `cargo fmt --check` +2. `cargo clippy --workspace --all-targets --all-features -- -D warnings` +3. `scripts/layer-deps-validator.sh` (defence-in-depth on the layer rule) +4. `cargo machete` (unused deps) +5. `cargo audit` (security advisories) +6. `cargo deny check` (license + bans + sources) +7. `cargo nextest run --workspace --all-features` -**Feature lane** — PRD-driven, vertically sliced, parallel execution: +Local mirror: `just audit`. `/code:ship` runs the same pipeline post-merge with a 1-pass auto-fix retry on CRITICAL/HIGH findings. **A green audit is the merge gate** — no manual override. -``` - YOU: "add dark mode support" - │ - ▼ - /code:grill ─── refines ────▶ scope, constraints, success criteria - │ - ▼ refined brief - /code:prd ─── writes ───────▶ plans/YYYY-MM-DD-.md - │ US-N stories, AC-N.M checkboxes - ▼ PRD - /code:plan-issue ─── uses ──▶ LSP for symbol-level precision - │ vertical-slice decomposition - │ each task: UI ↔ logic ↔ API ↔ DB - ▼ tasks - /code:implement ─── spawns ──▶ parallel agents (worktree isolation) - │ each agent: edit, test, commit - │ deletes superseded code in same commit - ▼ code ready - /commit-push-pr ─── runs ───▶ git commit + push + gh pr create - │ - ▼ PR open - /code-review ─── spawns ────▶ 5 review agents in parallel - │ - ▼ merged - /revise-claude-md ──────────▶ update CLAUDE.md with learnings -``` +> **GitHub Actions:** the workflow uses public actions (`actions/checkout`, `dtolnay/rust-toolchain`, `Swatinem/rust-cache`, `taiki-e/install-action`, `bnjbvr/cargo-machete`, `rustsec/audit-check`, `EmbarkStudios/cargo-deny-action`). GitHub fetches them automatically on first run — nothing to install. `secrets.GITHUB_TOKEN` is auto-provided. Just push the repo and the gate runs. -**Plugin responsibilities:** +## Install ``` - +-------------------+--------------------------------------------+ - | Plugin | What it does | - +-------------------+--------------------------------------------+ - | code-et | /go — single-bug intake → Task Brief | - | (this repo) | /grill — interview to refine an idea | - | | /prd — synthesize PRD from session | - | | /plan-issue — PRD → vertical-slice tasks | - | | /implement — parallel agents in worktrees | - +-------------------+--------------------------------------------+ - | commit-commands | /commit — auto-message git commit | - | (official) | /commit-push-pr — branch + commit + PR | - | | /clean_gone — prune merged branches | - +-------------------+--------------------------------------------+ - | code-review | /code-review — multi-agent PR review | - | (official) | /simplify — refactor changed code | - +-------------------+--------------------------------------------+ - | typescript-lsp | LSP navigation for /code:plan-issue | - | (official) | goToDefinition, findReferences, hover | - +-------------------+--------------------------------------------+ - | claude-md-mgmt | /revise-claude-md — update CLAUDE.md | - | (official) | /claude-md-improver — audit & improve | - +-------------------+--------------------------------------------+ - | frontend-design | /frontend-design — production-grade UI | - | (official) | bold design, avoids generic AI aesthetics | - +-------------------+--------------------------------------------+ - | feature-dev | /feature-dev — guided feature development | - | (official) | codebase analysis + architecture focus | - +-------------------+--------------------------------------------+ - | skill-creator | /skill-creator — create & optimize skills | - | (official) | build skills, run evals, benchmark | - +-------------------+--------------------------------------------+ - | agent-sdk-dev | Claude Agent SDK development helper | - | (official) | build custom agents with Agent SDK | - +-------------------+--------------------------------------------+ -``` - -**Install all plugins:** +# Required companions +/plugin marketplace add knowledge-work-plugins +/plugin install engineering@knowledge-work-plugins +/plugin install rust-analyzer-lsp@claude-plugins-official -``` -# Official plugins (from claude-plugins-official) +# Recommended /plugin install commit-commands@claude-plugins-official /plugin install code-review@claude-plugins-official /plugin install claude-md-management@claude-plugins-official -/plugin install frontend-design@claude-plugins-official -/plugin install feature-dev@claude-plugins-official -/plugin install skill-creator@claude-plugins-official -/plugin install agent-sdk-dev@claude-plugins-official -# code-et (from this repo) +# code-et /plugin marketplace add Emerging-Tech-Visma/code-et /plugin install code@code-et ``` -## Building a Plugin from Scratch - -This section documents how the code-et plugin was built — use it as a guide for creating your own Claude Code marketplace plugin. - -### Step 1: Repository Structure +The `engineering` plugin provides `code-review`, `tech-debt`, `testing-strategy`, `system-design` skills that code-et delegates to. `rust-analyzer-lsp` powers symbol-level precision in `/code:fix` and `/code:plan`. -A marketplace repo has two levels: +### Local development -- **Root** `.claude-plugin/marketplace.json` — declares which plugins exist and where to find them -- **Subdirectory** (e.g. `code-et-implementer/`) — contains the actual plugin with its own `.claude-plugin/plugin.json` - -``` -my-repo/ ← GitHub repo root -├── .claude-plugin/ -│ └── marketplace.json ← marketplace manifest (points to subdirs) -├── code-et-implementer/ ← plugin subdirectory -│ ├── .claude-plugin/ -│ │ ├── plugin.json ← plugin identity (name, version) -│ │ └── settings.json ← permissions, env vars, spinner tips -│ ├── CLAUDE.md ← instructions loaded when plugin is active -│ ├── commands/ ← slash commands (skills) -│ │ ├── plan-issue.md -│ │ └── implement.md -│ ├── hooks/ -│ │ └── hooks.json ← lifecycle hooks -│ └── scripts/ ← shell scripts invoked by hooks -│ ├── inject-rules.sh -│ ├── verify-gate.sh -│ └── run-tests.sh -├── README.md ← repo docs (not part of plugin) -└── package.json ← repo-level config (not part of plugin) -``` - -> **Key rule:** `marketplace.json` uses `"source": "./code-et-implementer"` — the source must point to a subdirectory, never `"."`. - -### Step 2: Create the Directory Structure +Test plugin changes without the install/update/restart cycle: ```bash -# From your repo root -mkdir -p code-et-implementer/.claude-plugin -mkdir -p code-et-implementer/{commands,hooks,scripts} -mkdir -p .claude-plugin -``` - -### Step 3: Marketplace Manifest - -Create `.claude-plugin/marketplace.json` at the **repo root**: - -```json -{ - "name": "code-et", - "owner": { - "name": "Your Name" - }, - "metadata": { - "description": "Task-driven coding workflow with parallel agents in worktrees", - "version": "1.0.0" - }, - "plugins": [ - { - "name": "code", - "source": "./code-et-implementer", - "description": "Task-driven coding workflow with native Task tools." - } - ] -} -``` - -- `name` — marketplace name (matches the repo) -- `plugins[].name` — becomes the skill prefix (`/code:*`) -- `plugins[].source` — relative path to the plugin subdirectory - -### Step 4: Plugin Identity - -Create `code-et-implementer/.claude-plugin/plugin.json`: - -```json -{ - "name": "code", - "version": "1.0.0", - "description": "Task-driven coding workflow with native Task tools.", - "author": { - "name": "Your Name" - }, - "license": "MIT", - "keywords": ["coding", "workflow", "agents"] -} -``` - -The `name` field here **must match** the `plugins[].name` in `marketplace.json`. This determines the skill prefix — `"code"` means `/code:implement`, `/code:setup`, etc. - -### Step 5: Plugin Settings - -Create `code-et-implementer/.claude-plugin/settings.json`: - -```json -{ - "plansDirectory": "plans", - "permissions": { - "allow": ["Bash(git:*)", "Bash(gh:*)", "Bash"] - } -} +claude --plugin-dir /path/to/code-et/code-et-implementer ``` -### Step 6: Add Commands (Skills) +Type `/code:` to confirm all six commands appear. -Each `.md` file in `code-et-implementer/commands/` becomes a skill callable as `/code:`. - -| File | Skill | -| ---------------- | ------------------ | -| `plan-issue.md` | `/code:plan-issue` | -| `implement.md` | `/code:implement` | +## Prerequisites -Commands are markdown files with instructions that Claude follows when the skill is invoked. +- **Claude Code** — `npm install -g @anthropic-ai/claude-code` +- **Rust toolchain** — `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` (Rust 2024 / 1.85+) +- **GitHub CLI (`gh`)** — for PRs and issues ([install](https://cli.github.com/)) +- **Audit tools**: + ``` + cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli + ``` + Or pass `--install-tools` to `/code:start` and the bootstrap will run this for you. -### Step 7: Add Hooks and Scripts +### LSP (recommended) -`code-et-implementer/hooks/hooks.json` defines lifecycle hooks. Scripts go in `code-et-implementer/scripts/` and are referenced via `${CLAUDE_PLUGIN_ROOT}/scripts/`: +Add to `~/.claude/settings.json`: ```json { - "hooks": { - "SubagentStop": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/scripts/verify-gate.sh" - } - ] - } - ] - } + "env": { "ENABLE_LSP_TOOL": "1" } } ``` -> **Tip:** Use `claude --plugin-dir ./your-plugin-dir` to test locally without installing. Hook scripts resolve via `${CLAUDE_PLUGIN_ROOT}`, and skill `.md` files can reference sibling files via `${CLAUDE_SKILL_DIR}`. - -### Step 8: Add Plugin CLAUDE.md - -`code-et-implementer/CLAUDE.md` contains instructions loaded when the plugin is active. This is where you document workflow rules, conventions, and project standards. - -### Step 9: Publish - -Commit your plugin files, push to a branch, and create a PR. After merging to main, the marketplace is live. - -## How to Install the code-et Plugin - -Open Claude Code and run: - -``` -/plugin marketplace add Emerging-Tech-Visma/code-et - -# Pin to a specific version: -/plugin marketplace add Emerging-Tech-Visma/code-et@v1.18.4 -``` - -Then install the plugin: - -``` -/plugin install code@code-et -``` - -> If SSH fails, use the HTTPS workaround: `/plugin` → Marketplaces → Add → paste `Emerging-Tech-Visma/code-et` - -After installation, these skills are available: - -- `/code:go` — single-bug intake → Task Brief; generates FILE-REFERENCE.md (does not chain into plan-issue/implement) -- `/code:grill` — refine an idea via one-question-at-a-time interview -- `/code:prd` — synthesize a PRD from the current conversation -- `/code:plan-issue` — PRD → vertical-slice tasks anchored at `file:line` -- `/code:implement` — parallel agents in worktree isolation - -For CLAUDE.md maintenance, use the `claude-md-management` plugin (`/revise-claude-md`, `/claude-md-improver`). -For commits and PRs, use the companion `commit-commands` plugin (`/commit`, `/commit-push-pr`). - -To update after new commits are pushed: - -``` -/plugin → Marketplaces → code-et → Update -``` - -## Clone & Install from GitHub - -Clone the repo and add it as a marketplace source: - -```bash -git clone https://github.com/Emerging-Tech-Visma/code-et.git -``` - -Then in Claude Code: +Install `rust-analyzer`: ``` -/plugin marketplace add Emerging-Tech-Visma/code-et -/plugin install code@code-et +rustup component add rust-analyzer +# or: brew install rust-analyzer ``` -After installation, verify skills are available by typing `/code:` — you should see go, plan-issue, and implement. +Verify with: *"use LSP to find the definition of ``"* — if it returns a `file:line`, all three layers (env var, plugin, binary) are wired. -## Local Development +## Always-latest dependencies -Test plugin changes instantly without the install/update/restart cycle: +`/code:start` runs `cargo update` post-scaffold so every dep is at its latest semver-compatible patch. Caret pins (`dioxus = "0.7"`, `axum = "0.8"`, `sqlx = "0.8"`, `tokio = "1"`, `tower = "0.5"`, etc.) deliver the latest minor/patch automatically. -```bash -claude --plugin-dir /path/to/code-et/code-et-implementer -``` - -This loads all commands, agents, and hooks directly from disk. Works from any project folder — hook scripts resolve via `${CLAUDE_PLUGIN_ROOT}`. - -Verify: type `/code:` and confirm all skills appear. - -## Prerequisites - -- **Claude Code** — `npm install -g @anthropic-ai/claude-code` -- **Rust toolchain** — `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` -- **GitHub CLI (`gh`)** — used by plugins for PRs, issues, and code review ([install](https://cli.github.com/)) -- **Audit tools** — `cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli` - -## Getting Started +**Major bumps** (e.g., `dioxus 0.7 → 0.8` once it ships) need a manual `cargo upgrade` (cargo-edit) and a smoke test: ```bash -# Existing Rust repo: install the CI gate -/code:install-ci - -# Brand new Rust project: bootstrap from scratch -/code:bootstrap myapp --targets web,desktop,mobile --db sqlite -cd myapp -just db-migrate && just run-server -``` - -## Plugin Installation - -**Required companions:** - -``` -/plugin marketplace add knowledge-work-plugins -/plugin install engineering@knowledge-work-plugins -/plugin install rust-analyzer-lsp@claude-plugins-official -``` - -The `engineering` plugin provides `code-review`, `tech-debt`, `testing-strategy`, and `system-design` skills that code-et's CLAUDE.md delegates to (see "Clean Architecture (Rust) — controlling rules" §"Delegation map"). `rust-analyzer-lsp` provides symbol-level precision for `/code:plan-issue` and `/code:go`. - -**Other official plugins (recommended):** - -``` -/plugin install commit-commands@claude-plugins-official -/plugin install code-review@claude-plugins-official -/plugin install claude-md-management@claude-plugins-official -/plugin install frontend-design@claude-plugins-official -/plugin install feature-dev@claude-plugins-official -/plugin install skill-creator@claude-plugins-official -/plugin install agent-sdk-dev@claude-plugins-official +cargo install cargo-edit +cargo upgrade --workspace +just audit # smoke ``` -> Other LSP plugins (`pyright-lsp`, `swift-lsp`) install per language — see [LSP Setup](#lsp-setup-optional-recommended) below. - -**code-et plugin** (add marketplace, then install): - -``` -/plugin marketplace add Emerging-Tech-Visma/code-et -/plugin install code@code-et -``` - -## Skills Reference - -### code-et plugin - -| Skill | Effort | Description | -| ------------------ | ------ | -------------------------------------------------------------------------------------------- | -| `/code:go` | xhigh | **Single-bug intake** — scopes app/screen/files → Task Brief (with per-file `Layer` on Rust). Does NOT chain into plan-issue/implement. Generates FILE-REFERENCE.md (non-derivable knowledge only) | -| `/code:grill` | high | Feature-lane intake — one-question-at-a-time interview refining scope, constraints, success criteria | -| `/code:prd` | xhigh | Writes PRD to `plans/YYYY-MM-DD-.md` with `US-N`/`AC-N.M` checklist | -| `/code:plan-issue` | xhigh | **PRD-only.** LSP decomposition into vertical slices. Replaces, doesn't accumulate — superseded code deleted in same commit. Tags tasks `US-N`/`AC-N.M`/`chore:` plus `metadata.layer` on Rust projects | -| `/code:implement` | xhigh | Parallel agents in worktree isolation. Ships a dispatch template so subagents start cold with full context. Prefixes commits `US-N:`, ticks PRD checklist | -| `/code:bootstrap` | xhigh | Scaffold a pure-Rust full-stack project (axum + sqlx + Dioxus 0.7+ + tokio) with the 4-crate Clean Architecture workspace + CI gate | -| `/code:install-ci` | high | Drop the audit GitHub workflow + layer-deps validator into an existing Rust repo | - -### Official plugins +`cargo audit` (CI step 5) catches yanked or vulnerable pins on every PR. `cargo machete` (step 4) catches accumulating unused deps. -| Skill | Plugin | Description | -| ------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `/commit` | commit-commands | Creates a git commit with auto-generated message | -| `/commit-push-pr` | commit-commands | Creates branch, commits, pushes, and opens a PR in one step | -| `/clean_gone` | commit-commands | Removes local branches marked as `[gone]` (deleted on remote), including associated worktrees | -| `/code-review` | code-review | Multi-agent PR review — 5 parallel agents check CLAUDE.md compliance, bugs, git history, past PR comments, and code comments. Scores each finding by confidence | -| `/frontend-design` | frontend-design | Creates distinctive, production-grade UI components with bold design direction. Avoids generic AI aesthetics | -| `/simplify` | code-review | Reviews changed code for reuse, quality, and efficiency, then fixes issues found | -| `/revise-claude-md` | claude-md-management | Update CLAUDE.md with learnings from the current session | -| `/claude-md-improver` | claude-md-management | Audit and improve CLAUDE.md files — scans, evaluates quality, makes targeted updates | -| `/feature-dev` | feature-dev | Guided feature development with codebase understanding and architecture focus | -| `/skill-creator` | skill-creator | Create new skills, modify existing ones, run evals, benchmark performance | - -## Configuration - -### Settings (`~/.claude/settings.json`) - -All environment variables go in the `env` block of your settings file: +## Settings ```json { "env": { "ENABLE_LSP_TOOL": "1", - "CLAUDE_CODE_TASK_LIST_ID": "my-project-tasks", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" + "CLAUDE_CODE_TASK_LIST_ID": "" } } ``` -| Variable | Purpose | -| -------------------------------------- | ---------------------------------------------------- | -| `ENABLE_LSP_TOOL` | Set to `"1"` to enable LSP (required for LSP plugins) | -| `CLAUDE_CODE_TASK_LIST_ID` | Scoped task list name for persistence across sessions | -| `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` | Set to `"1"` to enable Agent Swarm team mode | - -Task persistence lets `/code:implement` resume interrupted work across sessions via a manifest file at `.claude/.json`. - -### Execution Modes - -`/code:implement` auto-selects the best mode based on task count: +`CLAUDE_CODE_TASK_LIST_ID` lets `/code:ship` resume interrupted work across sessions via `.claude/.json`. -| Mode | When | How | -|------|------|-----| -| Inline | 1 task, or 2 simple tasks | Implements directly in main session | -| Background | 2-5 independent tasks | Spawns agents in worktree isolation | -| Swarm | 5+ tasks or `--team` flag | Agent Swarm (requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`) | +## Companion plugin map -### LSP Setup (optional, recommended) +| Plugin | What it gives you | +|---|---| +| `engineering` (knowledge-work-plugins) | `code-review`, `tech-debt`, `testing-strategy`, `system-design` skills — delegated to from code-et's CLAUDE.md | +| `rust-analyzer-lsp` (official) | Symbol-level precision for `/code:fix` and `/code:plan` | +| `commit-commands` (official) | `/commit`, `/commit-push-pr`, `/clean_gone` | +| `code-review` (official) | Multi-agent PR review | +| `claude-md-management` (official) | `/revise-claude-md`, `/claude-md-improver` | -LSP powers symbol-level precision in `/code:plan-issue` (full decomposition) and `/code:go` (narrow — only when the user names a specific function/component/type). Without it, both fall back to Grep/Read — works, but less precise. +## Deploy & upload — always via scripts -Three layers need to line up. Skip any one and LSP stays dark. +`/code:start` ships `scripts/deploy.sh` and `scripts/upload.sh` in every scaffolded project, plus `just deploy ` and `just upload ` targets in the justfile. **All deploys and uploads go through these scripts** — never raw `cargo`, `docker`, `gcloud`, or `gsutil` typed into a shell. -#### 1. Env var (gates the feature) - -Add to `~/.claude/settings.json`: - -```json -{ - "env": { - "ENABLE_LSP_TOOL": "1" - } -} -``` - -Must be the string `"1"`, not `"true"`. Restart Claude Code after editing. - -#### 2. LSP plugins (configure the connection) - -Install one per language you work in: +The scripts are starting points: pre-flight (clean tree + audit gate) → build container → run migrations → roll out → smoke check. Host-specific lines (`gcloud run deploy …`, `gsutil rsync …`, `flyctl deploy …`) are marked `# TODO:` blocks — fill in once for your project. From then on every deploy is one command: ``` -/plugin install typescript-lsp@claude-plugins-official # TS/JS — detects tsconfig.json -/plugin install pyright-lsp@claude-plugins-official # Python — detects pyproject.toml -/plugin install rust-analyzer-lsp@claude-plugins-official # Rust — detects Cargo.toml -/plugin install swift-lsp@claude-plugins-official # Swift — detects Package.swift +just deploy staging +just deploy prod +just upload web prod ``` -#### 3. Language server binaries (do the work) - -Each plugin shells out to a binary that must be in `$PATH`: - -| Language | Binary | Install | -|----------|--------|---------| -| TS/JS | `typescript-language-server` | `npm i -g typescript-language-server typescript` | -| Python | `pyright-langserver` | `npm i -g pyright` | -| Rust | `rust-analyzer` | `rustup component add rust-analyzer` *or* `brew install rust-analyzer` | -| Swift | `sourcekit-lsp` | Bundled with Xcode / Swift toolchain — no separate install | - -#### 4. Verify - -In a Claude Code session: - -```bash -which typescript-language-server # or pyright-langserver, rust-analyzer -``` - -Then ask Claude: *"use LSP to find the definition of ``"* — if it returns a `file:line`, all three layers are wired. If it falls back to grep, check the env var (most common miss) and restart. - -> **Why three layers?** Env var is project-wide on/off. Plugins teach Claude *how* to talk to a specific server. Binaries do the actual analysis. Each layer is independent — diagnose by checking from the bottom up: binary in `$PATH` → plugin installed → env var set. - -### Recommended Hooks - -Auto-format on file writes with Prettier — add to `~/.claude/settings.json`: - -```json -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "Write|Edit", - "hooks": [ - { - "type": "command", - "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\"" - } - ] - } - ] - } -} -``` - -## GitHub Workflow: Multiple PRs - -You can run several features in parallel, each on its own branch with its own PR. - -### How it works - -``` -main ─────────────────────────────*────────*──── (deploy-ready) - \ / / - feature/auth ──PR #1────┘ / - \ / - feature/dashboard ──PR #2──────┘ -``` +The bundled CLAUDE.md template encodes the rule: *if you find yourself typing the underlying command directly, stop and add the missing step to the script instead.* -1. **Start a bug fix** — run `/code:go "broken submit button"` to scope, then implement the 1-3 file edits directly. - **Start a feature** — run `/code:grill` (optional) → `/code:prd` to write a PRD → `/code:plan-issue` to create vertical-slice tasks → `/code:implement` for parallel execution. Each feature gets its own branch automatically. -2. **Open a PR** — run `/commit-push-pr`. This pushes the branch and creates a pull request on GitHub. -3. **Start the next piece of work** — switch back to main (`git checkout main`), then repeat step 1. Each PR is independent. +## How it stays simple -### Multiple PRs at the same time +1. **One stack.** axum + sqlx + Dioxus + tokio. No flags for "TS or Rust", no legacy branches. +2. **Six commands.** Most days you use three (`/code:fix`, `/code:plan`, `/code:ship`). +3. **Doctrine, not docs.** Three short markdown files (`architecture`, `anti-slop`, `testing`) loaded on demand. +4. **CI is the gate.** Local hooks give fast feedback; a green CI is the merge requirement. No process can override it. +5. **Vertical slices, not layers as tasks.** Each task touches every layer it needs to touch — `/code:plan` rejects horizontal tasks at decomposition time. -Each PR is independent. You can have 2, 5, or 10 open PRs — they don't block each other unless they change the same files. +## Migrating from v3.x -- **No conflicts** — each PR merges into main on its own. Click "Merge" on GitHub when the PR is approved. -- **Conflicts** — if two PRs change the same file, GitHub will flag it. Merge the first PR, then update the second branch (`git merge main`) to resolve. +| v3.x command | v4.x replacement | +|---|---| +| `/code:bootstrap` | `/code:start` | +| `/code:go` | `/code:fix` | +| `/code:grill` + `/code:prd` + `/code:plan-issue` | `/code:plan` (one extended turn, three checkpoints) | +| `/code:implement` | `/code:ship` | +| `/code:audit` | `just audit` (or runs automatically inside `/code:ship`) | +| `/code:install-ci` | `/code:install-ci` (unchanged) | -### When does code reach production? +The doctrine files (`docs/architecture.md`, `docs/anti-slop.md`, `docs/testing.md`) and the CI gate template are unchanged — same architecture, fewer commands. -PRs sit on GitHub until someone merges them. Nothing goes to main (and therefore production) until a PR is explicitly merged. This gives the team time to review and approve. +## License -Typical flow: -1. PR is created → team reviews -2. PR is approved → click "Merge pull request" on GitHub -3. Code is now on main → ready for deploy +MIT. See [LICENSE](LICENSE). diff --git a/code-et-implementer/.claude-plugin/plugin.json b/code-et-implementer/.claude-plugin/plugin.json index c191570..08f680e 100644 --- a/code-et-implementer/.claude-plugin/plugin.json +++ b/code-et-implementer/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "code", - "version": "3.9.0", - "description": "Pure-Rust Clean Architecture workflow. Bootstrap axum+sqlx+dioxus+tokio projects, plan with PRDs, implement with parallel agents, audit on CI.", + "version": "4.0.0", + "description": "Pure-Rust Clean Architecture workflow. Six commands: start, fix, plan, ship, review, install-ci. Always-latest deps, CI audit gate, anti-slop enforced.", "author": { "name": "Kennet Kusk" }, diff --git a/code-et-implementer/.claude-plugin/settings.json b/code-et-implementer/.claude-plugin/settings.json index ee0fcbb..4ecc54e 100644 --- a/code-et-implementer/.claude-plugin/settings.json +++ b/code-et-implementer/.claude-plugin/settings.json @@ -6,9 +6,10 @@ "spinnerTipsOverride": [ "Press ctrl+t to view task progress", "Background agents run in isolated worktrees", - "Use --team for Agent Swarm mode (5+ tasks)", "Each agent must pass verification before completing", "Use commit-commands plugin for PRs (/commit-push-pr)", - "Stuck? Run /code:implement again to resume" + "Stuck? Run /code:ship again to resume", + "/code:plan writes the PRD to disk before decomposing tasks", + "/code:ship auto-retries 1 fix-pass on CRITICAL/HIGH audit findings" ] } diff --git a/code-et-implementer/CLAUDE.md b/code-et-implementer/CLAUDE.md index 640a011..94008c9 100644 --- a/code-et-implementer/CLAUDE.md +++ b/code-et-implementer/CLAUDE.md @@ -1,40 +1,36 @@ -# code-et +# code-et v4.x -Task-driven coding workflow with parallel agents in worktree isolation. +Pure-Rust Clean Architecture workflow. Stack: `axum + sqlx + Dioxus 0.7+ + tokio`. One frontend codebase renders to web, desktop, and mobile. ## Git Rules -- **Never push directly to main** — always create a feature branch and PR -- **Branch naming:** `feature/`, `fix/`, `chore/` -- **Never force push** — rebase locally, push normally +- **Never push directly to main** — always create a feature branch and PR. +- **Branch naming:** `feature/`, `fix/`, `chore/`. +- **Never force push** — rebase locally, push normally. ## Commands | Task | Command | |------|---------| -| Single-bug intake | `/code:go` (scope → Task Brief) | -| Refine an idea | `/code:grill` (one-question-at-a-time interrogation) | -| Synthesize PRD | `/code:prd` (idea/conversation → `plans/YYYY-MM-DD-.md`) | -| Plan PRD | `/code:plan-issue` (PRD → vertical-slice tasks) | -| Implement tasks | `/code:implement` (parallel agents in worktrees) | +| Scaffold a new project | `/code:start ` | +| Add CI to existing repo | `/code:install-ci` | +| Single bug fix | `/code:fix` (intake → Task Brief; you implement) | +| Feature, end-to-end | `/code:plan` (idea → PRD → tasks) → `/code:ship` (parallel agents + audit) | +| Pre-merge gate | `/code:review` (audit + diff review) | For commits and PRs use `commit-commands` plugin (`/commit`, `/commit-push-pr`). -For code review use `code-review` plugin (`/code-review`, `/simplify`). -For CLAUDE.md maintenance use `claude-md-management` plugin (`/revise-claude-md`, `/claude-md-improver`). ## Workflow — two lanes -**Bug lane** (single fix): `/code:go` → implement directly → `/commit-push-pr` +**Bug lane.** `/code:fix` produces a Task Brief and stops. Most bugs are 1-3 file edits — no orchestration needed. If the work spans multiple coherent vertical slices (UI ↔ logic ↔ API ↔ DB), it's a feature in disguise — write a PRD instead. -**Feature lane** (PRD-driven): `/code:grill` (optional) → `/code:prd` → `/code:plan-issue` → `/code:implement` → `/commit-push-pr` +**Feature lane.** `/code:plan` (one extended turn: refined brief → PRD on disk → vertical-slice tasks) → `/code:ship` (parallel worktree agents + post-merge audit + 1 auto-retry on CRITICAL/HIGH) → `/code:review` (pre-merge gate) → `/commit-push-pr`. -`/code:go` does NOT chain into `/code:plan-issue` or `/code:implement` — it stops at the Task Brief. If a "bug" needs vertical decomposition (UI ↔ logic ↔ API ↔ DB), it's a feature in disguise — write a PRD. - -`/code:plan-issue` is PRD-only. Each task is a **vertical slice** (UI ↔ logic ↔ API ↔ DB, end-to-end testable). When a slice supersedes existing code, deletion of the old code is part of the same commit — no parallel utilities, no `// TODO: remove old X`. +Each task is a **vertical slice**: UI ↔ logic ↔ API ↔ DB, end-to-end testable. When a slice supersedes existing code, deletion of the old code is part of the same commit — no parallel utilities, no `// TODO: remove old X`. ## Task Metadata Convention -Tasks created with `TaskCreate` should include metadata: +Tasks created with `TaskCreate` carry: ``` metadata: { @@ -42,24 +38,26 @@ metadata: { files: ["crates//src/path/to/file.rs:42"], expected_outcome: "what success looks like", rationale: "why this task exists — the constraint or decision driving it", - user_story: "US-N" | "AC-N.M" | "chore:", // feature lane only + user_story: "US-N" | "AC-N.M" | "chore:", layer: "domain" | "application" | "infrastructure" | "interface" | "chore" } ``` -`rationale` is mandatory. Subagents in `/code:implement` start cold — they need the *why*, not just the *what*, to make judgment calls. +`rationale` is mandatory. Subagents in `/code:ship` start cold — they need the *why*, not just the *what*, to make judgment calls. -`layer` is mandatory on Rust projects (skip for non-Rust legacy projects). Each *file* declares its layer; vertical slices may span layers. +`layer` is mandatory. Each *file* declares its layer; vertical slices may span layers. ## Code Standards -- Rust 2024 edition; `cargo clippy --all-targets -- -D warnings`; `cargo fmt --check` -- Max 600 lines per file -- Compose at app boundaries (`apps//main.rs`); never instantiate `infrastructure` inside `interface` — pass via constructor or DI trait +- Rust 2024 edition; `cargo clippy --all-targets -- -D warnings`; `cargo fmt --check`. +- Max 600 lines per file. +- Compose at app boundaries (`apps//main.rs`); never instantiate `infrastructure` inside `interface` — pass via constructor or DI trait. +- All SQL through `sqlx::query!` / `query_as!` (compile-time-checked). Raw `sqlx::query` is forbidden in production code. +- Forward-only migrations under `migrations/`. Each rollback is its own forward migration. -## Clean Architecture (Rust) — controlling rules +## Clean Architecture — controlling rules -When operating on a Rust project, apply Clean Architecture per [`docs/architecture.md`](docs/architecture.md). Each new or modified file declares its layer (`domain` | `application` | `infrastructure` | `interface` | `chore`) in `metadata.layer`. Imports point inward; the `Cargo.toml` workspace deps already enforce this — violating imports fail at `cargo build`. +Apply Clean Architecture per [`docs/architecture.md`](docs/architecture.md). Each new or modified file declares its layer (`domain` | `application` | `infrastructure` | `interface` | `chore`) in `metadata.layer`. Imports point inward; the workspace `Cargo.toml` deps already enforce this — violating imports fail at `cargo build`. UI: Dioxus 0.7+ for web, desktop, and mobile from one component tree. DB: Postgres on GCP Cloud SQL for prod, SQLite for local. `sqlx` with `query!` (compile-time-checked); never raw SQL. Forward-only migrations. @@ -91,8 +89,12 @@ Token waste = worse plans + worse code. ## FILE-REFERENCE.md Lifecycle -`FILE-REFERENCE.md` is updated **only after a PR merges to main** that touched structural files. For Rust projects: `crates/*/Cargo.toml`, `apps/*/Cargo.toml`, root `Cargo.toml`, `migrations/*`, or new top-level apps/crates. For TS legacy projects: `*/page.tsx`, `*/route.ts`, `*/route.tsx`, `*/index.tsx`, `*/layout.tsx`, or new top-level apps/packages. Workflow: +`FILE-REFERENCE.md` is updated **only after a PR merges to main** that touched structural files: `crates/*/Cargo.toml`, `apps/*/Cargo.toml`, root `Cargo.toml`, `migrations/*`, or new top-level apps/crates. Workflow: 1. All changes start on a branch (`feature/`, `fix/`, `chore/`) and ship via PR. -2. After merging the PR to main, if the diff touched structural files, run `/code:go update` to refresh `FILE-REFERENCE.md`. Commit the refresh on a follow-up branch + PR. +2. After merging the PR to main, if the diff touched structural files, run `/code:fix update` to refresh `FILE-REFERENCE.md`. Commit the refresh on a follow-up branch + PR. 3. On a feature branch, do **not** edit `FILE-REFERENCE.md` — it tracks merged-to-main reality, not in-flight work. + +## Always-latest dependencies + +`/code:start` runs `cargo update` post-scaffold so every dep is at its latest semver-compatible patch. Major bumps (e.g., dioxus 0.7→0.8 once it ships) need a manual `cargo upgrade` (cargo-edit) and a `just audit` smoke. The CI gate's `cargo audit` step catches yanked or vulnerable pins on every PR. diff --git a/code-et-implementer/commands/bootstrap.md b/code-et-implementer/commands/bootstrap.md deleted file mode 100644 index 80c985b..0000000 --- a/code-et-implementer/commands/bootstrap.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -tools: Read, Bash, Glob, Skill, AskUserQuestion -description: "Scaffold a pure-Rust Clean Architecture project (axum + sqlx + dioxus + tokio)." -argument-hint: "[project-name] [--targets web,desktop,mobile] [--db sqlite|postgres] [--install-tools] [--force]" -effort: xhigh ---- - -Scaffold a new pure-Rust full-stack project from `${CLAUDE_PLUGIN_ROOT}/templates/rust/dioxus-fullstack/` with the CI gate, validator, and Clean Architecture skeleton. - -**Refuses to run** if CWD has `Cargo.toml` or `package.json` (avoids overwriting existing projects), unless `--force` is passed. - -## Inputs - -Parse `$ARGUMENTS`: -- **project name** (positional, required) — kebab-case, must match `^[a-z][a-z0-9-]{1,40}$`. Becomes the workspace root dir name. -- `--targets web,desktop,mobile` — comma-separated subset; default = all three. Mobile is best-effort. -- `--db sqlite|postgres` — default `sqlite`. (Postgres only switches `.env.example`; the template supports both at runtime.) -- `--install-tools` — also run `cargo install` for the audit toolchain. Default: print checklist only. -- `--force` — proceed even if CWD has a `Cargo.toml`/`package.json`. -- `--owner GH_USER_OR_ORG` — GitHub owner used to substitute `{{owner}}` in `Cargo.toml`'s `repository` field. Defaults to `git config user.name` munged to kebab-case if available, else `your-org`. - -If any required input is missing, ask via `AskUserQuestion` (one focused question per missing field, max two questions total). - -## Procedure - -1. **Pre-flight.** - ``` - Bash("test -f Cargo.toml || test -f package.json && echo CONFLICT || true") - ``` - If `CONFLICT` and not `--force`, stop with: *"This directory already has a Rust or TS project. Re-run with `--force` if you intend to overlay."* - -2. **Resolve template root.** - ``` - Bash('TPL="${CLAUDE_PLUGIN_ROOT}/templates/rust/dioxus-fullstack" && SHARED="${CLAUDE_PLUGIN_ROOT}/templates/shared" && echo "$TPL $SHARED"') - ``` - -3. **Copy template + shared assets** into a new directory `/`. - ``` - Bash('mkdir -p "" && cp -R "$TPL"/. "/" && cp -R "$SHARED/.github" "/.github" && mkdir -p "/scripts" && cp "$SHARED/scripts/layer-deps-validator.sh" "/scripts/" && cp "$SHARED/CLAUDE.md.template" "/CLAUDE.md"') - ``` - -4. **Filter apps by `--targets`.** If a target is not selected, remove its app dir + workspace member entry: - ``` - Bash('for t in web desktop mobile; do - case ",," in *",$t,"*) ;; *) rm -rf "/apps/$t"; sed -i.bak "/\\\"apps\\/$t\\\"/d" "/Cargo.toml" && rm "/Cargo.toml.bak";; esac - done') - ``` - The `server` app always stays (it's the SSR + API root). - -5. **Substitute placeholders** (`{{name}}`, `{{owner}}`, `{{db}}`) across the copied files. Use `find -print0 | xargs -0 sed -i.bak …` and clean `.bak` afterwards. Files to touch: `Cargo.toml`, `apps/**/Cargo.toml`, `crates/**/Cargo.toml`, `Dioxus.toml`, `CLAUDE.md`, `README.md`, `justfile`, `.env.example`. - -6. **Switch DB.** If `--db postgres`, edit `.env.example` to uncomment the postgres line and comment out the sqlite default. (No code change required — the template's `infrastructure` already supports both.) - -7. **chmod the validator.** - ``` - Bash('chmod +x "/scripts/layer-deps-validator.sh"') - ``` - -8. **Compile-check.** - ``` - Bash('cd "" && cargo check --workspace 2>&1 | tail -40 || echo "cargo check failed — see output above"') - ``` - The template's `infrastructure` uses runtime-checked `sqlx::query_as`, so check passes without a DB. If check fails, surface the error and stop — the template is broken, not the user. - -9. **Install tools.** If `--install-tools`, run: - ``` - Bash('cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli') - ``` - Otherwise print the checklist: - ``` - cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli - # optional (requires nightly toolchain): - rustup toolchain install nightly && cargo install --locked cargo-udeps - ``` - -10. **Print next steps.** - ``` - ✓ Bootstrapped at $(pwd)/ - - Next: - cd - cp .env.example .env - just db-migrate - just run-server # or run-web / run-desktop - just audit # local mirror of CI - - Then: - gh repo create --public --source=. --remote=origin --push - # CI runs on first push; clean run = ready for /code:grill or /code:go - ``` - -## Output - -Single line summary: `"Bootstrapped with targets , db . Run cd && just audit to verify."` - -``` -Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Bootstrap complete' --subtitle '' || true") -``` - -## Notes for the agent - -- **Never** scaffold inside this plugin's own repo — refuse if `pwd` contains `code-et-implementer`. -- **Never** auto-run `git init` if the user's CWD is already a git repo (they may want a sub-project layout). Run `git init` only when the new project's parent dir has no `.git`. -- **Never** install tools without `--install-tools`. Surface the checklist and wait. -- The CI workflow already lives at `.github/workflows/code-et-audit.yml` after step 3 — no separate copy needed. -- Apply the rules from `code-et-implementer/CLAUDE.md` §"Clean Architecture (Rust)" — Dioxus 0.7+, sqlx with parameter binding (migrate to `query!` macros once schema stabilises), forward-only migrations. diff --git a/code-et-implementer/commands/fix.md b/code-et-implementer/commands/fix.md new file mode 100644 index 0000000..104ecb7 --- /dev/null +++ b/code-et-implementer/commands/fix.md @@ -0,0 +1,132 @@ +--- +tools: Read, Grep, Glob, Bash, Agent, LSP +description: "Single-bug intake — scope work into a Task Brief. You implement directly. Generates/updates FILE-REFERENCE.md." +argument-hint: "[bug description] or 'update' to refresh FILE-REFERENCE.md" +effort: xhigh +--- + +# Fix — Single-Bug Intake + +You are an intake assistant. Your job is to scope **one bug fix** into a precise Task Brief — exact app, exact files (with `Layer`), nothing more. The user takes the brief and implements directly. + +This is a pure-Rust workflow: every project follows the four-crate Clean Architecture (`domain` | `application` | `infrastructure` | `interface`) from `code-et-implementer/docs/architecture.md`. Each touched file declares its layer. + +**Scope guard.** `/code:fix` is for single, contained bug fixes (1-3 file edits). If the work spans multiple coherent vertical slices (UI + logic + API + DB layered for a real feature), stop and route to `/code:plan`. Do not auto-chain. + +## Step 0 — Ensure FILE-REFERENCE.md exists + +Check `FILE-REFERENCE.md` at the project root. It holds **non-derivable knowledge only** — apps overview, hot paths, landmines, module invariants, schema purposes, domain rules. File inventories (routes, components, screens) are reachable via `Glob` on demand; do not enumerate. + +**If missing**, or if `$ARGUMENTS` contains "update": + +1. Scan only the non-derivable parts: + - **Apps Overview** (≤5 lines): one-line purpose per app under `apps/*` from its `CLAUDE.md` or `README`. + - **Hot Paths**: entry points — files that run on every primary user action vs once at startup. 3-5 max per bucket. + - **Landmines**: top-level `CLAUDE.md` and per-directory `CLAUDE.md` for "never"/"do not" rules; Grep for `@deprecated`, `// DO NOT USE`, `// LEGACY`. One row per rule + reason. + - **Module Invariants**: top-of-file docstrings for non-obvious constraints (per top-level module/crate). + - **Database Schema** (if applicable): `migrations/**` — one row per table, purposes only (names are derivable). + - **Domain Rules / DSL** (optional): ≤10-line summary, link to source. + +2. Build `FILE-REFERENCE.md` with the structure below. **Skip any section with no content.** + +```markdown +# FILE-REFERENCE.md + +Non-derivable project knowledge for `/code:fix` intake and `/code:plan` context. +**File inventories live in the filesystem.** Glob for routes, components, schemas on demand. + +## Apps Overview + +| App | Purpose | Root path | +|-----|---------|-----------| + +## Hot Paths + +| Path type | Files | +|-----------|-------| +| Per-request | … | +| Startup-only | … | + +## Landmines + +| Rule | Why | +|------|-----| + +## Module Invariants + +| Module | Invariant | +|--------|-----------| + + + +## Database Schema + +| Table | Purpose | Key relations | +|-------|---------|---------------| + +## Domain Rules / Grammar + +≤10 lines. Link to source. +``` + +3. Write the file, tell the user: *"Created FILE-REFERENCE.md — review and let me know if anything is missing."* On `update`, preserve hand-edited sections; refresh only re-derivable parts. +4. If this was an `update` request, stop. Otherwise continue to Step 1. + +**If it exists**, read it and continue. + +## Step 1 — Read the reference + +Read `FILE-REFERENCE.md`. + +## Step 2 — Understand the request + +Identify: +- **Bug class**: visual regression, broken behaviour, API error, data inconsistency, perf issue, etc. +- **Which app**: pick from Apps Overview (don't guess). +- **Which area**: handler, component, repo, use case. + +If the request describes a multi-slice feature, stop and route to `/code:plan`. + +## Step 3 — Ask clarifying questions + +Numbered list, max 3-4 questions: + +1. **Which app?** (only if ambiguous — use Apps Overview) +2. **Which file/area?** Once the app is picked, `Glob 'crates/*/src/**/*.rs'` or `Glob 'apps//**/*.rs'` to surface concrete options. +3. **What exactly should change?** (behavior, visual, data, API) +4. **Any related areas that might be affected?** + +Skip whatever the user already answered. + +## Step 4 — Output the Task Brief + +``` +## Task Brief + +**Type:** [bug fix / styling / refactor / API change] +**App:** [app name from Apps Overview] +**Area:** [crate or module] +**Description:** [1-2 sentence summary] + +### Files to touch +| File | Layer | Why | +|------|-------|-----| +| `crates//src/...rs` | domain/application/infrastructure/interface | reason | + +### Related files (check for impact) +| File | Layer | Why | +|------|-------|-----| +``` + +The `Layer` column is mandatory. Use file paths discovered via `Glob`/`Grep`. Reference FILE-REFERENCE for app names, hot paths, landmines that touch the affected files. + +**LSP precision (only if user named a symbol):** if the request references a specific function/type/component by name, use `LSP definition`/`references` once to pin `file:line`. Skip otherwise — Glob paths are enough. Never bulk-scan with LSP. + +## Rules + +- Concise — don't dump the reference back at the user. +- ≤4 questions, not a wall. +- If the user already gave enough context, skip straight to the Task Brief. +- Reference concrete paths (from Glob) so the user can point and say "that one". +- Description ≤2 sentences using fragments. File "Why" column ≤6 words. No hedging or filler. +- **Context budget**: FILE-REFERENCE = constraints + orientation. Glob/Grep = file discovery. LSP = scalpel for named symbols. Never read whole files in `/code:fix` — that's `/code:plan`'s job. diff --git a/code-et-implementer/commands/go.md b/code-et-implementer/commands/go.md deleted file mode 100644 index 6912a16..0000000 --- a/code-et-implementer/commands/go.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -tools: Read, Grep, Glob, Bash, Agent, LSP -description: "Single-bug intake — scope work into a Task Brief. Also generates/updates FILE-REFERENCE.md." -argument-hint: "[bug description] or 'update' to refresh FILE-REFERENCE.md" -effort: xhigh ---- - -# Go — Single-Bug Intake - -You are an intake assistant. Your job is to scope **one bug fix** into a precise Task Brief — exact app, screen, files. The user takes the brief and implements directly. - -For Rust projects, apply the Clean Architecture rules from `code-et-implementer/CLAUDE.md` §"Clean Architecture (Rust) — controlling rules" and `code-et-implementer/docs/architecture.md`. The Task Brief includes a `Layer` row per touched file (`domain` | `application` | `infrastructure` | `interface`). - -**Scope guard.** `/code:go` is for single, contained bug fixes. If the work spans multiple coherent vertical slices (UI + logic + API + DB layered for a real feature), stop and tell the user to use `/code:prd` → `/code:plan-issue` → `/code:implement` instead. Don't auto-chain — `/code:go` ends at the Task Brief. - -## Step 0 — Ensure FILE-REFERENCE.md exists - -Check if `FILE-REFERENCE.md` exists at the project root. - -`FILE-REFERENCE.md` holds **non-derivable knowledge only** — apps overview, hot paths, landmines, module invariants, schema purposes, domain rules. File inventories (routes, components, screens, API endpoints) are reachable via `Glob` on demand. Do NOT enumerate them here — duplicating the filesystem just bloats tokens. - -**If it does NOT exist** (first run), or if `$ARGUMENTS` contains "update": - -1. Scan for the non-derivable parts only: - - **Apps Overview** (≤5 lines): Glob top-level dirs (`apps/*`, `packages/*`, `src/`); one-line purpose per app from its CLAUDE.md or README - - **Hot Paths**: identify entry points — files that run on every primary user action vs once at startup. 3-5 max per bucket - - **Landmines**: read top-level CLAUDE.md and per-directory CLAUDE.md for "never"/"do not" rules; Grep for `@deprecated`, `// DO NOT USE`, `// LEGACY`. One row per rule + reason - - **Module Invariants**: read top-of-file docstrings for non-obvious constraints (per top-level module) - - **Database Schema** (if applicable): Glob for `prisma/schema.prisma`, `**/schema.sql`, `**/migrations/**`. One row per table — names are derivable, purposes aren't - - **Domain Rules / DSL** (if applicable): Glob for `rules.md`, `*.lark`, `*.peg`. ≤10-line summary, link to source - -2. **Do NOT enumerate** routes, components, screens, API endpoints, or file paths in general. `Glob '**/page.tsx'`, `Glob '**/route.ts'` etc. retrieve them on demand at the planning step. - -3. Build `FILE-REFERENCE.md` with the structure below. **Skip any section that has no content — no empty stubs.** - -```markdown -# FILE-REFERENCE.md - -Non-derivable project knowledge for `/code:go` intake and `/code:plan-issue` context. -**File inventories live in the filesystem.** Glob for routes, components, schemas on demand — they are NOT enumerated here. - -## Apps Overview - -| App | Purpose | Root path | -|-----|---------|-----------| -| App Name | One-line purpose | `apps/name/` | - -## Hot Paths - -Files that run on every primary user action vs once at startup. Used to gauge blast radius. - -| Path type | Files | -|-----------|-------| -| Per-request | `app/api/handler.ts`, `lib/auth/verify.ts` | -| Startup-only | `lib/config/load.ts`, `db/migrate.ts` | - -## Landmines - -Never-do rules with reasons. One row per rule. - -| Rule | Why | -|------|-----| -| Never use `legacyClient` from `lib/old-client.ts` | Replaced by `lib/client.ts` in v3; legacy lacks retry | - -## Module Invariants - -Per top-level module: the non-obvious constraint callers must respect. - -| Module | Invariant | -|--------|-----------| -| `lib/billing` | All amounts are integer cents, never floats | -| `app/api/auth` | Routes must run on Node runtime, not Edge | - - - -## Database Schema - -One row per table. Names are derivable; purposes and key relations aren't. - -| Table | Purpose | Key relations | -|-------|---------|---------------| -| `users` | Auth principals | `sessions.user_id`, `orgs.owner_id` | - -## Domain Rules / Grammar - -≤10 lines. Just enough that a planner doesn't need to re-read the source. Link to it. -``` - -4. Write the file and tell the user: "Created FILE-REFERENCE.md — review it and let me know if anything is missing." When updating, preserve any hand-edited sections; only refresh the sections you can re-derive. -5. If this was an "update" request, stop here. Otherwise continue to Step 1. - -**If it exists**, read it and continue. - -## Step 1 — Read the reference - -Read `FILE-REFERENCE.md` at the project root. This is your map of every app, screen, and file. - -## Step 2 — Understand the request - -Read what the user said (their initial message or args). Identify: -- **Bug class**: visual regression, broken behaviour, API error, data inconsistency, perf issue, etc. -- **Which app(s)**: pick from the Apps Overview in `FILE-REFERENCE.md` (don't guess from memory) -- **Which screen/area**: e.g. "home page", "editor", "step modal", "dashboard" - -If the request describes a multi-slice feature rather than a single bug, stop and route to `/code:prd` → `/code:plan-issue` → `/code:implement` (see scope guard at top). - -## Step 3 — Ask clarifying questions - -Ask **only the questions you need** to narrow down: - -1. **Which app?** (if ambiguous — use FILE-REFERENCE Apps Overview; skip if obvious) -2. **Which screen/area?** Once the app is picked, `Glob 'apps//**/page.tsx'` (or equivalent) to list concrete screens. FILE-REFERENCE doesn't enumerate them. -3. **What exactly should change?** (behavior, visual, data, API) -4. **Any related areas that might be affected?** - -Format your questions as a numbered list. Reference apps from FILE-REFERENCE and discover concrete screens via `Glob` so the user picks from real options rather than guessing. - -## Step 4 — Output a scoped summary - -Once you have answers, output a **Task Brief** in this format: - -``` -## Task Brief - -**Type:** [bug fix / styling / refactor / API change] -**App:** [app name from FILE-REFERENCE.md Apps Overview] -**Screen:** [specific screen name from FILE-REFERENCE.md] -**Description:** [1-2 sentence summary of what needs to happen] - -### Files to touch -| File | Layer | Why | -|------|-------|-----| -| `path/to/file` | domain/application/infrastructure/interface | reason | - -### Related files (check for impact) -| File | Layer | Why | -|------|-------|-----| -| `path/to/file` | domain/application/infrastructure/interface | reason | -``` - -The `Layer` column is required on Rust projects; omit on legacy TS projects without a 4-crate workspace. - -Use file paths discovered via `Glob`/`Grep`. Reference FILE-REFERENCE for app names, hot paths, and any landmines that touch the affected files. Only list files that are actually relevant. - -**LSP precision (only if user named a symbol):** if the request references a specific function, component, type, or hook by name, use `LSP definition`/`references` once to pin `file:line`. Skip otherwise — Glob paths are enough. Never bulk-scan with LSP. - -## Rules - -- Be concise — don't dump the whole reference file back at the user -- Ask max 3-4 questions, not a wall of questions -- If the user already gave enough context, skip straight to the Task Brief -- Reference concrete screen names (from Glob) and apps (from FILE-REFERENCE) so the user can point and say "that one" -- **Task Brief format**: Description ≤2 sentences using fragments. File "Why" column ≤6 words. No hedging or filler. -- **Context budget**: FILE-REFERENCE = constraints + orientation (apps, hot paths, landmines, invariants). Glob/Grep = file discovery. LSP = scalpel for named symbols. Never read whole files in `/code:go` — that's `/code:plan-issue`'s job. See Context Hygiene in `code-et-implementer/CLAUDE.md`. diff --git a/code-et-implementer/commands/grill.md b/code-et-implementer/commands/grill.md deleted file mode 100644 index 8d338fa..0000000 --- a/code-et-implementer/commands/grill.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -tools: Read, Grep, Glob, Bash, Agent -description: "Grill an idea into a refined brief — one question at a time, codebase-first, converges automatically." -argument-hint: "[rough idea or @brief-file]" -effort: high ---- - -# Grill — Idea Refinement - -Turn a rough idea into a refined brief you can hand to `/code:prd`. Ask one question at a time with a recommended answer. Stop when every open decision is resolved. - -## Rules - -1. **If a question can be answered from the codebase, answer it yourself.** Use Read/Grep/Glob/Bash (git log). Do not ask the user. -2. **Ask one question per message.** Number it. Include a recommended answer and 1-sentence rationale. -3. **Accept "you decide" as a final answer.** Record the recommendation as the decision. Do not re-ask. -4. **Accept "defer: "** — mark deferred, move on. -5. **Stop rule:** when every item in the decisions ledger is resolved (`answered` | `recommended-accepted` | `deferred`), announce completion and print the refined brief. - -## Decisions Ledger - -Maintain an in-session ledger. Each entry: -- `id` (D-1, D-2, …) -- `question` -- `recommendation` -- `state`: `pending` | `answered` | `recommended-accepted` | `deferred` -- `final_answer` - -Present the ledger summary after every 3 answered questions so the user can see progress. - -## Process - -1. **Parse input.** `$ARGUMENTS` is either the rough idea as text or `@path/to/brief.md`. If `@path`, read the file. -2. **Explore context.** Run `Glob` on `**/*.md` for existing plans, `git log --oneline -20`, and scan key config files. Build a project-state snapshot. -3. **Seed the ledger.** From the input and context, draft 5-10 open decisions. Common axes: scope, actor, data model, UI surface, integration points, non-goals, success criteria. -4. **Interrogate.** One question at a time. Resolve codebase-answerable items yourself before asking. -5. **Converge.** When ledger is fully resolved, print: - -``` -## Refined Brief - -**Idea:** <1-sentence> -**Actors:** -**Scope:** -**Key decisions:** -- D-1: -- D-2: … - -**Suggested slug:** - -Next: run /code:prd to convert this brief into a PRD file. -``` - -## Brevity - -Drop filler. Questions ≤2 sentences. Recommendations ≤1 sentence. diff --git a/code-et-implementer/commands/implement.md b/code-et-implementer/commands/implement.md deleted file mode 100644 index 3a410c3..0000000 --- a/code-et-implementer/commands/implement.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -background: true -tools: Bash, Bash(gh:*), Bash(git:*), Read, Edit, Grep, Glob, Agent, Skill, TaskCreate, TaskList, TaskGet, TaskUpdate -description: Implement pending tasks with parallel agents. Tags commits with US-N. Ticks PRD checklist. -argument-hint: [task-id] -effort: xhigh ---- - -Load pending tasks from `TaskList` or `.claude/${CLAUDE_CODE_TASK_LIST_ID}.json`. If on main, create a feature branch. - -Every task runs as a forked subagent in its own worktree. Dispatch via the `Agent` tool with `isolation: "worktree"` and `subagent_type: "general-purpose"`, passing the prompt template below as the agent's first (and only) turn. Do NOT shell out to `git worktree add` — `isolation: "worktree"` handles it and the fork inherits no parent process state (requires `CLAUDE_CODE_FORK_SUBAGENT=1` on external builds; already on by default in the harness this skill ships under). - -Use the dependency graph to run independent tasks in parallel. Independent tasks MUST be dispatched in a single message with multiple `Agent` tool calls so they run concurrently — never serialize what could fan out. - -## Dispatch prompt template - -Each subagent starts cold. Send one comprehensive first turn — intent, constraints, acceptance criteria, `file:line` anchors, verification, and rationale — so the subagent can operate autonomously without back-and-forth. Use this template verbatim (fill `<…>` from task metadata and PRD): - -``` -# Task : - -## Tag -<metadata.user_story — one of: US-N | AC-N.M | chore:<reason> | none> - -## PRD context (if US-N or AC-N.M) -<Paste the matching US-N / AC-N.M block from the active PRD, resolved via ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh. Include the parent User Story and all its ACs so the agent sees the full intent.> - -## Rationale -<metadata.rationale — why this task exists; the constraint or decision driving it. Verbatim from plan.> - -## Files to touch -<metadata.files — exact file:line anchors from LSP. Read these before editing.> - -## Expected outcome -<metadata.expected_outcome — observable success criterion.> - -## Verification -Run `<metadata.verification>`. Must exit 0. All existing tests must still pass. - -## Constraints -- Follow rules in `code-et-implementer/CLAUDE.md` (Brevity, Context Hygiene, Clean Architecture (Rust) controlling rules, ≤600 lines/file). For Rust projects, see `code-et-implementer/docs/architecture.md` for layer rules and `code-et-implementer/docs/anti-slop.md` for the anti-slop hard rules -- Each new or modified file belongs to exactly one layer (`domain` | `application` | `infrastructure` | `interface` | `chore`); imports point inward -- Read in slices: `Read(offset, limit)` for files >200 lines; never re-read the same file twice for different blocks -- Delegate breadth to `Agent(subagent_type: "Explore")` if the fix path is unclear — do not Grep-and-Read your way through unknown territory -- Every acceptance criterion must have a corresponding test -- No scope expansion — implement exactly what the task specifies; flag adjacent issues instead of fixing inline -- If the slice supersedes existing code, delete the superseded code in the same commit. No parallel utilities, no `// TODO: remove old X`. New code obsoletes old. -- Commit format: `<prefix>: <subject>` where prefix is US-N | AC-N.M | chore (or no prefix if tag is `none`) - -## Deliverables (subagent reports back) -1. Code changes committed in the isolated worktree -2. Passing verification -3. Single commit with correct prefix -4. PRD checkbox ticked (if US-N) — flip `- [ ] US-N` to `- [x] US-N` and stage with the commit -5. Final report: commit SHA, branch name, worktree path (returned by the `Agent` tool result) - -Do not merge back to the parent feature branch — the orchestrator handles that. Do not ask clarifying questions. If blocked, flag in the final report with a specific file:line reference. -``` - -## Each subagent must - -1. Implement the task. Ensure every acceptance criterion has a corresponding test. -2. Run `metadata.verification` — code must compile, all tests must pass. -3. **Commit with US-N prefix.** If `metadata.user_story` is set, format the commit message as: - - `US-N: <subject>` when tag is `US-N` - - `AC-N.M: <subject>` when tag is `AC-N.M` - - `chore: <subject>` when tag starts with `chore:` - - No prefix when tag is `none` or absent (ad-hoc task). -4. **Tick the PRD checkbox.** If `metadata.user_story` is `US-N`, resolve the PRD via `${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh` and use `Edit` to flip `- [ ] US-N` to `- [x] US-N` on that exact line. Stage the PRD change with the agent's commit. - -The subagent stops after step 4 and returns. It must NOT merge or remove its own worktree — it has no view of the parent's feature branch from inside the isolated worktree. - -## Orchestrator (this skill) must - -After each `Agent` call returns: -1. Read the returned worktree path and branch from the tool result. -2. From the parent feature branch, `git merge --no-ff <subagent-branch>` to bring the commit in. -3. Remove the worktree with `git worktree remove <path>` (the harness auto-cleans empty worktrees, but populated ones need explicit removal after merge). -4. Mark the task completed via `TaskUpdate` only after the merge lands. - -If a subagent reports failure, leave the worktree in place for inspection — do not auto-discard work. - -When done, run `Skill("simplify")` and report summary. - -``` -Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Implement Done' --subtitle 'All tasks complete' || true") -``` diff --git a/code-et-implementer/commands/plan-issue.md b/code-et-implementer/commands/plan-issue.md deleted file mode 100644 index a5b818e..0000000 --- a/code-et-implementer/commands/plan-issue.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -tools: Read, Grep, Glob, Bash, LSP, Agent, TaskCreate, TaskUpdate, TaskList, TaskGet, Skill -description: "Plan a PRD-driven feature into vertical-slice tasks anchored at file:line." -argument-hint: "[feature-description]" -effort: xhigh ---- - -Decompose a PRD into vertical-slice tasks. **PRD-only** — if there's no PRD, route the user to `/code:prd` first (or `/code:go` for a single bug). - -For Rust projects, apply the Clean Architecture rules from `code-et-implementer/CLAUDE.md` §"Clean Architecture (Rust) — controlling rules" and `code-et-implementer/docs/architecture.md`. Each task carries `metadata.layer` (per-file). Vertical slices may span layers; each *file* belongs to exactly one. - -## Context Budget - -Quality of plan = quality of context. Bad context = bad tasks. - -Follow Context Hygiene rules in `code-et-implementer/CLAUDE.md` (slice reads, parallel Explore, trim attachments). Plus, plan-issue specifics: - -1. **FILE-REFERENCE.md = constraints + orientation.** Apps overview, hot paths, landmines, invariants. File inventories are NOT in here — Glob the filesystem for routes/components/schemas when you need them. -2. **Order of ops:** Read FILE-REFERENCE first (cheap, has the rules) → Glob for files in the affected area → LSP to pin symbols. -3. **LSP for symbols, not sweeps.** Use `documentSymbol`/`findReferences`/`definition` to anchor `file:line`. Never use LSP to enumerate a whole project. For Rust projects, the `rust-analyzer-lsp` companion plugin is the LSP backend; for TS legacy projects, `typescript-lsp`. -4. **Tip for token-tight sessions:** scope to one user story or AC per `/code:plan-issue` invocation. Smaller batches keep the context window cool and let `/code:implement` finish before the next plan. - -Better: 5 tasks with sharp `file:line` + real rationale. Worse: 15 tasks with vague paths. - -## Decomposition Rules (apply to every task) - -### Vertical slicing — non-negotiable - -Each task implements **one full vertical slice**: UI ↔ logic ↔ API ↔ database, end-to-end and testable. A task that touches only one layer is wrong — split or merge until each task ships a working slice. - -- ✗ "Add API endpoint" + "Wire UI button" + "Migrate schema" — three half-tasks -- ✓ "Submit-feedback flow: form → API → `feedback` row → email confirmation" — one slice - -`metadata.verification` must exercise the full slice end-to-end, not a single layer. - -### Replace, don't accumulate - -If a slice supersedes existing logic, the task scope **includes deletion of the superseded code**. No duplicate utilities, no dead branches, no `// TODO: remove old X`. New code obsoletes old in the same commit. State the path:line of what's being replaced in `metadata.rationale`. - -## Procedure - -1. Resolve active PRD via `Bash("${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh")`. If no PRD is returned, stop and tell the user: "No PRD found. Run `/code:prd` to create one, or `/code:go` if this is a single bug." -2. Read the PRD; treat it as the authoritative spec. -3. Use LSP (`documentSymbol`, `findReferences`, `definition`) to anchor each US/AC to `file:line`. Grep/Glob for discovery, LSP for precision. For 3+ independent areas, spawn parallel Explore agents. -4. Decompose into **vertical slices** (see Decomposition Rules above). For each task assign a `user_story` tag: - - Implements a specific AC → `AC-N.M` - - Serves a US → `US-N` - - Cross-cutting plumbing required to enable a story → `chore:<one-sentence reason>` - - Reject any task that can't be tagged — split or ask for clarification. -5. Create tasks via `TaskCreate` with metadata: - ``` - { - "verification": "<cmd that exercises the slice end-to-end>", - "files": ["path:line", ...], - "expected_outcome": "<observable end-to-end behaviour>", - "rationale": "<1-2 sentences: why this slice exists, the constraint driving it; if replacing existing code, name the path:line being deleted>", - "user_story": "US-N" | "AC-N.M" | "chore:<reason>", - "layer": "domain" | "application" | "infrastructure" | "interface" | "chore" - } - ``` - `rationale` is mandatory — the subagent starts cold and needs the *why*, not the *what*. Reject "because the PRD says so" — restate the underlying constraint. `layer` is mandatory on Rust projects; reject layer-violating import directions at planning time (see `code-et-implementer/docs/architecture.md` §"Layer model"). -6. Set dependencies with `TaskUpdate(addBlockedBy)`. Independent slices stay parallel. -7. Save manifest to `.claude/${CLAUDE_CODE_TASK_LIST_ID}.json`. - -> Tip: for richer upstream planning, run `/ultraplan` manually before `/code:plan-issue` and commit the refined plan to `plans/` so this command picks it up. - -## Output - -`"Plan complete: N tasks created. Run /code:implement to start."` - -``` -Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Plan Complete' --subtitle 'N tasks created' || true") -``` diff --git a/code-et-implementer/commands/plan.md b/code-et-implementer/commands/plan.md new file mode 100644 index 0000000..c010e29 --- /dev/null +++ b/code-et-implementer/commands/plan.md @@ -0,0 +1,185 @@ +--- +tools: Read, Write, Edit, Grep, Glob, Bash, LSP, Agent, TaskCreate, TaskUpdate, TaskList, TaskGet, AskUserQuestion +description: "Refine an idea, write a PRD, and decompose into vertical-slice tasks. One extended turn — three checkpoints (brief, PRD on disk, tasks)." +argument-hint: "[rough idea | @path/to/brief.md]" +effort: xhigh +--- + +# Plan — Idea → PRD → Tasks + +A single command that walks the feature lane end-to-end. Three checkpoints — at each, the artifact lands on disk before the next phase starts so you can interrupt, edit, and resume. + +``` +Phase 1: Refined Brief → print to chat +Phase 2: PRD → write plans/YYYY-MM-DD-<slug>.md, ask "continue?" +Phase 3: Tasks → TaskCreate with metadata.layer + rationale +``` + +Pure-Rust four-crate Clean Architecture from `code-et-implementer/docs/architecture.md`. Each task carries `metadata.layer ∈ {domain, application, infrastructure, interface, chore}`. The Dependency Rule is enforced at the `Cargo.toml` level — flag layer-violating import directions at planning time, not at `cargo build`. + +## Phase 1 — Refined Brief (interrogation) + +Goal: every open decision resolved before any document is written. Cost: ~10 questions max, almost always fewer when the codebase has the answer. + +**Rules:** + +1. **If a question can be answered from the codebase, answer it yourself.** Run `Read`/`Grep`/`Glob`/`git log`. Do not ask the user. +2. **One question per message.** Number it. Include a recommended answer + 1-sentence rationale. +3. **Accept "you decide"** — record the recommendation as the decision; do not re-ask. +4. **Accept "defer: <reason>"** — mark deferred, move on. +5. **Stop rule:** when every ledger item is `answered | recommended-accepted | deferred`, print the refined brief and proceed to Phase 2. + +**Decisions ledger** (in-session). Each entry: `id` (D-1, D-2, …), `question`, `recommendation`, `state`, `final_answer`. Show ledger summary every 3 answered items. + +**Process:** +- Parse `$ARGUMENTS`. If `@path`, read the file. Otherwise treat as the rough idea. +- Glob `plans/**/*.md` for prior plans, `git log --oneline -20` for recent work. +- Seed 5-10 open decisions across: scope, actors, data model, UI surface, integration points, non-goals, success criteria. +- Interrogate. Resolve from the codebase first. + +**Output of Phase 1** (printed to chat): + +``` +## Refined Brief + +**Idea:** <1-sentence> +**Actors:** <list> +**Scope:** <in / out> +**Key decisions:** +- D-1: <q> → <answer> +- D-2: … + +**Suggested slug:** <kebab-case> +``` + +Pause. Confirm the slug with the user (one `AskUserQuestion`) unless `$ARGUMENTS` already supplied one. + +## Phase 2 — PRD on disk + +1. **Branch.** If on `main` or `master`, create `feature/<slug>`: + ``` + Bash('branch="$(git rev-parse --abbrev-ref HEAD)" && [ "$branch" = "main" ] || [ "$branch" = "master" ] && git checkout -b "feature/<slug>"') + ``` + +2. **Write** `plans/$(date +%Y-%m-%d)-<slug>.md` using this template — replace every `<…>` placeholder; do **not** leave TBDs: + +```markdown +# <Feature Title> + +**Date:** <YYYY-MM-DD> +**Slug:** <slug> +**Status:** Draft + +## Problem Statement + +<User-facing. 2-4 sentences.> + +## Solution + +<User-facing. 2-4 sentences.> + +## User Stories + +1. US-1: As a <actor>, I want <feature>, so that <benefit>. +2. US-2: … + +## Acceptance Criteria + +### US-1 +- AC-1.1: <observable behaviour> +- AC-1.2: … + +### US-2 +- AC-2.1: … + +## Implementation Decisions + +<Module-level. NO file paths. NO code snippets. Cover: crates affected, ports/use cases, data shape, integration points.> + +## Testing Decisions + +<Per-layer test matrix from docs/testing.md: domain unit / application use-case / infrastructure integration / interface e2e. Reference similar tests in the codebase by module name, not path. External behaviour only.> + +## Out of Scope + +<Bullets.> + +## Story Checklist + +- [ ] US-1 +- [ ] US-2 +… +``` + +3. **Set session title** by emitting `{"sessionTitle": "feat:<slug>"}` to stdout (Claude Code's `UserPromptSubmit` JSON channel). + +4. **Pause and ask** the user via `AskUserQuestion`: *"PRD written to `<path>`. Continue to task decomposition, or pause to edit?"* Choices: `Continue | Pause`. On `Pause`, print *"Resume with `/code:plan` once edits are saved."* and stop. + +## Phase 3 — Vertical-slice task decomposition + +Read the PRD (it is the authoritative spec). + +### Decomposition rules + +**Vertical slicing — non-negotiable.** Each task implements **one full vertical slice** UI ↔ logic ↔ API ↔ DB, end-to-end and testable. A task that touches only one layer is wrong — split or merge. + +- ✗ "Add API endpoint" + "Wire UI button" + "Migrate schema" — three half-tasks +- ✓ "Submit-feedback flow: form → API → `feedback` row → email confirmation" — one slice + +`metadata.verification` exercises the full slice end-to-end. + +**Replace, don't accumulate.** When a slice supersedes existing logic, the task scope **includes deletion of the superseded code**. State the `path:line` being replaced in `metadata.rationale`. No parallel utilities, no `// TODO: remove old X`. + +**LSP for symbols.** Use `documentSymbol` / `findReferences` / `definition` to anchor each US/AC to `file:line`. Grep/Glob for discovery; LSP for precision. Never use LSP to enumerate the project. For 3+ independent areas, spawn parallel `Agent(subagent_type: "Explore")` queries in a single message. + +### Anti-slop self-critique (before TaskCreate) + +After drafting tasks, walk the list once and **reject** any task that: + +- Touches only one layer → split or merge into a vertical slice. +- Has rationale "because the PRD says so" → restate the underlying constraint. +- Adds a duplicate utility instead of extracting (Rule of Three) → the third occurrence triggers refactor in the same task. +- Adds defensive validation between trusted modules (interface↔application is the only validation boundary) → drop. +- Adds a mirror test (`assert_eq!(format!("{:?}", err), "DomainError::NotFound")` style) → reframe to assert observable behaviour. +- Has no test for at least one acceptance criterion → fix. + +The list lives in `code-et-implementer/docs/anti-slop.md`; the inline summary above is enough for plan-time review. + +### TaskCreate metadata + +```json +{ + "verification": "<cmd that exercises the slice end-to-end>", + "files": ["crates/<layer>/src/path.rs:42", ...], + "expected_outcome": "<observable end-to-end behaviour>", + "rationale": "<1-2 sentences: why this slice exists, the constraint driving it. If replacing existing code, name the path:line being deleted.>", + "user_story": "US-N | AC-N.M | chore:<reason>", + "layer": "domain | application | infrastructure | interface | chore" +} +``` + +`rationale` is mandatory — the subagent starts cold and needs the *why*. `layer` is mandatory; the per-file layer also feeds the validator on every file the task touches. `verification` exercises the full slice — `cargo nextest run -p <crate>` for unit, `cargo nextest run --workspace` for cross-layer. + +Set dependencies with `TaskUpdate(addBlockedBy)`. Independent slices stay parallel. + +Save manifest to `.claude/${CLAUDE_CODE_TASK_LIST_ID}.json`. + +### Output + +``` +Plan complete: N tasks created. Run /code:ship to execute. +``` + +``` +Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Plan ready' --subtitle 'N tasks' || true") +``` + +## Brevity + +Drop filler ("just", "simply"), hedging ("perhaps", "maybe"), pleasantries ("Sure!"). Fragments over sentences when meaning is clear. Pattern: `[thing] [action] [reason]. [next].` Question messages ≤2 sentences. Recommendation rationale ≤1 sentence. + +## When to skip phases + +- User pasted a refined brief / linked an existing PRD → skip Phase 1, jump to Phase 2 confirmation step (or Phase 3 if PRD already on disk). +- User says "just give me the tasks for `<existing PRD path>`" → resolve PRD via `Bash("${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh")`, jump to Phase 3. +- Bug-shaped request — route to `/code:fix`. Don't write a PRD for a 2-file change. diff --git a/code-et-implementer/commands/prd.md b/code-et-implementer/commands/prd.md deleted file mode 100644 index 4ba310a..0000000 --- a/code-et-implementer/commands/prd.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -tools: Read, Write, Bash, Grep, Glob -description: "Synthesise current conversation into a PRD file at plans/YYYY-MM-DD-<slug>.md. Sets session title. Local only — no GitHub issue." -argument-hint: "[optional slug override]" -effort: xhigh ---- - -# PRD — Product Requirements Document - -Synthesise the current conversation (ideally a `/code:grill` output) into a PRD file. Local only. - -## Process - -1. **Derive the slug.** - - If `$ARGUMENTS` is non-empty, use it (lowercased, kebab-cased). - - Else derive from the refined brief's "Idea" line (kebab-case, ≤40 chars). - -2. **Create the feature branch if not on one.** - -```bash -branch="$(git rev-parse --abbrev-ref HEAD)" -if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then - git checkout -b "feature/<slug>" -fi -``` - -3. **Compute the PRD path.** - -```bash -date="$(date +%Y-%m-%d)" -prd_path="plans/${date}-<slug>.md" -``` - -4. **Write the PRD** at `$prd_path` using this template (replace `<…>` placeholders — do NOT leave TBDs): - -```markdown -# <Feature Title> - -**Date:** <YYYY-MM-DD> -**Slug:** <slug> -**Status:** Draft - -## Problem Statement - -<From the user's perspective. 2-4 sentences.> - -## Solution - -<From the user's perspective. 2-4 sentences.> - -## User Stories - -1. US-1: As a <actor>, I want <feature>, so that <benefit>. -2. US-2: As a <actor>, I want <feature>, so that <benefit>. -… - -## Acceptance Criteria - -### US-1 -- AC-1.1: <specific observable behaviour> -- AC-1.2: … - -### US-2 -- AC-2.1: … - -## Implementation Decisions - -<Module-level. NO file paths. NO code snippets. Cover: modules affected, interfaces, data shape, integration points.> - -## Testing Decisions - -<What to test. Reference similar tests in the codebase by module name, not path. External behaviour only.> - -## Out of Scope - -<Bullet list.> - -## Further Notes - -<Anything else.> - -## Story Checklist - -- [ ] US-1 -- [ ] US-2 -… -``` - -5. **Set session title** by emitting Claude Code's `UserPromptSubmit` output JSON: - -``` -{"sessionTitle": "feat:<slug>"} -``` - -6. **Announce completion** and the path. - -## Rules - -- **No file paths in the PRD.** Implementation Decisions stay module-level. -- **User stories get unique `US-N` ids.** Acceptance criteria get `AC-<story>.<M>` ids. -- **Long user-story list** — aim for 10+ stories covering all aspects of the feature. -- **Story Checklist must match the US-N ids exactly** — `/code:implement` ticks them. diff --git a/code-et-implementer/commands/review.md b/code-et-implementer/commands/review.md new file mode 100644 index 0000000..b21cb8a --- /dev/null +++ b/code-et-implementer/commands/review.md @@ -0,0 +1,63 @@ +--- +tools: Bash, Read, Skill +description: "Pre-merge review — local audit + diff review against merge-base. Delegates to engineering plugin's code-review skill if installed." +argument-hint: "[--no-audit]" +effort: high +--- + +# Review — Pre-merge gate + +Run before opening a PR (or after `/code:ship`). Two steps: + +1. **Static gate** — full local audit pipeline (mirrors CI). Same as `/code:ship`'s tail step. Skipped with `--no-audit` if you just ran ship and the report is fresh. +2. **Diff review** — capture `git diff <merge-base>..HEAD` and route to the engineering plugin's `code-review` skill for a human-judgment pass over the change set. + +This command does **not** push or open the PR — that's `/commit-push-pr`. It is a local gate that mirrors what the cloud reviewer would catch. + +## Procedure + +### Step 1 — Static gate (skip with `--no-audit`) + +``` +Bash('bash "${CLAUDE_PLUGIN_ROOT}/scripts/audit.sh" "$PWD"') +``` + +This runs the same seven-stage pipeline as CI (`fmt`, `clippy -D warnings`, `layer-deps-validator.sh`, `cargo machete`, `cargo audit`, `cargo deny check`, `cargo nextest`). Report at `.claude/audit-<UTC>.md`. If it exits non-zero, surface the highest finding from the report and **stop** — fix the static gate before running Step 2 (a noisy diff review on top of broken static checks wastes everyone's time). + +### Step 2 — Code-review + +If the engineering plugin's `code-review` skill is installed, delegate to it: + +``` +Skill("code-review") +``` + +The skill reads `git diff <merge-base>..HEAD` (and the audit report from Step 1 if present) and returns findings ordered by confidence × severity. Feed the output back to the user. + +If the engineering plugin is **not** installed, run a 5-area inline review: +1. **Layer compliance** — does any new file violate the inward dependency rule? (`code-et-implementer/docs/architecture.md` §"The Dependency Rule") +2. **Anti-slop** — Rule of Three duplicates, mirror tests, defensive validation, dead re-exports, complexity ≥ 15. (`code-et-implementer/docs/anti-slop.md` §"Hard rules") +3. **Test coverage** — every acceptance criterion has a corresponding test. (`code-et-implementer/docs/testing.md` §"Per-layer test matrix") +4. **Security** — `cargo audit` clean; secrets in `secrecy::Secret<T>`; auth at every interface entry point. (`code-et-implementer/docs/architecture.md` §"Rust security checklist") +5. **Slice integrity** — each commit (US-N / AC-N.M / chore) is a coherent vertical slice; superseded code deleted in same commit; no `// TODO: remove old X`. + +Report findings with `severity | path:line | issue | suggested fix`. Group by severity (CRITICAL > HIGH > MEDIUM > LOW). Cite from the audit report and the diff. + +## Output + +``` +Review complete. +- Audit: <PASS | FAIL — see .claude/audit-<UTC>.md> +- Code-review: <N findings | CLEAN> + +Next: /commit-push-pr to ship, or fix and re-run /code:review. +``` + +``` +Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Review done' --subtitle 'See chat for findings' || true") +``` + +## Notes + +- Use this in parallel with the cloud `/ultrareview` (built-in research preview). `/code:review` is the local gate; `/ultrareview` is the multi-agent cloud pass. Both are safe to run; they catch different things. +- If `--no-audit` is passed and no fresh `.claude/audit-*.md` exists, the diff review is still useful but lacks the static-gate context. diff --git a/code-et-implementer/commands/ship.md b/code-et-implementer/commands/ship.md new file mode 100644 index 0000000..3ea0317 --- /dev/null +++ b/code-et-implementer/commands/ship.md @@ -0,0 +1,141 @@ +--- +background: true +tools: Bash, Bash(gh:*), Bash(git:*), Read, Edit, Grep, Glob, Agent, Skill, TaskCreate, TaskList, TaskGet, TaskUpdate +description: "Execute pending tasks in parallel worktrees, then audit. Auto-retries 1 fix-pass on CRITICAL/HIGH findings." +argument-hint: "[task-id]" +effort: xhigh +--- + +# Ship — Execute + Audit + +Loads pending tasks from `TaskList` (or `.claude/${CLAUDE_CODE_TASK_LIST_ID}.json`), dispatches them as parallel worktree-isolated subagents, then runs the local audit gate. On CRITICAL or HIGH findings, dispatches one fix-pass subagent and re-audits. After 1 retry the chain halts and surfaces findings. + +If the current branch is `main` or `master`, create `feature/<slug-from-prd-or-tasks>` first. + +## Dispatch + +Every task runs as a forked subagent in its own worktree. Use `Agent` with `isolation: "worktree"` and `subagent_type: "general-purpose"`. **Do not** shell out to `git worktree add` — `isolation: "worktree"` handles it (requires `CLAUDE_CODE_FORK_SUBAGENT=1` on external builds; default-on inside this harness). + +Dependency graph drives order. Independent tasks **must** dispatch in a single message with multiple `Agent` calls so they run concurrently — never serialize what could fan out. + +### Dispatch prompt template + +Each subagent starts cold. Send one comprehensive first turn — intent, constraints, acceptance criteria, `file:line` anchors, verification, and rationale — so it operates autonomously without back-and-forth. Use this template verbatim (fill `<…>` from task metadata + active PRD): + +``` +# Task <id>: <title> + +## Tag +<metadata.user_story — one of: US-N | AC-N.M | chore:<reason> | none> + +## PRD context (if US-N or AC-N.M) +<Paste the matching US-N / AC-N.M block from the active PRD, resolved via ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh. Include the parent User Story and all its ACs so the agent sees full intent.> + +## Rationale +<metadata.rationale — verbatim from plan. The why, not the what.> + +## Files to touch +<metadata.files — exact file:line anchors. Read these before editing.> + +## Layer +<metadata.layer — domain | application | infrastructure | interface | chore. Imports point inward; `cargo build` enforces this.> + +## Expected outcome +<metadata.expected_outcome — observable success criterion.> + +## Verification +Run `<metadata.verification>`. Must exit 0. All existing tests must still pass. + +## Constraints +- Follow rules in `code-et-implementer/CLAUDE.md` (Brevity, Context Hygiene, Clean Architecture controlling rules, ≤600 lines/file). +- Layer rules: `code-et-implementer/docs/architecture.md`. Anti-slop hard rules: `code-et-implementer/docs/anti-slop.md`. Test matrix: `code-et-implementer/docs/testing.md`. +- Read in slices: `Read(offset, limit)` for files >200 lines; never re-read the same file twice for different blocks. +- Delegate breadth to `Agent(subagent_type: "Explore")` if the fix path is unclear — do not Grep-and-Read your way through unknown territory. +- Every acceptance criterion must have a corresponding test. +- No scope expansion — implement exactly what the task specifies; flag adjacent issues instead of fixing inline. +- If the slice supersedes existing code, delete the superseded code in the same commit. No parallel utilities, no `// TODO: remove old X`. New code obsoletes old. +- All SQL via `sqlx::query!` / `query_as!` (compile-time-checked). Raw `sqlx::query` is forbidden. +- Commit format: `<prefix>: <subject>` where prefix is US-N | AC-N.M | chore (or no prefix if tag is `none`). + +## Deliverables (subagent reports back) +1. Code changes committed in the isolated worktree. +2. `metadata.verification` exits 0. +3. Single commit with correct prefix. +4. PRD checkbox ticked (if `US-N`) — flip `- [ ] US-N` to `- [x] US-N` and stage with the commit. +5. Final report: commit SHA, branch name, worktree path (returned in the `Agent` tool result). + +Do not merge back to the parent feature branch — the orchestrator handles that. Do not ask clarifying questions. If blocked, flag in the final report with a specific file:line reference. +``` + +### Per-subagent contract + +1. Implement the task. Every acceptance criterion has a corresponding test. +2. Run `metadata.verification`. Must compile, tests must pass. +3. Commit with the right prefix: + - `US-N: <subject>` when tag is `US-N` + - `AC-N.M: <subject>` when tag is `AC-N.M` + - `chore: <subject>` when tag starts with `chore:` + - No prefix when tag is `none` or absent. +4. **Tick the PRD checkbox.** If `metadata.user_story` is `US-N`, resolve the PRD via `${CLAUDE_PLUGIN_ROOT}/scripts/resolve-prd.sh` and `Edit` to flip `- [ ] US-N` → `- [x] US-N`. Stage with the commit. + +The subagent stops after step 4 and returns. It must **not** merge or remove its own worktree — it has no view of the parent feature branch. + +## Orchestrator (this skill) + +After each `Agent` call returns: +1. Read the returned worktree path and branch from the tool result. +2. From the parent feature branch: `git merge --no-ff <subagent-branch>`. +3. `git worktree remove <path>` (the harness auto-cleans empty worktrees, but populated ones need explicit removal). +4. Mark the task completed via `TaskUpdate` only after the merge lands. + +If a subagent reports failure, leave the worktree in place for inspection — do not auto-discard. + +## After all tasks land — audit + +Run `Skill("simplify")` first (changed-code refactor pass). Then run the local audit: + +``` +Bash('bash "${CLAUDE_PLUGIN_ROOT}/scripts/audit.sh" "$PWD"') +``` + +The audit mirrors the v4.0 CI gate: `cargo fmt --check`, `cargo clippy -D warnings`, `scripts/layer-deps-validator.sh`, `cargo machete`, `cargo audit`, `cargo deny check`, `cargo nextest run --workspace`. Report at `.claude/audit-<UTC>.md`. + +### Auto-retry on CRITICAL/HIGH (max 1 pass) + +If audit exits non-zero with CRITICAL or HIGH findings (the typical: layer violation, dependency advisory, clippy lint, test failure): + +1. Read `.claude/audit-<UTC>.md` — extract the highest-severity finding's `path:line` + message. +2. Dispatch **one** fix-pass subagent via `Agent` (no worktree isolation — work directly on the feature branch since the task swarm already merged): + ``` + # Audit fix-pass + The post-implement audit returned <severity> at <path:line>: <message>. + Read the report at .claude/audit-<UTC>.md for full context. + Fix the finding(s). Then re-run `bash "${CLAUDE_PLUGIN_ROOT}/scripts/audit.sh" "$PWD"`. + Constraints: same as task subagents (see ship.md). No scope expansion — fix the audit findings, nothing else. + ``` +3. Wait for the fix-pass to return. +4. Re-run audit once. If still failing, **stop the chain** and surface: + ``` + Audit still failing after 1 fix-pass. Latest report: .claude/audit-<UTC>.md + Highest finding: <severity> <path:line> — <message> + Inspect, fix manually, then re-run /code:ship to retry the audit step only. + ``` + +Never loop more than once — repeated AI fix-passes on a stuck audit waste tokens and hide the real issue. + +### On clean audit + +``` +✓ All tasks landed. Audit clean. +Next: /code:review (or /commit-push-pr to ship). +``` + +``` +Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Ship done' --subtitle 'Audit clean' || true") +``` + +## Notes + +- The `SubagentStop` hook (`scripts/verify-gate.sh`) runs `cargo test` + `audit --fast` after each subagent — that's the inner loop. The full audit at the end is the outer gate. +- `--fast` runs only fmt + clippy. The post-tasks pass runs the full seven stages. +- The audit is **the** anti-slop enforcement. Never tweak its findings to make it pass — fix the code, or accept a `LOW` finding when a tool is genuinely missing on the host. diff --git a/code-et-implementer/commands/start.md b/code-et-implementer/commands/start.md new file mode 100644 index 0000000..6ac6db4 --- /dev/null +++ b/code-et-implementer/commands/start.md @@ -0,0 +1,102 @@ +--- +tools: Read, Bash, Glob, AskUserQuestion +description: "Scaffold a new pure-Rust Clean Architecture project (axum + sqlx + Dioxus 0.7+ + tokio). Always-latest deps." +argument-hint: "<project-name> [--targets web,desktop,mobile,server] [--db sqlite|postgres] [--install-tools]" +effort: xhigh +--- + +Scaffold a fresh pure-Rust full-stack project from `${CLAUDE_PLUGIN_ROOT}/templates/rust/dioxus-fullstack/`. + +**The whole stack is fixed:** `axum + sqlx + Dioxus 0.7+ + tokio`, four-crate Clean Architecture workspace (`domain`, `application`, `infrastructure`, `interface`), apps under `apps/` (`server`, `web`, `desktop`, `mobile`). The CI gate (`.github/workflows/code-et-audit.yml` + `scripts/layer-deps-validator.sh`) is copied in. Always-latest semver-compatible deps via post-scaffold `cargo update`. + +**Refuses to run** if CWD has `Cargo.toml` or `package.json` unless `--force` is passed. **Never** scaffolds inside this plugin's repo. + +## Inputs + +Parse `$ARGUMENTS`: +- **project name** (positional, required) — `^[a-z][a-z0-9-]{1,40}$` +- `--targets <list>` — comma-separated subset of `web,desktop,mobile,server`. Default: all four. `server` always stays — it is the SSR + API root. +- `--db sqlite|postgres` — default `sqlite`. +- `--install-tools` — also `cargo install` the audit toolchain. Default: print checklist. +- `--force` — overlay onto a non-empty CWD. +- `--owner <gh-user>` — used to substitute `{{owner}}` in `Cargo.toml`'s `repository` field. Default: `git config user.name` slugified, else `your-org`. + +If a required input is missing, ask via `AskUserQuestion` (one focused question per missing field, max two questions total). + +## Procedure + +1. **Pre-flight.** + ``` + Bash('test -f Cargo.toml || test -f package.json && echo CONFLICT || true') + ``` + On `CONFLICT` without `--force`, stop with: *"This directory already has a Rust or TS project. Re-run with `--force` to overlay."* Refuse if `pwd` contains `code-et-implementer` (don't scaffold inside the plugin repo). + +2. **Copy template + shared assets.** + ``` + Bash('TPL="${CLAUDE_PLUGIN_ROOT}/templates/rust/dioxus-fullstack" && SHARED="${CLAUDE_PLUGIN_ROOT}/templates/shared" && mkdir -p "<name>" && cp -R "$TPL"/. "<name>/" && cp -R "$SHARED/.github" "<name>/.github" && mkdir -p "<name>/scripts" && cp "$SHARED/scripts/layer-deps-validator.sh" "<name>/scripts/" && cp "$SHARED/CLAUDE.md.template" "<name>/CLAUDE.md"') + ``` + +3. **Filter targets.** Remove unselected app dirs and their workspace member lines. Keep `apps/server` always. + ``` + Bash('cd "<name>" && for t in web desktop mobile; do + case ",<targets>," in *",$t,"*) ;; *) rm -rf "apps/$t"; sed -i.bak "/\\\"apps\\/$t\\\"/d" Cargo.toml && rm Cargo.toml.bak;; esac + done') + ``` + +4. **Substitute placeholders** (`{{name}}`, `{{owner}}`, `{{db}}`) across `Cargo.toml`, `apps/**/Cargo.toml`, `crates/**/Cargo.toml`, `Dioxus.toml`, `CLAUDE.md`, `README.md`, `justfile`, `.env.example`, `scripts/deploy.sh`, `scripts/upload.sh`. Use `find -print0 | xargs -0 sed -i.bak …` and clean `.bak`. + +5. **Switch DB.** If `--db postgres`, edit `.env.example` to uncomment the postgres line, comment out the sqlite default. The template's `infrastructure` supports both at runtime. + +6. **chmod the scripts.** `chmod +x "<name>/scripts/"*.sh` — covers `layer-deps-validator.sh`, `deploy.sh`, `upload.sh`. + +7. **Latest deps.** Pull every workspace dep to its latest semver-compatible patch. Non-fatal — a transient yank or registry hiccup should not abort the bootstrap; `cargo check` (next step) is the actual gate: + ``` + Bash('cd "<name>" && cargo update 2>&1 | tail -20 || echo "cargo update warned — see output above; bootstrap continues"') + ``` + Caret pins (`dioxus = "0.7"`, `axum = "0.8"`, `sqlx = "0.8"`, `tokio = "1"`, etc.) deliver latest minor/patch automatically. **Major bumps** (e.g., dioxus 0.7→0.8) need a manual `cargo upgrade` (cargo-edit) and a smoke test — flag this on first `just audit` failure rather than gambling here. + +8. **Compile-check.** + ``` + Bash('cd "<name>" && cargo check --workspace 2>&1 | tail -40 || echo "cargo check failed — see output above"') + ``` + Template's `infrastructure` uses runtime-checked queries, so check passes without a DB. If check fails, the template is broken — surface and stop. + +9. **Install tools.** If `--install-tools`: + ``` + Bash('cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli') + ``` + Otherwise print checklist: + ``` + cargo install --locked cargo-machete cargo-audit cargo-deny cargo-nextest sqlx-cli dioxus-cli + ``` + +10. **Print next steps.** + ``` + ✓ Bootstrapped <name> at $(pwd)/<name> + + Next: + cd <name> + cp .env.example .env + just db-migrate + just run-server # axum + dioxus-fullstack SSR on :3000 + just audit # local mirror of CI + + Publish: + gh repo create <name> --public --source=. --remote=origin --push + # CI runs on first push; clean run = ready for /code:plan or /code:fix + ``` + +## Output + +`"Bootstrapped <name> with targets <list>, db <db>. cd <name> && just audit to verify."` + +``` +Bash("command -v cmux &>/dev/null && [ -n \"$CMUX_SOCKET_PATH\" ] && cmux notify --title 'Project ready' --subtitle '<name>' || true") +``` + +## Notes + +- **Never** auto-run `git init` if the user's CWD is already a git repo (sub-project layouts are valid). Run `git init` only when the new project's parent dir has no `.git`. +- **Never** install tools without `--install-tools`. Print the checklist and wait. +- The CI workflow lives at `.github/workflows/code-et-audit.yml` after step 2 — no separate copy. +- Apply rules from `code-et-implementer/CLAUDE.md` and `code-et-implementer/docs/architecture.md`. Dioxus 0.7+ for one-codebase web/desktop/mobile. `sqlx::query!` macros for compile-time-checked SQL. Forward-only migrations. diff --git a/code-et-implementer/docs/anti-slop.md b/code-et-implementer/docs/anti-slop.md index 936dbc0..36f007c 100644 --- a/code-et-implementer/docs/anti-slop.md +++ b/code-et-implementer/docs/anti-slop.md @@ -51,7 +51,7 @@ AI-generated code accumulates structural debt that file-local linters miss: re-e **How to detect.** - **Compiler:** the workspace `Cargo.toml` `[dependencies]` table is the primary gate. Adding a forbidden dep makes `cargo build` fail. -- **CI validator:** `scripts/layer-deps-validator.sh` (30 lines, ships in every project from `/code:bootstrap`) re-asserts the layer rule explicitly. +- **CI validator:** `scripts/layer-deps-validator.sh` (30 lines, ships in every project from `/code:start`) re-asserts the layer rule explicitly. - **Circular deps:** `cargo` already forbids them at the crate level. Within a crate, large modules can still cycle; if it gets bad, restructure into sub-crates. **How to fix.** Move the type to the inner layer. Introduce a port (trait) in `application` and implement it in `infrastructure`. Never collapse layers to "make it compile" — that's the slop. @@ -70,7 +70,7 @@ When the CI gate is green but something still feels wrong, look for these patter ## The 4-stage verification loop (what the CI gate runs) -The `.github/workflows/code-et-audit.yml` workflow shipped by `/code:bootstrap` and `/code:install-ci` runs these stages on every PR. v3.10.0's `/code:audit` will mirror them locally for fast feedback. +The `.github/workflows/code-et-audit.yml` workflow shipped by `/code:start` and `/code:install-ci` runs these stages on every PR. The same pipeline runs locally via `just audit` (in scaffolded projects) and as the tail step of `/code:ship`. | Stage | What | Tool | |---|---|---| diff --git a/code-et-implementer/docs/architecture.md b/code-et-implementer/docs/architecture.md index 51efd64..293975b 100644 --- a/code-et-implementer/docs/architecture.md +++ b/code-et-implementer/docs/architecture.md @@ -1,6 +1,6 @@ --- name: architecture -description: Rust Clean Architecture doctrine for code-et. Loaded on demand by /code:bootstrap, /code:go, /code:plan-issue, /code:implement. +description: Rust Clean Architecture doctrine for code-et. Loaded on demand by /code:start, /code:fix, /code:plan, /code:ship. applies_to: rust --- diff --git a/code-et-implementer/scripts/audit-report.sh b/code-et-implementer/scripts/audit-report.sh new file mode 100755 index 0000000..f583a5a --- /dev/null +++ b/code-et-implementer/scripts/audit-report.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash +# audit-report.sh — write a markdown audit report from findings on stdin. +# +# Findings format (one per line, pipe-delimited): +# <severity>|<stage>|<path>|<line>|<message> +# +# severity ∈ { CRITICAL, HIGH, MEDIUM, LOW }. +# +# Usage: audit-report.sh <report-path> [--review-file <diff-path>] +# stdin: zero or more finding lines. +# Writes <report-path> with findings grouped by severity (CRITICAL > HIGH > +# MEDIUM > LOW); within a group, original input order is preserved. +# +# When any non-LOW finding is present, the report is prepended with a single +# summary line of the form (US-12 / AC-12.1): +# [<SEVERITY>] <stage>: <message-head> — fix: <hint> +# LOW-only and empty inputs do NOT trigger a summary prefix. +# +# When --review-file is supplied (US-5), appends a "## Review" section under +# the static findings. The file's contents (a captured `git diff`) are queued +# for consumption by the engineering plugin's code-review skill — this writer +# does not invoke the skill itself. +# +# Empty input → a "no findings" report; the caller still gets a written file +# (AC-7.1: every audit run writes a report). + +set -euo pipefail + +if [ "$#" -lt 1 ]; then + echo "usage: audit-report.sh <report-path> [--review-file <diff-path>]" >&2 + exit 2 +fi + +REPORT_PATH="$1" +shift +REVIEW_FILE="" +while [ "$#" -gt 0 ]; do + case "$1" in + --review-file) + shift + REVIEW_FILE="${1:-}" + [ -z "$REVIEW_FILE" ] && { echo "audit-report.sh: --review-file requires a path" >&2; exit 2; } + shift + ;; + *) + echo "audit-report.sh: unknown arg: $1" >&2 + exit 2 + ;; + esac +done + +mkdir -p "$(dirname "$REPORT_PATH")" + +INPUT="$(cat)" + +# Group lines by severity, preserving input order within each group. +filter_severity() { + local sev="$1" + printf '%s\n' "$INPUT" | awk -F'|' -v sev="$sev" '$1 == sev { print }' +} + +emit_group() { + local sev="$1" header="$2" lines + lines="$(filter_severity "$sev")" + if [ -z "$lines" ]; then return; fi + printf '## %s\n\n' "$header" + while IFS='|' read -r severity stage path line message; do + [ -z "$severity" ] && continue + # `printf --` ends option parsing so format strings starting with `-` are + # accepted on bash 5.3+ (which treats a leading `-` as a flag otherwise). + printf -- '- **%s** (%s) — %s — `%s:%s`\n' "$severity" "$stage" "$message" "$path" "$line" + done <<< "$lines" + printf '\n' +} + +# Stage-specific remediation hint. Closed set per the v3.9.0 gate; unknown +# stages fall back to a generic pointer. +remediation_for() { + case "$1" in + "fmt") echo "run \`cargo fmt --all\`" ;; + "clippy (deny warnings)") echo "address the warning or allow it explicitly" ;; + "layer-deps validator") echo "remove the disallowed cross-layer import" ;; + "cargo-machete (unused deps)") echo "drop the unused dependency from Cargo.toml" ;; + "cargo-audit (advisories)") echo "upgrade the affected crate or pin an advisory exception" ;; + "cargo-deny (license + bans + sources + advisories)") echo "review deny.toml and update the offending dependency" ;; + "tests") echo "fix the failing test(s) shown above" ;; + *) echo "see report for details" ;; + esac +} + +# Compose the one-line summary from the highest-severity finding present. +# Echoes the line on stdout; emits nothing when only LOW (or no) findings. +summary_line() { + local sev stage path line message hint head + for sev in CRITICAL HIGH MEDIUM; do + IFS='|' read -r _severity stage path line message \ + < <(filter_severity "$sev" | head -n 1) || true + if [ -n "${stage:-}" ]; then + hint="$(remediation_for "$stage")" + # Trim the message to its first 80 chars so the summary stays compact. + head="$message" + if [ "${#head}" -gt 80 ]; then + head="${head:0:77}..." + fi + printf '[%s] %s: %s — fix: %s\n' "$sev" "$stage" "$head" "$hint" + return 0 + fi + done + return 0 +} + +SUMMARY="$(summary_line)" + +emit_review() { + [ -z "$REVIEW_FILE" ] && return + printf '## Review\n\n' + printf '_Diff queued for the engineering plugin'\''s code-review skill._\n\n' + if [ -s "$REVIEW_FILE" ]; then + printf '```diff\n' + cat "$REVIEW_FILE" + printf '\n```\n\n' + else + printf '_No diff captured — branch matches its merge base._\n\n' + fi +} + +{ + if [ -n "$SUMMARY" ]; then + printf '%s\n\n' "$SUMMARY" + fi + + printf '# code-et audit report\n\n' + printf '_Generated: %s UTC_\n\n' "$(date -u '+%Y-%m-%d %H:%M:%S')" + + if [ -z "$INPUT" ]; then + printf 'All stages passed — no findings.\n\n' + else + emit_group "CRITICAL" "CRITICAL" + emit_group "HIGH" "HIGH" + emit_group "MEDIUM" "MEDIUM" + emit_group "LOW" "LOW" + fi + + emit_review +} > "$REPORT_PATH" + +# Echo the summary line (if any) on stdout so callers can surface it without +# re-reading the report file. +if [ -n "$SUMMARY" ]; then + printf '%s\n' "$SUMMARY" +fi diff --git a/code-et-implementer/scripts/audit-stages.sh b/code-et-implementer/scripts/audit-stages.sh new file mode 100755 index 0000000..82f3869 --- /dev/null +++ b/code-et-implementer/scripts/audit-stages.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# audit-stages.sh — yaml parser for the audit pipeline (used by audit.sh and /code:ship). +# Emits one `name|command` line per gate stage on stdout, in declared order. +# Reads the workflow yaml at $1 (default: shared template path). +# The yaml is the single source of truth — no hardcoded stage list. +# +# Mapping for `uses:` steps (GitHub Action → local cargo subcommand): +# bnjbvr/cargo-machete → cargo machete +# rustsec/audit-check → cargo audit +# EmbarkStudios/cargo-deny-action → cargo deny check +# +# Setup-only `uses:` steps are skipped (checkout, rust-toolchain, rust-cache, +# install-action). They install tooling, not run audit stages. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEFAULT_YAML="$SCRIPT_DIR/../templates/shared/.github/workflows/code-et-audit.yml" +YAML_PATH="${1:-$DEFAULT_YAML}" + +if [ ! -f "$YAML_PATH" ]; then + echo "audit-stages.sh: yaml not found: $YAML_PATH" >&2 + exit 2 +fi + +# Walk the yaml line by line. The workflow is hand-written and stable, so a +# small awk pass is enough — we don't need a real yaml lib. +awk ' + function flush( cmd) { + if (name == "") return + if (run != "") { + cmd = run + } else if (uses != "") { + # Strip @<ref> suffix. + action = uses + sub(/@.*/, "", action) + if (action == "actions/checkout") { reset(); return } + if (action == "dtolnay/rust-toolchain") { reset(); return } + if (action == "Swatinem/rust-cache") { reset(); return } + if (action == "taiki-e/install-action") { reset(); return } + if (action == "bnjbvr/cargo-machete") { cmd = "cargo machete" } + else if (action == "rustsec/audit-check") { cmd = "cargo audit" } + else if (action == "EmbarkStudios/cargo-deny-action") { cmd = "cargo deny check" } + else { + printf "audit-stages.sh: unknown uses action: %s\n", action > "/dev/stderr" + exit 3 + } + } else { + reset(); return + } + printf "%s|%s\n", name, cmd + reset() + } + function reset() { name=""; run=""; uses="" } + + BEGIN { in_steps = 0; reset() } + + # Detect entering the steps: block. Match exactly two-space indent under + # jobs.audit, then four-space "steps:". + /^ steps:[[:space:]]*$/ { in_steps = 1; next } + + # Leaving steps: a top-level key (no leading space) or a sibling at indent <=4 + in_steps == 1 && /^[a-zA-Z]/ { in_steps = 0 } + + in_steps == 1 { + # A new step starts with " - " (six spaces, dash, space) at the steps + # list indent. + if ($0 ~ /^ - /) { + flush() + # The dash-line itself may carry "uses:" or "name:" inline. + line = $0 + sub(/^ - /, "", line) + if (line ~ /^uses:[[:space:]]/) { + sub(/^uses:[[:space:]]+/, "", line) + uses = line + } else if (line ~ /^name:[[:space:]]/) { + sub(/^name:[[:space:]]+/, "", line) + name = line + } + next + } + # Continuation keys for the current step: indent eight spaces. + if ($0 ~ /^ name:[[:space:]]/) { + line = $0 + sub(/^ name:[[:space:]]+/, "", line) + name = line + next + } + if ($0 ~ /^ uses:[[:space:]]/) { + line = $0 + sub(/^ uses:[[:space:]]+/, "", line) + uses = line + next + } + if ($0 ~ /^ run:[[:space:]]/) { + line = $0 + sub(/^ run:[[:space:]]+/, "", line) + run = line + next + } + } + + END { flush() } +' "$YAML_PATH" diff --git a/code-et-implementer/scripts/audit.sh b/code-et-implementer/scripts/audit.sh new file mode 100755 index 0000000..eefb08e --- /dev/null +++ b/code-et-implementer/scripts/audit.sh @@ -0,0 +1,325 @@ +#!/usr/bin/env bash +# audit.sh — local mirror of the v3.9.0 CI audit gate. +# +# Usage: audit.sh [--fast | --stage <n>] [--review] [target-dir] +# --fast runs only stages 1-2 (fmt + clippy) +# --stage <n> runs only the n-th stage (1-indexed) from the parsed yaml +# --review chains the engineering plugin's code-review skill against +# `git diff <merge-base>..HEAD`; missing plugin → exit non-zero +# target-dir defaults to the current working directory. +# +# Exits 0 with "not a Rust workspace, skipping" on stderr if no Cargo.toml +# is found at target or its git root. Otherwise parses the workflow yaml +# and runs every stage in order; missing tools (cargo-machete/audit/deny, +# cargo-nextest, layer-deps script) emit a WARNING and are recorded as +# LOW-severity findings. Always writes <target>/.claude/audit-<UTC>.md. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +STAGES_SCRIPT="$SCRIPT_DIR/audit-stages.sh" +REPORT_SCRIPT="$SCRIPT_DIR/audit-report.sh" + +MODE="all" +STAGE_NUM="" +REVIEW=0 +POSITIONAL=() + +print_usage() { + local stages + stages="$(bash "$STAGES_SCRIPT")" || return 1 + echo "Usage: audit.sh [--fast | --stage <n>] [--review] [target-dir]" >&2 + echo "" >&2 + echo "Valid stages:" >&2 + local i=0 + while IFS='|' read -r name _cmd; do + [ -z "$name" ] && continue + i=$((i + 1)) + echo " $i) $name" >&2 + done <<< "$stages" +} + +while [ $# -gt 0 ]; do + case "$1" in + --fast) + if [ "$MODE" = "stage" ]; then + echo "audit.sh: --fast and --stage are mutually exclusive" >&2 + print_usage + exit 2 + fi + MODE="fast" + shift + ;; + --stage) + if [ "$MODE" = "fast" ]; then + echo "audit.sh: --fast and --stage are mutually exclusive" >&2 + print_usage + exit 2 + fi + if [ $# -lt 2 ] || [ -z "${2:-}" ]; then + echo "audit.sh: --stage requires a numeric argument" >&2 + print_usage + exit 2 + fi + MODE="stage" + STAGE_NUM="$2" + shift 2 + ;; + --review) + REVIEW=1 + shift + ;; + --) + shift + while [ $# -gt 0 ]; do POSITIONAL+=("$1"); shift; done + ;; + -*) + echo "audit.sh: unknown flag: $1" >&2 + print_usage + exit 2 + ;; + *) + POSITIONAL+=("$1") + shift + ;; + esac +done + +TARGET_RAW="${POSITIONAL[0]:-$PWD}" +TARGET="$(cd "$TARGET_RAW" 2>/dev/null && pwd)" || { + echo "audit.sh: target dir not found: $TARGET_RAW" >&2 + exit 2 +} + +STAGES="$(bash "$STAGES_SCRIPT")" +STAGE_COUNT="$(printf '%s\n' "$STAGES" | grep -c '|' || true)" + +# --stage <n> validation runs before the workspace guard so bad input always +# exits non-zero, regardless of whether the target is a Rust workspace. +if [ "$MODE" = "stage" ]; then + if ! [[ "$STAGE_NUM" =~ ^[1-9][0-9]*$ ]] || [ "$STAGE_NUM" -gt "$STAGE_COUNT" ]; then + echo "audit.sh: invalid stage '$STAGE_NUM' (valid: 1..$STAGE_COUNT)" >&2 + print_usage + exit 2 + fi +fi + +# Target dir checked first (fixture is a Rust workspace nested inside a +# non-Rust outer repo); git root fallback only if target itself has no Cargo.toml. +find_workspace_root() { + local dir="$1" + if [ -f "$dir/Cargo.toml" ]; then + echo "$dir" + return 0 + fi + local git_root + if git_root="$(git -C "$dir" rev-parse --show-toplevel 2>/dev/null)"; then + if [ -f "$git_root/Cargo.toml" ]; then + echo "$git_root" + return 0 + fi + fi + return 1 +} + +if ! WORKSPACE="$(find_workspace_root "$TARGET")"; then + echo "audit.sh: not a Rust workspace, skipping" >&2 + exit 0 +fi + +severity_for() { + case "$1" in + "fmt") echo "MEDIUM" ;; + "clippy (deny warnings)") echo "HIGH" ;; + "layer-deps validator") echo "HIGH" ;; + "cargo-machete (unused deps)") echo "MEDIUM" ;; + "cargo-audit (advisories)") echo "CRITICAL" ;; + "cargo-deny (license + bans + sources + advisories)") echo "CRITICAL" ;; + "tests") echo "HIGH" ;; + *) echo "MEDIUM" ;; + esac +} + +stage_runnable() { + local cmd="$1" first sub path + first="$(echo "$cmd" | awk '{print $1}')" + case "$first" in + cargo) + sub="$(echo "$cmd" | awk '{print $2}')" + # `cargo fmt`/`cargo clippy` ship with the toolchain via rustup — they + # are dispatched by `cargo` itself, not by separate `cargo-fmt` binaries. + if [ "$sub" = "fmt" ] || [ "$sub" = "clippy" ]; then + command -v cargo >/dev/null + return $? + fi + command -v "cargo-$sub" >/dev/null + ;; + bash) + path="$(echo "$cmd" | awk '{print $2}')" + [ -f "$WORKSPACE/$path" ] + ;; + *) + command -v "$first" >/dev/null + ;; + esac +} + +# Describe the missing artifact when a stage is not runnable. +# Emits "<artifact>|<install_hint>" — the artifact is the canonical binary or +# script name (used as the finding's `path` slot), and the hint is a short +# remediation string with no pipe characters. +missing_artifact_for() { + local cmd="$1" + local first sub path + first="$(echo "$cmd" | awk '{print $1}')" + case "$first" in + cargo) + sub="$(echo "$cmd" | awk '{print $2}')" + printf 'cargo-%s|tool not installed; skipping. Install with: cargo install cargo-%s\n' "$sub" "$sub" + ;; + bash) + path="$(echo "$cmd" | awk '{print $2}')" + printf '%s|script not present in workspace; skipping. Add %s to your workspace.\n' "$path" "$path" + ;; + *) + printf '%s|tool not installed; skipping. Install %s and re-run.\n' "$first" "$first" + ;; + esac +} + +# Heuristic: extract a path:line citation from stage stderr/stdout. Falls back +# to <workspace-relative-cargo>:1 so every finding has a citation per AC-7.3. +extract_citation() { + local log="$1" + local hit + # rust diagnostic format: " --> path/to/file.rs:LINE:COL" + hit="$(grep -oE '[^[:space:]]+\.rs:[0-9]+:[0-9]+' "$log" 2>/dev/null | head -n1 || true)" + if [ -n "$hit" ]; then + # Drop the column; keep path:line. + echo "$hit" | awk -F: '{print $1 ":" $2}' + return + fi + # generic "path:line:" prefix + hit="$(grep -oE '[^[:space:]]+:[0-9]+:' "$log" 2>/dev/null | head -n1 || true)" + if [ -n "$hit" ]; then + echo "${hit%:}" + return + fi + echo "Cargo.toml:1" +} + +# Detect engineering plugin's code-review skill. Returns 0 and prints the +# SKILL.md path on stdout; 1 if absent. +detect_engineering_plugin() { + local cand + if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then + cand="$CLAUDE_PLUGIN_ROOT/../engineering/skills/code-review/SKILL.md" + if [ -f "$cand" ]; then echo "$cand"; return 0; fi + fi + if [ -n "${HOME:-}" ]; then + for cand in "$HOME"/.claude/plugins/cache/*/engineering/skills/code-review/SKILL.md; do + [ -f "$cand" ] && { echo "$cand"; return 0; } + done + fi + cand="$WORKSPACE/.claude/plugins/engineering/skills/code-review/SKILL.md" + if [ -f "$cand" ]; then echo "$cand"; return 0; fi + return 1 +} + +# Capture diff against merge-base, falling through origin/main → main → HEAD~1 +# so a branch with no upstream still gets something reviewable. Writes to $1. +capture_review_diff() { + local out="$1" base="" head="" + head="$(git -C "$WORKSPACE" rev-parse HEAD 2>/dev/null || true)" + base="$(git -C "$WORKSPACE" merge-base origin/main HEAD 2>/dev/null || true)" + if [ -z "$base" ] || [ "$base" = "$head" ]; then + base="$(git -C "$WORKSPACE" merge-base main HEAD 2>/dev/null || true)" + fi + if [ -z "$base" ] || [ "$base" = "$head" ]; then + base="$(git -C "$WORKSPACE" rev-parse HEAD~1 2>/dev/null || true)" + fi + if [ -n "$base" ] && [ "$base" != "$head" ]; then + git -C "$WORKSPACE" diff "$base"..HEAD > "$out" 2>/dev/null || : > "$out" + else + : > "$out" + fi +} + +REPORT_DIR="$WORKSPACE/.claude" +TIMESTAMP="$(date -u +%Y%m%d-%H%M%S)" +REPORT_PATH="$REPORT_DIR/audit-$TIMESTAMP.md" +mkdir -p "$REPORT_DIR" + +FINDINGS_TMP="$(mktemp -t code-et-audit.XXXXXX)" +REVIEW_TMP="$(mktemp -t code-et-audit-review.XXXXXX)" +trap 'rm -f "$FINDINGS_TMP" "$REVIEW_TMP"' EXIT + +OVERALL_FAIL=0 + +case "$MODE" in + fast) STAGES="$(printf '%s\n' "$STAGES" | sed -n '1,2p')" ;; + stage) STAGES="$(printf '%s\n' "$STAGES" | sed -n "${STAGE_NUM}p")" ;; + all) ;; +esac + +while IFS='|' read -r name cmd; do + [ -z "$name" ] && continue + severity="$(severity_for "$name")" + + if ! stage_runnable "$cmd"; then + artifact_hint="$(missing_artifact_for "$cmd")" + artifact="${artifact_hint%%|*}" + hint="${artifact_hint#*|}" + echo "audit: WARNING: $name — $hint" >&2 + # Record as LOW finding; exit code stays 0 unless another stage fails. + msg="${hint//|/\\|}" + printf '%s|%s|%s|%s|%s\n' \ + "LOW" "$name" "$artifact" "1" "$msg" \ + >> "$FINDINGS_TMP" + continue + fi + + echo "audit: $name — running" >&2 + stage_log="$(mktemp -t code-et-audit-stage.XXXXXX)" + set +e + ( cd "$WORKSPACE" && eval "$cmd" ) > "$stage_log" 2>&1 + rc=$? + set -e + cat "$stage_log" >&2 + if [ "$rc" -ne 0 ]; then + OVERALL_FAIL=1 + citation="$(extract_citation "$stage_log")" + msg="stage '$name' failed (exit $rc): $cmd" + # Strip pipes from the message so the delimited format survives. + msg="${msg//|/\\|}" + printf '%s|%s|%s|%s|%s\n' \ + "$severity" "$name" "${citation%:*}" "${citation##*:}" "$msg" \ + >> "$FINDINGS_TMP" + fi + rm -f "$stage_log" +done <<< "$STAGES" + +REPORT_ARGS=("$REPORT_PATH") + +# Review wiring only attaches when static stages pass — a failing audit is +# already actionable without prose review noise. +if [ "$REVIEW" -eq 1 ] && [ "$OVERALL_FAIL" -eq 0 ]; then + if ! SKILL_PATH="$(detect_engineering_plugin)"; then + echo "audit: engineering plugin not installed; run /plugin install engineering to enable --review" >&2 + exit 1 + fi + echo "audit: --review enabled — capturing diff for $SKILL_PATH" >&2 + capture_review_diff "$REVIEW_TMP" + REPORT_ARGS+=(--review-file "$REVIEW_TMP") +fi + +SUMMARY_LINE="$(bash "$REPORT_SCRIPT" "${REPORT_ARGS[@]}" < "$FINDINGS_TMP")" +echo "audit: report written to $REPORT_PATH" >&2 + +# AC-12.2: on failure, surface the writer's summary line on stderr so the user +# sees the highest-severity finding without opening the report. +if [ "$OVERALL_FAIL" -ne 0 ] && [ -n "$SUMMARY_LINE" ]; then + echo "$SUMMARY_LINE" >&2 +fi + +exit "$OVERALL_FAIL" diff --git a/code-et-implementer/scripts/verify-gate.sh b/code-et-implementer/scripts/verify-gate.sh index 5e7ebb5..9b8ea02 100755 --- a/code-et-implementer/scripts/verify-gate.sh +++ b/code-et-implementer/scripts/verify-gate.sh @@ -1,4 +1,16 @@ #!/bin/bash # SubagentStop hook — verification gate after background agent DIR="$(cd "$(dirname "$0")" && pwd)" -"$DIR/run-tests.sh" "Agent" 1 +"$DIR/run-tests.sh" "Agent" 1 || exit $? + +# US-3: Run audit --fast on Rust workspaces. Detect repo root via git, fall +# back to cwd. Skip silently when no Cargo.toml — keeps the hook safe to wire +# into shared SubagentStop on non-Rust repos. +ROOT="" +if command -v git >/dev/null 2>&1; then + ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)" +fi +[ -z "$ROOT" ] && ROOT="$PWD" +if [ -f "$ROOT/Cargo.toml" ]; then + "$DIR/audit.sh" --fast "$ROOT" </dev/null +fi diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/.gitignore b/code-et-implementer/templates/rust/dioxus-fullstack/.gitignore index 2119a2a..3c9e710 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/.gitignore +++ b/code-et-implementer/templates/rust/dioxus-fullstack/.gitignore @@ -5,6 +5,7 @@ /dist /.dioxus /Cargo.lock.bak +/.claude/audit-*.md # IDE /.idea diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/Cargo.lock b/code-et-implementer/templates/rust/dioxus-fullstack/Cargo.lock new file mode 100644 index 0000000..262b95f --- /dev/null +++ b/code-et-implementer/templates/rust/dioxus-fullstack/Cargo.lock @@ -0,0 +1,7681 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "application" +version = "0.1.0" +dependencies = [ + "async-trait", + "domain", + "mockall", + "serde", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "askama_escape" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df27b8d5ddb458c5fb1bbc1ce172d4a38c614a97d550b0ac89003897fb01de4" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-tungstenite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee88b4c88ac8c9ea446ad43498955750a4bbe64c4392f21ccfe5d952865e318f" +dependencies = [ + "atomic-waker", + "futures-core", + "futures-io", + "futures-task", + "futures-util", + "log", + "pin-project-lite", + "tungstenite 0.27.0", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "axum-macros", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite 0.29.0", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "base16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.11.1", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "charset" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e" +dependencies = [ + "base64", + "encoding_rs", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.1", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cocoa" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c" +dependencies = [ + "bitflags 2.11.1", + "block", + "cocoa-foundation", + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" +dependencies = [ + "bitflags 2.11.1", + "block", + "core-foundation 0.10.1", + "core-graphics-types", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0664d2867b4a32697dfe655557f5c3b187e9b605b38612a748e5ec99811d160" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "content_disposition" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc14a88e1463ddd193906285abe5c360c7e8564e05ccc5d501755f7fbc9ca9c" +dependencies = [ + "charset", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf 0.10.1", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", + "unicode-xid", +] + +[[package]] +name = "desktop" +version = "0.1.0" +dependencies = [ + "dioxus", + "interface", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daed3da3e7678d7267bc8523c607dbd19c970f4154cef30b9a58acf35e0a44d5" +dependencies = [ + "dioxus-asset-resolver", + "dioxus-cli-config", + "dioxus-config-macro", + "dioxus-config-macros", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-html", + "dioxus-server", + "dioxus-signals", + "dioxus-stores", + "dioxus-web", + "serde", + "subsecond", +] + +[[package]] +name = "dioxus-asset-resolver" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d28cf8859bac5946200df9dc0de8bdc3df60bec007e465dbd3684dbd05fc3ac7" +dependencies = [ + "dioxus-cli-config", + "http", + "infer", + "jni 0.21.1", + "js-sys", + "ndk", + "ndk-context", + "ndk-sys", + "percent-encoding", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef4c22f0a239158f77966d7e7d39b637a10cb374cbd85f881704a336fd3f5c8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a57abdf7bf60e22732a76588e75274ca4eb5d6399b716c735d187d743060b9" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-config-macros" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b571d361abed46996a489e88ced2a335f0ca608305e238859a1a6cca1d85fb15" + +[[package]] +name = "dioxus-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1ff62d7073a4dd48670093469e2c099ef3c895345998a064554274eb39dc91" +dependencies = [ + "anyhow", + "const_format", + "dioxus-core-types", + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash 2.1.2", + "rustversion", + "serde", + "slab", + "slotmap", + "subsecond", + "tracing", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e22483ccaaf037e600683f25a6114754b9610e85e6fafb11adf45ccc2bd7116" +dependencies = [ + "convert_case 0.8.0", + "dioxus-rsx", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dioxus-core-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a47a4908cc9680a27b314aa8c3832a517274f896a31b0cce7d7adee57eacbb" + +[[package]] +name = "dioxus-desktop" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3fc8ad2cb85e7810e35f69eccd31ad1cc715f2718f33b3e6b84ff5c794c6236" +dependencies = [ + "anyhow", + "async-trait", + "base64", + "bytes", + "cocoa", + "core-foundation 0.10.1", + "dioxus-asset-resolver", + "dioxus-cli-config", + "dioxus-core", + "dioxus-devtools", + "dioxus-document", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-signals", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "image", + "infer", + "jni 0.21.1", + "lazy-js-bundle", + "libc", + "muda", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "percent-encoding", + "rand 0.9.4", + "rfd", + "rustc-hash 2.1.2", + "serde", + "serde_json", + "signal-hook", + "slab", + "subtle", + "tao", + "thiserror 2.0.18", + "tokio", + "tracing", + "tray-icon", + "tungstenite 0.28.0", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-devtools" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58abe580e75d1e6bbab658681b126d4a79df3e9c9fd66d0a0627206d1669f65d" +dependencies = [ + "dioxus-cli-config", + "dioxus-core", + "dioxus-devtools-types", + "dioxus-signals", + "futures-channel", + "futures-util", + "serde", + "serde_json", + "subsecond", + "thiserror 2.0.18", + "tracing", + "tungstenite 0.28.0", +] + +[[package]] +name = "dioxus-devtools-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5678f9f53962936765d0d767c13200bc6911a943492b8614d666425da62662d2" +dependencies = [ + "dioxus-core", + "serde", + "subsecond-types", +] + +[[package]] +name = "dioxus-document" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28eea4bea227f6eb0852ab67ed97a20474f234e8bb2a6abab17401d443aa746c" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-html", + "futures-channel", + "futures-util", + "generational-box", + "lazy-js-bundle", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d9db5bb54c6305ed3f1cd71ea245a9935126f7586faf92950b40be76be5ae" +dependencies = [ + "anyhow", + "async-stream", + "async-tungstenite", + "axum", + "axum-core", + "base64", + "bytes", + "ciborium", + "const-str", + "const_format", + "content_disposition", + "derive_more 2.1.1", + "dioxus-asset-resolver", + "dioxus-cli-config", + "dioxus-core", + "dioxus-fullstack-core", + "dioxus-fullstack-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-signals", + "form_urlencoded", + "futures", + "futures-channel", + "futures-util", + "gloo-net", + "headers", + "http", + "http-body", + "http-body-util", + "js-sys", + "mime", + "pin-project", + "reqwest", + "rustversion", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "thiserror 2.0.18", + "tokio-util", + "tracing", + "tungstenite 0.27.0", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "dioxus-fullstack-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a1d1f1ff784a78709f95b5021888bcd6bb2a4acf972213acd23c1a8a69701b1" +dependencies = [ + "anyhow", + "axum-core", + "base64", + "ciborium", + "dioxus-core", + "dioxus-document", + "dioxus-history", + "dioxus-hooks", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "http", + "inventory", + "parking_lot", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-fullstack-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8927db4a9d939677a921eccf2ed36762f05b2e624787f2ef3c095fabc6e6c1c4" +dependencies = [ + "const_format", + "convert_case 0.8.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "xxhash-rust", +] + +[[package]] +name = "dioxus-history" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e304644c2131e00c4a58ecd91e8f7f86dba007532732bd1a391506b75a974a9" +dependencies = [ + "dioxus-core", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50506e568a9786247993a29dd3b06fd84f30605312beea2b5f4e51f4ab543b0c" +dependencies = [ + "dioxus-core", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "rustversion", + "slab", + "tracing", +] + +[[package]] +name = "dioxus-html" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c52bdcc2355437ca8bd9986aa1c43c5439af459a2c16012d949b6a35092ae2" +dependencies = [ + "async-trait", + "bytes", + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-hooks", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "futures-util", + "generational-box", + "keyboard-types", + "lazy-js-bundle", + "rustversion", + "serde", + "serde_json", + "serde_repr", + "tracing", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a48657a6a20a65894abba7e7876937e37e924ecaed63e8b654b364c4ffa133" +dependencies = [ + "convert_case 0.8.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a36364418afcb181e19c67d9dbea766dbc2e6bfc0751365a60727ebe665362" +dependencies = [ + "dioxus-core", + "dioxus-core-types", + "dioxus-html", + "js-sys", + "lazy-js-bundle", + "rustc-hash 2.1.2", + "serde", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-logger" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32720fddff9b73266502b15b2c36b139bd8fbfd5b85b67d29ba06d5b4ed60669" +dependencies = [ + "dioxus-cli-config", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "dioxus-router" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74cbe73ab4e815aacaa41726d25b84a2debacade3f35db357495dd1d5ed14949" +dependencies = [ + "dioxus-cli-config", + "dioxus-core", + "dioxus-core-macro", + "dioxus-fullstack-core", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-router-macro", + "dioxus-signals", + "percent-encoding", + "rustversion", + "tracing", + "url", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b91aa613f584c2dd598436c33d22b94b85b132b449eae8f81e538300980222" +dependencies = [ + "base16", + "digest", + "proc-macro2", + "quote", + "sha2", + "slab", + "syn 2.0.117", +] + +[[package]] +name = "dioxus-rsx" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767e37207d120b978643f5d1f68675984dfa68d44ceb4dab3d4337b36695d339" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "rustversion", + "syn 2.0.117", +] + +[[package]] +name = "dioxus-server" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba9bd5435d3b92a77f1f6ccb175b009efbb708c02247a786bd5bff4bf1253f3" +dependencies = [ + "anyhow", + "async-trait", + "axum", + "base64", + "bytes", + "chrono", + "ciborium", + "dashmap", + "dioxus-cli-config", + "dioxus-core", + "dioxus-core-macro", + "dioxus-devtools", + "dioxus-document", + "dioxus-fullstack-core", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-logger", + "dioxus-router", + "dioxus-signals", + "dioxus-ssr", + "enumset", + "futures", + "futures-channel", + "futures-util", + "generational-box", + "http", + "http-body-util", + "hyper", + "hyper-util", + "inventory", + "lru", + "parking_lot", + "pin-project", + "rustc-hash 2.1.2", + "serde", + "serde_json", + "serde_qs", + "subsecond", + "thiserror 2.0.18", + "tokio", + "tokio-tungstenite 0.28.0", + "tokio-util", + "tower", + "tower-http", + "tracing", + "tracing-futures", + "url", + "walkdir", +] + +[[package]] +name = "dioxus-signals" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef8e274214375597ad26a2e4407b681de4f876785724171ab605a51b272c51e" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "parking_lot", + "rustc-hash 2.1.2", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-ssr" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa8abccb3124dd16ff0e557c5659fbe00f8ae3b6a29c1be01e4e440fb69f7c7" +dependencies = [ + "askama_escape", + "dioxus-core", + "dioxus-core-types", + "rustc-hash 2.1.2", +] + +[[package]] +name = "dioxus-stores" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6a8e92a6df3b3e875f268a2f20d2aeaf017fc952af86810b4d4e435b9a8c1" +dependencies = [ + "dioxus-core", + "dioxus-signals", + "dioxus-stores-macro", + "generational-box", +] + +[[package]] +name = "dioxus-stores-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb44211a301fd4ddcb21b9c46807658690c2d5a52a7313393001090764ca1ab" +dependencies = [ + "convert_case 0.8.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dioxus-web" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0e3fcfea97e9a1755d06a38c05e9d8b19fd09228ede36f2c9cfab69891c628" +dependencies = [ + "dioxus-cli-config", + "dioxus-core", + "dioxus-core-types", + "dioxus-devtools", + "dioxus-document", + "dioxus-fullstack-core", + "dioxus-history", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "gloo-timers", + "js-sys", + "lazy-js-bundle", + "rustc-hash 2.1.2", + "send_wrapper", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.1", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "domain" +version = "0.1.0" +dependencies = [ + "serde", + "thiserror 2.0.18", + "uuid", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enumset" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155c81d7c7cae205289a26248ede524fdb1b7266b1fdb0a479b57bd3a6db2235" +dependencies = [ + "parking_lot", + "tracing", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.11.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "objc2", + "objc2-app-kit", + "once_cell", + "thiserror 2.0.18", + "windows-sys 0.59.0", + "x11rb", + "xkeysym", +] + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "html5ever" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +dependencies = [ + "log", + "mac", + "markup5ever", + "match_token", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.7", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-layer", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "infrastructure" +version = "0.1.0" +dependencies = [ + "anyhow", + "application", + "async-trait", + "domain", + "dotenvy", + "secrecy", + "serde", + "sqlx", + "tokio", + "uuid", +] + +[[package]] +name = "interface" +version = "0.1.0" +dependencies = [ + "application", + "axum", + "dioxus", + "domain", + "serde", + "serde_json", + "tokio", + "tower", + "tower-http", + "uuid", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.11.1", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "kuchikiki" +version = "0.8.8-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +dependencies = [ + "cssparser", + "html5ever", + "indexmap", + "selectors", +] + +[[package]] +name = "lazy-js-bundle" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76317b9348f1c069d7e652722c3fa86683ec047ba4c1551bde1daa576ce3807e" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags 2.11.1", + "libc", + "plain", + "redox_syscall 0.7.5", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix", +] + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.2", +] + +[[package]] +name = "mobile" +version = "0.1.0" +dependencies = [ + "dioxus", + "interface", +] + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9fec5a4e89860383d778d10563a605838f8f0b2f9303868937e5ff32e86177" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libxdo", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "thiserror 2.0.18", + "windows-sys 0.60.2", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.1", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.6.2", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.6", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.6", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.6", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.3", +] + +[[package]] +name = "pin-project" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.11+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "version_check", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.2", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash 2.1.2", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.4", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_syscall" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "cookie", + "cookie_store", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 1.0.7", +] + +[[package]] +name = "rfd" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" +dependencies = [ + "block2", + "dispatch2", + "js-sys", + "libc", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "percent-encoding", + "pollster", + "raw-window-handle 0.6.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more 0.99.20", + "fxhash", + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_qs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "server" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "infrastructure", + "interface", + "secrecy", + "sqlx", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "servo_arc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "sledgehammer_bindgen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e83e178d176459c92bc129cfd0958afac3ced925471b889b3a75546cfc4133" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb251b407f50028476a600541542b605bb864d35d9ee1de4f6cab45d88475e6d" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdd4b83524961983cea3c55383b3910fd2f24fd13a188f5b091d2d504a61ae" +dependencies = [ + "rustc-hash 1.1.0", +] + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.5", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rustls", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots 0.26.11", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.117", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.117", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64", + "bitflags 2.11.1", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.6", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.18", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64", + "bitflags 2.11.1", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.6", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.18", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.18", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "subsecond" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a046b8921cd8a8b3bbeba6339d4ea8cc687653b30c73a158ed4d730d402199db" +dependencies = [ + "js-sys", + "libc", + "libloading 0.8.9", + "memfd", + "memmap2", + "serde", + "subsecond-types", + "thiserror 2.0.18", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "subsecond-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3c20eb7361e08d071ee249a687cddf16a1ae9d36f9bcd92481f31d6ac17eee" +dependencies = [ + "serde", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.34.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9103edf55f2da3c82aea4c7fab7c4241032bfeea0e71fa557d98e00e7ce7cc20" +dependencies = [ + "bitflags 2.11.1", + "block2", + "core-foundation 0.10.1", + "core-graphics 0.25.0", + "crossbeam-channel", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni 0.21.1", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "parking_lot", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.2", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.2", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +dependencies = [ + "bitflags 2.11.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "tray-icon" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "thiserror 2.0.18", + "windows-sys 0.60.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "native-tls", + "rand 0.9.4", + "rustls", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warnings" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f68998838dab65727c9b30465595c6f7c953313559371ca8bf31759b3680ad" +dependencies = [ + "pin-project", + "tracing", + "warnings-macro", +] + +[[package]] +name = "warnings-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59195a1db0e95b920366d949ba5e0d3fc0e70b67c09be15ce5abb790106b0571" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web" +version = "0.1.0" +dependencies = [ + "dioxus", + "interface", +] + +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +dependencies = [ + "core-foundation 0.10.1", + "jni 0.22.4", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.1", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2" +dependencies = [ + "base64", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dpi", + "dunce", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/README.md b/code-et-implementer/templates/rust/dioxus-fullstack/README.md index 0da36f1..e7785f5 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/README.md +++ b/code-et-implementer/templates/rust/dioxus-fullstack/README.md @@ -1,6 +1,6 @@ # {{name}} -Pure-Rust full-stack project bootstrapped with [code-et](https://github.com/Emerging-Tech-Visma/code-et) v3.9.0+. +Pure-Rust full-stack project bootstrapped with [code-et](https://github.com/Emerging-Tech-Visma/code-et) v4.0+. **Stack:** axum + sqlx + Dioxus 0.7+ + tokio. **Frontend:** Dioxus on web, desktop, and mobile from one component tree. **Database:** SQLite for local, PostgreSQL on GCP Cloud SQL for production. @@ -45,22 +45,44 @@ cargo install dioxus-cli cargo install cargo-udeps ``` -If `/code:bootstrap` was invoked with `--install-tools`, these are already on your machine. +If `/code:start` was invoked with `--install-tools`, these are already on your machine. -## Adding a feature - -Use code-et's feature lane: +## Daily workflow ``` -/code:grill # refine the idea -/code:prd # write a PRD into plans/<slug>.md -/code:plan-issue # break PRD into vertical-slice tasks -/code:implement # parallel agents in worktrees -/commit-push-pr # PR; CI runs the audit gate +# Bug +/code:fix "<one-line bug>" # intake → Task Brief → you implement → /commit-push-pr + +# Feature +/code:plan "<idea>" # refined brief → PRD on disk → vertical-slice tasks +/code:ship # parallel worktree agents + post-merge audit (1 auto-retry) +/code:review # full audit + diff review (pre-merge gate) +/commit-push-pr # PR; CI runs the same audit pipeline ``` Each task carries `metadata.layer ∈ {domain, application, infrastructure, interface, chore}`. Vertical slices may span layers; each *file* belongs to exactly one. Imports point inward. +## Deploy & upload — always via scripts + +**Discipline:** never deploy or upload {{name}} via raw `cargo run`, `docker push`, `gcloud run deploy`, `gsutil cp`, `scp`, or any other ad-hoc command typed into a shell. All paths route through: + +``` +just deploy staging # bash scripts/deploy.sh staging +just deploy prod # bash scripts/deploy.sh prod +just upload web staging # bash scripts/upload.sh web staging +just upload desktop prod # bash scripts/upload.sh desktop prod +``` + +The scripts are starting points — host-specific commands are marked `# TODO:` blocks (Cloud Run / GKE / Fly / your VM). Fill them in once for your project; after that, every deploy is: + +1. **Pre-flight:** clean tree, audit gate green, required tools on PATH. +2. **Build:** container image tagged with `git rev-parse --short HEAD`. +3. **Migrate:** `sqlx migrate run` against the target DB (Cloud SQL Auth Proxy + IAM token in prod). +4. **Roll out:** push image, update Cloud Run revision (or your equivalent). +5. **Smoke check:** curl `/healthz`, expect 200. + +This is the only sane way to ship Rust workspaces with `sqlx::query!` macros — the build needs the right `DATABASE_URL` (or offline `sqlx-data.json`), and the deploy needs migrations to land before the new image gets traffic. A script enforces that order; ad-hoc commands forget it. + ## Doctrine - [`docs/architecture.md`](https://github.com/Emerging-Tech-Visma/code-et/blob/main/code-et-implementer/docs/architecture.md) — Clean Architecture details diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/apps/server/Cargo.toml b/code-et-implementer/templates/rust/dioxus-fullstack/apps/server/Cargo.toml index 85eaba4..bfad322 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/apps/server/Cargo.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/apps/server/Cargo.toml @@ -10,8 +10,6 @@ name = "server" path = "src/main.rs" [dependencies] -domain = { workspace = true } -application = { workspace = true } infrastructure = { workspace = true } interface = { workspace = true, features = ["server"] } axum = { workspace = true } @@ -21,3 +19,8 @@ anyhow = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } secrecy = { workspace = true } +# As the composition root grows to instantiate use cases directly, add: +# domain = { workspace = true } +# application = { workspace = true } +# These are removed for now to keep the audit gate clean — `interface::http::router` +# already wires the sample CreateUser path. diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/audit.toml b/code-et-implementer/templates/rust/dioxus-fullstack/audit.toml new file mode 100644 index 0000000..7e36ad7 --- /dev/null +++ b/code-et-implementer/templates/rust/dioxus-fullstack/audit.toml @@ -0,0 +1,17 @@ +# cargo-audit configuration. +# Format: https://docs.rs/cargo-audit/latest/cargo_audit/config/struct.AuditConfig.html +# +# Each ignore must include a rationale comment. Re-evaluate every time we +# bump deps. Unmaintained / unsound warnings (gtk-rs 0.18 chain via +# dioxus-desktop, etc.) exit 0 by default and don't need ignores — they +# show up in the cargo-audit output as informational warnings. + +[advisories] +ignore = [ + # RUSTSEC-2023-0071: rsa 0.9.x Marvin Attack (timing side-channel, no fixed upgrade). + # Reason: pulled in transitively by sqlx-macros 0.8.x, which compiles all DB + # backend support for compile-time `query!` checking. We use sqlite + postgres + # only — the rsa crate is never reached at runtime. Re-check on every sqlx bump + # and remove this ignore once sqlx-macros decouples mysql. + "RUSTSEC-2023-0071", +] diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/crates/application/Cargo.toml b/code-et-implementer/templates/rust/dioxus-fullstack/crates/application/Cargo.toml index 82fadb3..887214f 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/crates/application/Cargo.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/crates/application/Cargo.toml @@ -8,9 +8,10 @@ license.workspace = true [dependencies] domain = { workspace = true } async-trait = { workspace = true } -anyhow = { workspace = true } thiserror = { workspace = true } serde = { workspace = true } +# Add `anyhow = { workspace = true }` if you need glue-error wrapping in a +# use case (rare — application errors should be `thiserror`-derived). [dev-dependencies] mockall = { workspace = true } diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/crates/domain/Cargo.toml b/code-et-implementer/templates/rust/dioxus-fullstack/crates/domain/Cargo.toml index 2557938..6334eca 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/crates/domain/Cargo.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/crates/domain/Cargo.toml @@ -10,4 +10,5 @@ license.workspace = true serde = { workspace = true } thiserror = { workspace = true } uuid = { workspace = true } -time = { workspace = true } +# Add `time = { workspace = true }` when you introduce a value object that +# needs DateTime/Duration. The workspace already pins it. diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/crates/infrastructure/Cargo.toml b/code-et-implementer/templates/rust/dioxus-fullstack/crates/infrastructure/Cargo.toml index 641bfef..e189ac6 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/crates/infrastructure/Cargo.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/crates/infrastructure/Cargo.toml @@ -10,16 +10,18 @@ domain = { workspace = true } application = { workspace = true } async-trait = { workspace = true } anyhow = { workspace = true } -thiserror = { workspace = true } serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } secrecy = { workspace = true } -tracing = { workspace = true } dotenvy = { workspace = true } uuid = { workspace = true } sqlx = { workspace = true, features = ["sqlite", "postgres", "uuid"] } -reqwest = { workspace = true } +# Add when first used: +# thiserror — for explicit error enums in adapters (use case errors should be +# thiserror-derived; adapter-internal can use anyhow). +# serde_json — for HTTP client request/response bodies. +# tokio = { workspace = true } — for async helpers beyond what sqlx exposes. +# tracing — for adapter-level instrumentation. +# reqwest — for HTTP-client adapters (http_clients/). [dev-dependencies] tokio = { workspace = true } diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/crates/interface/Cargo.toml b/code-et-implementer/templates/rust/dioxus-fullstack/crates/interface/Cargo.toml index c69058a..90fbfc8 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/crates/interface/Cargo.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/crates/interface/Cargo.toml @@ -10,9 +10,6 @@ domain = { workspace = true } application = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -async-trait = { workspace = true } -anyhow = { workspace = true } -tracing = { workspace = true } uuid = { workspace = true } # UI @@ -24,6 +21,12 @@ tower = { workspace = true, optional = true } tower-http = { workspace = true, optional = true } tokio = { workspace = true, optional = true } +# cargo-machete misses feature-gated optional deps; tower/tower-http/tokio +# are wired into the `server` feature even when not directly imported by +# the current handler set. Remove ignores once a handler imports them. +[package.metadata.cargo-machete] +ignored = ["tokio", "tower", "tower-http"] + [features] default = ["server"] server = ["dep:axum", "dep:tower", "dep:tower-http", "dep:tokio", "dioxus/fullstack"] diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/deny.toml b/code-et-implementer/templates/rust/dioxus-fullstack/deny.toml index 85ad064..df29209 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/deny.toml +++ b/code-et-implementer/templates/rust/dioxus-fullstack/deny.toml @@ -6,6 +6,27 @@ all-features = true [advisories] version = 2 yanked = "deny" +# `unmaintained` defaults to "all" (error on any unmaintained crate) in +# cargo-deny v2. The dioxus-desktop stack drags in 10+ unmaintained +# gtk-rs 0.18 / fxhash crates that we cannot fix without dioxus migrating +# upstream. Set to "workspace" — only error if one of OUR workspace crates +# becomes unmaintained. cargo-audit (stage 5) still surfaces every transitive +# advisory in its report and CI logs. +unmaintained = "workspace" +# `unsound` — keep as error; if a real unsoundness lands, we want to fail. +# Each ignore below needs a one-line rationale. See audit.toml for long-form. +# Re-evaluate every time deps bump. +ignore = [ + # rsa Marvin Attack — transitive via sqlx-macros (compile-time only), + # no mysql at runtime. Remove once sqlx-macros decouples mysql. + "RUSTSEC-2023-0071", + # glib 0.18 unsound — transitive via dioxus-desktop's gtk-rs 0.18 chain. + # Removed once dioxus-desktop migrates off the unmaintained stack. + "RUSTSEC-2024-0429", + # rand 0.7 unsound (custom logger) — transitive via dioxus-desktop's + # kuchikiki/selectors. Removed once dioxus-desktop deps modernise. + "RUSTSEC-2026-0097", +] [licenses] version = 2 @@ -21,12 +42,28 @@ allow = [ "Zlib", "MPL-2.0", "CC0-1.0", + # NCSA: University of Illinois / NCSA Open Source License. OSI-approved + # permissive, equivalent in scope to MIT/BSD. Required by libfuzzer-sys + # (transitive via dioxus-desktop → image → ravif → rav1e). + "NCSA", + # CDLA-Permissive-2.0: Community Data License Agreement Permissive 2.0. + # Used by webpki-roots. Permissive, equivalent in scope to MIT/BSD. + "CDLA-Permissive-2.0", + # BSL-1.0: Boost Software License 1.0. OSI-approved, very permissive + # (no attribution required for binary distributions). Used by xxhash-rust. + "BSL-1.0", ] confidence-threshold = 0.8 [bans] multiple-versions = "warn" -wildcards = "deny" +# Workspace path deps (`domain = { workspace = true }`) carry no version and +# read as wildcards to cargo-deny. `allow-wildcard-paths` only suppresses this +# for crates marked `publish = false`; we don't enforce that across every +# member, so we downgrade wildcards to warn — the workspace path deps are +# internal-only by construction. Use review to catch real `serde = "*"` slips. +wildcards = "warn" +allow-wildcard-paths = true deny = [] [sources] diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/justfile b/code-et-implementer/templates/rust/dioxus-fullstack/justfile index 00ca147..e362cc2 100644 --- a/code-et-implementer/templates/rust/dioxus-fullstack/justfile +++ b/code-et-implementer/templates/rust/dioxus-fullstack/justfile @@ -58,3 +58,11 @@ build-mobile: # Regenerate sqlx offline cache after schema or query changes. sqlx-prepare: cargo sqlx prepare --workspace + +# Ship the server to staging or prod. Always via scripts/deploy.sh — never raw cargo/gcloud. +deploy env="staging" *FLAGS="": + bash scripts/deploy.sh {{env}} {{FLAGS}} + +# Upload static artifacts (web bundle / desktop / mobile) to CDN or release bucket. +upload kind="web" env="staging": + bash scripts/upload.sh {{kind}} {{env}} diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/scripts/deploy.sh b/code-et-implementer/templates/rust/dioxus-fullstack/scripts/deploy.sh new file mode 100644 index 0000000..90bacee --- /dev/null +++ b/code-et-implementer/templates/rust/dioxus-fullstack/scripts/deploy.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# deploy.sh — single entry point for shipping {{name}} to a server. +# +# Discipline: NEVER deploy {{name}} via raw `cargo`, `docker`, or `gcloud` +# commands typed into a shell. All deploy paths go through this script so +# the steps are deterministic, reviewable, and reproducible across machines. +# +# Usage: +# bash scripts/deploy.sh <env> [--skip-migrate] [--skip-build] +# +# Environments: +# staging — staging Cloud Run / staging Cloud SQL +# prod — production Cloud Run / production Cloud SQL +# +# Required env vars (set in your shell or via 1Password / GCP Secret Manager): +# GCP_PROJECT_ID — target GCP project +# GCP_REGION — e.g. europe-north1 +# CLOUD_RUN_SERVICE — service name in Cloud Run +# ARTIFACT_REGISTRY_REPO — Artifact Registry repo for the image +# DATABASE_URL — Cloud SQL connection string (postgres://…) for migrations +# +# This script is a STARTING POINT — wire your actual hosting (Cloud Run, +# GKE, Fly, Render, raw VM) where the placeholder blocks are. The structure +# is fixed; the host-specific commands are the part you fill in. + +set -euo pipefail + +ENV="${1:?usage: deploy.sh <staging|prod> [--skip-migrate] [--skip-build]}" +shift || true + +SKIP_MIGRATE=0 +SKIP_BUILD=0 +for arg in "$@"; do + case "$arg" in + --skip-migrate) SKIP_MIGRATE=1 ;; + --skip-build) SKIP_BUILD=1 ;; + *) echo "deploy.sh: unknown flag: $arg" >&2; exit 2 ;; + esac +done + +case "$ENV" in + staging|prod) ;; + *) echo "deploy.sh: env must be staging|prod, got: $ENV" >&2; exit 2 ;; +esac + +# 1. Pre-flight: clean tree, on main (or release branch), tools present. +git diff --quiet || { echo "deploy.sh: working tree dirty — commit or stash first" >&2; exit 1; } +GIT_SHA="$(git rev-parse --short HEAD)" +GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +echo "deploy.sh: shipping {{name}} @$GIT_SHA from branch $GIT_BRANCH to $ENV" + +for tool in cargo gcloud docker; do + command -v "$tool" >/dev/null 2>&1 || { echo "deploy.sh: $tool not on PATH" >&2; exit 1; } +done + +# 2. Audit gate — never deploy a workspace that fails CI locally. +echo "deploy.sh: running just audit..." +just audit + +# 3. Build container image. +if [ "$SKIP_BUILD" -eq 0 ]; then + IMAGE_TAG="${GCP_REGION:?}-docker.pkg.dev/${GCP_PROJECT_ID:?}/${ARTIFACT_REGISTRY_REPO:?}/{{name}}-server:$GIT_SHA" + echo "deploy.sh: building $IMAGE_TAG" + # TODO: replace with your actual build. Two common patterns: + # (a) Dockerfile in repo root, multi-stage cargo build → distroless + # (b) cargo build --release + custom buildpack + docker build -t "$IMAGE_TAG" . + echo "deploy.sh: pushing $IMAGE_TAG" + docker push "$IMAGE_TAG" +fi + +# 4. Run migrations against the target database. +if [ "$SKIP_MIGRATE" -eq 0 ]; then + echo "deploy.sh: running sqlx migrations against $ENV database" + # TODO: configure DATABASE_URL for $ENV (Cloud SQL Auth Proxy + IAM token). + # Example via Cloud SQL Auth Proxy: + # cloud-sql-proxy --port 5433 "$GCP_PROJECT_ID:$GCP_REGION:$DB_INSTANCE" & + # PROXY_PID=$! + # trap 'kill $PROXY_PID' EXIT + # DATABASE_URL="postgres://app@127.0.0.1:5433/{{name}}?sslmode=disable" sqlx migrate run + sqlx migrate run +fi + +# 5. Roll out the new revision. +echo "deploy.sh: deploying $IMAGE_TAG to Cloud Run service ${CLOUD_RUN_SERVICE:?}" +# TODO: replace with your actual deploy. Common patterns: +# gcloud run deploy "$CLOUD_RUN_SERVICE" --image "$IMAGE_TAG" --region "$GCP_REGION" --project "$GCP_PROJECT_ID" +# kubectl set image deployment/{{name}}-server server="$IMAGE_TAG" +# flyctl deploy --image "$IMAGE_TAG" + +# 6. Smoke check. +echo "deploy.sh: post-deploy smoke check" +# TODO: curl the health endpoint, expect 200. +# curl -fsS "https://${CLOUD_RUN_SERVICE}.run.app/healthz" >/dev/null + +echo "deploy.sh: ✓ {{name}} @$GIT_SHA shipped to $ENV" diff --git a/code-et-implementer/templates/rust/dioxus-fullstack/scripts/upload.sh b/code-et-implementer/templates/rust/dioxus-fullstack/scripts/upload.sh new file mode 100644 index 0000000..5331bd1 --- /dev/null +++ b/code-et-implementer/templates/rust/dioxus-fullstack/scripts/upload.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# upload.sh — push static assets (web bundle, desktop binaries, mobile builds) to a CDN/object store. +# +# Discipline: NEVER upload {{name}} artifacts via raw `gsutil`/`aws s3`/`scp` +# commands. All uploads route through this script so the artifacts, paths, +# and cache rules are deterministic. +# +# Usage: +# bash scripts/upload.sh <kind> <env> +# +# Kinds: +# web — Dioxus web bundle (WASM + JS + assets) → CDN/object store +# desktop — desktop release binaries → release bucket +# mobile — mobile release builds → app store / TestFlight upload +# +# Required env vars: +# GCS_BUCKET — destination bucket (or your CDN's equivalent) +# GCS_PREFIX — path prefix inside the bucket (e.g. "{{name}}/web") +# CDN_INVALIDATE_PATHS — optional: comma-separated paths to invalidate after upload + +set -euo pipefail + +KIND="${1:?usage: upload.sh <web|desktop|mobile> <staging|prod>}" +ENV="${2:?usage: upload.sh <web|desktop|mobile> <staging|prod>}" + +case "$ENV" in + staging|prod) ;; + *) echo "upload.sh: env must be staging|prod, got: $ENV" >&2; exit 2 ;; +esac + +git diff --quiet || { echo "upload.sh: working tree dirty — commit first" >&2; exit 1; } +GIT_SHA="$(git rev-parse --short HEAD)" +DEST="gs://${GCS_BUCKET:?}/${GCS_PREFIX:?}/$ENV/$GIT_SHA" + +echo "upload.sh: $KIND artifacts → $DEST" + +case "$KIND" in + web) + echo "upload.sh: building Dioxus web bundle" + dx build --platform web --release + # TODO: configure your destination. Example with gsutil: + # gsutil -m -h "Cache-Control:public,max-age=31536000,immutable" rsync -r dist/ "$DEST/" + # (then upload index.html separately with Cache-Control: no-cache) + ;; + desktop) + echo "upload.sh: building desktop release" + cargo build -p desktop --release + # TODO: tar + gsutil cp to release bucket + ;; + mobile) + echo "upload.sh: building mobile release (best-effort)" + dx build --platform mobile --release + # TODO: app store / TestFlight upload (fastlane, eas, etc.) + ;; + *) + echo "upload.sh: unknown kind: $KIND (web|desktop|mobile)" >&2 + exit 2 + ;; +esac + +# Optional CDN invalidate. +if [ -n "${CDN_INVALIDATE_PATHS:-}" ]; then + echo "upload.sh: invalidating CDN paths: $CDN_INVALIDATE_PATHS" + # TODO: gcloud compute url-maps invalidate-cdn-cache, or your CDN equivalent +fi + +echo "upload.sh: ✓ $KIND @$GIT_SHA uploaded to $ENV ($DEST)" diff --git a/code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml b/code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml index 000bdf7..7f0a518 100644 --- a/code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml +++ b/code-et-implementer/templates/shared/.github/workflows/code-et-audit.yml @@ -30,6 +30,18 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install GTK + WebKit + xdo system libs (dioxus-desktop) + # dioxus-desktop on Linux uses wry → webkit2gtk → glib/gtk + xdo for + # tray-icon. Without these, `cargo build` fails on apps/desktop. Skip + # this step (and remove libxdo-dev) if your project's `--targets` + # excludes desktop. + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libgtk-3-dev libglib2.0-dev libsoup-3.0-dev \ + libjavascriptcoregtk-4.1-dev libwebkit2gtk-4.1-dev \ + libxdo-dev + - name: install rust toolchain uses: dtolnay/rust-toolchain@stable with: @@ -42,6 +54,11 @@ jobs: with: tool: cargo-nextest + - name: install cargo-audit + uses: taiki-e/install-action@v2 + with: + tool: cargo-audit + - name: fmt run: cargo fmt --all -- --check @@ -54,10 +71,10 @@ jobs: - name: cargo-machete (unused deps) uses: bnjbvr/cargo-machete@main + # cargo-audit ignores: each must have a rationale comment in audit.toml. + # Re-evaluate every time deps bump. - name: cargo-audit (advisories) - uses: rustsec/audit-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} + run: cargo audit --ignore RUSTSEC-2023-0071 - name: cargo-deny (license + bans + sources + advisories) uses: EmbarkStudios/cargo-deny-action@v1 diff --git a/code-et-implementer/templates/shared/CLAUDE.md.template b/code-et-implementer/templates/shared/CLAUDE.md.template index 6a87d15..962c5c3 100644 --- a/code-et-implementer/templates/shared/CLAUDE.md.template +++ b/code-et-implementer/templates/shared/CLAUDE.md.template @@ -27,7 +27,16 @@ This project follows the doctrine in code-et's [`docs/architecture.md`](https:// ## Recommended companions - [`knowledge-work-plugins/engineering`](https://claude.com/product/cowork) — provides `code-review`, `tech-debt`, `testing-strategy`, `system-design` skills. CLAUDE.md (the plugin's, not this one) has the delegation map. -- `rust-analyzer-lsp` — symbol-level precision for `/code:plan-issue` (used by code-et). +- `rust-analyzer-lsp` — symbol-level precision for `/code:plan` and `/code:fix` (used by code-et). + +## Deploy & upload rule + +**Never** deploy or upload {{name}} via raw shell commands. All paths route through: + +- `just deploy <env>` → `scripts/deploy.sh` (audit gate → build → migrate → roll out → smoke). +- `just upload <kind> <env>` → `scripts/upload.sh` (web/desktop/mobile artifacts → CDN/object store). + +If you find yourself typing `gcloud run deploy …`, `docker push …`, or `gsutil cp …` directly, stop — add the missing step to the script instead. The discipline keeps deploys deterministic and reviewable. ## CI gate @@ -46,8 +55,8 @@ A failing CI is the merge blocker. Local feedback: `just audit`. | Task | Command | |---|---| -| Single bug fix | `/code:go` (scope) → implement → `/commit-push-pr` | -| Feature (PRD-driven) | `/code:grill` → `/code:prd` → `/code:plan-issue` → `/code:implement` → `/commit-push-pr` | +| Single bug fix | `/code:fix` (scope) → implement → `/commit-push-pr` | +| Feature | `/code:plan` (idea → PRD → tasks) → `/code:ship` (parallel + audit) → `/code:review` → `/commit-push-pr` | | Add CI to existing repo | `/code:install-ci` | Each task carries `metadata.layer ∈ {domain, application, infrastructure, interface, chore}` (per-file). Vertical slices may span layers; each *file* belongs to exactly one. diff --git a/code-et-implementer/templates/shared/UPDATING.md b/code-et-implementer/templates/shared/UPDATING.md index c4628ab..1856d49 100644 --- a/code-et-implementer/templates/shared/UPDATING.md +++ b/code-et-implementer/templates/shared/UPDATING.md @@ -42,4 +42,4 @@ Cadence: review every quarter even if nothing has shipped. Rust crate ecosystems | Dioxus 0.x breaking changes between minors | The smoke project's `apps/{web,desktop,mobile}/main.rs` is the canary; if any fails to build, hold the bump until reviewed. | | `sqlx` schema incompatibility on bump | The smoke project's `migrations/20250101000000_init.sql` runs against both Postgres and SQLite in CI; if either fails, fix the migration before merging the template bump. | | GHA action removal | Pin majors; check the action's repo for archived/deprecated status before bumping. | -| Mobile target moves faster than web/desktop | If the bump breaks only mobile, gate the mobile app behind a feature flag in the bump PR. The `--targets` flag in `/code:bootstrap` already supports excluding mobile. | +| Mobile target moves faster than web/desktop | If the bump breaks only mobile, gate the mobile app behind a feature flag in the bump PR. The `--targets` flag in `/code:start` already supports excluding mobile. | diff --git a/code-et-implementer/tests/audit-review-with-plugin.sh b/code-et-implementer/tests/audit-review-with-plugin.sh new file mode 100755 index 0000000..57593cb --- /dev/null +++ b/code-et-implementer/tests/audit-review-with-plugin.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# audit-review-with-plugin.sh — assert audit.sh --review writes a Review +# section into the report when the engineering plugin is detectable +# (AC-5.1, AC-5.2). +# +# Strategy: stub plugin presence by creating a fake SKILL.md under a sandboxed +# HOME, build a minimal Rust workspace in a tempdir (Cargo.toml only), seed it +# as a git repo with two commits so a diff exists, then invoke audit --review +# and assert exit 0 and the report contains "## Review". + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +TMPROOT="$(mktemp -d -t code-et-audit-rev.XXXXXX)" +trap 'rm -rf "$TMPROOT"' EXIT + +# Stub plugin presence under a sandboxed HOME. +FAKE_HOME="$TMPROOT/home" +SKILL_DIR="$FAKE_HOME/.claude/plugins/cache/knowledge-work-plugins/engineering/skills/code-review" +mkdir -p "$SKILL_DIR" +cat > "$SKILL_DIR/SKILL.md" <<'EOF' +--- +name: code-review +--- +stub +EOF + +# Minimal Rust workspace + git history so the merge-base diff is non-empty. +WS="$TMPROOT/ws" +mkdir -p "$WS/src" +cat > "$WS/Cargo.toml" <<'EOF' +[package] +name = "stub" +version = "0.1.0" +edition = "2021" +EOF +echo 'fn main() {}' > "$WS/src/main.rs" + +git -C "$WS" init -q -b main +git -C "$WS" -c user.email=test@test -c user.name=test add -A +git -C "$WS" -c user.email=test@test -c user.name=test commit -q -m "init" +echo '// edit' >> "$WS/src/main.rs" +git -C "$WS" -c user.email=test@test -c user.name=test commit -q -am "edit" + +# Sandbox PATH to a minimal toolset so cargo/clippy are absent and every +# stage skips cleanly. We need git, awk, grep, mktemp, date, sed, cat, head, +# bash, and core POSIX tools — symlink them in. +SANDBOX_BIN="$TMPROOT/bin" +mkdir -p "$SANDBOX_BIN" +for t in bash sh awk grep sed cat head tail mktemp date dirname basename ls cd rm cp mv mkdir tr cut wc sort uniq printf id git env which command find chmod test pwd; do + if cmd_path="$(command -v "$t" 2>/dev/null)"; then + ln -sf "$cmd_path" "$SANDBOX_BIN/$t" + fi +done + +STDERR_LOG="$TMPROOT/stderr.log" +set +e +HOME="$FAKE_HOME" CLAUDE_PLUGIN_ROOT="" PATH="$SANDBOX_BIN" \ + bash "$AUDIT" --review "$WS" 2> "$STDERR_LOG" +rc=$? +set -e + +if [ "$rc" -ne 0 ]; then + echo "FAIL: audit --review exited $rc; expected 0" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +REPORT="$(ls "$WS"/.claude/audit-*.md 2>/dev/null | head -n1 || true)" +if [ -z "$REPORT" ]; then + echo "FAIL: no report file written" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if ! grep -q '^## Review' "$REPORT"; then + echo "FAIL: report missing '## Review' section" >&2 + cat "$REPORT" >&2 + exit 1 +fi + +if ! grep -q "code-review skill" "$REPORT"; then + echo "FAIL: review section missing skill-consumption note" >&2 + cat "$REPORT" >&2 + exit 1 +fi + +if ! grep -q '```diff' "$REPORT"; then + echo "FAIL: review section missing fenced diff block" >&2 + cat "$REPORT" >&2 + exit 1 +fi + +echo "PASS: audit-review-with-plugin" diff --git a/code-et-implementer/tests/audit-review-without-plugin.sh b/code-et-implementer/tests/audit-review-without-plugin.sh new file mode 100755 index 0000000..0a293c8 --- /dev/null +++ b/code-et-implementer/tests/audit-review-without-plugin.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# audit-review-without-plugin.sh — assert audit.sh --review exits non-zero +# with the install hint when the engineering plugin is not detectable +# (AC-5.3). +# +# Strategy: sandbox HOME to an empty tempdir so no plugin SKILL.md is reachable, +# point CLAUDE_PLUGIN_ROOT at a stub with no engineering sibling, build a +# minimal Rust workspace with a git history, and invoke audit --review. +# Assert non-zero exit and the hint string on stderr. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +TMPROOT="$(mktemp -d -t code-et-audit-norev.XXXXXX)" +trap 'rm -rf "$TMPROOT"' EXIT + +# Sandboxed HOME with no plugins. CLAUDE_PLUGIN_ROOT points at a stub dir +# whose ../engineering/skills/code-review/SKILL.md does NOT exist. +FAKE_HOME="$TMPROOT/home" +mkdir -p "$FAKE_HOME" +STUB_PLUGIN_ROOT="$TMPROOT/code-et-implementer" +mkdir -p "$STUB_PLUGIN_ROOT" + +# Minimal Rust workspace + git history. +WS="$TMPROOT/ws" +mkdir -p "$WS/src" +cat > "$WS/Cargo.toml" <<'EOF' +[package] +name = "stub" +version = "0.1.0" +edition = "2021" +EOF +echo 'fn main() {}' > "$WS/src/main.rs" + +git -C "$WS" init -q -b main +git -C "$WS" -c user.email=test@test -c user.name=test add -A +git -C "$WS" -c user.email=test@test -c user.name=test commit -q -m "init" + +# Sandbox PATH so cargo/clippy don't surface real failures unrelated to the +# plugin-detection branch we're testing. +SANDBOX_BIN="$TMPROOT/bin" +mkdir -p "$SANDBOX_BIN" +for t in bash sh awk grep sed cat head tail mktemp date dirname basename ls cd rm cp mv mkdir tr cut wc sort uniq printf id git env which command find chmod test pwd; do + if cmd_path="$(command -v "$t" 2>/dev/null)"; then + ln -sf "$cmd_path" "$SANDBOX_BIN/$t" + fi +done + +STDERR_LOG="$TMPROOT/stderr.log" +set +e +HOME="$FAKE_HOME" CLAUDE_PLUGIN_ROOT="$STUB_PLUGIN_ROOT" PATH="$SANDBOX_BIN" \ + bash "$AUDIT" --review "$WS" 2> "$STDERR_LOG" +rc=$? +set -e + +if [ "$rc" -eq 0 ]; then + echo "FAIL: audit --review exited 0; expected non-zero (no plugin)" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if ! grep -q "engineering plugin not installed" "$STDERR_LOG"; then + echo "FAIL: missing install-hint string on stderr" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if ! grep -q "/plugin install engineering" "$STDERR_LOG"; then + echo "FAIL: hint missing install command" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +echo "PASS: audit-review-without-plugin" diff --git a/code-et-implementer/tests/audit-skips-missing-tools.sh b/code-et-implementer/tests/audit-skips-missing-tools.sh new file mode 100755 index 0000000..9f13b80 --- /dev/null +++ b/code-et-implementer/tests/audit-skips-missing-tools.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# audit-skips-missing-tools.sh — assert audit.sh degrades gracefully when +# optional toolchain binaries (and the layer-deps validator script) are +# missing on a fresh dev machine (US-9, AC-9.1, AC-9.2). +# +# Strategy: build a minimal Rust workspace fixture, then invoke audit.sh +# with a sanitised PATH that contains only a stub `cargo` shim and the bare +# Unix utilities. The stub cargo handles `fmt` and `clippy` (noops) but no +# `cargo-machete` / `cargo-audit` / `cargo-deny` / `cargo-nextest` exist on +# PATH, and the workspace ships no `scripts/layer-deps-validator.sh`. +# +# Expected outcome: +# - exit code 0 (no genuine stage failure) +# - five LOW findings: machete, audit, deny, layer-deps, nextest +# - one "WARNING" line per skipped stage on stderr +# - report contains a `## LOW` section listing each skip + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +TMPDIR_TEST="$(mktemp -d -t code-et-audit-missing.XXXXXX)" +trap 'rm -rf "$TMPDIR_TEST"' EXIT + +WORKSPACE="$TMPDIR_TEST/workspace" +STUB_BIN="$TMPDIR_TEST/bin" +mkdir -p "$WORKSPACE" "$STUB_BIN" + +# Minimal empty Rust workspace — enough for find_workspace_root to accept it. +cat > "$WORKSPACE/Cargo.toml" <<'EOF' +[workspace] +members = [] +resolver = "2" +EOF + +# Stub `cargo` — handles fmt/clippy as noops, refuses unknown subcommands the +# way real cargo does so audit.sh's stage_runnable probe (which checks for +# `cargo-<sub>` on PATH) still rejects them. +cat > "$STUB_BIN/cargo" <<'EOF' +#!/usr/bin/env bash +case "${1:-}" in + fmt|clippy) exit 0 ;; + *) + echo "error: no such subcommand: \`$1\`" >&2 + exit 101 + ;; +esac +EOF +chmod +x "$STUB_BIN/cargo" + +STDERR_LOG="$TMPDIR_TEST/stderr.log" + +set +e +PATH="$STUB_BIN:/usr/bin:/bin" bash "$AUDIT" "$WORKSPACE" 2> "$STDERR_LOG" +rc=$? +set -e + +if [ "$rc" -ne 0 ]; then + echo "FAIL: audit.sh exited $rc with all optional tools missing; expected 0" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +# Locate the freshly-written report. There should be exactly one. +REPORT="$(ls "$WORKSPACE/.claude/audit-"*.md 2>/dev/null | head -n1 || true)" +if [ -z "$REPORT" ]; then + echo "FAIL: no audit report written" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +# AC-9.1: a WARNING line on stderr for each missing tool/script. +expected_warnings=( + "cargo-machete (unused deps)" + "cargo-audit (advisories)" + "cargo-deny (license + bans + sources + advisories)" + "layer-deps validator" + "tests" +) +for stage in "${expected_warnings[@]}"; do + if ! grep -qF "WARNING: $stage" "$STDERR_LOG"; then + echo "FAIL: missing WARNING line for stage '$stage' on stderr" >&2 + cat "$STDERR_LOG" >&2 + exit 1 + fi +done + +# AC-9.2: each skip is recorded in the report under the LOW group. +if ! grep -q '^## LOW' "$REPORT"; then + echo "FAIL: report has no LOW severity section" >&2 + cat "$REPORT" >&2 + exit 1 +fi + +expected_paths=( + "cargo-machete" + "cargo-audit" + "cargo-deny" + "scripts/layer-deps-validator.sh" + "cargo-nextest" +) +for path in "${expected_paths[@]}"; do + if ! grep -qF "\`$path:1\`" "$REPORT"; then + echo "FAIL: report missing LOW finding with path '$path:1'" >&2 + cat "$REPORT" >&2 + exit 1 + fi +done + +# Sanity: every LOW finding line carries the install/remediation hint stem. +low_lines="$(awk '/^## LOW/{flag=1; next} /^## /{flag=0} flag && /^- /' "$REPORT")" +low_count="$(printf '%s\n' "$low_lines" | grep -c '^- ' || true)" +if [ "$low_count" -ne 5 ]; then + echo "FAIL: expected 5 LOW findings, got $low_count" >&2 + cat "$REPORT" >&2 + exit 1 +fi + +# Bracket: no CRITICAL/HIGH/MEDIUM sections — only skips, no real failures. +for sev in CRITICAL HIGH MEDIUM; do + if grep -q "^## $sev" "$REPORT"; then + echo "FAIL: report has unexpected $sev section" >&2 + cat "$REPORT" >&2 + exit 1 + fi +done + +echo "PASS: audit-skips-missing-tools ($low_count LOW findings, exit 0)" diff --git a/code-et-implementer/tests/audit-skips-non-rust.sh b/code-et-implementer/tests/audit-skips-non-rust.sh new file mode 100755 index 0000000..dae59d1 --- /dev/null +++ b/code-et-implementer/tests/audit-skips-non-rust.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# audit-skips-non-rust.sh — assert audit.sh exits 0 with no report when the +# target dir is not a Rust workspace (AC-6.1). + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +TMPDIR_TEST="$(mktemp -d -t code-et-audit-test.XXXXXX)" +trap 'rm -rf "$TMPDIR_TEST"' EXIT + +# A bare temp dir with no Cargo.toml and no enclosing git tree. +STDERR_LOG="$(mktemp -t code-et-audit-stderr.XXXXXX)" +set +e +bash "$AUDIT" "$TMPDIR_TEST" 2> "$STDERR_LOG" +rc=$? +set -e + +if [ "$rc" -ne 0 ]; then + echo "FAIL: audit.sh exited $rc on non-Rust dir; expected 0" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if ! grep -q "not a Rust workspace, skipping" "$STDERR_LOG"; then + echo "FAIL: missing skip message on stderr" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if ls "$TMPDIR_TEST/.claude/audit-"*.md >/dev/null 2>&1; then + echo "FAIL: report file written despite skip" >&2 + exit 1 +fi + +rm -f "$STDERR_LOG" +echo "PASS: audit-skips-non-rust" diff --git a/code-et-implementer/tests/audit-stage-list-matches-yaml.sh b/code-et-implementer/tests/audit-stage-list-matches-yaml.sh new file mode 100755 index 0000000..2a0e32b --- /dev/null +++ b/code-et-implementer/tests/audit-stage-list-matches-yaml.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# audit-stage-list-matches-yaml.sh — assert audit-stages.sh's parsed list +# matches the workflow yaml step-for-step (AC-8.2). +# +# We re-parse the yaml here using a deliberately different extraction (grep on +# `name:` and `run:` / `uses:` lines) so the comparison has signal, not a +# tautology — a typo in audit-stages.sh's mapping would still be caught. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +STAGES="$SCRIPT_DIR/../scripts/audit-stages.sh" +YAML="$SCRIPT_DIR/../templates/shared/.github/workflows/code-et-audit.yml" + +# Setup-only steps to ignore. Match by `uses:` action prefix (no @ref). +SETUP_USES_RE='^(actions/checkout|dtolnay/rust-toolchain|Swatinem/rust-cache|taiki-e/install-action)' + +# Action-to-command map for `uses:` gate steps. Mirrored in audit-stages.sh — +# the test's job is to fail loudly if the two ever diverge. +map_uses_to_cmd() { + case "$1" in + bnjbvr/cargo-machete) echo "cargo machete" ;; + rustsec/audit-check) echo "cargo audit" ;; + EmbarkStudios/cargo-deny-action) echo "cargo deny check" ;; + *) echo "UNKNOWN" ;; + esac +} + +# Walk the yaml. For each step, capture `name:` and `run:` / `uses:`. +# Emit `name|command` for non-setup steps. Use Python for indent-aware parsing +# only if available; otherwise a portable awk pass. +expected="$(awk ' + function flush( cmd, action) { + if (name == "") return + if (run != "") { + cmd = run + } else if (uses != "") { + action = uses + sub(/@.*/, "", action) + if (action ~ /^actions\/checkout$/ || + action ~ /^dtolnay\/rust-toolchain$/ || + action ~ /^Swatinem\/rust-cache$/ || + action ~ /^taiki-e\/install-action$/) { + reset(); return + } + if (action == "bnjbvr/cargo-machete") cmd = "cargo machete" + else if (action == "rustsec/audit-check") cmd = "cargo audit" + else if (action == "EmbarkStudios/cargo-deny-action") cmd = "cargo deny check" + else { printf "test: unmapped uses %s\n", action > "/dev/stderr"; exit 5 } + } else { reset(); return } + printf "%s|%s\n", name, cmd + reset() + } + function reset() { name=""; run=""; uses="" } + BEGIN { in_steps = 0; reset() } + /^ steps:[[:space:]]*$/ { in_steps = 1; next } + in_steps == 1 && /^[a-zA-Z]/ { in_steps = 0 } + in_steps == 1 && /^ - / { + flush() + line = $0; sub(/^ - /, "", line) + if (line ~ /^uses:[[:space:]]/) { sub(/^uses:[[:space:]]+/, "", line); uses = line } + else if (line ~ /^name:[[:space:]]/) { sub(/^name:[[:space:]]+/, "", line); name = line } + next + } + in_steps == 1 && /^ name:[[:space:]]/ { line = $0; sub(/^ name:[[:space:]]+/, "", line); name = line; next } + in_steps == 1 && /^ uses:[[:space:]]/ { line = $0; sub(/^ uses:[[:space:]]+/, "", line); uses = line; next } + in_steps == 1 && /^ run:[[:space:]]/ { line = $0; sub(/^ run:[[:space:]]+/, "", line); run = line; next } + END { flush() } +' "$YAML")" + +actual="$(bash "$STAGES" "$YAML")" + +if [ "$expected" != "$actual" ]; then + echo "FAIL: audit-stages.sh output diverges from yaml" >&2 + diff <(echo "$expected") <(echo "$actual") >&2 || true + exit 1 +fi + +# Sanity: exactly seven gate stages. +count="$(echo "$actual" | grep -c '|')" +if [ "$count" -ne 7 ]; then + echo "FAIL: expected 7 stages, got $count" >&2 + echo "$actual" >&2 + exit 1 +fi + +# Suppress unused-var warning for SETUP_USES_RE (kept for documentation). +: "$SETUP_USES_RE" + +echo "PASS: audit-stage-list-matches-yaml ($count stages)" diff --git a/code-et-implementer/tests/audit-summary-line-on-failure.sh b/code-et-implementer/tests/audit-summary-line-on-failure.sh new file mode 100755 index 0000000..1e59842 --- /dev/null +++ b/code-et-implementer/tests/audit-summary-line-on-failure.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +# audit-summary-line-on-failure.sh — assert the highest-severity finding is +# surfaced as a one-line summary at the top of the report and echoed to stderr +# on failure (US-12 / AC-12.1, AC-12.2). +# +# Two phases: +# 1. Writer-only: pipe synthetic findings into audit-report.sh and inspect +# the produced file. No cargo / rust toolchain needed. +# 2. Runner: drive audit.sh against a tiny tmp Cargo workspace whose source +# has a deterministic `cargo fmt --check` violation. Asserts the same +# summary line surfaces on stderr and matches the report's first line. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WRITER="$SCRIPT_DIR/../scripts/audit-report.sh" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +# AC-12.1 pattern: literal `[<SEV>]` prefix where SEV is non-LOW, then space, +# then arbitrary content, then ` — fix:` (em-dash, U+2014). +SUMMARY_RE='^\[(CRITICAL|HIGH|MEDIUM)\] .* — fix:' + +TMPROOT="$(mktemp -d -t code-et-audit-summary.XXXXXX)" +trap 'rm -rf "$TMPROOT"' EXIT + +# ---------------------------------------------------------------------------- +# Phase 1: writer — synthetic findings. +# ---------------------------------------------------------------------------- + +# Case A: HIGH finding present → first line matches the summary pattern. +report_a="$TMPROOT/case-a.md" +printf 'MEDIUM|fmt|src/foo.rs|1|formatting drift\nHIGH|clippy (deny warnings)|src/bar.rs|42|unused variable `x`\n' \ + | bash "$WRITER" "$report_a" + +first_a="$(head -n 1 "$report_a")" +if ! [[ "$first_a" =~ $SUMMARY_RE ]]; then + echo "FAIL (case A): first line did not match summary regex" >&2 + echo " got: $first_a" >&2 + exit 1 +fi +# Should pick HIGH (not MEDIUM): the prefix tag must be HIGH. +if [[ "$first_a" != \[HIGH\]* ]]; then + echo "FAIL (case A): expected [HIGH] prefix, got: $first_a" >&2 + exit 1 +fi + +# Case B: LOW-only findings → no summary prefix; report starts with heading. +report_b="$TMPROOT/case-b.md" +printf 'LOW|cargo-machete (unused deps)|Cargo.toml|1|tool not installed\n' \ + | bash "$WRITER" "$report_b" + +first_b="$(head -n 1 "$report_b")" +if [[ "$first_b" =~ $SUMMARY_RE ]]; then + echo "FAIL (case B): LOW-only run produced a summary prefix" >&2 + echo " got: $first_b" >&2 + exit 1 +fi +if [[ "$first_b" != "# code-et audit report" ]]; then + echo "FAIL (case B): expected '# code-et audit report' as first line, got: $first_b" >&2 + exit 1 +fi + +# Case C: no findings → no summary prefix; report starts with heading. +report_c="$TMPROOT/case-c.md" +: | bash "$WRITER" "$report_c" +first_c="$(head -n 1 "$report_c")" +if [[ "$first_c" =~ $SUMMARY_RE ]]; then + echo "FAIL (case C): empty input produced a summary prefix" >&2 + echo " got: $first_c" >&2 + exit 1 +fi +if [[ "$first_c" != "# code-et audit report" ]]; then + echo "FAIL (case C): expected '# code-et audit report' as first line, got: $first_c" >&2 + exit 1 +fi + +# Case D: CRITICAL beats HIGH beats MEDIUM in the summary tag. +report_d="$TMPROOT/case-d.md" +printf 'MEDIUM|fmt|src/a.rs|1|m\nHIGH|tests|src/b.rs|2|h\nCRITICAL|cargo-audit (advisories)|Cargo.lock|1|RUSTSEC-XXXX\n' \ + | bash "$WRITER" "$report_d" +first_d="$(head -n 1 "$report_d")" +if [[ "$first_d" != \[CRITICAL\]* ]]; then + echo "FAIL (case D): expected [CRITICAL] prefix, got: $first_d" >&2 + exit 1 +fi + +# ---------------------------------------------------------------------------- +# Phase 2: runner — drive audit.sh against a tmp workspace with a deterministic +# fmt violation. Skip if `cargo` is not on PATH (keeps the writer phase usable +# on stripped CI images). +# ---------------------------------------------------------------------------- + +if ! command -v cargo >/dev/null 2>&1; then + echo "PASS: audit-summary-line-on-failure (writer phase only — cargo missing)" + exit 0 +fi + +WS="$TMPROOT/ws" +mkdir -p "$WS/src" +cat > "$WS/Cargo.toml" <<'EOF' +[package] +name = "audit-summary-fixture" +version = "0.0.1" +edition = "2021" + +[[bin]] +name = "audit-summary-fixture" +path = "src/main.rs" +EOF +# Single line, no trailing newline guarantees `cargo fmt --check` will flag it. +printf 'fn main(){let x=1;let _=x;}' > "$WS/src/main.rs" + +stderr_log="$TMPROOT/audit.stderr" +set +e +bash "$AUDIT" "$WS" 2> "$stderr_log" >/dev/null +rc=$? +set -e + +if [ "$rc" -eq 0 ]; then + echo "FAIL (runner): audit.sh exited 0 on a workspace with fmt violation" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +# Locate the report file the runner wrote. +report="$(ls "$WS/.claude/audit-"*.md 2>/dev/null | head -n1 || true)" +if [ -z "$report" ] || [ ! -f "$report" ]; then + echo "FAIL (runner): no audit report written under $WS/.claude/" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +first_line="$(head -n 1 "$report")" +if ! [[ "$first_line" =~ $SUMMARY_RE ]]; then + echo "FAIL (runner): report first line missing summary prefix" >&2 + echo " got: $first_line" >&2 + exit 1 +fi + +# AC-12.2: the same summary line must appear on the runner's stderr. +if ! grep -Fq -- "$first_line" "$stderr_log"; then + echo "FAIL (runner): summary line not echoed to stderr" >&2 + echo " expected: $first_line" >&2 + echo " --- stderr ---" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +echo "PASS: audit-summary-line-on-failure" diff --git a/code-et-implementer/tests/implement-chain-halts-on-audit-failure.sh b/code-et-implementer/tests/implement-chain-halts-on-audit-failure.sh new file mode 100755 index 0000000..b341391 --- /dev/null +++ b/code-et-implementer/tests/implement-chain-halts-on-audit-failure.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# implement-chain-halts-on-audit-failure.sh — assert /code:implement chains +# `--fast` audit after simplify and that a fast audit failure halts the loop +# with the audit report path surfaced (US-2 / AC-2.1, AC-2.2). +# +# We can't drive the full Claude orchestrator from a bash test, so the chain +# assertion is split into two empirical phases: +# +# Phase A — wiring (AC-2.1): grep implement.md to confirm the line +# containing `Skill("simplify")` is followed by an `audit` invocation +# in `--fast` mode. This mirrors the verification in the task manifest. +# +# Phase B — regression fixture (AC-2.2): build a tiny Rust workspace whose +# `cargo clippy ... -D warnings` fails (unused variable). Drive +# `audit.sh --fast` directly, assert non-zero exit, that a report file +# was written under `<workspace>/.claude/`, and that the runner echoes +# `audit: report written to <path>` on stderr — the surfacing mechanism +# a Skill chain inherits via the harness. Skip phase B if `cargo` is +# missing so the structural check still runs on stripped CI images. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +IMPLEMENT_MD="$SCRIPT_DIR/../commands/implement.md" +AUDIT="$SCRIPT_DIR/../scripts/audit.sh" + +# ---------------------------------------------------------------------------- +# Phase A — structural wiring (AC-2.1). +# ---------------------------------------------------------------------------- + +if [ ! -f "$IMPLEMENT_MD" ]; then + echo "FAIL (phase A): implement.md not found at $IMPLEMENT_MD" >&2 + exit 1 +fi + +# The line carrying Skill("simplify") must be followed (within `grep -A1`) by +# an `audit … --fast` reference. Same predicate the task manifest uses. +if ! grep -A1 'Skill("simplify")' "$IMPLEMENT_MD" | grep -q 'audit.*--fast'; then + echo "FAIL (phase A): implement.md does not chain audit --fast after Skill(\"simplify\")" >&2 + echo " expected: a line matching 'audit.*--fast' immediately after the simplify call" >&2 + echo " --- relevant slice ---" >&2 + grep -n -A2 'Skill("simplify")' "$IMPLEMENT_MD" >&2 || true + exit 1 +fi + +# ---------------------------------------------------------------------------- +# Phase B — regression fixture (AC-2.2). +# Skip cleanly if cargo is not on PATH; phase A still proves the wiring. +# ---------------------------------------------------------------------------- + +if ! command -v cargo >/dev/null 2>&1; then + echo "PASS: implement-chain-halts-on-audit-failure (phase A only — cargo missing)" + exit 0 +fi + +TMPROOT="$(mktemp -d -t code-et-implement-chain.XXXXXX)" +trap 'rm -rf "$TMPROOT"' EXIT + +WS="$TMPROOT/ws" +mkdir -p "$WS/src" + +# Standalone Cargo package — `audit.sh` accepts a target dir and runs the +# stages with that dir as the working directory. +cat > "$WS/Cargo.toml" <<'EOF' +[package] +name = "implement-chain-fixture" +version = "0.0.1" +edition = "2021" + +[[bin]] +name = "implement-chain-fixture" +path = "src/main.rs" +EOF + +# Source: well-formatted (so `cargo fmt --check` passes) but carries an +# unused variable — `cargo clippy ... -D warnings` promotes the rustc +# `unused_variables` lint to an error and stage 2 fails. That is the +# "known clippy regression" the chain must catch. +cat > "$WS/src/main.rs" <<'EOF' +fn main() { + let x = 1; +} +EOF + +stderr_log="$TMPROOT/audit.stderr" +stdout_log="$TMPROOT/audit.stdout" + +set +e +bash "$AUDIT" --fast "$WS" > "$stdout_log" 2> "$stderr_log" +rc=$? +set -e + +if [ "$rc" -eq 0 ]; then + echo "FAIL (phase B): audit.sh --fast exited 0 on a workspace with a clippy regression" >&2 + echo " --- stderr ---" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +# Locate the report file the runner wrote. +report="$(ls "$WS/.claude/audit-"*.md 2>/dev/null | head -n1 || true)" +if [ -z "$report" ] || [ ! -f "$report" ]; then + echo "FAIL (phase B): no audit report written under $WS/.claude/" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +if [ ! -s "$report" ]; then + echo "FAIL (phase B): audit report is empty: $report" >&2 + exit 1 +fi + +# AC-2.2 surfacing: the runner echoes the report path on stderr. That is the +# string the implement chain (or any caller) sees and can forward to chat. +if ! grep -Fq "audit: report written to $report" "$stderr_log"; then + echo "FAIL (phase B): report path not surfaced on stderr" >&2 + echo " expected: audit: report written to $report" >&2 + echo " --- stderr ---" >&2 + cat "$stderr_log" >&2 + exit 1 +fi + +# Sanity: `--fast` must run only stages 1-2. Stage 3+ names should never +# appear in the runner's stderr trace. If they do, the chain hook would be +# slower than the PRD allows. +for forbidden in "layer-deps validator" "cargo-machete" "cargo-audit" "cargo-deny" "tests"; do + if grep -Fq "$forbidden — running" "$stderr_log"; then + echo "FAIL (phase B): --fast invoked a stage outside 1-2: '$forbidden'" >&2 + cat "$stderr_log" >&2 + exit 1 + fi +done + +echo "PASS: implement-chain-halts-on-audit-failure" diff --git a/code-et-implementer/tests/verify-gate-runs-audit-on-rust.sh b/code-et-implementer/tests/verify-gate-runs-audit-on-rust.sh new file mode 100755 index 0000000..51faaf1 --- /dev/null +++ b/code-et-implementer/tests/verify-gate-runs-audit-on-rust.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# verify-gate-runs-audit-on-rust.sh — assert verify-gate.sh invokes +# audit.sh --fast when a Cargo.toml is present at the repo root (AC-3.1). +# +# Strategy: stage a copy of verify-gate.sh in a fresh scripts dir alongside +# stub run-tests.sh and audit.sh shims that drop sentinel files when called. +# Run a Rust workspace fixture (just a Cargo.toml) through the gate; assert +# both stubs were called and the gate exits 0. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GATE_SRC="$SCRIPT_DIR/../scripts/verify-gate.sh" + +TMPDIR_TEST="$(mktemp -d -t code-et-gate-rust.XXXXXX)" +trap 'rm -rf "$TMPDIR_TEST"' EXIT + +WORKSPACE="$TMPDIR_TEST/workspace" +SCRIPTS="$TMPDIR_TEST/scripts" +mkdir -p "$WORKSPACE" "$SCRIPTS" + +# Minimal Rust workspace — Cargo.toml at root is the only signal verify-gate +# uses to gate the audit step. +cat > "$WORKSPACE/Cargo.toml" <<'EOF' +[workspace] +members = [] +resolver = "2" +EOF + +SENTINEL_TESTS="$TMPDIR_TEST/run-tests-was-called" +SENTINEL_AUDIT="$TMPDIR_TEST/audit-was-called" + +# Stub run-tests.sh — drops sentinel, exits 0. +cat > "$SCRIPTS/run-tests.sh" <<EOF +#!/usr/bin/env bash +touch "$SENTINEL_TESTS" +exit 0 +EOF +chmod +x "$SCRIPTS/run-tests.sh" + +# Stub audit.sh — drops sentinel with the args it received, exits 0. +cat > "$SCRIPTS/audit.sh" <<EOF +#!/usr/bin/env bash +echo "\$*" > "$SENTINEL_AUDIT" +exit 0 +EOF +chmod +x "$SCRIPTS/audit.sh" + +cp "$GATE_SRC" "$SCRIPTS/verify-gate.sh" +chmod +x "$SCRIPTS/verify-gate.sh" + +# Run from inside the workspace so the git/pwd fallback resolves there. +# Outside any git tree -> falls back to PWD; PWD has Cargo.toml -> audit runs. +set +e +(cd "$WORKSPACE" && bash "$SCRIPTS/verify-gate.sh" </dev/null) +rc=$? +set -e + +if [ "$rc" -ne 0 ]; then + echo "FAIL: verify-gate.sh exited $rc on Rust workspace; expected 0" >&2 + exit 1 +fi + +if [ ! -f "$SENTINEL_TESTS" ]; then + echo "FAIL: run-tests.sh stub was not invoked" >&2 + exit 1 +fi + +if [ ! -f "$SENTINEL_AUDIT" ]; then + echo "FAIL: audit.sh stub was not invoked on Rust workspace" >&2 + exit 1 +fi + +# Sanity: the audit was called with --fast and the workspace path. +if ! grep -q -- "--fast" "$SENTINEL_AUDIT"; then + echo "FAIL: audit invocation missing --fast flag" >&2 + cat "$SENTINEL_AUDIT" >&2 + exit 1 +fi +if ! grep -qF "$WORKSPACE" "$SENTINEL_AUDIT"; then + echo "FAIL: audit invocation missing workspace path" >&2 + cat "$SENTINEL_AUDIT" >&2 + exit 1 +fi + +echo "PASS: verify-gate-runs-audit-on-rust" diff --git a/code-et-implementer/tests/verify-gate-skips-audit-on-non-rust.sh b/code-et-implementer/tests/verify-gate-skips-audit-on-non-rust.sh new file mode 100755 index 0000000..ad23d44 --- /dev/null +++ b/code-et-implementer/tests/verify-gate-skips-audit-on-non-rust.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# verify-gate-skips-audit-on-non-rust.sh — assert verify-gate.sh skips the +# audit step when no Cargo.toml is present at the repo root (AC-3.2). +# +# Same shim strategy as the rust variant: a sentinel-touching stub for +# audit.sh that must NOT be created when the gate runs on a bare temp dir. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GATE_SRC="$SCRIPT_DIR/../scripts/verify-gate.sh" + +TMPDIR_TEST="$(mktemp -d -t code-et-gate-norust.XXXXXX)" +trap 'rm -rf "$TMPDIR_TEST"' EXIT + +WORKSPACE="$TMPDIR_TEST/workspace" +SCRIPTS="$TMPDIR_TEST/scripts" +mkdir -p "$WORKSPACE" "$SCRIPTS" + +# Bare workspace — no Cargo.toml, no git tree. + +SENTINEL_TESTS="$TMPDIR_TEST/run-tests-was-called" +SENTINEL_AUDIT="$TMPDIR_TEST/audit-was-called" + +cat > "$SCRIPTS/run-tests.sh" <<EOF +#!/usr/bin/env bash +touch "$SENTINEL_TESTS" +exit 0 +EOF +chmod +x "$SCRIPTS/run-tests.sh" + +# Audit shim — if invoked on a non-Rust tree, this is a regression. The +# sentinel must not be created. +cat > "$SCRIPTS/audit.sh" <<EOF +#!/usr/bin/env bash +echo "\$*" > "$SENTINEL_AUDIT" +exit 0 +EOF +chmod +x "$SCRIPTS/audit.sh" + +cp "$GATE_SRC" "$SCRIPTS/verify-gate.sh" +chmod +x "$SCRIPTS/verify-gate.sh" + +STDERR_LOG="$TMPDIR_TEST/stderr.log" + +set +e +(cd "$WORKSPACE" && bash "$SCRIPTS/verify-gate.sh" </dev/null) 2> "$STDERR_LOG" +rc=$? +set -e + +if [ "$rc" -ne 0 ]; then + echo "FAIL: verify-gate.sh exited $rc on non-Rust dir; expected 0" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +if [ ! -f "$SENTINEL_TESTS" ]; then + echo "FAIL: run-tests.sh stub was not invoked" >&2 + exit 1 +fi + +if [ -f "$SENTINEL_AUDIT" ]; then + echo "FAIL: audit.sh was invoked on non-Rust dir; expected silent skip" >&2 + cat "$SENTINEL_AUDIT" >&2 + exit 1 +fi + +# Silent skip: stderr should not mention the audit or the workspace skip. +if grep -qi "audit\|not a Rust workspace" "$STDERR_LOG"; then + echo "FAIL: skip path emitted stderr; expected silent" >&2 + cat "$STDERR_LOG" >&2 + exit 1 +fi + +echo "PASS: verify-gate-skips-audit-on-non-rust" diff --git a/plans/2026-05-06-v3.10-audit-command.md b/plans/2026-05-06-v3.10-audit-command.md new file mode 100644 index 0000000..2751fca --- /dev/null +++ b/plans/2026-05-06-v3.10-audit-command.md @@ -0,0 +1,145 @@ +# /code:audit — Local Mirror of the v3.9.0 CI Gate + +**Date:** 2026-05-06 +**Slug:** v3.10-audit-command +**Status:** Draft + +## Problem Statement + +In v3.9.0, code-et shipped a Rust Clean Architecture CI gate that runs seven stages of static analysis and tests on every PR. Today developers only see those failures after pushing — the round-trip (push → wait → fix → push) is slow and breaks flow during `/code:implement` runs. There is no local equivalent, and no way to invoke the gate ad-hoc on a working tree before a commit lands. + +## Solution + +Add `/code:audit`, a local command that runs the exact same seven-stage pipeline as the v3.9.0 CI gate against the current working tree: (1) `cargo fmt --all -- --check`, (2) `cargo clippy --workspace --all-targets --all-features -- -D warnings`, (3) layer-deps validator, (4) cargo-machete, (5) cargo-audit, (6) cargo-deny, (7) `cargo nextest run --workspace --all-features`. The CI workflow YAML remains the single source of truth for stage definitions; the audit command reads it and mirrors it stage-for-stage. A `--fast` mode (stages 1–2 only — fmt + clippy) plugs into `verify-gate.sh` so pre-commit feedback is sub-second on small diffs, and a `--review` mode chains the engineering plugin's `code-review` skill against the diff for human-readable findings. + +## User Stories + +1. US-1: As a developer working on a Rust crate, I want to run `/code:audit` and get the same pass/fail verdict CI will give me, so that I can catch gate failures before pushing. +2. US-2: As a developer mid-implementation, I want `/code:implement` to run a fast subset of the audit automatically after `simplify`, so that style and dead-code regressions surface inside the same loop. +3. US-3: As a developer running `verify-gate.sh`, I want stages 1–2 of the audit to run as part of the gate, so that pre-commit blocks regressions without adding seconds of latency. +4. US-4: As a developer debugging a single failing stage, I want `/code:audit --stage 3` to run only the dependency-hygiene checks, so that I can iterate without re-running the whole pipeline. +5. US-5: As a reviewer, I want `/code:audit --review` to chain the engineering plugin's code-review skill against the working diff, so that I get prose findings alongside the static-analysis output. +6. US-6: As a developer on a non-Rust repo, I want `/code:audit` to detect the absence of `Cargo.toml` and exit cleanly with a no-op message, so that the command is safe to wire into shared hooks. +7. US-7: As a developer reading audit output, I want a markdown report at `.claude/audit-<UTC>.md` with CRITICAL findings first and `path:line` citations on every finding, so that I can jump straight to the offending code. +8. US-8: As a maintainer of the v3.9.0 CI workflow, I want the audit command to parse stage definitions from the workflow YAML rather than duplicating them, so that local and CI never drift. +9. US-9: As a developer on a machine without `cargo-machete` / `cargo-audit` / `cargo-deny` installed, I want those stages to skip with a WARNING when the tool is missing, so that the command remains usable on a fresh dev machine. +10. US-10: As a developer using the bundled `templates/rust/dioxus-fullstack/` fixture, I want `/code:audit` to run cleanly against it as a smoke test, so that the fixture doubles as the audit's regression suite. +11. US-11: As Kennet using code-et across multiple repos (not just Visma projects), I want the audit command to contain zero hardcoded app names or org-specific paths, so that the plugin stays portable. +12. US-12: As a developer who just ran `/code:audit` and got a non-zero exit, I want the report to surface the single highest-severity finding at the top with a one-line remediation hint, so that I know exactly what to fix first. + +## Acceptance Criteria + +### US-1 +- AC-1.1: Running `/code:audit` in a Rust workspace executes all seven stages from the v3.9.0 CI gate in order: (1) fmt (`cargo fmt --all -- --check`), (2) clippy with `-D warnings` (`cargo clippy --workspace --all-targets --all-features -- -D warnings`), (3) layer-deps validator (`bash scripts/layer-deps-validator.sh`), (4) cargo-machete, (5) cargo-audit, (6) cargo-deny, (7) tests (`cargo nextest run --workspace --all-features`). +- AC-1.2: Exit code is non-zero if any stage fails; the failing stage and command are named in the terminal output. +- AC-1.3: A clean working tree on the bundled fixture exits 0 with all seven stages reported as passed. + +### US-2 +- AC-2.1: The `implement` command's skill chain calls the audit skill with `--fast` immediately after `simplify`. +- AC-2.2: A `--fast` audit failure halts the implement loop with the audit report path surfaced in the chat. + +### US-3 +- AC-3.1: `verify-gate.sh` invokes the audit command in `--fast` mode when a `Cargo.toml` is detected at repo root. +- AC-3.2: When no `Cargo.toml` exists, `verify-gate.sh` skips the audit step without erroring. + +### US-4 +- AC-4.1: `/code:audit --stage <n>` for n in 1..7 runs only the named stage. +- AC-4.2: Invalid stage numbers exit non-zero with a usage message listing valid stages. + +### US-5 +- AC-5.1: `/code:audit --review` runs the seven stages, then invokes the engineering plugin's `code-review` skill against `git diff` against the merge base of the current branch. +- AC-5.2: The combined output (static findings + review prose) is written to the same `.claude/audit-<UTC>.md` report. +- AC-5.3: If the engineering plugin is not installed, `--review` exits non-zero with a hint to install it. + +### US-6 +- AC-6.1: Running `/code:audit` in a directory with no `Cargo.toml` at any depth exits 0 with a "not a Rust workspace, skipping" message and writes no report. + +### US-7 +- AC-7.1: Every audit run writes `.claude/audit-<UTC>.md` where `<UTC>` is `YYYYMMDD-HHMMSS` in UTC. +- AC-7.2: Findings are grouped by severity with CRITICAL first, then HIGH, MEDIUM, LOW. +- AC-7.3: Every finding includes a `path:line` citation linking to the exact source location. + +### US-8 +- AC-8.1: Stage commands in the audit script are derived from parsing the v3.9.0 CI workflow YAML, not hardcoded duplicates. +- AC-8.2: A unit test asserts the audit's stage list matches the CI workflow's stage list by name and command string. + +### US-9 +- AC-9.1: When `cargo-machete`, `cargo-audit`, or `cargo-deny` is not on PATH, the corresponding stage emits a WARNING and is skipped. +- AC-9.2: A skipped tool is recorded in the report at LOW severity; exit code remains 0 if no other stage fails. + +### US-10 +- AC-10.1: Running `/code:audit` against `templates/rust/dioxus-fullstack/` exits 0. +- AC-10.2: A CI job runs the audit against the fixture on every PR to code-et. + +### US-11 +- AC-11.1: A grep for known org-specific tokens (e.g. "visma", "emerging-tech") across the audit command and scripts returns zero hits. +- AC-11.2: The audit reads its target directory from the current working tree, not from any hardcoded path. + +### US-12 +- AC-12.1: When the audit reports any non-LOW findings, the first line of the report is a one-sentence summary of the single highest-severity finding plus a remediation hint. +- AC-12.2: The terminal output on failure echoes that same one-line summary so it is visible without opening the report. + +## Implementation Decisions + +**New module: audit command.** A new slash command under the code module, exposing the audit skill. The skill orchestrates the seven stages, owns the report format, and implements `--fast`, `--stage`, and `--review` flags. + +**Stage definitions sourced from CI workflow.** The audit module parses the existing GitHub Actions workflow YAML for the v3.9.0 gate. The parser extracts each step's name and shell command. The audit then shells out to those exact commands in order. This keeps CI as the single source of truth and makes drift impossible. + +**Report writer.** A small helper that buffers findings from each stage, sorts by severity, and writes the markdown report. Findings are normalised into a common shape: severity, message, path, line, stage. The writer also prepends the highest-severity summary line. + +**Hook into verify-gate.** The verify-gate script gets a guarded invocation: if `Cargo.toml` exists at repo root, run `audit --fast`; otherwise skip. No other gate behaviour changes. + +**Hook into implement.** The implement command's skill chain inserts the audit skill (with `--fast`) directly after the simplify skill. Failures bubble up the same way other skill failures do. + +**Review-mode chaining.** The `--review` flag conditionally invokes the engineering plugin's code-review skill, passing it the git diff against the merge base. Output is captured and appended to the report under a "Review" section. + +**Fixture as smoke test.** The bundled Rust fixture from v3.9.0 doubles as the audit's regression suite. A new CI job in the code-et repo runs the audit against the fixture on every PR. + +**Portability.** No hardcoded org names or app paths anywhere. All paths derived from `pwd` or `git rev-parse --show-toplevel`. Bun for any TS shims, UV for any Python shims (per global rules). + +## Testing Decisions + +External-behaviour tests only — exit codes, report contents, and integration-point side effects. Mirror the test style already used by the simplify and verify-gate modules. + +- **Stage-list parity test.** Assert the audit's parsed stage list matches the v3.9.0 CI workflow's stage list by name and command string. This guards US-8 against drift. +- **Fixture smoke test.** Run the audit against `templates/rust/dioxus-fullstack/` in CI; assert exit 0 and a report file exists. Mirror the fixture-based tests already used by the v3.9.0 gate validators. +- **Skip-on-non-Rust test.** Run the audit in a temp dir with no `Cargo.toml`; assert exit 0 and the "not a Rust workspace" message. +- **Stage-flag tests.** For each `--stage <n>`, assert only that stage's commands are invoked (e.g. via a trace log or shim) and the others are skipped. +- **Fast-mode test.** Assert `--fast` runs only stages 1 and 2. +- **Review-mode test.** With the engineering plugin stubbed, assert `--review` produces a report containing both a "Static Analysis" section and a "Review" section. Without the plugin, assert non-zero exit and a hint message. +- **Verify-gate integration test.** With and without `Cargo.toml` in the test workspace, assert the verify-gate either invokes or skips the audit accordingly. +- **Implement-chain integration test.** Run the implement command's skill chain against a fixture with a known dead-code regression; assert the chain halts at the audit step and the report path appears in the chat output. +- **Missing-tool test.** Stub `cargo-machete` (or `cargo-audit` / `cargo-deny`) as missing on PATH; assert the stage is skipped with a WARNING and other stages still run. +- **Portability grep test.** Grep the audit module for org-specific tokens; assert zero hits. + +## Out of Scope + +- Auto-fix mode (`--fix`). The audit reports; remediation stays manual or via downstream skills. +- Non-Rust language support. v3.10 ships Rust-only. TypeScript/Python audits are deferred. +- Replacing the v3.9.0 CI gate. The CI gate stays; the audit mirrors it locally. +- Changes to `clippy.toml` thresholds or the layer-deps validator script. Those land in their own future PRs. +- Persistent audit history beyond the timestamped reports already written to `.claude/`. +- IDE integration (no LSP, no inline annotations). +- Parallelising stages. v3.10 runs them serially to keep output deterministic; parallelisation can come later if measured slow. + +## Further Notes + +- **CI yaml as single source of truth** is the load-bearing design choice. Every other piece of v3.10 follows from it. If the parser turns out to be brittle, fall back to a generated `stages.json` checked into the repo and regenerated by a CI job — but never duplicate the stage list by hand. +- The audit report timestamp uses UTC to make reports comparable across machines and CI runs. +- The `--review` flag is the bridge to the engineering plugin's review skill. This is the first cross-plugin dependency code-et takes; document the dependency clearly in the README so users know to install the engineering plugin if they want `--review`. +- v3.9.0's plan (`plans/v3.9.0-rust-clean-architecture.md`) already references audit as a v3.10 follow-up — link this PRD from there for traceability. + +## Story Checklist + +- [x] US-1 +- [x] US-2 +- [x] US-3 +- [x] US-4 +- [x] US-5 +- [x] US-6 +- [x] US-7 +- [x] US-8 +- [x] US-9 +- [x] US-10 +- [x] US-11 +- [x] US-12