An atelier for Common Lisp developers.
Atelier provides project scaffolding and a linter for Common Lisp projects. The linter checks file structure, coding style, and naming conventions at the file, line, and CST (concrete syntax tree) levels. It supports autofix for most findings and is extensible via ASDF companion systems.
This software is Copyright © 2017–2026 Michaël Le Barbier and is distributed under the terms of the MIT licence.
Atelier can generate a new Common Lisp project with a standard layout, copyright header, licence, and ASDF system definition:
(atelier:new-lisp-project #p"~/Lisp/myproject/"
:copyright-holder "Jane Smith"
:copyright-year "2025"
:project-filename "org.example.myproject"
:project-name "My Project"
:project-description "A short description."
:homepage "https://github.com/example/myproject"
:license :mit)Inspectors examine source files and produce findings. There are 13 built-in inspectors at three levels:
File-level — examine the file as a whole:
check-file-encoding— UTF-8 validitycheck-spdx-license-header— SPDX licence identifier presence and correctnesscheck-header-line— canonical header line (;;;; filename — description)check-footer-line— canonical footer line (;;;; End of file 'filename')check-project-identification— project name, homepage, copyright block
Line-level — work over lines as strings:
check-trailing-whitespace— trailing spaces or tabscheck-mixed-indentation— tabs vs. spaces consistency
Syntax-level — analyse parsed structure via Eclector's CST reader:
check-earmuffs—DEFVAR/DEFPARAMETERnames lack*earmuffs*check-constant-naming—DEFCONSTANTnames lack+plus-convention+check-bare-lambda— bareLAMBDAin higher-order calls instead of namedFLETcheck-loop-keywords— bareLOOPclause keywords instead of keyword symbolscheck-labels-for-flet—LABELSwith no mutual/self-recursion (should be nestedFLET)
File and line inspectors support multiple file types via file-comment-prefix:
Lisp, Shell, Makefile, Dockerfile, Terraform, C/C++, TeX/Metapost, and Autoconf.
Syntax inspectors are Lisp-specific.
Each inspector has a paired maintainer that produces a resolution — a concrete fix that can be applied automatically:
| Maintainer | Fix |
|---|---|
fix-trailing-whitespace |
Strip trailing whitespace |
fix-mixed-indentation |
Normalise to spaces or tabs |
fix-earmuffs |
Add *earmuffs* to special variable names |
fix-constant-naming |
Add +plus+ to constant names |
fix-bare-loop-keywords |
Replace bare keywords with keyword symbols |
fix-bare-lambda |
Extract lambda to named FLET function |
fix-labels-to-flet |
Rewrite spurious LABELS as nested FLET |
fix-header-line |
Set canonical header line |
fix-footer-line |
Set canonical footer line |
fix-project-identification |
Set canonical project identification block |
Lint a system using the DWIM default (inspect, fix what is safe to fix, return the remaining findings):
(atelier:lint "org.my.project")Two keyword-valued options describe the outcome:
| Option | Values | Meaning |
|---|---|---|
:action |
:inspect |
Inspect only; return findings; write no files. |
:preview |
Inspect and plan resolutions; return the list of resolutions that would be applied; write no files. | |
:fix |
Inspect, plan, and apply resolutions; iterate until convergence or a per-pass limit of 10; return remaining findings. (Default.) | |
:scope |
:system |
Requested system's source files plus its .asd. (Default.) |
:project |
Every source file of every system declared in the same .asd. |
Examples:
(atelier:lint "org.my.project" :action :inspect) ; findings only
(atelier:lint "org.my.project" :action :preview) ; planned resolutions
(atelier:lint "org.my.project" :scope :project) ; whole project, with fixesThe underlying pipeline is exposed as four primitives for callers that need to compose the linter into a larger workflow (CI dry-run reporter, MCP tool, LSP endpoint):
(atelier:collect-lint-files system &key scope) ; → list of pathnames
(atelier:inspect-lint-files pathnames &key system-designator) ; → findings
(atelier:plan-resolutions findings) ; → resolutions
(atelier:apply-lint-resolutions resolutions) ; → written pathnamesAs an ASDF operation:
(asdf:operate 'atelier:lint-op "org.my.project")Linter configuration is declared as ASDF components in the system definition:
(asdf:defsystem "org.my.project"
:depends-on ("org.melusina.atelier")
:components
((:module "src" :components ((:file "package") (:file "core")))
(atelier:asdf-project-configuration "project-configuration")
(atelier:asdf-linter-configuration "linter-configuration")))project-configuration.sexp — project metadata used by header/footer inspectors:
(:project-name "My Project"
:homepage "https://github.com/example/myproject"
:copyright-year "2025"
:copyright-holder "Jane Smith"
:project-filename "org.example.myproject"
:license "MIT")linter-configuration.sexp — linter policy:
(:indentation-style :spaces
:disabled-inspectors (check-earmuffs)
:severity-overrides ((check-trailing-whitespace . :error))
:maintainer-overrides ((fix-labels-to-flet . :interactive)))When no configuration components are present, lint applies
sensible defaults and emits a warning.
During lint :action :fix, each proposed resolution is signalled
as a resolution-proposed condition with two restarts:
apply-resolution— apply the fixskip-resolution— skip this fix
Maintainers have a maturity level (:stable or :experimental).
Stable maintainers auto-apply in batch mode. Experimental maintainers
signal a warning and are skipped unless the operator invokes
apply-resolution from the debugger.
Per-maintainer disposition can be overridden in the linter configuration:
:maintainer-overrides ((fix-labels-to-flet . :interactive)
(fix-trailing-whitespace . :auto))Dispositions: :auto (apply silently), :interactive (signal warning),
:skip (do not apply).
Any project can register additional inspectors by defining and registering them in a companion ASDF system:
(atelier:define-syntax-inspector my-project:check-handler-naming (form)
"Check that HTTP handler functions follow naming conventions."
;; Walk CST and produce findings...
)Loading the system is sufficient to activate the inspector in subsequent
lint calls.
Atelier includes an MCP (Model Context Protocol) server that exposes a Common Lisp development environment to AI coding agents. The server provides 30 tools covering evaluation, debugging, documentation lookup, HyperSpec access, ASDF/Quicklisp operations, and test running.
Build the standalone binary with ASDF:
(asdf:make "org.melusina.atelier/mcp-server")This produces the atelier_mcp executable in the system source
directory.
Add the server to your Claude Code MCP configuration
(~/.claude/claude_code_config.json):
{
"mcpServers": {
"atelier": {
"command": "/path/to/atelier_mcp"
}
}
}Replace /path/to/atelier_mcp with the actual path to the built
binary. The server communicates over stdio using JSON-RPC.
Without building a binary, the server can be started directly:
(ql:quickload "org.melusina.atelier/mcp")
(atelier/mcp:serve-two-way-stream)- SBCL — the MCP server and its child SBCL process require SBCL.
The server automatically sets
SBCL_HOMEwhen running from a dumped image. - Quicklisp — required in the child image for
quickloadsupport. - HyperSpec (optional) — install via MacPorts (
sudo port install lisp-hyperspec) to enablehyperspec-lookup,hyperspec-issue, andhyperspec-issuestools. These tools read from the local filesystem only and never make network requests.
(ql:quickload "org.melusina.atelier")The linter uses Eclector for CST parsing. All inspectors are portable
ANSI Common Lisp. No SBCL-specific extensions are used without an
explicit #+sbcl guard.