A code-generation and auto-test tool. Describe a task in plain English and it writes the code and its own tests, runs them, and repairs the code when they fail -- steered by a persona and skill set selected from a 528-agent, 1034-skill knowledge graph.
Runs on Groq's free API (console.groq.com) with no card required. Every model is configuration, not code: see Model Stack.
What it actually does, end to end. From run "add a REST endpoint for creating an order" --lang java it produced a complete Maven project -- 10
source files plus an integration test -- compiled it, ran the tests, and when
they failed, diagnosed and fixed them on the second attempt. That is the whole
loop, not a demo of one step.
Scope, stated honestly. Single-user and local. It holds no server, no
multi-tenancy and no auth; the knowledge graph it reads is a separate
repository on disk. Running it costs model calls against whatever key is in
.env.
- Get a free Groq key at https://console.groq.com/keys (no card required).
- Copy
.env.exampleto.envand fill in your key:(On Windows PowerShell:GROQ_API_KEY=gsk_...$env:GROQ_API_KEY = "gsk_...") - Install:
pip install -e ".[dev]"
Every role is an ORDERED LIST of {provider, model} candidates in
models.yaml -- not a single hardcoded ID. At startup, and again on any
404/410/403 mid-session, the router walks each role's list and settles on
the first candidate whose model is still live on Groq's catalog. Add a
candidate, reorder preference, or edit the pins entirely by editing
models.yaml -- never the Python code.
| Role | Candidate chain | Purpose |
|---|---|---|
| Primary coder | openai/gpt-oss-120b → openai/gpt-oss-20b |
Writes code AND its own tests in one call |
| Fast router | openai/gpt-oss-20b → openai/gpt-oss-120b |
Language-detection + simple/complex classification only |
| Reasoner | qwen/qwen3.6-27b → openai/gpt-oss-120b |
Implementation planning (complex tasks) + self-heal failure diagnosis |
| Fallback | openai/gpt-oss-120b → openai/gpt-oss-20b |
Long-context backup |
Every candidate above was live-tested with a real chat completion call
(not just checked against /v1/models) and confirmed working -- gpt-oss-120b
answered in ~1.2s with clean, correct code; gpt-oss-20b in ~0.3s.
llama-3.1-8b-instant and llama-3.3-70b-versatile, though documented on
Groq's docs site, are NOT in this account's live catalog and were dropped
rather than left as dead candidates. If a pinned model ever disappears,
Vishwakarma falls to the role's next candidate automatically, and only
fails with a clear ConfigError (naming the role and how to fix
models.yaml) if every candidate for that role is gone.
Other providers. models.yaml also defines ollama, openai and an
anthropic-compatible block. None is wired to a role, so they cost nothing at
startup -- the router only walks providers a role actually names -- and pointing
a role at one is a config edit rather than a code change.
One of them needs its base URL read carefully, because getting it wrong fails on
the request shape rather than on the key, which is a confusing way to fail.
anthropic-compatible points at a local proxy rather than at
api.anthropic.com directly.
Neither openai nor anthropic-compatible has been live-tested from this
account. That caveat is not boilerplate: this configuration's own history is
two providers (NVIDIA NIM, Cerebras) whose advertised free tiers turned out not to
be entitled.
Ollama is the reason api_key_required: false exists. Availability was decided
purely by whether an API key env var was set, so a local Ollama -- which has no
key at all -- was unselectable no matter how it was configured, and "any LLM is
just configuration" was false for exactly the provider that runs without spend.
A keyless provider omits api_key_env entirely rather than naming a variable
that means nothing.
Neither has been live-tested from this account, and that matters here:
this repo's history records two providers whose advertised free tiers turned out
not to be entitled (NVIDIA NIM and Cerebras, both documented in models.yaml).
Treat them as correct starting points, not as verified access.
Gemini fallback. gemini is wired as the mid-chain fallback on
primary_coder and reasoner -- Groq first, then gemini, then
ollama-colab last -- deliberately not on router_fast for the same reason
ollama-colab isn't (see above), and because its own rpm_budget: 10 free-tier
ceiling is too tight for a frequent classification role regardless. It points
at /v1beta/openai/ -- Google's OpenAI-compatible layer, not its native
/v1beta API, which fails on the request shape rather than the key if you
get it wrong. gemini-3.6-flash is the pinned model, live-tested from this
account (#72) and confirmed working -- it wasn't the first choice tried:
gemini-2.5-pro and gemini-2.5-flash are both retired for new users (404,
pointing at their 3.x successors), and gemini-3.1-pro-preview resolves
but its error body is explicit: free-tier quota for that model is limit: 0,
not merely tight -- confirmed from an account with a paid Google Pro
subscription (an AI Studio key's quota tracks whether its backing Cloud
project has billing enabled, not a separate consumer subscription).
gemini-3.6-flash was the first candidate that actually
returned a response.
One tradeoff worth knowing before relying on it under load: once Groq is
exhausted for a session, the router stays pinned on gemini for the rest of
that session -- a plain rate limit does not advance past it (the same
protection that already exists for Groq, see models.yaml's gemini block),
so gemini's tight rpm_budget: 10 is shared across every subsequent
interactive and self-heal call rather than falling through to
ollama-colab's looser rpm_budget: 120. A gemini call that can't be
admitted in time hard-fails to the caller instead of silently degrading to
the local GPU. And because GEMINI_API_KEY is a real Google Cloud credential
(unlike Groq's no-card tier or ollama-colab's keyless local GPU), set a
billing/quota cap on the project backing it before leaning on this fallback.
Colab-hosted Ollama fallback. ollama-colab is wired as the last
candidate on primary_coder and reasoner -- deliberately not on
router_fast, because that role's small max_tokens budget is exactly where
an unrecognized reasoning-capable model burns its completion on an uncapped
<think> trace instead of answering (see models.yaml's xkiro block for
the same precedent). It runs Ollama on Colab's free GPU, reached over an
ngrok tunnel rather than local 127.0.0.1 (see colab/ ollama_colab_server.ipynb for the setup). ngrok, not a cloudflared quick
tunnel -- trycloudflare.com was tried first and consistently returned 403 Forbidden straight from Cloudflare's edge, reproduced across 3 fresh
tunnels and two protocols; live-verified working instead with ngrok. One
catch, also live-verified: Ollama has its own server-side Host-header check
(unrelated to OLLAMA_ORIGINS, which only governs browser CORS), so
ngrok.connect() must pass host_header="localhost:11434" or Ollama 403s
the tunnel's own public hostname. The tunnel's public URL is session-scoped
-- a Colab session disconnects after ~12h or on idle (and has been observed
to silently reset the whole backend, wiping the installed binary and pulled
models, while the notebook UI still shows "Connected") -- so it is read from
OLLAMA_COLAB_BASE_URL (set in .env, already gitignored) rather than
written into the git-tracked models.yaml; update that variable after every
Colab restart. A stale or unset URL is not a startup failure --
router.validate_startup treats an unreachable candidate as an ordinary
skip, same as any other candidate whose provider is down, and falls through
to the next one. deepseek-coder-v2:16b was chosen over the originally
requested deepseek-v4-pro, which turned out to resolve to Ollama's
cloud-hosted tier (401 Unauthorized without a separate ollama.com
sign-in) rather than a model that runs on this GPU; both
deepseek-coder-v2:16b and the qwen2.5-coder:7b fallback have been
live-tested end-to-end through the tunnel from this account.
Adaptive routing (opt-in). Set VISHWAKARMA_ADAPTIVE_ROUTING=1 to let
primary_coder/reasoner start on a different candidate per task instead of
always index 0: run_task's existing simple/complex signal picks the
starting point right after startup validation, never reordering or removing
a candidate, and never interfering with a genuine rate-limit hold (see
ADR-0014).
Unset (the default), routing is exactly as described above -- this is a
pure opt-in, not a behavior change.
Config-gated dormant candidate. A role candidate in models.yaml can
now carry an enable_env: key; it's excluded from that role's chain
entirely at config-load time unless the named variable is set.
openai/gpt-4o-mini is wired onto primary_coder this way behind
VISHWAKARMA_ENABLE_OPENAI, defaulting off and not live-tested from this
account -- OPENAI_API_KEY (and ANTHROPIC_API_KEY, XKIRO_API_KEY for
the other two dormant providers) is absent from .env here. Same caveat as
the untested providers above: treat it as a correct starting point, not
verified access, until someone with a real key exercises it.
Why Groq, not NVIDIA: this project originally ran on NVIDIA's free NIM
API, but hit two unresolved platform-side issues there: a 403 "Authorization failed" bug on Personal-org accounts (listing models worked,
chat completions didn't), and most of the catalog returning 404 "Function not found for account" (listed but never actually entitled). Groq's free
tier is an ongoing rate-limited tier (~30 RPM configured / 8K TPM measured / ~14.4K RPD), not a
burnable dollar credit, with no card required -- and every candidate above
was live-tested and confirmed working before being pinned here.
router_fastdetects the target language from the task text itself (python/java/web) -- there's no language dropdown; just say "in Java" or "a React component" if you want to be explicit.- The knowledge graph selects an agent for the engineered prompt and assembles a budgeted context from its skill closure -- see below.
router_fastclassifies the task as simple or complex (one cheap call). That answer no longer picks a code path: it prunes a phase graph. A complex task runsphase:A(architecture) andphase:2(joint blueprint validation) before implementation; a simple one prunes both, and the remaining phases are rewired onto what survives rather than left dangling.primary_codergenerates the code and its tests together, in one call, steered by the selected agent's persona when the graph is confident enough in the match. Both this call andphase:A's architecture call carry a boundeddispatch_agenttool (v1.2.0) when the resolved candidate'smodels.yamlentry declaressupports_tools: true-- if the model genuinely hits a gap outside its own expertise mid-task, it can consult ONE real specialist from the knowledge graph (the same selection the step above already runs), get back a text answer, and continue with its own judgment. Capped at one hop deep (a consulted specialist never gets the tool itself), one consult per specialist per task, and a hard token budget -- seeCHANGELOG.md's 1.2.0 entry andSRS.md's FR-006 for the full design. A candidate withoutsupports_tools: truenever receives the tool and behaves exactly as it always has.- The tests run in a subprocess appropriate to the target language.
- On failure, the self-heal loop asks
reasonerto diagnose the failure (given the actual test file and the history of prior failed attempts, so it won't repeat a fix that already didn't work), then asksprimary_coderto apply the fix. This repeats up to--max-attempts(default 3) or 5 minutes wall-clock, whichever comes first.
Every model call, provider fallback, and self-heal attempt is logged
(VISHWAKARMA_LOG_LEVEL=DEBUG for more detail) and streamed live to the CLI
console and the web UI's Activity panel, so you always see which
provider/model handled which step.
All knowledge comes from claude-global-library, read through kgf, the
knowledge-graph framework in this repo. Set KGF_LIBRARY_PATH to point at a
different library location, or leave it unset to use the sibling
../claude-global-library directory automatically.
Selection is graph-driven, not keyword-driven:
- An agent is ranked over the whole catalogue (1034 skills, 528 agents, 9146 edges), with a confidence and the edge path that justified it. A run prints both, so a surprising choice can be traced instead of guessed at.
- Its skill closure is expanded from the graph -- mandatory skills, their transitive requirements, math delegation, coordinating agents and regulations. The median agent needs 4 mandatory skills, not one.
- Context is assembled section by section within a token budget, and the
run records which sections were included and which were dropped. This
replaced a position-blind 800-character head cut of a single document: on
java-spring-boot-microservicesthat window ended at offset 796 while the coding guidelines began at 8476, so the part that mattered was never sent. - A persona is applied only when the graph is confident in the match. A low-confidence match still contributes its context, but does not steer the coder -- it is recorded and reported instead.
There is no local skill/agent loader any more. Dropping a
skills/{name}/SKILL.md into this repo does nothing; the directories it used
to read never existed in practice, and everything now comes from the graph.
To bias a run toward a particular skill, force it with --skill NAME, which
puts that skill at the front of the closure so it is first to earn context
budget.
Inspect the graph directly with the kgf CLI -- every command is offline, with
no API key and no model call:
kgf stats # counts, edge types, load time
kgf route "add a REST endpoint" --limit 3 # who would be selected, and why
kgf closure --agent spring-boot-microservices
kgf context "..." --budget 2000 --intent implement
kgf topology --validate # the 44-phase graph vs the library
kgf validate # 0 FATAL, plus every DEFECT found
A handful of library files with pre-existing YAML quirks are reported as defects rather than crashing the loader, and a selected document that cannot be parsed is dropped from the closure with a recorded defect so the run continues.
List everything available:
vishwakarma skills
vishwakarma agents
CLI (language is auto-detected -- --lang only if you want to force it):
vishwakarma run "write a function that reverses a linked list"
vishwakarma run "add a REST endpoint for creating an order" --skill spring-boot-api
vishwakarma run "build a login form component" --agent strict-tester
Flags: --lang NAME (force a stack instead of auto-detecting), --workdir PATH,
--no-heal, --max-attempts N, --rag --project-dir PATH (search an
existing project for lightweight keyword-based context), --skill NAME (put a
library skill at the front of the closure), --agent NAME (skip ranking).
vishwakarma sdlc full "<task>" runs the same generation/self-heal as run,
plus whichever of SRS, HLD, Jira sprint/epic, and git commit+push+PR you
opt into -- each stage is off by default, so sdlc full with no flags does
exactly what run does:
vishwakarma sdlc full "add password reset" --generate-srs --generate-hld
vishwakarma sdlc full "add password reset" --create-sprint --jira-project-key PROJ
vishwakarma sdlc full "add password reset" --push-pr --branch feat/password-reset
--push-pr commits, pushes, and opens the PR as one atomic step (it reuses
push_and_create_pr, which already does all three) -- there is no
commit-only mode. When both --create-sprint and --push-pr produce a
result in the same run, the created PR's URL is linked back to the Jira
Epic; a link failure is logged and does not fail the run (see
ADR-0013).
The standalone sdlc srs, sdlc hld, sdlc jira, and push/PR commands are
unchanged and still work independently.
kgf route and kgf context can write a run manifest: the library version and
per-registry content hashes, the agent selected with its confidence and edge
path, the skill closure, and every context section included or dropped with its
token count.
kgf context "add a REST endpoint for creating an order" --budget 2000 --manifest run.json
kgf replay run.json
Replay re-derives all of it from the library as it stands now and reports what changed -- a released version, an unreleased local edit (caught by content hash, which a version number cannot see), a different selected agent, a smaller context. It makes no model call, so it is cheap enough to run as a drift gate: exit 0 when nothing changed, 1 when something did, 2 when the manifest itself cannot be read.
Web app (shares the same engine as the CLI, Tailwind UI with a live Code / Preview / Tests workspace and a real-time Activity log):
uvicorn vishwakarma.webapp.server:app --reload
Then open http://127.0.0.1:8000 in a browser.
Source comments justify their design by citing architecture decision records --
"per ADR-2", "ADR-4's no-cache decision". Those records are in
docs/adr/, one file each, and every one states the accepted
cost rather than only the choice. Several were decided against a reviewer's
recommendation, and a decision without its price is an assertion rather than a
record.
Start with ADR-0002
(kgf imports nothing from vishwakarma, enforced by a subprocess test) and
ADR-0012 (absent evidence is
not success) -- the first explains the layout, the second explains why a run
that produced no tests is reported as a failure.
pytest
Runs fully offline against a mocked LLM client -- no API keys required, no
quota consumed. testpaths is pinned to tests/, so a bare pytest does not
sweep in vishwakarma-output/ and report on whatever the tool last generated.
- No git repository is created or expected for this project.
- The free Groq tier is for personal/prototype use, not production traffic.
- RAG here is a lightweight keyword/file-tree scan, not a vector database -- appropriate for a personal-sized codebase. Revisit only if the project grows large enough that keyword matching starts missing relevant files.