diff --git a/.gitignore b/.gitignore index 0ad80fa..754ba3a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ .venv/ .claude/worktrees/ rpa-run-tab/logs/ +skills/rpa-run-tab/logs/ diff --git a/CLAUDE.md b/CLAUDE.md index f7c2c3f..984089c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # RPA SKILLS -Shared Claude skills for building RPA dashboards: `rpa-dashboard-no-ai`, `rpa-dashboard-ai`, and `rpa-run-tab` (Tebra-style Run tab). See `PLAN.md`. +Shared Claude skills for RPA bots, all in `skills/`: `rpa-run-tab` (Tebra-style Run tab), `rpa-dashboard-no-ai` and `rpa-dashboard-ai`. See `README.md` and `PLAN.md`. ## Agent skills diff --git a/PLAN.md b/PLAN.md index a2d1aec..f2e3c9f 100644 --- a/PLAN.md +++ b/PLAN.md @@ -73,12 +73,12 @@ Build both in `D:\RPA SKILLS\\`. The user installs them into Claude ## Status: what already exists ``` -rpa-dashboard-no-ai/ +skills/rpa-dashboard-no-ai/ templates/dashboard.html DONE: generic page, works in Qt (QWebChannel) or web (polling) templates/contract.md DONE: event/JSON contract + Tebra lessons templates/bridge.py DONE: minimal PyQt6 host + subprocess log tailing templates/reference/ DONE: tebra_dashboard.html, tebra_bridge_part1/2.py (raw extracts) -rpa-dashboard-ai/ +skills/rpa-dashboard-ai/ templates/reference/web/ DONE: Superbill index.css, DESIGN.md, bands.ts, theme.tsx, types.ts, api.ts, components (BrandHeader, Sidebar, StatsBar, Donut, SplitPane, RunPanel, ReviewPane, RoiTab, PdfPane, Tooltip, icons) @@ -86,24 +86,24 @@ rpa-dashboard-ai/ ``` ## TODO for the implement skill -1. **Fix the lint error in `rpa-dashboard-no-ai/templates/bridge.py:15`.** `STEPS = {{STEPS_PY}}` is not valid Python, so the lint hook flags it (F821). Change it to `STEPS = ["{{STEP_1}}", "{{STEP_2}}"]` or similar. -2. **Write `rpa-dashboard-no-ai/SKILL.md`.** +1. **Fix the lint error in `skills/rpa-dashboard-no-ai/templates/bridge.py:15`.** `STEPS = {{STEPS_PY}}` is not valid Python, so the lint hook flags it (F821). Change it to `STEPS = ["{{STEP_1}}", "{{STEP_2}}"]` or similar. +2. **Write `skills/rpa-dashboard-no-ai/SKILL.md`.** - Frontmatter `name` and `description`. The trigger should cover "RPA dashboard", "automation dashboard", "bot dashboard", and "without AI". - The workflow above. - Point to `templates/*`. -3. **Write `rpa-dashboard-ai/SKILL.md`.** +3. **Write `skills/rpa-dashboard-ai/SKILL.md`.** - Trigger: "AI RPA dashboard", "extraction dashboard", "LLM/OCR review dashboard". - The workflow above. - Web path: React + Vite + Tailwind v4, adapted from `reference/web`. - Desktop path: copy the no-AI `dashboard.html`, then add the AI sections. 4. **Write `templates/themes.md` in both skills.** Content is in the Theme presets section above. -5. **Write `rpa-dashboard-ai/templates/contract.md`.** It extends the no-AI contract with: +5. **Write `skills/rpa-dashboard-ai/templates/contract.md`.** It extends the no-AI contract with: - `Run{job_id,status:pending|queued|running|done|failed,model,pages_done,pages_total,summary:{green,amber,red,unscored}}` - `ReviewRow{row_id,field,value,band,score,reason,verified,corrected_value}` - `RoiRun{model,input_tokens,output_tokens,thinking_tokens,total_cost}` - Endpoints: `PATCH /review/{row_id}`, `POST /runs/{id}/approve` (server gate), `GET /roi`. - Polling: 1.5–2s only while a run is busy. -6. **Write `rpa-dashboard-ai/templates/ai-sections.html`.** Plain HTML/CSS/JS versions of the band legend, the review split pane and the ROI tiles, for the desktop path. +6. **Write `skills/rpa-dashboard-ai/templates/ai-sections.html`.** Plain HTML/CSS/JS versions of the band legend, the review split pane and the ROI tiles, for the desktop path. 7. **Strip project-specific bits from the AI reference files.** Remove ECW/Tebra project names, SPB_* tables and patient/ticket wording, or mark them as such. **Never** ship real patient data (PHI), internal IPs or keys. The reference copies have already been checked for these, but check again after editing. 8. **Verify.** Open `dashboard.html` in a browser using sample data. Check light, dark and narrow width. Load each SKILL.md and make sure every path it references exists. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f23606 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# RPA SKILLS + +Claude skills for building the dashboard of an RPA bot. All three live in [`skills/`](skills/). + +| Skill | Use it when | What it builds | +|---|---|---| +| [`rpa-run-tab`](skills/rpa-run-tab/README.md) | You have a PyQt6 desktop bot and only need a Run tab, in about 5 minutes. | The Tebra-style Run tab: trigger button, Excel input, progress, checkpoints, live log, dark mode. | +| [`rpa-dashboard-no-ai`](skills/rpa-dashboard-no-ai/SKILL.md) | The bot has no AI step. | A full dashboard: run panel, overview, report, optional sections (checkpoints, admin, login). Desktop (PyQt) or web. | +| [`rpa-dashboard-ai`](skills/rpa-dashboard-ai/SKILL.md) | The bot uses an LLM or OCR. | Everything in the no-AI skill, plus confidence bands, review-and-approve (the server refuses approval until fields are verified), and a cost/ROI page. | + +## Install + +Copy the skill folders you need from `skills/` into `~/.claude/skills/`. +`rpa-dashboard-ai` reuses files from `rpa-dashboard-no-ai`, so install the two dashboard skills together, side by side. + +## Use + +In your bot's project, ask Claude for what you want, for example *"add the run tab"* or *"build an RPA dashboard"*. +If the project already has a frontend, the dashboard skills report it and ask whether to keep its look or restyle it. + +## Repo layout + +| Path | What it is | +|---|---| +| `skills/` | The three skills | +| `tests/` | Template tests, page checks (Playwright) and skill evals. See [`tests/README.md`](tests/README.md). | +| `PLAN.md`, `GLOSSARY.md`, `docs/` | Background, domain terms and decisions | +| `.scratch/` | Spec and tickets | + +Run all tests: `uv run --with pytest --with playwright pytest tests` diff --git a/docs/adr/0002-bot-talks-through-tagged-log-file.md b/docs/adr/0002-bot-talks-through-tagged-log-file.md index 22a62e7..c44ffe3 100644 --- a/docs/adr/0002-bot-talks-through-tagged-log-file.md +++ b/docs/adr/0002-bot-talks-through-tagged-log-file.md @@ -8,4 +8,4 @@ The bot runs as a **separate process** with no Qt. The Run tab starts it with `- **Why:** this comes from Tebra Auto Post. UI automation inside a Qt worker thread was unreliable (bug-094). A windowed PyInstaller child's stdout pipe is block-buffered, so log lines arrived in late bursts; reading the log file fixed that. Tagging lines saves each project from writing a table of phrase→friendly-text rules. -**Conflict:** `rpa-dashboard-no-ai/templates/bot_runner.py` reads `stdout=PIPE`, which is the approach Tebra dropped. Fix it there, or point it at the Run tab skill's runner. +**Conflict:** `skills/rpa-dashboard-no-ai/templates/bot_runner.py` reads `stdout=PIPE`, which is the approach Tebra dropped. Fix it there, or point it at the Run tab skill's runner. diff --git a/rpa-dashboard-ai/SKILL.md b/skills/rpa-dashboard-ai/SKILL.md similarity index 100% rename from rpa-dashboard-ai/SKILL.md rename to skills/rpa-dashboard-ai/SKILL.md diff --git a/rpa-dashboard-ai/templates/contract.md b/skills/rpa-dashboard-ai/templates/contract.md similarity index 100% rename from rpa-dashboard-ai/templates/contract.md rename to skills/rpa-dashboard-ai/templates/contract.md diff --git a/rpa-dashboard-ai/templates/desktop/ai_bridge.py b/skills/rpa-dashboard-ai/templates/desktop/ai_bridge.py similarity index 100% rename from rpa-dashboard-ai/templates/desktop/ai_bridge.py rename to skills/rpa-dashboard-ai/templates/desktop/ai_bridge.py diff --git a/rpa-dashboard-ai/templates/desktop/ai_sections.html b/skills/rpa-dashboard-ai/templates/desktop/ai_sections.html similarity index 100% rename from rpa-dashboard-ai/templates/desktop/ai_sections.html rename to skills/rpa-dashboard-ai/templates/desktop/ai_sections.html diff --git a/rpa-dashboard-ai/templates/reference/backend/gemini_cost.py b/skills/rpa-dashboard-ai/templates/reference/backend/gemini_cost.py similarity index 100% rename from rpa-dashboard-ai/templates/reference/backend/gemini_cost.py rename to skills/rpa-dashboard-ai/templates/reference/backend/gemini_cost.py diff --git a/rpa-dashboard-ai/templates/reference/web/DESIGN.md b/skills/rpa-dashboard-ai/templates/reference/web/DESIGN.md similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/DESIGN.md rename to skills/rpa-dashboard-ai/templates/reference/web/DESIGN.md diff --git a/rpa-dashboard-ai/templates/reference/web/api.ts b/skills/rpa-dashboard-ai/templates/reference/web/api.ts similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/api.ts rename to skills/rpa-dashboard-ai/templates/reference/web/api.ts diff --git a/rpa-dashboard-ai/templates/reference/web/bands.ts b/skills/rpa-dashboard-ai/templates/reference/web/bands.ts similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/bands.ts rename to skills/rpa-dashboard-ai/templates/reference/web/bands.ts diff --git a/rpa-dashboard-ai/templates/reference/web/components/BrandHeader.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/BrandHeader.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/BrandHeader.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/BrandHeader.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/Donut.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/Donut.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/Donut.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/Donut.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/PdfPane.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/PdfPane.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/PdfPane.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/PdfPane.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/ReviewPane.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/ReviewPane.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/ReviewPane.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/ReviewPane.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/RoiTab.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/RoiTab.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/RoiTab.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/RoiTab.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/RunPanel.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/RunPanel.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/RunPanel.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/RunPanel.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/Sidebar.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/Sidebar.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/Sidebar.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/Sidebar.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/SplitPane.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/SplitPane.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/SplitPane.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/SplitPane.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/StatsBar.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/StatsBar.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/StatsBar.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/StatsBar.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/Tooltip.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/Tooltip.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/Tooltip.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/Tooltip.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/components/icons.tsx b/skills/rpa-dashboard-ai/templates/reference/web/components/icons.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/components/icons.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/components/icons.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/index.css b/skills/rpa-dashboard-ai/templates/reference/web/index.css similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/index.css rename to skills/rpa-dashboard-ai/templates/reference/web/index.css diff --git a/rpa-dashboard-ai/templates/reference/web/index.html b/skills/rpa-dashboard-ai/templates/reference/web/index.html similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/index.html rename to skills/rpa-dashboard-ai/templates/reference/web/index.html diff --git a/rpa-dashboard-ai/templates/reference/web/theme.tsx b/skills/rpa-dashboard-ai/templates/reference/web/theme.tsx similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/theme.tsx rename to skills/rpa-dashboard-ai/templates/reference/web/theme.tsx diff --git a/rpa-dashboard-ai/templates/reference/web/types.ts b/skills/rpa-dashboard-ai/templates/reference/web/types.ts similarity index 100% rename from rpa-dashboard-ai/templates/reference/web/types.ts rename to skills/rpa-dashboard-ai/templates/reference/web/types.ts diff --git a/rpa-dashboard-ai/templates/web/bands.py b/skills/rpa-dashboard-ai/templates/web/bands.py similarity index 100% rename from rpa-dashboard-ai/templates/web/bands.py rename to skills/rpa-dashboard-ai/templates/web/bands.py diff --git a/rpa-dashboard-ai/templates/web/index.html b/skills/rpa-dashboard-ai/templates/web/index.html similarity index 100% rename from rpa-dashboard-ai/templates/web/index.html rename to skills/rpa-dashboard-ai/templates/web/index.html diff --git a/rpa-dashboard-ai/templates/web/package-lock.json b/skills/rpa-dashboard-ai/templates/web/package-lock.json similarity index 100% rename from rpa-dashboard-ai/templates/web/package-lock.json rename to skills/rpa-dashboard-ai/templates/web/package-lock.json diff --git a/rpa-dashboard-ai/templates/web/package.json b/skills/rpa-dashboard-ai/templates/web/package.json similarity index 100% rename from rpa-dashboard-ai/templates/web/package.json rename to skills/rpa-dashboard-ai/templates/web/package.json diff --git a/rpa-dashboard-ai/templates/web/pricing.py b/skills/rpa-dashboard-ai/templates/web/pricing.py similarity index 100% rename from rpa-dashboard-ai/templates/web/pricing.py rename to skills/rpa-dashboard-ai/templates/web/pricing.py diff --git a/rpa-dashboard-ai/templates/web/server_stub.py b/skills/rpa-dashboard-ai/templates/web/server_stub.py similarity index 100% rename from rpa-dashboard-ai/templates/web/server_stub.py rename to skills/rpa-dashboard-ai/templates/web/server_stub.py diff --git a/rpa-dashboard-ai/templates/web/src/App.tsx b/skills/rpa-dashboard-ai/templates/web/src/App.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/App.tsx rename to skills/rpa-dashboard-ai/templates/web/src/App.tsx diff --git a/rpa-dashboard-ai/templates/web/src/ai.ts b/skills/rpa-dashboard-ai/templates/web/src/ai.ts similarity index 100% rename from rpa-dashboard-ai/templates/web/src/ai.ts rename to skills/rpa-dashboard-ai/templates/web/src/ai.ts diff --git a/rpa-dashboard-ai/templates/web/src/components/Cost.tsx b/skills/rpa-dashboard-ai/templates/web/src/components/Cost.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/components/Cost.tsx rename to skills/rpa-dashboard-ai/templates/web/src/components/Cost.tsx diff --git a/rpa-dashboard-ai/templates/web/src/components/Overview.tsx b/skills/rpa-dashboard-ai/templates/web/src/components/Overview.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/components/Overview.tsx rename to skills/rpa-dashboard-ai/templates/web/src/components/Overview.tsx diff --git a/rpa-dashboard-ai/templates/web/src/components/Report.tsx b/skills/rpa-dashboard-ai/templates/web/src/components/Report.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/components/Report.tsx rename to skills/rpa-dashboard-ai/templates/web/src/components/Report.tsx diff --git a/rpa-dashboard-ai/templates/web/src/components/Review.tsx b/skills/rpa-dashboard-ai/templates/web/src/components/Review.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/components/Review.tsx rename to skills/rpa-dashboard-ai/templates/web/src/components/Review.tsx diff --git a/rpa-dashboard-ai/templates/web/src/components/RunPanel.tsx b/skills/rpa-dashboard-ai/templates/web/src/components/RunPanel.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/components/RunPanel.tsx rename to skills/rpa-dashboard-ai/templates/web/src/components/RunPanel.tsx diff --git a/rpa-dashboard-ai/templates/web/src/config.d.ts b/skills/rpa-dashboard-ai/templates/web/src/config.d.ts similarity index 100% rename from rpa-dashboard-ai/templates/web/src/config.d.ts rename to skills/rpa-dashboard-ai/templates/web/src/config.d.ts diff --git a/rpa-dashboard-ai/templates/web/src/index.css b/skills/rpa-dashboard-ai/templates/web/src/index.css similarity index 100% rename from rpa-dashboard-ai/templates/web/src/index.css rename to skills/rpa-dashboard-ai/templates/web/src/index.css diff --git a/rpa-dashboard-ai/templates/web/src/main.tsx b/skills/rpa-dashboard-ai/templates/web/src/main.tsx similarity index 100% rename from rpa-dashboard-ai/templates/web/src/main.tsx rename to skills/rpa-dashboard-ai/templates/web/src/main.tsx diff --git a/rpa-dashboard-ai/templates/web/src/store.ts b/skills/rpa-dashboard-ai/templates/web/src/store.ts similarity index 100% rename from rpa-dashboard-ai/templates/web/src/store.ts rename to skills/rpa-dashboard-ai/templates/web/src/store.ts diff --git a/rpa-dashboard-ai/templates/web/tsconfig.json b/skills/rpa-dashboard-ai/templates/web/tsconfig.json similarity index 100% rename from rpa-dashboard-ai/templates/web/tsconfig.json rename to skills/rpa-dashboard-ai/templates/web/tsconfig.json diff --git a/rpa-dashboard-ai/templates/web/vite.config.ts b/skills/rpa-dashboard-ai/templates/web/vite.config.ts similarity index 100% rename from rpa-dashboard-ai/templates/web/vite.config.ts rename to skills/rpa-dashboard-ai/templates/web/vite.config.ts diff --git a/rpa-dashboard-no-ai/SKILL.md b/skills/rpa-dashboard-no-ai/SKILL.md similarity index 100% rename from rpa-dashboard-no-ai/SKILL.md rename to skills/rpa-dashboard-no-ai/SKILL.md diff --git a/rpa-dashboard-no-ai/templates/bot_runner.py b/skills/rpa-dashboard-no-ai/templates/bot_runner.py similarity index 100% rename from rpa-dashboard-no-ai/templates/bot_runner.py rename to skills/rpa-dashboard-no-ai/templates/bot_runner.py diff --git a/rpa-dashboard-no-ai/templates/bridge.py b/skills/rpa-dashboard-no-ai/templates/bridge.py similarity index 100% rename from rpa-dashboard-no-ai/templates/bridge.py rename to skills/rpa-dashboard-no-ai/templates/bridge.py diff --git a/rpa-dashboard-no-ai/templates/contract.md b/skills/rpa-dashboard-no-ai/templates/contract.md similarity index 100% rename from rpa-dashboard-no-ai/templates/contract.md rename to skills/rpa-dashboard-no-ai/templates/contract.md diff --git a/rpa-dashboard-no-ai/templates/dashboard.html b/skills/rpa-dashboard-no-ai/templates/dashboard.html similarity index 100% rename from rpa-dashboard-no-ai/templates/dashboard.html rename to skills/rpa-dashboard-no-ai/templates/dashboard.html diff --git a/rpa-dashboard-no-ai/templates/detect_frontend.py b/skills/rpa-dashboard-no-ai/templates/detect_frontend.py similarity index 100% rename from rpa-dashboard-no-ai/templates/detect_frontend.py rename to skills/rpa-dashboard-no-ai/templates/detect_frontend.py diff --git a/rpa-dashboard-no-ai/templates/frontend.md b/skills/rpa-dashboard-no-ai/templates/frontend.md similarity index 100% rename from rpa-dashboard-no-ai/templates/frontend.md rename to skills/rpa-dashboard-no-ai/templates/frontend.md diff --git a/rpa-dashboard-no-ai/templates/palette.py b/skills/rpa-dashboard-no-ai/templates/palette.py similarity index 100% rename from rpa-dashboard-no-ai/templates/palette.py rename to skills/rpa-dashboard-no-ai/templates/palette.py diff --git a/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part1.py b/skills/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part1.py similarity index 100% rename from rpa-dashboard-no-ai/templates/reference/tebra_bridge_part1.py rename to skills/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part1.py diff --git a/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part2.py b/skills/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part2.py similarity index 100% rename from rpa-dashboard-no-ai/templates/reference/tebra_bridge_part2.py rename to skills/rpa-dashboard-no-ai/templates/reference/tebra_bridge_part2.py diff --git a/rpa-dashboard-no-ai/templates/reference/tebra_dashboard.html b/skills/rpa-dashboard-no-ai/templates/reference/tebra_dashboard.html similarity index 100% rename from rpa-dashboard-no-ai/templates/reference/tebra_dashboard.html rename to skills/rpa-dashboard-no-ai/templates/reference/tebra_dashboard.html diff --git a/rpa-dashboard-no-ai/templates/sections.md b/skills/rpa-dashboard-no-ai/templates/sections.md similarity index 100% rename from rpa-dashboard-no-ai/templates/sections.md rename to skills/rpa-dashboard-no-ai/templates/sections.md diff --git a/rpa-dashboard-no-ai/templates/sections.py b/skills/rpa-dashboard-no-ai/templates/sections.py similarity index 100% rename from rpa-dashboard-no-ai/templates/sections.py rename to skills/rpa-dashboard-no-ai/templates/sections.py diff --git a/rpa-dashboard-no-ai/templates/themes.md b/skills/rpa-dashboard-no-ai/templates/themes.md similarity index 100% rename from rpa-dashboard-no-ai/templates/themes.md rename to skills/rpa-dashboard-no-ai/templates/themes.md diff --git a/skills/rpa-run-tab/README.md b/skills/rpa-run-tab/README.md new file mode 100644 index 0000000..d5da154 --- /dev/null +++ b/skills/rpa-run-tab/README.md @@ -0,0 +1,35 @@ +# rpa-run-tab + +A Claude skill that adds a **Tebra-style Run tab** to any desktop RPA bot (PyQt6) in about 5 minutes. + +## What you get +- **Trigger button** with three states: Upload a file / Trigger automation / Running... +- **Excel input card:** drop zone, sheet tabs, a 200-row preview with passwords masked, and a Sample input download +- **Progress bar** with an elapsed timer, a step list and a friendly live log +- **Checkpoint boxes** with time limits. A stuck checkpoint is killed and shown in red. +- **Endings:** green Completed, amber "N of M failed", red Failed or Stuck +- **Dark mode** and a close-while-running guard. The bot runs in its own process. + +## Install +Copy this folder to `~/.claude/skills/rpa-run-tab/`. + +## Use +In your bot project, ask Claude: *"add the run tab"*. The skill: +1. Scans your bot. It never runs it. +2. Asks three things: **colour** (Tebra Sky, TVL Teal, or your hex), **checkpoints** (names and time limits), and **Excel input or none**. +3. Builds `run_tab/` and adds a few `rt.checkpoint()` / `rt.ui()` lines to your bot. +4. Checks the result with a fake bot (`python run_tab/run_tab.py --selfcheck`). + +Start it with `python run_tab/run_tab.py`. + +## Files +| File | What it is | +|---|---| +| `SKILL.md` | Steps Claude follows | +| `templates/run_tab.html` | The page (layout fixed, colours themable) | +| `templates/run_tab.py` | PyQt6 window and bridge, plus the CONFIG block and `--selfcheck` | +| `templates/runner.py` | Starts the bot, reads its log, handles stuck checkpoints and the ending | +| `templates/rt.py` | Helpers the bot calls | +| `templates/fake_bot.py` | Fake bot used by the self-check | +| `templates/palette.py` | Colour presets or a custom hex, with a contrast check | +| `docs/adr/` | Why it works this way | diff --git a/rpa-run-tab/SKILL.md b/skills/rpa-run-tab/SKILL.md similarity index 100% rename from rpa-run-tab/SKILL.md rename to skills/rpa-run-tab/SKILL.md diff --git a/rpa-run-tab/templates/fake_bot.py b/skills/rpa-run-tab/templates/fake_bot.py similarity index 100% rename from rpa-run-tab/templates/fake_bot.py rename to skills/rpa-run-tab/templates/fake_bot.py diff --git a/rpa-run-tab/templates/palette.py b/skills/rpa-run-tab/templates/palette.py similarity index 100% rename from rpa-run-tab/templates/palette.py rename to skills/rpa-run-tab/templates/palette.py diff --git a/rpa-run-tab/templates/rt.py b/skills/rpa-run-tab/templates/rt.py similarity index 100% rename from rpa-run-tab/templates/rt.py rename to skills/rpa-run-tab/templates/rt.py diff --git a/rpa-run-tab/templates/run_tab.html b/skills/rpa-run-tab/templates/run_tab.html similarity index 100% rename from rpa-run-tab/templates/run_tab.html rename to skills/rpa-run-tab/templates/run_tab.html diff --git a/rpa-run-tab/templates/run_tab.py b/skills/rpa-run-tab/templates/run_tab.py similarity index 100% rename from rpa-run-tab/templates/run_tab.py rename to skills/rpa-run-tab/templates/run_tab.py diff --git a/rpa-run-tab/templates/runner.py b/skills/rpa-run-tab/templates/runner.py similarity index 100% rename from rpa-run-tab/templates/runner.py rename to skills/rpa-run-tab/templates/runner.py diff --git a/tests/README.md b/tests/README.md index b74f7b3..5b44466 100644 --- a/tests/README.md +++ b/tests/README.md @@ -72,4 +72,4 @@ uv run --with pytest --with playwright pytest tests passwords, checkpoints driving steps/boxes/progress, green/amber/red endings, the no-input mode, the dark toggle and no horizontal scroll, each in light, dark and narrow mode. The Python side covers line parsing, endings, the sample workbook and `palette.py --write`. - The end-to-end fake-bot check lives in the skill itself: `python rpa-run-tab/templates/run_tab.py --selfcheck`. + The end-to-end fake-bot check lives in the skill itself: `python skills/rpa-run-tab/templates/run_tab.py --selfcheck`. diff --git a/tests/evals/run_eval.py b/tests/evals/run_eval.py index 5654617..3591568 100644 --- a/tests/evals/run_eval.py +++ b/tests/evals/run_eval.py @@ -202,7 +202,7 @@ def main(fixture): shutil.copy(pages[0], results / f"{fixture}.dashboard.html") checks["no placeholders left"] = "{{" not in html if cfg.get("brand"): - spec = importlib.util.spec_from_file_location("palette", ROOT / "rpa-dashboard-no-ai/templates/palette.py") + spec = importlib.util.spec_from_file_location("palette", ROOT / "skills/rpa-dashboard-no-ai/templates/palette.py") palette = importlib.util.module_from_spec(spec) spec.loader.exec_module(palette) t = palette.derive(cfg["brand"]) diff --git a/tests/page_check/conftest.py b/tests/page_check/conftest.py index ba6811e..ef4ce6a 100644 --- a/tests/page_check/conftest.py +++ b/tests/page_check/conftest.py @@ -16,7 +16,7 @@ import pytest from page_check_common import FAKE_BRIDGE, MODES, TEST_FILL, WIRED, open_page ROOT = Path(__file__).resolve().parents[2] -TEMPLATE = ROOT / "rpa-dashboard-no-ai" / "templates" / "dashboard.html" +TEMPLATE = ROOT / "skills" / "rpa-dashboard-no-ai" / "templates" / "dashboard.html" @pytest.fixture(scope="session") diff --git a/tests/page_check_ai/conftest.py b/tests/page_check_ai/conftest.py index 44a5645..a25c154 100644 --- a/tests/page_check_ai/conftest.py +++ b/tests/page_check_ai/conftest.py @@ -23,7 +23,7 @@ import pytest from page_check_common import MODES, TEST_FILL, load_stub, open_page ROOT = Path(__file__).resolve().parents[2] -TEMPLATE = ROOT / "rpa-dashboard-ai" / "templates" / "web" +TEMPLATE = ROOT / "skills" / "rpa-dashboard-ai" / "templates" / "web" TEST_FILL = {**TEST_FILL, "APP_NAME": "Test Extractor", "API_BASE": "/api"} RECORD_ACTIONS = """ window.__calls = []; diff --git a/tests/page_check_ai_desktop/conftest.py b/tests/page_check_ai_desktop/conftest.py index d661cf5..37201d7 100644 --- a/tests/page_check_ai_desktop/conftest.py +++ b/tests/page_check_ai_desktop/conftest.py @@ -22,8 +22,8 @@ import pytest from page_check_common import FAKE_BRIDGE, MODES, TEST_FILL, WIRED, load_stub, open_page ROOT = Path(__file__).resolve().parents[2] -NO_AI = ROOT / "rpa-dashboard-no-ai" / "templates" / "dashboard.html" -AI = ROOT / "rpa-dashboard-ai" / "templates" +NO_AI = ROOT / "skills" / "rpa-dashboard-no-ai" / "templates" / "dashboard.html" +AI = ROOT / "skills" / "rpa-dashboard-ai" / "templates" @pytest.fixture(scope="session") diff --git a/tests/page_check_run_tab/conftest.py b/tests/page_check_run_tab/conftest.py index f6fd73d..88476b9 100644 --- a/tests/page_check_run_tab/conftest.py +++ b/tests/page_check_run_tab/conftest.py @@ -9,7 +9,7 @@ import pytest from page_check_common import FAKE_BRIDGE, MODES, open_page ROOT = Path(__file__).resolve().parents[2] -TEMPLATE = ROOT / "rpa-run-tab" / "templates" / "run_tab.html" +TEMPLATE = ROOT / "skills" / "rpa-run-tab" / "templates" / "run_tab.html" CFG = {"title": "Test Bot", "sub": "Page check", "input": True, "inputHint": "Test workbook", "sample": True, "darkKey": "test_dark", "checkpoints": [{"name": "Sign in", "max": 300}, {"name": "Process rows", "max": 0}, diff --git a/tests/test_bands.py b/tests/test_bands.py index e78a406..4f749fa 100644 --- a/tests/test_bands.py +++ b/tests/test_bands.py @@ -3,7 +3,7 @@ import importlib.util import re from pathlib import Path -BANDS = Path(__file__).resolve().parents[1] / "rpa-dashboard-ai" / "templates" / "web" / "bands.py" +BANDS = Path(__file__).resolve().parents[1] / "skills" / "rpa-dashboard-ai" / "templates" / "web" / "bands.py" spec = importlib.util.spec_from_file_location("bands", BANDS) bands = importlib.util.module_from_spec(spec) spec.loader.exec_module(bands) diff --git a/tests/test_bot_runner.py b/tests/test_bot_runner.py index 788e8e7..93b1b30 100644 --- a/tests/test_bot_runner.py +++ b/tests/test_bot_runner.py @@ -7,7 +7,7 @@ from pathlib import Path import pytest -RUNNER = Path(__file__).resolve().parents[1] / "rpa-dashboard-no-ai" / "templates" / "bot_runner.py" +RUNNER = Path(__file__).resolve().parents[1] / "skills" / "rpa-dashboard-no-ai" / "templates" / "bot_runner.py" @pytest.fixture diff --git a/tests/test_detect_frontend.py b/tests/test_detect_frontend.py index 37fb4fa..b9dd4e6 100644 --- a/tests/test_detect_frontend.py +++ b/tests/test_detect_frontend.py @@ -5,7 +5,7 @@ import subprocess import sys from pathlib import Path -DETECT = Path(__file__).resolve().parents[1] / "rpa-dashboard-no-ai" / "templates" / "detect_frontend.py" +DETECT = Path(__file__).resolve().parents[1] / "skills" / "rpa-dashboard-no-ai" / "templates" / "detect_frontend.py" spec = importlib.util.spec_from_file_location("detect_frontend", DETECT) detect_frontend = importlib.util.module_from_spec(spec) spec.loader.exec_module(detect_frontend) diff --git a/tests/test_palette.py b/tests/test_palette.py index 51c802f..838f738 100644 --- a/tests/test_palette.py +++ b/tests/test_palette.py @@ -4,7 +4,7 @@ import subprocess import sys from pathlib import Path -PALETTE = Path(__file__).resolve().parents[1] / "rpa-dashboard-no-ai" / "templates" / "palette.py" +PALETTE = Path(__file__).resolve().parents[1] / "skills" / "rpa-dashboard-no-ai" / "templates" / "palette.py" spec = importlib.util.spec_from_file_location("palette", PALETTE) palette = importlib.util.module_from_spec(spec) spec.loader.exec_module(palette) diff --git a/tests/test_pricing.py b/tests/test_pricing.py index 76e4f13..5c0c813 100644 --- a/tests/test_pricing.py +++ b/tests/test_pricing.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest -SRC = Path(__file__).resolve().parents[1] / "rpa-dashboard-ai" / "templates" / "web" / "pricing.py" +SRC = Path(__file__).resolve().parents[1] / "skills" / "rpa-dashboard-ai" / "templates" / "web" / "pricing.py" spec = importlib.util.spec_from_file_location("pricing", SRC) pricing = importlib.util.module_from_spec(spec) spec.loader.exec_module(pricing) diff --git a/tests/test_reference_scan.py b/tests/test_reference_scan.py index 1927e22..68eadcb 100644 --- a/tests/test_reference_scan.py +++ b/tests/test_reference_scan.py @@ -6,8 +6,8 @@ from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] -SKILLS = [ROOT / "rpa-dashboard-ai", ROOT / "rpa-dashboard-no-ai"] -AI_REFERENCE = ROOT / "rpa-dashboard-ai" / "templates" / "reference" +SKILLS = [ROOT / "skills" / "rpa-dashboard-ai", ROOT / "skills" / "rpa-dashboard-no-ai"] +AI_REFERENCE = ROOT / "skills" / "rpa-dashboard-ai" / "templates" / "reference" FORBIDDEN = { "internal IP": r"\b(10\.\d{1,3}|192\.168|172\.(1[6-9]|2\d|3[01]))\.\d{1,3}\.\d{1,3}\b", "Google API key": r"AIza[0-9A-Za-z_-]{30,}", diff --git a/tests/test_run_tab.py b/tests/test_run_tab.py index 036dbbc..6b4ca34 100644 --- a/tests/test_run_tab.py +++ b/tests/test_run_tab.py @@ -4,7 +4,7 @@ import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -T = ROOT / "rpa-run-tab" / "templates" +T = ROOT / "skills" / "rpa-run-tab" / "templates" sys.path.insert(0, str(T)) import runner # noqa: E402 diff --git a/tests/test_sections.py b/tests/test_sections.py index 52b4f32..7fa6b89 100644 --- a/tests/test_sections.py +++ b/tests/test_sections.py @@ -10,7 +10,7 @@ from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] -TEMPLATES = ROOT / "rpa-dashboard-no-ai" / "templates" +TEMPLATES = ROOT / "skills" / "rpa-dashboard-no-ai" / "templates" spec = importlib.util.spec_from_file_location("sections", TEMPLATES / "sections.py") sections = importlib.util.module_from_spec(spec) spec.loader.exec_module(sections) diff --git a/tests/test_templates.py b/tests/test_templates.py index a6d35ff..11a37e7 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -10,9 +10,9 @@ ROOT = Path(__file__).resolve().parents[1] HOOK_RULES = "E9,F63,F7,F82,F811" # same rule set as the PostToolUse lint hook -@pytest.mark.parametrize("template", ["rpa-dashboard-no-ai/templates/bridge.py", - "rpa-dashboard-no-ai/templates/bot_runner.py", - "rpa-dashboard-ai/templates/desktop/ai_bridge.py"]) +@pytest.mark.parametrize("template", ["skills/rpa-dashboard-no-ai/templates/bridge.py", + "skills/rpa-dashboard-no-ai/templates/bot_runner.py", + "skills/rpa-dashboard-ai/templates/desktop/ai_bridge.py"]) def test_template_passes_lint_hook_unfilled(template): ruff = shutil.which("ruff") or pytest.skip("ruff not on PATH") r = subprocess.run([ruff, "check", "--select", HOOK_RULES, "--no-cache", str(ROOT / template)], @@ -21,7 +21,7 @@ def test_template_passes_lint_hook_unfilled(template): def test_tebra_sky_preset_is_the_template_default(): - templates = ROOT / "rpa-dashboard-no-ai" / "templates" + templates = ROOT / "skills" / "rpa-dashboard-no-ai" / "templates" themes = (templates / "themes.md").read_text(encoding="utf-8") sky = themes.split("## 1. Tebra Sky")[1].split("```css\n")[1].split("```")[0] assert sky in (templates / "dashboard.html").read_text(encoding="utf-8") @@ -29,13 +29,13 @@ def test_tebra_sky_preset_is_the_template_default(): @pytest.mark.parametrize("skill", ["rpa-dashboard-no-ai", "rpa-dashboard-ai"]) def test_skill_description_fits_the_1024_char_limit(skill): - text = (ROOT / skill / "SKILL.md").read_text(encoding="utf-8") + text = (ROOT / "skills" / skill / "SKILL.md").read_text(encoding="utf-8") description = re.search(r"^description: (.+)$", text, re.MULTILINE)[1] assert len(description) <= 1024, len(description) def test_ai_skill_references_files_that_exist(): - skill = ROOT / "rpa-dashboard-ai" + skill = ROOT / "skills" / "rpa-dashboard-ai" text = (skill / "SKILL.md").read_text(encoding="utf-8") paths = re.findall(r"`((?:templates/|)[\w./-]+)`", text) assert len(paths) > 5