diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 435ebe1..552fe0e 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -53,6 +53,17 @@ "url": "https://nothingfancy.ai" }, "source": "./plugins/gemini-tts-prompting" + }, + { + "name": "launching-google-ai", + "version": "0.1.0", + "description": "Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool (image, video, music, deep research, canvas, guided learning). Use when asked to 'ask Gemini', 'open Gemini with', 'stitch this', 'use deep research on', 'generate a video in Gemini', or otherwise launch a prefilled Gemini or Stitch session via URL parameters from the shell. Does not auto-submit — only pre-fills the prompt box.", + "author": { + "name": "Wesley O. Nichols", + "email": "wes@nothingfancy.ai", + "url": "https://nothingfancy.ai" + }, + "source": "./plugins/launching-google-ai" } ] } diff --git a/.codex/skills/launching-google-ai b/.codex/skills/launching-google-ai new file mode 120000 index 0000000..933e96c --- /dev/null +++ b/.codex/skills/launching-google-ai @@ -0,0 +1 @@ +../../plugins/launching-google-ai/skills/launching-google-ai \ No newline at end of file diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1c57f8c..54a0b96 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -27,6 +27,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v1 + - uses: astral-sh/ruff-action@v3 with: - version: latest + args: check diff --git a/.gitignore b/.gitignore index 33aed0d..f67afc6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ evals/transcripts/* evals/reports/* !evals/reports/.gitkeep evals/.promptfoo-cache/ + +scratchpad/ diff --git a/CODEOWNERS b/CODEOWNERS index 5befc8a..d2ad1cd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,6 +9,7 @@ /plugins/justfile/ @wesnick /plugins/nano-banana-prompting/ @wesnick /plugins/gemini-tts-prompting/ @wesnick +/plugins/launching-google-ai/ @wesnick # Shared infrastructure /.github/ @wesnick diff --git a/README.md b/README.md index 223557a..8679fd6 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ This is the preferred mode when you want a narrow, focused session. |--------|-------------| | [nano-banana-prompting](plugins/nano-banana-prompting/) | Crafts and reviews prompts for Google's Nano Banana 2 (Gemini 3.1 Flash Image) and Nano Banana Pro (Gemini 3 Pro Image) image generation and editing models — frameworks, camera/lighting direction, and text rendering. | | [gemini-tts-prompting](plugins/gemini-tts-prompting/) | Crafts and reviews prompts for Google's Gemini 3.1 Flash TTS using audio tags, voice style instructions, and pacing controls — narration, audiobooks, IVR, accessibility, multilingual content. | +| [launching-google-ai](plugins/launching-google-ai/) | Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool selection (image, video, music, deep research, canvas, guided learning) via URL parameters. | ## Philosophy diff --git a/evals/cases/launching-google-ai/cases.yaml b/evals/cases/launching-google-ai/cases.yaml new file mode 100644 index 0000000..a8d4dfc --- /dev/null +++ b/evals/cases/launching-google-ai/cases.yaml @@ -0,0 +1,135 @@ +# Regression suite for launching-google-ai skill + +description: "launching-google-ai skill regression cases" + +tests: + # --- Positive: Gemini deep-research launch --- + - description: "Positive — 'ask gemini to research X' should build a Gemini deep-research URL" + vars: + prompt: | + ask gemini to research the history of LISP + assert: + - type: contains + value: "https://gemini.google.com/?prompt=" + - type: contains + value: "tool=research" + - type: contains-any + value: + - "the%20history%20of%20LISP" + - "the+history+of+LISP" + - type: contains-any + value: + - "${BROWSER:-xdg-open}" + - "$BROWSER" + - type: llm-rubric + value: | + The response must: (1) build a URL on gemini.google.com (NOT + stitch.withgoogle.com, NOT any other Google surface), (2) use + ?prompt= as the parameter (NOT ?q= or ?query=), (3) percent-encode + the prompt text, (4) include &tool=research because the user said + "research", using the SHORT alias "research" rather than the human + label "Deep research", and (5) open the URL via ${BROWSER:-xdg-open} + (or equivalent fallback) so a user-configured browser is honored. + The response must NOT attempt to auto-submit the prompt — pre-fill + only. + + # --- Positive: Stitch design launch (no tool param) --- + - description: "Positive — 'stitch a pricing page' should build a Stitch URL with no tool parameter" + vars: + prompt: | + stitch a clean SaaS pricing page with three tiers + assert: + - type: contains + value: "https://stitch.withgoogle.com/?prompt=" + - type: not-contains + value: "tool=" + - type: llm-rubric + value: | + The response must: (1) target stitch.withgoogle.com, NOT + gemini.google.com, because the user explicitly said "stitch", + (2) NOT include any &tool=... parameter — Stitch does not accept a + tool parameter and adding one is wrong, (3) percent-encode the + design brief into the prompt= parameter verbatim (the user's words, + not paraphrased), (4) open via ${BROWSER:-xdg-open} or equivalent. + + # --- Negative: prompt-craft request should NOT fire this skill --- + - description: "Negative — 'help me write a prompt for Gemini image gen' is a prompt-craft task, not a launch task" + vars: + prompt: | + Help me write a prompt for Gemini image generation of a vintage + bookstore at golden hour with reference photos I'll attach. + assert: + - type: not-contains + value: "gemini.google.com/?prompt=" + - type: not-contains + value: "xdg-open" + - type: not-contains + value: "${BROWSER" + - type: llm-rubric + value: | + The response must address prompt-craft for Gemini image generation + on its own terms (subject + action + location + composition + style, + camera/lighting direction, treatment of reference images, etc.) — + this is the territory of the nano-banana-prompting skill, NOT + launching-google-ai. The response must NOT construct a + gemini.google.com URL, must NOT call xdg-open or $BROWSER, and must + NOT instruct the user to launch the Gemini web UI. The launching + skill should not fire because the user is asking how to write the + prompt, not how to open Gemini. + + # --- Edge: multi-line prompt with special chars must encode newlines --- + - description: "Edge — multi-line prompt should encode newlines as %0A and preserve special chars" + vars: + prompt: | + Open Gemini with this multi-line prompt and use deep research: + + Compare Rust and Zig for systems work. + Cover: memory model, async story, & C interop. + Cite sources. + assert: + - type: contains + value: "https://gemini.google.com/?prompt=" + - type: contains + value: "tool=research" + - type: contains + value: "%0A" + - type: contains-any + value: + - "%26" + - "&" + - type: llm-rubric + value: | + The response must: (1) percent-encode every newline in the + multi-line prompt as %0A so the prompt arrives in the Gemini box + with line breaks intact, (2) percent-encode the literal "&" + character inside the prompt body as %26 so it is NOT interpreted as + a URL parameter separator (this is the historically-broken case — + an unencoded "&" silently truncates the prompt at the ampersand), + (3) still produce a single well-formed URL with ?prompt=... and + &tool=research, (4) open via ${BROWSER:-xdg-open}. + + # --- Edge: ambiguous tool cue should default to omitting tool= --- + - description: "Edge — ambiguous request without a clear tool cue should omit the tool parameter" + vars: + prompt: | + open gemini and ask it what the weather will be like in Madrid this + weekend + assert: + - type: contains + value: "https://gemini.google.com/?prompt=" + - type: not-contains + value: "tool=research" + - type: not-contains + value: "tool=image" + - type: not-contains + value: "tool=video" + - type: llm-rubric + value: | + The response must build a Gemini URL with ?prompt=... but must NOT + include any &tool=... parameter, because the user's request ("ask + it what the weather will be like") does not clearly match any of + the six known tool aliases (image, video, music, research, canvas, + learn). The skill's documented behavior is to omit tool= when the + intent is ambiguous and let Gemini pick a default. The response + must NOT fabricate an unsupported tool name like "weather", + "search", or "ask". diff --git a/plugins/launching-google-ai/.claude-plugin/plugin.json b/plugins/launching-google-ai/.claude-plugin/plugin.json new file mode 100644 index 0000000..690fcfd --- /dev/null +++ b/plugins/launching-google-ai/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "launching-google-ai", + "version": "0.1.0", + "description": "Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool (image, video, music, deep research, canvas, guided learning). Use when asked to 'ask Gemini', 'open Gemini with', 'stitch this', 'use deep research on', 'generate a video in Gemini', or otherwise launch a prefilled Gemini or Stitch session via URL parameters from the shell. Does not auto-submit — only pre-fills the prompt box.", + "author": { + "name": "Wesley O. Nichols", + "email": "wes@nothingfancy.ai", + "url": "https://nothingfancy.ai" + } +} diff --git a/plugins/launching-google-ai/README.md b/plugins/launching-google-ai/README.md new file mode 100644 index 0000000..d6d6af9 --- /dev/null +++ b/plugins/launching-google-ai/README.md @@ -0,0 +1,52 @@ +# launching-google-ai + +Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool selection (image, video, music, deep research, canvas, guided learning). + +## What it does + +- Builds the right URL for Gemini (`https://gemini.google.com/?prompt=...&tool=...`) or Stitch (`https://stitch.withgoogle.com/?prompt=...`). +- Picks the correct Gemini tool alias from natural-language cues — "research this" → `tool=research`, "make a video of" → `tool=video`, "generate an image of" → `tool=image`. +- URL-encodes the prompt safely, including newlines (`%0A`) and special characters. +- Honors `$BROWSER` (so users can route to a specific Chrome profile or browser binary), falling back to `xdg-open`. +- Stops at pre-fill — never auto-submits, so the user keeps a final-edit step. + +## Usage + +### Claude Code + +```sh +claude --plugin-dir ./plugins/launching-google-ai +``` + +Or via the marketplace, once installed. + +### pi + +```sh +pi -e ./plugins/launching-google-ai # ephemeral +pi install ./plugins/launching-google-ai +``` + +### Codex + +See the root [`.codex/INSTALL.md`](../../.codex/INSTALL.md). + +## Harness support + +| Feature | Claude Code | Codex | pi | +|---|---|---|---| +| Skill (SKILL.md) | ✅ | ✅ | ✅ | + +Pure skill content — runs identically in all three harnesses. The only host requirement is a shell with `$BROWSER` or `xdg-open` available (Linux/macOS); on macOS, `open` works as a drop-in replacement for `xdg-open` and the skill will use it via `$BROWSER` if set. + +## Evals + +Regression cases live in [`../../evals/cases/launching-google-ai/cases.yaml`](../../evals/cases/launching-google-ai/cases.yaml). + +```sh +uv run evals/framework/run.py --skill launching-google-ai +``` + +## License + +[CC BY-SA 4.0](../../LICENSE) — made by [Nothing Fancy](https://nothingfancy.ai). diff --git a/plugins/launching-google-ai/package.json b/plugins/launching-google-ai/package.json new file mode 100644 index 0000000..08bea75 --- /dev/null +++ b/plugins/launching-google-ai/package.json @@ -0,0 +1,26 @@ +{ + "name": "@nothingfancy/launching-google-ai", + "version": "0.1.0", + "description": "Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool selection.", + "keywords": [ + "pi-package", + "agent-skill", + "gemini", + "stitch", + "google-ai", + "browser-launcher", + "url-prompt" + ], + "author": { + "name": "Wesley O. Nichols", + "email": "wes@nothingfancy.ai", + "url": "https://nothingfancy.ai" + }, + "license": "CC-BY-SA-4.0", + "pi": { + "skills": ["./skills"] + }, + "peerDependencies": { + "@mariozechner/pi-coding-agent": "*" + } +} diff --git a/plugins/launching-google-ai/skills/launching-google-ai/SKILL.md b/plugins/launching-google-ai/skills/launching-google-ai/SKILL.md new file mode 100644 index 0000000..242bb3b --- /dev/null +++ b/plugins/launching-google-ai/skills/launching-google-ai/SKILL.md @@ -0,0 +1,109 @@ +--- +name: launching-google-ai +description: Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool (image, video, music, deep research, canvas, guided learning). Use when asked to "ask Gemini", "open Gemini with", "stitch this", "use deep research on", "generate a video in Gemini", or otherwise launch a prefilled Gemini or Stitch session via URL parameters from the shell. Does not auto-submit — only pre-fills the prompt box. +--- + +# Launching Google AI + +Opens a Google Gemini or Google Stitch session in the user's browser with a prompt already typed in the box and (for Gemini) a tool already selected. The skill exists because the user wants to hand off to a hosted UI for the next step — usually a multi-modal session, a deep-research run, or a Stitch design loop — without retyping context. + +The guiding principle: **build the URL, open it, stop.** Don't auto-submit. The user owns the final edit and the send button, every time. + +## When to Use + +- The user says "ask Gemini …", "open Gemini with …", "in Gemini, …", "fire up Gemini for …". +- The user says "stitch …", "stitch this …", "open Stitch with …", or names a UI mockup task they want Stitch to handle. +- The user wants a specific Gemini tool: "deep research X", "use canvas to …", "generate a video of …", "generate an image of …", "make some music about …", "guided learning for …". +- The user wants to take prompt text already in the conversation and continue it in a hosted Google AI UI. + +## When NOT to Use + +- The user wants to **call the Gemini API or Vertex AI** programmatically — that's an SDK task, not a URL launcher. +- The user wants to **write or refine** a prompt for Gemini image generation — use `nano-banana-prompting`. +- The user wants to **write or refine** a prompt for Gemini TTS — use `gemini-tts-prompting`. +- The user wants the model's actual answer in this conversation — answer it here; don't bounce them to the web UI. +- The user wants Google AI Studio, NotebookLM, or another Google surface — those are not covered (Gemini and Stitch only). +- The user wants the browser to **submit** automatically — that's outside this skill's contract and the underlying URL parameters do not support it. + +## URL Patterns + +### Gemini (default) + +``` +https://gemini.google.com/?prompt= +https://gemini.google.com/?prompt=&tool= +``` + +### Stitch + +``` +https://stitch.withgoogle.com/?prompt= +``` + +Stitch does not accept a `tool` parameter. Adding one is a silent no-op at best; treat it as a hard error and omit it. + +## Gemini Tool Aliases + +Pick the alias from natural-language cues. The aliases on the right are what go in `&tool=` — use the short form, not the human label. + +| User cue | Tool | Alias | +|---|---|---| +| "generate an image", "draw", "make a picture" | Create image | `image` | +| "make a video", "generate a video", "animate" | Create video | `video` | +| "write a song", "compose music", "make music" | Create music | `music` | +| "deep research", "research this thoroughly", "comprehensive report on" | Deep research | `research` | +| "open canvas", "iterate in canvas", "use canvas for …" | Canvas | `canvas` | +| "teach me", "guided learning", "walk me through learning …" | Guided learning | `learn` | + +If the user's intent doesn't clearly match one of these, omit the `tool` parameter — Gemini will pick a sensible default. + +## Building the URL + +1. **Pick the surface.** Default to Gemini. Switch to Stitch only when the user explicitly says Stitch or describes a UI/mockup design task that fits Stitch's scope. +2. **Pick the tool** (Gemini only) using the alias table above. If unsure, omit it. +3. **URL-encode the prompt** using standard percent-encoding. Newlines become `%0A`. Spaces can be `%20` or `+` (prefer `%20` for readability). Pass raw text — the receiving page handles HTML escaping. +4. **Assemble**: `?prompt=` first, then `&tool=` if present. + +## Opening the URL + +```sh +${BROWSER:-xdg-open} "" +``` + +Honor `$BROWSER` so the user can route to a specific browser or profile. Common pattern: + +```sh +export BROWSER="google-chrome --profile-directory='Profile 1'" +``` + +On macOS, users typically set `$BROWSER` to `open` or to a specific app bundle path. Don't hardcode `xdg-open` without the `${BROWSER:-…}` fallback — it breaks Mac users. + +## Worked Example + +User: *"ask gemini to research the history of LISP"* + +1. Surface: Gemini (default). +2. Tool: "research" → `research`. +3. Encode prompt `the history of LISP` → `the%20history%20of%20LISP`. +4. URL: `https://gemini.google.com/?prompt=the%20history%20of%20LISP&tool=research`. +5. Open: `${BROWSER:-xdg-open} "https://gemini.google.com/?prompt=the%20history%20of%20LISP&tool=research"`. + +User: *"stitch a clean SaaS pricing page with three tiers"* + +1. Surface: Stitch (explicit). +2. Tool: omit (Stitch does not support `tool`). +3. Encode prompt `a clean SaaS pricing page with three tiers` → `a%20clean%20SaaS%20pricing%20page%20with%20three%20tiers`. +4. URL: `https://stitch.withgoogle.com/?prompt=a%20clean%20SaaS%20pricing%20page%20with%20three%20tiers`. +5. Open with `${BROWSER:-xdg-open}`. + +## Anti-patterns + +| Anti-pattern | Signal | Fix | +|---|---|---| +| Auto-submitting the prompt | The skill tries to script a click or send-key after opening the URL | Pre-fill is the contract. Stop at the URL — the user submits. | +| Using `?q=` instead of `?prompt=` | URL has `?q=…` | The Gemini/Stitch URL contract is `prompt=`. `q=` does not pre-fill. | +| Adding `tool=` to a Stitch URL | URL is `stitch.withgoogle.com/?prompt=…&tool=…` | Drop the `tool=` — Stitch ignores it (and "ignore" is a generous read). | +| Hardcoding the browser | `xdg-open` or `chromium` literal in the command | Use `${BROWSER:-xdg-open}` — Mac and per-profile users depend on it. | +| Translating tool names | `&tool=Create%20image` | Use the short alias (`image`, `video`, `research`, etc.), not the human label. | +| Re-typing prompt content into the URL | Prompt in URL diverges from what the user said | Encode the user's prompt verbatim; only normalize whitespace. | +| Fabricating an unsupported tool | `&tool=summarize`, `&tool=translate`, etc. | Only the six aliases in the table are real. If the user wants something else, omit `tool=` entirely. | diff --git a/plugins/spec-anchored-development/skills/spec-anchored-development/SKILL.md b/plugins/spec-anchored-development/skills/spec-anchored-development/SKILL.md index 1b09676..d92f671 100644 --- a/plugins/spec-anchored-development/skills/spec-anchored-development/SKILL.md +++ b/plugins/spec-anchored-development/skills/spec-anchored-development/SKILL.md @@ -1,5 +1,5 @@ --- -name: specs +name: spec-anchored-development description: This skill should be used when the user asks to "create a spec", "write a specification", "generate a spec from code", "maintain specs", "sync specs with code", "spec:code graph", "greenfield spec", "brownfield spec", "reverse-engineer a spec", "document system architecture", or mentions spec-anchored development. Supports Python, Go, and TypeScript projects. --- @@ -7,6 +7,22 @@ description: This skill should be used when the user asks to "create a spec", "w Specifications are living system maps, not static documentation. Every spec maintains a bidirectional graph with implementing code: spec to code for execution, code to spec for comprehension. +## When to Use + +- The user asks to author a new spec, formalize a system's design, or bootstrap a system map for an existing codebase. +- The user wants to derive specs from existing code (brownfield / reverse-engineering) and have them stay synced. +- The user is editing code or specs in a project that already uses spec-anchored development and wants the bidirectional graph kept current. +- The user mentions "spec drift", "spec:code graph", "system map", "greenfield spec", "brownfield spec", or asks to audit a project's specs against its code. +- The project is in Python, Go, or TypeScript — the language references in this skill cover those three. + +## When NOT to Use + +- The user wants a one-off design doc, README, or ADR with no ongoing maintenance contract — this skill assumes a maintained spec/code graph and adds friction otherwise. +- The user wants pure interface documentation (OpenAPI, protobuf, JSON Schema) — those are interface specs, not system specs, and have their own tooling. +- The user wants a project-management plan, milestone roadmap, or sprint breakdown — use a planning skill; specs own the *what* and *why*, not the *when*. +- The user wants implementation-level pseudocode or algorithm walkthroughs — code owns the *how*; this skill explicitly pushes back on implementation in specs. +- The project is in a language other than Python, Go, or TypeScript — the language-specific guidance does not yet cover other ecosystems, and harness-neutral parts won't be enough on their own. + ## Optimization targets A spec system earns its keep by improving three things. Every rule below ladders up to one of them: diff --git a/scratchpad/.gitkeep b/scratchpad/.gitkeep new file mode 100644 index 0000000..e69de29