Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .factory-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
"description": "Core Skills for essential functionalities and integrations",
"source": "./plugins/core",
"category": "core"
},
{
"name": "rfspec",
"description": "Request for Spec: fan out a prompt to multiple AI models in parallel and pick or synthesize the best implementation spec",
"source": "./plugins/rfspec",
"category": "productivity"
}
]
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ Skills for continuous learning and improvement.
- `frontend-design` - Build web apps, websites, HTML pages with good design
- `browser-navigation` - Browser automation with agent-browser

### rfspec

Fan out a prompt to multiple AI models in parallel and pick or synthesize the best result.

**Skills:**

- `rfspec` - Multi-model spec generation and synthesis workflow

## Plugin Structure

Each plugin follows the Factory plugin format:
Expand Down
9 changes: 9 additions & 0 deletions plugins/rfspec/.factory-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "rfspec",
"description": "Request for Spec: fan out a prompt to multiple AI models in parallel and pick or synthesize the best implementation spec",
"version": "1.0.0",
"author": {
"name": "Andy Taylor",
"email": "andrew.taylor@factory.ai"
}
}
32 changes: 32 additions & 0 deletions plugins/rfspec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# rfspec

Request for Spec -- fan out a prompt to multiple AI models in parallel and choose or synthesize the best result.

## What it does

`/rfspec` sends your prompt to three models simultaneously (Claude Opus 4.6, GPT-5.4, Gemini 3.1 Pro), each at its maximum reasoning tier. The results are presented side-by-side so you can pick the strongest one or synthesize a combination. No prescriptive system prompt is injected -- the models bring their own reasoning to your request.

## Usage

```
/rfspec <describe what you want to build>
```

Example:

```
/rfspec add a dark mode toggle to the settings page with persistent user preference
```

The command will:

1. Send the prompt to all three models in parallel via `droid exec`, each at its maximum reasoning tier (Opus: max, GPT-5.4: xhigh, Gemini: high)
2. Collect and display each model's response (Options A, B, C)
3. Ask you to pick one as-is or synthesize the best parts
4. Save the chosen result to `specs/active/YYYY-MM-DD-<slug>.md`

## Requirements

- **droid CLI** -- must be installed and authenticated
- **jq** -- for JSON parsing (`brew install jq` on macOS)
- Access to at least one of the three models. Models that fail are skipped gracefully; the command only errors if all three fail.
2 changes: 2 additions & 0 deletions plugins/rfspec/commands/rfspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(dirname "$0")/../skills/rfspec/scripts/run.sh" "$@"
93 changes: 93 additions & 0 deletions plugins/rfspec/skills/rfspec/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
name: rfspec
version: 1.2.0
description: |
Multi-model spec generation and synthesis. Use when the user wants to:
- Get competing proposals from different AI models
- Compare approaches to a problem from different perspectives
- Synthesize the best parts of several proposals into one spec
Keywords: rfspec, competing specs, multi-model, compare approaches,
multiple perspectives, request for spec, fan out, model comparison.
NOT for: single-model generation, code review, or running tests.
---

# rfspec -- Request for Spec

Fan out a prompt to multiple models, compare their responses, and help the user pick or synthesize the best result.

## Quick Reference

| Task | Action |
|------|--------|
| Generate competing specs | `/rfspec <prompt>` |
| Pick one result | Select via AskUser after comparison |
| Synthesize results | Combine strongest elements when user chooses synthesis |
| Save final spec | Write to `specs/active/YYYY-MM-DD-<slug>.md` |

## Workflow

1. Run `/rfspec <user's prompt>` -- fires parallel model calls, returns labeled options (A, B, C).
2. Evaluate the results -- see [references/evaluation-guide.md](references/evaluation-guide.md).
3. Present the choice to the user via AskUser.
4. Present the selected or synthesized spec via ExitSpecMode for user review.
5. Save to `specs/active/` only after the user approves in spec mode.

## Saving

**Do not save immediately.** After the user picks or synthesis is complete, present the
final spec via ExitSpecMode for review. Only after approval, save to:

```
specs/active/YYYY-MM-DD-<slug>.md
```

Where `<slug>` is a short kebab-case name derived from the topic.

## Pitfalls

- Don't summarize each option individually -- compare them against each other.
- Don't concatenate when synthesizing -- resolve contradictions and produce a coherent document.
- If all options are rejected, gather feedback and re-run with a refined prompt.

## Verification

After saving a spec:

1. Confirm the file exists at the expected path.
2. Verify it contains the selected or synthesized content.
3. Report the saved path to the user.

## Examples

Example 1: User wants competing specs
User says: "Get me specs from multiple models for adding a dark mode toggle"
Actions:

1. Run `/rfspec add a dark mode toggle to the settings page with persistent user preference`
2. Read Options A, B, C
3. Compare: "Option A uses CSS variables with a React context, Option B uses Tailwind's dark class with localStorage, Option C uses a theme provider with system preference detection."
4. Present choice via AskUser
Result: User picks Option B, saved to `specs/active/2026-03-06-dark-mode-toggle.md`

Example 2: User wants synthesis
User says: "rfspec this: refactor the auth module to use JWT"
Actions:

1. Run `/rfspec refactor the auth module to use JWT`
2. Compare results, noting Option A has better token rotation but Option C has cleaner middleware
3. User selects "Synthesize"
4. Combine Option A's rotation logic with Option C's middleware structure
Result: Synthesized spec saved to `specs/active/2026-03-06-auth-jwt-refactor.md`

Example 3: All options rejected
User says: "None of these work, they all miss the caching layer"
Actions:

1. Ask what's missing -- user explains the Redis caching requirement
2. Offer to re-run: `/rfspec refactor auth module to use JWT with Redis session caching`
Result: New round of specs generated with caching addressed

## References

- [references/evaluation-guide.md](references/evaluation-guide.md) -- how to compare, synthesize, and handle rejection
- [references/troubleshooting.md](references/troubleshooting.md) -- error codes and fixes
38 changes: 38 additions & 0 deletions plugins/rfspec/skills/rfspec/references/evaluation-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Evaluation Guide

How to compare and evaluate competing model responses.

## Comparing Options

When results come back from `/rfspec`:

1. **Read all options** before responding. Understand each approach fully.
2. **Identify meaningful differences** -- not cosmetic ones. Focus on:
- Architectural choices (patterns, libraries, data flow)
- Scope differences (what each included or excluded)
- Risk areas (where one flagged something the others missed)
- Concrete vs. vague (which named actual files, functions, steps)
3. **Write a brief comparison** -- 2-4 sentences per option covering strengths and gaps. Compare, don't summarize.
4. **Present the choice** using AskUser:
- Use Option A as-is
- Use Option B as-is
- Use Option C as-is
- Synthesize a refined version combining the best of all three
- None of these work

## Synthesizing

If the user picks synthesis:

1. Start from the strongest option as the base.
2. Pull in specific elements from the others -- name what you're taking and why.
3. Resolve contradictions (don't concatenate).
4. The final result should read as a single coherent document, not a patchwork.

## Handling Rejection

If the user rejects all options:

1. Ask what's missing or wrong.
2. Incorporate their feedback into a refined prompt.
3. Offer to re-run `/rfspec` with the updated prompt.
37 changes: 37 additions & 0 deletions plugins/rfspec/skills/rfspec/references/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Troubleshooting

## All three models failed

```
Error: All three models failed. Check that your droid CLI is authenticated...
```

**Cause:** droid CLI not authenticated or models unavailable.
**Solution:** Run `droid` interactively to verify auth, then retry.

## jq not installed

```
Error: jq is required but not installed.
```

**Cause:** jq not on PATH.
**Solution:** `brew install jq` (macOS) or `apt-get install jq` (Linux).

## One or two models failed

```
Note: The following models encountered errors: Opus 4.6
```

**Cause:** Specific model unavailable or rate-limited.
**Solution:** This is handled gracefully -- compare the options that did return. No action needed unless the failed model was critical.

## Command not found

```
/rfspec: command not found
```

**Cause:** Plugin not installed.
**Solution:** Install via `/plugins` UI or `droid plugin install rfspec@factory-plugins --scope user`.
93 changes: 93 additions & 0 deletions plugins/rfspec/skills/rfspec/scripts/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -euo pipefail

# ── guard: dependencies ──────────────────────────────────────────────
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required but not installed. Install it with: brew install jq"; exit 1; }
command -v droid >/dev/null 2>&1 || { echo "Error: droid CLI is required but not found on PATH."; exit 1; }

PROMPT="$*"

if [ -z "$PROMPT" ]; then
echo "Usage: /rfspec <your prompt>"
echo ""
echo "Sends your prompt to three models in parallel (Opus, GPT, Gemini),"
echo "then lets you pick the best spec or synthesize a combination."
exit 1
fi

# ── prompt ────────────────────────────────────────────────────────────
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

echo "$PROMPT" > "$TMPDIR/prompt.md"

# ── models (id, label, max reasoning) ────────────────────────────────
MODEL_A="claude-opus-4-6"; LABEL_A="Opus 4.6"; RE_A="max"
MODEL_B="gpt-5.4"; LABEL_B="GPT-5.4"; RE_B="xhigh"
MODEL_C="gemini-3.1-pro-preview"; LABEL_C="Gemini 3.1 Pro"; RE_C="high"

# ── fire all three in parallel ───────────────────────────────────────
droid exec -m "$MODEL_A" -r "$RE_A" --auto medium -f "$TMPDIR/prompt.md" -o json 2>/dev/null > "$TMPDIR/a.json" &
PID_A=$!
droid exec -m "$MODEL_B" -r "$RE_B" --auto medium -f "$TMPDIR/prompt.md" -o json 2>/dev/null > "$TMPDIR/b.json" &
PID_B=$!
droid exec -m "$MODEL_C" -r "$RE_C" --auto medium -f "$TMPDIR/prompt.md" -o json 2>/dev/null > "$TMPDIR/c.json" &
PID_C=$!

FAIL=""
wait $PID_A 2>/dev/null || FAIL="${FAIL}${LABEL_A} "
wait $PID_B 2>/dev/null || FAIL="${FAIL}${LABEL_B} "
wait $PID_C 2>/dev/null || FAIL="${FAIL}${LABEL_C} "

# ── extract results ──────────────────────────────────────────────────
extract() {
local file="$1"
if [ -s "$file" ]; then
jq -r '.result // empty' "$file" 2>/dev/null || cat "$file"
fi
}

RESULT_A=$(extract "$TMPDIR/a.json")
RESULT_B=$(extract "$TMPDIR/b.json")
RESULT_C=$(extract "$TMPDIR/c.json")

# ── present results ──────────────────────────────────────────────────
echo "=== RFSPEC RESULTS ==="
echo ""
echo "User request: ${PROMPT}"
echo ""

[ -n "$RESULT_A" ] && printf '### Option A -- %s\n\n%s\n\n' "$LABEL_A" "$RESULT_A"
[ -n "$RESULT_B" ] && printf '### Option B -- %s\n\n%s\n\n' "$LABEL_B" "$RESULT_B"
[ -n "$RESULT_C" ] && printf '### Option C -- %s\n\n%s\n\n' "$LABEL_C" "$RESULT_C"

if [ -n "$FAIL" ]; then
echo "Note: The following models encountered errors: ${FAIL}"
echo ""
fi

SUCCESS=0
[ -n "$RESULT_A" ] && SUCCESS=$((SUCCESS + 1))
[ -n "$RESULT_B" ] && SUCCESS=$((SUCCESS + 1))
[ -n "$RESULT_C" ] && SUCCESS=$((SUCCESS + 1))

if [ "$SUCCESS" -eq 0 ]; then
echo "Error: All three models failed. Check that your droid CLI is authenticated"
echo "and the models (${MODEL_A}, ${MODEL_B}, ${MODEL_C}) are available."
exit 1
fi

echo "=== AGENT INSTRUCTIONS ==="
echo "Analyze the specs above. Provide a brief comparison of each model's"
echo "strengths and weaknesses. Then use the AskUser tool to offer:"
echo "- Use Option A (${LABEL_A}) as-is"
echo "- Use Option B (${LABEL_B}) as-is"
echo "- Use Option C (${LABEL_C}) as-is"
echo "- Synthesize a refined spec combining the best of all three"
echo "- No -- none of these work (explain why)"
echo ""
echo "CRITICAL: Do NOT save the spec directly. After the user picks an option"
echo "or requests synthesis, use the ExitSpecMode tool to present the final"
echo "spec content for review. Only save to specs/active/YYYY-MM-DD-<slug>.md"
echo "AFTER the user approves the spec in spec mode. If rejected, gather"
echo "feedback and revise."