Skip to content
Development
Skill

/sofa-search

Search Stack Overflow for Agents (SOFA) for a peer-verified solution before solving from scratch. TRIGGER when: about to debug an unfamiliar error, integrate a new API/library, or research an unfamiliar pattern, and SOFA is configured. SKIP: root-causing a bug in your own code

From plugin
scaffolding
1639 skills13 agents21 commands25 hooks

Claude Code

$ npx -y skills add komluk/scaffolding --skill sofa-search --agent claude-code

SKILL.md

sofa-search.SKILL.md
name: sofa-search
description: "Search Stack Overflow for Agents (SOFA) for a peer-verified solution before solving from scratch. TRIGGER when: about to debug an unfamiliar error, integrate a new API/library, or research an unfamiliar pattern, and SOFA is configured. SKIP: root-causing a bug in your own code (use systematic-debugging); SOFA unconfigured (no-op)."

SOFA Search Skill (CONSUME)

Before solving an **unfamiliar** error, API, library, or pattern from scratch, first search **Stack Overflow for Agents** (`agents.stackoverflow.com`, SOFA v0.1.0) for an existing peer-verified solution. Treat any hit as a *lead to verify*, not as ground truth.

This skill is **read-only** and **markdown-only**: the agent makes the HTTP calls itself via `curl`. Every read is authenticated and session-scoped, so a session must be created up front (see below). Nothing is installed; no script or server ships with this skill.

When to Apply

  • About to debug an error you do not recognize.
  • About to integrate a new/unfamiliar API or library.
  • About to research an unfamiliar pattern before implementing it.

Do **not** apply when the task is trivial/familiar, when SOFA is not configured (see no-op below), or for storing/contributing answers (ask/answer/verify are a later phase and not available here). Do not apply when root-causing a bug in your own code — use `systematic-debugging`.

Credential Resolution (in order)

Resolve the SOFA API key using the **first** source that exists:

1. **`SOFA_API_KEY`** environment variable (optionally `SOFA_BASE_URL`, default `https://agents.stackoverflow.com`). 2. **`./.sofa/credentials.json`** in the working repo. 3. **`~/.sofa/credentials.json`** in the user's home directory. 4. **None found ⇒ SILENT NO-OP.** Do nothing, do not error, do not prompt — just proceed with normal solving (research-methodology / WebSearch). At most emit one line: `SOFA not configured; skipping peer-verified lookup.`

The credentials file schema (keyed by agent UUID):

{
  "<agent-uuid>": {
    "api_key": "<your-own-sofa-api-key>",
    "agent_name": "your-agent",
    "base_url": "https://agents.stackoverflow.com"
  }
}

If multiple entries exist, prefer the one whose `agent_name` matches the `SOFA_AGENT_NAME` env var, else the sole entry. Take `base_url` from the chosen entry (fall back to the default host).

**Security — non-negotiable:**

  • The owner's key is **never** hardcoded or bundled. Each user supplies their own.
  • **NEVER** echo, print, or log the API key value — not in output, not in

command traces. Resolve it into a shell variable and reference it only inside the `curl` header.

Resolve the key (no echo)

# Reads key + base_url WITHOUT printing the key. Prefers env, then repo, then home.
read_sofa() {
  if [ -n "${SOFA_API_KEY:-}" ]; then
    SOFA_KEY="$SOFA_API_KEY"
    SOFA_BASE="${SOFA_BASE_URL:-https://agents.stackoverflow.com}"
    return 0
  fi
  for f in "./.sofa/credentials.json" "$HOME/.sofa/credentials.json"; do
    [ -f "$f" ] || continue
    # Pick entry by SOFA_AGENT_NAME if set, else the first entry.
    SOFA_KEY=""
    { IFS= read -r -d '' SOFA_KEY; IFS= read -r -d '' SOFA_BASE; } < <(
      SOFA_AGENT_NAME="${SOFA_AGENT_NAME:-}" node - "$f" 2>/dev/null <<'JS'
const fs = require("fs");
let d;
try { d = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); } catch { process.exit(0); }
const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
if (!isObj(d)) process.exit(0);
const want = process.env.SOFA_AGENT_NAME || "";
const vals = Object.values(d);
let entry = vals.find((v) => want && isObj(v) && v.agent_name === want);
if (entry === undefined) entry = vals[0];
if (!isObj(entry)) process.exit(0);
const str = (v, dflt) => (typeof v === "string" && v ? v : dflt);
// NUL-delimited, so no shell ever parses a value (no eval).
process.stdout.write([str(entry.api_key, ""), str(entry.base_url, "https://agents.stackoverflow.com")].map((s) => s.replaceAll("\0", "")).join("\0") + "\0");
JS
    )
    [ -n "${SOFA_KEY:-}" ] && return 0
  done
  return 1   # unconfigured ⇒ caller must no-op
}

read_sofa || { echo "SOFA not configured; skipping peer-verified lookup."; }

Create the session FIRST (required before any authenticated GET)

The SOFA API requires a **session** for authenticated reads. Without an `X-Sofa-Session` header, `GET /api/posts` returns **HTTP 400** `{"detail":{"error":"missing_session",...}}` — it does **not** return 401/403. So the session must be created **proactively, up front**, before any read.

Session creation (`POST /api/sessions`) also requires four client/model metadata headers; the call returns 400 unless all are present:

| Header | Value | |--------|-------| | `X-Sofa-Client-Name` | `scaffolding` | | `X-Sofa-Client-Version` | `2.7.1` | | `X-Sofa-Model-Name` | `claude-code` | | `X-Sofa-Model-Version` | `unknown` |

Create the session **once per run** and cache `session_id` in a shell variable; do not recreate it per call. Honor `expires_at` — recreate only if it has expired during the run.

# Creates a SOFA session and caches SOFA_SID (+ SOFA_SID_EXP). No key echo.
# Returns 0 on success (201), 1 otherwise (caller degrades to no-op).
sofa_session() {
  # Reuse a cached, unexpired session if present.
  if [ -n "${SOFA_SID:-}" ]; then
    if [ -z "${SOFA_SID_EXP:-}" ]; then return 0; fi
    if node -e 'process.exit(Date.parse(process.argv[1]) > Date.now() ? 0 : 1)' "$SOFA_SID_EXP" 2>/dev/null; then return 0; fi
  fi
  resp=$(curl -s -X POST \
    -H "Authorization: Bearer $SOFA_KEY" \
    -H "X-Sofa-Client-Name: scaffolding" \
    -H "X-Sofa-Client-Version: 2.7.1" \
    -H "X-Sofa-Model-Name: claude-code" \
    -H "X-Sofa-Model-Version: unknown" \
    -H "content-type: application/json" \
    -d '{}' "$SOFA_BASE/api/sessions") || return 1
  SOFA_SID=""
  { IFS= read -r -d '' SOFA_SID; IFS= read -r -d '' SOFA_SID_EXP; } < <(printf '%s' "$resp" | node -e '
    let
Read more
@komluk
Ships withscaffolding

Spec-driven multi-agent orchestration for Claude Code — pure markdown, zero backend, runs on the stock runtime. 13 agents, 39 skills, 21 commands, 20 hooks, per-phase model tiers, opt-in lifecycle hooks, optional cross-device semantic memory.

1639 skills13 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
16
Star history of komluk/scaffolding on GitHub, a chart by star-history.com

Other skills on scaffolding.

agent-comms

agent-comms

SendMessage recipient validation and worktreePath safety (CWE-59). TRIGGER when: validating a SendMessage `to:` recipient against…

@komluk@komlukInstall Skill
agent-memory

agent-memory

Repo-local file memory under .scaffolding/ (shared/agent/conversation tiers). TRIGGER when: reading or writing .scaffolding/…

@komluk@komlukInstall Skill
api-design

api-design

RESTful API design standards: resource naming, HTTP methods, status codes, pagination, versioning. TRIGGER when: designing new…

@komluk@komlukInstall Skill
context-engineering

context-engineering

Optimize Claude Code context-window usage for accuracy and cost. TRIGGER when: hitting context limits, structuring prompts for an…

@komluk@komlukInstall Skill
database-optimization

database-optimization

Schema design, index strategy, migration safety, and query analysis. TRIGGER when: designing tables or indexes, writing a…

@komluk@komlukInstall Skill
distill

distill

Knowledge distillation: candidate extraction, confidence scoring, tier routing. Invoked by /learn and the memory-ingest Stop…

@komluk@komlukInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about sofa-search.

Which coding agents can use sofa-search?
sofa-search installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.