{"id":51332316,"url":"https://github.com/sergiparpal/cambrian","last_synced_at":"2026-07-02T00:01:24.025Z","repository":{"id":364807495,"uuid":"1269099142","full_name":"sergiparpal/Cambrian","owner":"sergiparpal","description":"Domain-agnostic Claude Code plugin that turns any creative brief into a diverse, non-cliché slate of ideas—a Cambrian burst, not a regression to the mean. A local, server-less engine (MAP-Elites + geometric novelty + DPP) keeps the LLM from collapsing; you steer and select in chat. No extra API key needed.","archived":false,"fork":false,"pushed_at":"2026-07-01T19:50:23.000Z","size":560,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T21:26:24.533Z","etag":null,"topics":["brainstroming","claude-code","claude-code-plugin","creative-brief","creativity-tool","skill"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sergiparpal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T09:59:51.000Z","updated_at":"2026-07-01T19:57:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sergiparpal/Cambrian","commit_stats":null,"previous_names":["sergiparpal/creativity-amplifier","sergiparpal/cambrian"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/sergiparpal/Cambrian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiparpal%2FCambrian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiparpal%2FCambrian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiparpal%2FCambrian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiparpal%2FCambrian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergiparpal","download_url":"https://codeload.github.com/sergiparpal/Cambrian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiparpal%2FCambrian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35027341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["brainstroming","claude-code","claude-code-plugin","creative-brief","creativity-tool","skill"],"created_at":"2026-07-02T00:00:37.555Z","updated_at":"2026-07-02T00:01:24.018Z","avatar_url":"https://github.com/sergiparpal.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cambrian\n\n![cambrian-explosion.jpg](images/cambrian-explosion.jpg)\n\nA **domain-agnostic** Claude Code plugin that turns a creative brief in *any*\nsubject into a **diverse, non-cliché slate** of ideas, using a blind-variation →\ndiverse-archive → human-selection loop with you (the user) as the selector.\n\nIt ships as **one model-invoked skill** (`/cambrian:ideate`) plus a\nbundled, server-less **Python engine** that owns the anti-convergence math. The\nLLM parts — generating variations and the skeptical judge prefilter — are done by\nthe agent (Claude) itself, so **no extra chat-LLM API key is needed**.\n\n## Why it's different\n\n- **Diversity is decoupled from the judge.** Geometry owns *novelty* (embeddings,\n  MAP-Elites niching, k-NN novelty, DPP selection); the judge only filters\n  *validity / on-brief* and ranks *within* a niche. Its fitness has just a\n  **bounded, low-weight** say in the DPP slate — quality-weighted diversity\n  (weight 0.3, fitness clipped to a [0.7, 1.3] multiplier) — so it can never\n  prune variety or collapse the slate's diversity, and **you** remain the\n  final selector.\n- **A different embedding family from the agent.** The default embedder is\n  `model2vec` (`minishlab/potion-multilingual-128M`, CPU) — a different model\n  family from Claude, **multilingual (101 languages)**, and torch-free — so\n  \"what's novel\" isn't judged by the same lineage that generated the ideas, in\n  any language. A higher-fidelity English-only `BAAI/bge-small-en-v1.5` embedder\n  is available as an opt-in.\n- **Anti-cliché generation, measured honestly.** Before generating, Claude maps\n  the **~6 most obvious answers** to *your* brief and deliberately generates\n  *away* from them (a recipe re-derived per brief, not a fixed list). Originality\n  is then reported advisorily as an idea's distance to a **held-out** half of that\n  obvious set — so you're never scored against the very clichés you steered around.\n  It **hedges** cliché rather than guaranteeing world-novelty, and it is\n  **measurement only**: the cliché signal never feeds the selection geometry, so\n  it can't prune variety.\n- **Mechanism-first generation, novelty measured where it counts.** Because the\n  engine niches ideas on their *mechanism* (the open \"how it works\" axis), Claude\n  commits to a **distinct mechanism before writing each surface idea** — so variety\n  is pursued in approach-space, not just wording. To keep that honest, the engine\n  also reports **novelty in mechanism space** (the same k-NN novelty, computed on\n  each idea's mechanism embedding against the session's accumulated mechanisms)\n  alongside the surface novelty — and an opt-in **surface/mechanism gap** probe that\n  flags when wording diversity *overstates* approach diversity. Both are **advisory\n  measurement only**, never wired into selection.\n- **An anti-collapse monitor that's never bypassed.** Shannon entropy over niche\n  occupancy + mean pairwise cosine flag convergence; the similarity signal is\n  **calibrated to a rolling baseline** (and the dedup threshold is per-embedder),\n  so it doesn't misfire when the embedder or domain changes. When it fires, the\n  skill raises diversity pressure next round. Two **advisory** sensors back it up\n  without ever touching its verdict or the selection geometry: a **prefilter\n  guard** that flags when the agent submits too few candidates (possible\n  over-prefiltering), and a **variety-erosion** sensor that flags when survivor\n  novelty starts *decaying faster over time* — the signature of a generator\n  quietly regressing to the mode. Both only nudge the skill to widen the search.\n- **Axes resolved per session.** \"Domain-agnostic\" doesn't remove the need for\n  descriptor axes — it resolves them per session (named domain → inferred \u0026\n  confirmed → generic fallback). Nothing about a domain is baked into the plugin.\n\n## Install (one command)\n\nRequirements: Claude Code (latest), Python 3.11+ on your PATH. Works the same in the\n**CLI** and the **desktop app**, on **Windows (incl. WSL), macOS, and Linux**.\n\nIn Claude Code, add this repo as a plugin marketplace and install the plugin:\n\n```\n/plugin marketplace add sergiparpal/Cambrian\n/plugin install cambrian@sergiparpal\n```\n\n**Updating:** grab the latest version anytime with:\n\n```\nclaude plugin update cambrian@sergiparpal\n```\n\nThat's it — no clone, no `setup.sh`, no `--plugin-dir`. On the next session start, the\nplugin **provisions its own Python engine in the background**: it builds a virtualenv\nand installs the default stack (numpy, scikit-learn, and the multilingual\n`model2vec` embedder, `minishlab/potion-multilingual-128M`). The embedder weights are\n**~120 MB** and need **only numpy at inference — no PyTorch**, so the first-run download\nis small and fast, and the whole build runs **non-blocking** — Claude Code stays usable throughout.\nThe venv is stored in the plugin's persistent data directory, so it **survives plugin\nupdates**.\n\nIf `uv` is on your PATH it's used for a faster install; otherwise the bundled\n`python -m venv` + `pip` path is used. Nothing is auto-installed onto your system\nbeyond that venv.\n\nThen invoke the skill with a brief in ANY subject:\n\n```\n/cambrian:ideate names for a privacy-first calendar app\n/cambrian:ideate research hypotheses for why week-2 retention dropped\n```\n\nIf you run `/cambrian:ideate` before that one-time setup finishes, the skill says it's\n**setting up the engine** and continues on its own once it's ready — you never run a\nsetup step yourself. (If Python 3.11+ isn't found, it says so, with a fix.)\n\n## What happens when you run it\n\nYou don't need any of the math below to use the plugin — a session is just a\nback-and-forth chat, and **you are the one choosing the ideas**. Here is what each\nstep looks like and what you do.\n\n1. **You give a brief.** Run `/cambrian:ideate \u003cyour brief\u003e` for whatever\n   you want ideas about — product names, campaign angles, plot twists, research\n   hypotheses, anything. That's the only command you have to remember.\n2. **Claude confirms the \"angles\" — one quick question.** Before generating, Claude\n   proposes a handful of *axes*: the dimensions it will deliberately spread ideas\n   across (for names, say: tone, imagery, length, and how the name is built). It asks\n   **one** short question to confirm them. Just reply \"ok\", or tweak in plain words —\n   \"make it edgier\", \"drop the length one\". *(If you named a known domain it skips\n   straight ahead.)*\n3. **Claude shows you a varied slate.** Claude drafts a batch of ideas, quietly drops\n   any that are off-brief or don't make sense, and the engine picks a few that are as\n   *different from one another* as possible — not just the \"best\" ones. Each idea comes\n   with a short note on why it counts as distinct, so you can see the spread. **You\n   read them over.**\n4. **You steer — pin your favorites, and answer a couple of quick comparisons.**\n   Claude asks only the few most useful **A-vs-B** questions — \"which points in a\n   better direction, A or B?\" (you can also say \"neither\"). But the main lever is\n   **pinning**: tell Claude to pin *any* idea you like as a \"stepping stone\" to keep\n   exploring from — **including ones it didn't ask you about**. If a slate idea you\n   love isn't one of the two being compared, just say \"pin that one\" — a pin is the\n   strong, lasting signal (the comparisons only fine-tune the ranking), so your\n   favorites are never lost just because they weren't in a question. The opposite lever\n   is there too: say \"drop that one\" to **discard** ideas you don't want — they\n   disappear from later slates and are never built on again (re-pin to undo). Pins and\n   discards both persist for next time.\n5. **Claude runs another round, building on your picks.** Using what you preferred and\n   pinned, Claude generates a fresh batch — pushing for ideas that are genuinely *new*,\n   not variations on the same theme. If things start looking samey, a built-in monitor\n   notices and forces more variety the next round.\n6. **Repeat until you're happy, then stop.** Loop as many rounds as you like; say\n   \"stop\" (or \"that's enough\") when you have what you need. Your preferences are\n   remembered for the next time you ideate in the same kind of domain.\n\nIn short: **you give a brief, confirm the angles once, then react to slates and pin\nfavorites while Claude keeps widening the search** until you're satisfied. The rest of\nthis README is for people who want to develop on it or understand the internals.\n\n## Local development (fallback)\n\nTo hack on the plugin from a checkout instead of installing it:\n\n```bash\n# 1. Build the engine venv (Windows / macOS / Linux)\npython3 skills/ideate/scripts/bootstrap.py     # Windows: python ... or py ...\n#    or, equivalently:  bash skills/ideate/scripts/setup.sh\n\n# 2. Load the plugin in Claude Code without installing it\nclaude --plugin-dir .\n\n# 3. Invoke the skill\n/cambrian:ideate names for a privacy-first calendar app\n```\n\nIn this mode the venv lives at `skills/ideate/.venv/` and the engine is installed\neditable, so source edits take effect without a rebuild. Validate the plugin at any\ntime:\n\n```bash\nclaude plugin validate .          # or: claude plugin validate --strict .\n```\n\n### The one configuration choice (embedding provider)\n\nBy default the engine uses the **static** `model2vec` embedder\n(`minishlab/potion-multilingual-128M`) — no API key, CPU-only, **multilingual**,\ntorch-free (~120 MB), downloaded once on first use. Select a different provider with\nthe `CAMBRIAN_EMBEDDER` environment variable before launching Claude Code:\n\n```bash\nexport CAMBRIAN_EMBEDDER=local   # static | local | hash | api\n```\n\n- **`static`** (default) — `potion-multilingual-128M`, 256-dim, 101 languages,\n  numpy-only inference.\n- **`local`** — higher-fidelity **English-only** `BAAI/bge-small-en-v1.5` (384-dim).\n  Pulls the ~2 GB PyTorch stack, so it's **opt-in**: install it with\n  `pip install -r skills/ideate/scripts/requirements-local.txt`.\n- **`hash`** — deterministic, dependency-light char-n-gram embedder used by the tests\n  and the offline self-test (no model download).\n- **`api`** — an **extension point** for a hosted provider (Voyage/OpenAI/Cohere). It\n  is a stub: constructing it is cheap, but embedding raises until you wire a backend\n  into `embed.py`.\n\n\u003e **Switching the default is breaking for existing projects.** `static` is 256-dim and\n\u003e `local` is 384-dim; the engine refuses to mix embedding widths within one project. A\n\u003e project created/persisted under one embedder can't be re-ingested under another —\n\u003e start a **new** project, or pin the original embedder (e.g.\n\u003e `export CAMBRIAN_EMBEDDER=local`) and re-embed if you must switch.\n\n## How a session works (under the hood)\n\nThe plain-language walkthrough is in [**What happens when you run it**](#what-happens-when-you-run-it)\nabove; this is the same loop with the internals. The skill follows\n`skills/ideate/references/loop.md`. One cycle:\n\n1. **Resolve axes.** If you name a domain with a config in\n   `skills/ideate/config/domains/examples/`, it's loaded. Otherwise the agent\n   **infers 4–6 axes** from your brief (marking one `open` axis as the primary\n   novelty carrier) and confirms them with **one short question**. If it can't,\n   it falls back to `config/domains/generic.yaml`.\n2. **Generate (agent).** Claude first maps the brief's ~6 most obvious answers,\n   then applies several variation operators (`references/operators.md`) to draft\n   candidates that deliberately steer *away* from those clichés. Generation is\n   **mechanism-first**: Claude commits to a distinct `mechanism` (the open novelty\n   axis the engine niches on) *before* writing each surface idea. Every candidate\n   carries a descriptor on the resolved axes and genealogy.\n3. **Prefilter (agent).** Claude applies `references/judge_rubric.md` to drop only\n   invalid / off-brief candidates — never to cut variety.\n4. **Ingest (engine).** Survivors are embedded, deduped, placed into MAP-Elites\n   niches over the resolved axes (the open \"mechanism\" axis uses a **data-adaptive\n   partition** — deterministic cold-start cells that fit once via k-means and then\n   freeze, so niche ids stay stable), scored for novelty, kept one-elite-per-niche,\n   and a **DPP** picks a quality-weighted diverse slate (geometry dominates; the\n   judge's bounded fitness only nudges ordering). The **anti-collapse monitor** runs,\n   plus the two **advisory** sensors (prefilter guard + variety erosion) that ask the\n   skill to widen the search without ever influencing selection. Two more **advisory\n   measurements** ride along: **mechanism-space novelty** (the surface k-NN novelty,\n   recomputed on the mechanism axis) and an opt-in **surface/mechanism gap** probe\n   (`engine.gap_probe`, default off) that quantifies whether wording diversity\n   overstates approach diversity — both reported, never wired into selection.\n5. **Select (you).** Claude shows the slate with each idea's niche coordinates,\n   asks only the most-informative A-vs-B pairs, and **explicitly invites you to pin\n   any idea — not just the ones it asked about**. A pin is the strong, durable\n   preference signal (always kept as a parent for the next generation, recalled\n   across sessions); the A-vs-B answers only refine a bounded, low-weight `fitness`\n   that can never prune variety or pick the slate. It also invites you to **discard**\n   ideas — the negative of a pin: a human veto that drops the idea from future slates\n   and parents and persists across sessions (mutually exclusive with a pin of the same\n   id, latest action wins). Discarding is filter-only — it never feeds novelty, DPP, or\n   the monitor. *(The pair policy is tunable: by\n   default it favors **similar**, boundary-clarifying pairs to learn your preference;\n   an opt-in `explore_until_generation` schedule asks **region-separating** pairs in\n   the first few generations, then switches to refine.)*\n6. **Remember \u0026 loop.** Choices/pins/discards go to local preference memory (namespaced\n   per domain); diverse parents seed the next generation (pins kept, discards excluded).\n\n## Adding a domain template\n\nDomain templates are optional conveniences — the skill works without them. To add\none, drop a YAML file in `skills/ideate/config/domains/examples/` following\n`skills/ideate/config/domains/_schema.md`:\n\n```yaml\ndomain: naming\nunit_of_generation: name\naxes:\n  - {name: tone, type: categorical}\n  - {name: imagery, type: categorical}\n  - {name: length, type: continuous, range: [1, 4]}\n  - {name: construction, type: open, primary_novelty: true}\njudge_rubric: references/judge_rubric.md\nslate_size: 6\ncandidates_per_generation: 12\n```\n\nThen a user who says \"name ideas (naming)\" gets these axes; everyone else gets\ninferred-or-generic axes. See `references/axis_inference.md` for how inference\nworks. A template may also carry an optional `engine:` block to override tuning\nknobs (open-niche count, dedup τ, quality weight, monitor thresholds,\nvariety-erosion window/persistence, ask-pair policy \u0026 `explore_until_generation`\nschedule, the opt-in `gap_probe`, …) per domain — defaults reproduce the standard\nbehavior; see `_schema.md` for the keys.\n\n## The engine CLI (for the curious / for tests)\n\n```\npython -m cambrian_engine \u003ccommand\u003e --project \u003cid\u003e [--axes axes.json] [--seed N]\n```\n\n| Command | Does |\n| :-- | :-- |\n| `init-project` | create state dirs, snapshot the resolved axes + session settings |\n| `paths` | ensure the project state dir (incl. its `tmp/` scratch dir) + return resolved paths |\n| `recall` | return preference memory for in-context injection |\n| `ingest` | embed → dedup → place → novelty → archive → DPP → monitor |\n| `remember` | append a comparison/pin/discard to preference memory |\n| `parents` | diverse parents for the next generation (pins always kept, discards excluded) |\n| `metrics` | archive health (entropy, mean cosine, coverage, n) + mechanism spread + open-axis freeze progress |\n| `selftest` | full loop with a stubbed LLM + human; variety gate + collapse reversal |\n\nRuntime state is written **outside** the plugin (so reinstalls don't wipe it):\n`~/.cambrian/\u003cproject\u003e/...`, preferences namespaced per domain.\nOverride the base directory with `CAMBRIAN_HOME`.\n\n## Running the self-test \u0026 the suite\n\n```bash\n# offline end-to-end check (stubbed LLM + human, no model download); exits 0\nskills/ideate/.venv/bin/python -m cambrian_engine selftest\n\n# unit / property / e2e tests\nskills/ideate/.venv/bin/python -m pytest -q\n```\n\nThe self-test enforces a **variety gate** — the engine's diverse slate must beat a\nsingle-shot baseline on mean pairwise distance, Vendi score, and niche entropy,\nand DPP must beat naive first-N selection on the same pool (**shuffled** so\nfirst-N isn't trivially the near-clones, and **averaged over several seeds**),\nwith a **null check** that DPP doesn't regress below a random subset on an\nalready-uniform pool — plus an **induced-collapse reversal** (a samey generation\ntrips the monitor; the next generation recovers once diversity pressure rises).\nA `--live` run adds a semantic sanity check (a paraphrase beats an unrelated\nsentence under the real default `static` embedder), skipped cleanly when that\nembedder can't be built or downloaded.\n\n## Layout\n\n```\nCambrian/                  # plugin root\n├── .claude-plugin/\n│   ├── plugin.json                    # manifest\n│   └── marketplace.json               # marketplace entry (for /plugin marketplace add)\n├── hooks/\n│   ├── hooks.json                     # SessionStart hook: auto-provision the venv\n│   ├── provision.mjs                  # Node dispatcher: picks the matching OS launcher\n│   ├── provision.sh                   # POSIX launcher (sh / Git Bash / WSL)\n│   └── provision.ps1                  # Windows-PowerShell launcher\n├── skills/ideate/\n│   ├── SKILL.md                       # model-invoked orchestration (concise)\n│   ├── references/                    # loop, operators, judge rubric, axis inference\n│   ├── config/domains/                # _schema.md, generic.yaml, examples/*.yaml\n│   └── scripts/\n│       ├── bootstrap.py               # cross-platform self-provisioning installer\n│       ├── setup.sh, pyproject.toml   # dev setup wrapper; package metadata (deps in requirements*.txt)\n│       ├── requirements.txt           # runtime deps (version-bounded)\n│       ├── requirements-dev.txt       # + pytest (dev/CI); requirements-local.txt = opt-in torch embedder\n│       └── cambrian_engine/         # the Python engine (CLI)\n├── tests/                             # pytest (dev-only)\n├── docs/PAPER.md                      # reference-architecture paper (rationale)\n└── README.md\n```\n\n## Background\n\nThe design rationale — why diversity is owned by geometry while the judge's say in the slate stays\nbounded, and how this maps onto blind-variation/selective-retention, Quality-Diversity (MAP-Elites,\nnovelty search), and DPP selection — is written up in [`docs/PAPER.md`](docs/PAPER.md).\n\n## License\n\nMIT — see `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiparpal%2Fcambrian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergiparpal%2Fcambrian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiparpal%2Fcambrian/lists"}