A SvelteKit web app that generates XLSForm survey files. Uses @ax-llm/ax (TypeScript DSPy) with the qwac question bank to generate validated survey instruments.
Also ships as a Claude Code skill for terminal/IDE use — see Skills.
- Bun (package manager and runtime)
- Node.js >= 20
- An OpenRouter API key
bun install
cp .env.example .env
# fill in OPENROUTER_API_KEY| Variable | Required | Description |
|---|---|---|
OPENROUTER_API_KEY |
Yes (scripts only) | Used by scripts/initialize_agents.ts for agent optimization. The web app takes the key from the user at runtime. |
GITHUB_TOKEN |
No | Avoids GitHub API rate limits (60 req/h unauthenticated) when fetching CDL content snippets during build. |
QWACBACK_MCP_URL |
No | qwacback MCP endpoint. Defaults to https://qwacback.correlaid.org/mcp. |
QWACBACK_MCP_TOKEN |
No | Bearer token for qwacback MCP authentication, if required. |
bun run devbun run buildPreview the production build locally:
bun run previewDeployed via Coolify using nixpacks. The nixpacks.toml pins the Node and Bun versions used in the build container. The production server runs bun serve.js.
bun run test:workflowRuns three test cases (employee satisfaction, NGO member survey, impact measurement) through the full pipeline and saves outputs to scripts/test_output/ as questionnaire.xlsx + questions.json. Override the model with TEST_MODEL=<openrouter-slug>.
The survey generation logic is also packaged as a Claude Code skill for use in the Claude web app:
- Add the MCP connector — go to Customize → Add custom connector, enter
https://qwacback.correlaid.org/mcp - Install the skill — download
skills/xlsform.zip, then go to Customize → Skills → upload the zip file
Rebuild the skill with fresh reference data from qwac and civic-data.de:
bun run build:skillFormulAid ships in two forms — web app and Claude Code skill — that share the same prompts, reference data, and backend tools.
scripts/skill-content/generate-instructions.md is the single source of truth for the generation instruction:
- Imported via
?rawinsrc/lib/agents/survey_generator.ts→ used as theAxGensystem description - Copied by
build_skill.shtoskills/xlsform/generate-instructions.md→ referenced in SKILL.md Step 3
build_skill.sh fetches live data from civic-data.de and the qwacback API. The methodology is embedded into generate-instructions.md at build time (used as the AxGen system prompt in the web app; read directly by Claude Code in the skill):
| File | Skill | Web app |
|---|---|---|
generate-instructions.md |
read in Step 3 | ?raw import → AxGen system description |
references/demographic-templates.md |
read in Step 0 | not used (demographics hardcoded in constants.ts) |
Both the skill and the web app call the same qwacback MCP server to search for validated survey instruments before generating questions from scratch.
| Claude Code skill | Web app | |
|---|---|---|
| Transport | native qwacback:* tools via Claude Code MCP config |
src/lib/agents/qwacback.ts — custom stateful HTTP transport |
| Tools | search_questions, search_studies, get_question, list_questions |
same 4, auto-discovered via AxMCPClient.toFunction() |
| When | Step 2, explicit | during AxGen.forward(), LLM decides |
| Skill | Web app | |
|---|---|---|
| Intake | Conversational phases 1–3 | 3-section wizard form |
| XLSForm output | Claude Code writes .xlsx directly |
XLSFormGenerator builds it server-side |
| Demographics | fetched from qwac / demographic-templates.md |
hardcoded in src/lib/constants.ts |
See AI_DISCLOSURE.md.