{
project_name: 'my-app',
project_type: 'python',
want_docs: true,
// ...only what you want to change
}
Starting a new project means recreating the same boilerplate every time
CI/CD workflows, linter configs, packaging, documentation scaffolding…
Keeping all of this in sync across projects is even harder
Manual updates are tedious, error-prone, and easy to forget
Popular project templating tool
One-shot generation — no mechanism for pulling template updates
Once generated, the project drifts from the template permanently
Mostly used for Python projects
Built on top of Cookiecutter to add updates via patching
Patch-based updates lead to merge conflicts with local changes
Sparse PyPI releases — it is common for two years or more to pass between published versions; that cadence is too slow if you want generator tooling to track a fast-moving packaging and CI ecosystem
Still inherits Cookiecutter’s Python-centric limitations
Templates are static — no programmable logic
No automatic dependency version resolution
No integration with GitHub/GitLab APIs
Limited to a single language ecosystem
A declarative, regeneration-based project generator
Multi-language: Python, TypeScript, C, C++, Lua, Xcode, generic layouts, and more
Programmable configuration with Jsonnet
Automatic latest version resolution from PyPI, npm, GitHub
Deep GitHub and GitLab integration
Create a .wiswa.jsonnet in your project with only the differences
from defaults:
{
project_name: 'my-app',
project_type: 'python',
want_docs: true,
// ...only what you want to change
}
Jsonnet is a superset of JSON with programmable features:
| Local variables | Conditionals |
|---|---|
|
|
|
Deep merge with |
Hidden fields with |
|
|
|
Imports and library functions |
|
|
Merge with 400+ built-in defaults from defaults.libsonnet (and your
own
[1]).
defaults.libsonnet + user defaults.jsonnet +
.wiswa.jsonnet
uses_user_defaults in
.wiswa.jsonnet — when true, merges shared personal
defaults.jsonnet from the user config directory.
Evaluate the merged configuration with native functions — version resolution, date helpers, URI generation, and more.
Settings map directly to output files. For example, package.json:
.wiswa.jsonnet |
package.json (generated)
|
|---|---|
|
|
Lock dependencies, then run the generated project’s format and QA scripts
(for example yarn format and yarn qa).
Configure your GitHub or GitLab repository via API — merge settings, branch protection, security scanning, and more.
.wiswa.jsonnetOnly specify what differs from defaults:
|
|
utils.libsonnet Library
Import it in any .wiswa.jsonnet to access built-in helper functions:
local utils = import 'utils.libsonnet';
utils.year(),
utils.isodate() — current year and ISO date
utils.manifestYaml(obj),
utils.manifestToml(obj) — serialisation helpers
utils.gitHubRepositoryUri(user, project) — generate common URIs
utils.pyprojectAuthors(authors) — transform
author metadata
utils.pyprojectClassifiers(settings) — generate
sorted PyPI classifiers
utils.formatPep508Deps(name, val) — convert
dependency specs to PEP 508
utils.poetryVerToPep508(ver) — translate
^/~ version syntax
The utils library also provides functions that fetch the latest versions at generation time:
local utils = import 'utils.libsonnet';
// PyPI (caret, tilde, >= prefixed, or bare)
utils.latestPypiPackageVersionCaret('click') // "^8.1.8"
utils.latestPypiPackageVersionTilde('django') // "~5.2.1"
utils.latestPypiPackageVersionGe('requests') // ">=2.32.3"
// npm
utils.latestNpmPackageVersion('typescript') // "5.7.3"
utils.latestNpmPackageVersionCaret('eslint') // "^9.20.0"
// GitHub releases and tags
utils.githubLatestReleaseTag('owner', 'repo') // "v2.4.1"
utils.githubLatestActionTag('actions', 'checkout') // "v4"
utils.githubLatestTag('owner', 'repo') // "v3.1.0"
// Yarn
utils.latestYarnVersion() // "4.6.0"
Make changes to .wiswa.jsonnet (or re-run to pick up new defaults)
Run wiswa to regenerate all managed files (set
uses_user_defaults: true to merge user-level
defaults.jsonnet)
Post-processing locks dependencies and runs yarn format,
yarn qa, and related steps
Review the git diff
Commit
Key insight: regeneration, not patching.
No merge conflicts. Managed files are always overwritten from templates and
settings. Post-processing still runs yarn format,
yarn qa, and similar; anything those scripts run with auto-fix
will change sources when fixes apply.
What if you change a managed file directly?
Example: uv add requests modifies pyproject.toml
The wiswa-sync agent reflects the change back into
.wiswa.jsonnet
Next regeneration preserves your addition
Configuration and files stay in sync in both directions
| Category | Examples |
|---|---|
|
Build & packaging |
|
|
CI/CD |
GitHub Actions workflows, GitLab CI, Dependabot config |
|
Code quality |
Ruff, ESLint, clang-format, pre-commit hooks, mypy |
|
Documentation |
Sphinx + ReadTheDocs, GitHub Pages, CONTRIBUTING, SECURITY |
|
IDE |
VS Code settings, launch configs, recommended extensions |
|
AI tooling |
|
|
Distribution |
AppImage, PyInstaller, Snap, Flatpak, winget |
|
Badges |
|
| Language | Highlights |
|---|---|
|
Python |
pyproject.toml, uv/Poetry, Ruff, mypy, pytest, Sphinx, PyPI publishing |
|
TypeScript |
package.json, Jest, ESLint, Prettier, npm publishing |
|
C / C++ |
CMake, CMakePresets, vcpkg, clang-format |
|
Lua |
luacheck, Busted tests, LuaRocks publishing |
|
Xcode ( |
clang-format, Xcode-oriented defaults |
|
Generic |
package.json, Prettier, basic CI |
Wiswa configures your repository via API:
Merge settings (squash, rebase, delete branch on merge)
Branch protection and commit signature requirements
Secret scanning and push protection
Pages deployment (GitHub Pages / GitLab Pages)
Dependabot security updates (GitHub only)
CodeQL security analysis (GitHub only)
Wiswa exposes its configuration system as an MCP server for AI integration.
claude mcp add wiswa-mcp -- wiswa-mcp
Four tools available:
get_defaults(key_path) — retrieve resolved
default settings
lookup_setting(key_path) — get a value with its
Jsonnet override snippet
list_settings(key_path, depth) — browse the
settings hierarchy
search_settings(query) — full-text search
across all settings
> lookup_setting("pyproject.tool.ruff.line-length")
{
"key_path": "pyproject.tool.ruff.line-length",
"default_value": 100,
"type": "number",
"override_snippet": "pyproject+: { tool+: { ruff+: { 'line-length': <YOUR_VALUE> } } }",
"notes": []
}
AI assistants can explore and modify Wiswa configuration programmatically.
Jsonnet merge operators (+:) for deep, surgical
overrides
User-level defaults
(~/.config/wiswa/defaults.jsonnet) shared across all projects
Feature flags (want_docs, want_tests,
want_main, want_codeql, …)
-J flag to add directories to the Jsonnet search
path for custom libraries
wiswa/
main.py -- async orchestrator
mcp.py -- FastMCP server
session.py -- filesystem-cached niquests session
utils/
jsonnet.py -- Jsonnet eval + native callbacks
versions.py -- package version fetching
postprocess.py -- formatting, locking, cleanup
wiswa-jsonnet/
defaults.libsonnet -- base defaults (400+ fields)
project.jsonnet -- generates all config files
utils.libsonnet -- helper functions
Declarative: one .wiswa.jsonnet file describes
your entire project setup
Regeneration-based: no merge conflicts, always up to date
Multi-language: Python, TypeScript, C/C++, Lua, Xcode, generic
Smart: automatic version resolution, GitHub/GitLab API integration
Extensible: Jsonnet configuration, user defaults, MCP server
AI-native: MCP server, Claude Code agents, Copilot/Cursor instructions