Skip to content

Command model

Every source-facing command belongs to one of three namespaces. This gives automation a stable first decision: inspect, edit, or refactor.

  • paredit inspect reads and reports without writing.
  • paredit edit transforms one selected form; stdout by default, --diff for a unified diff, --write to update the file in place.
  • paredit refactor plans, previews, verifies, and applies semantic changes.

The only command outside these namespaces is the paredit completions <shell> meta command, which prints shell completion scripts for bash, zsh, fish, elvish, and powershell.

Run paredit <namespace> --help for the authoritative list on your installed version, and paredit <namespace> <command> --help for each command's contract, arguments, and output formats. For a machine-readable catalog of the entire surface in one call, run:

paredit inspect capabilities --output json

Inspect

paredit inspect never writes source files. Prefer these commands for discovery, impact analysis, and preflight checks.

Command Purpose
check Validate that input is a balanced S-expression document.
dialect Detect Lisp dialect from --file extension or explicit --dialect.
stats Print parse, dialect, and structural metrics for agent planning.
agent-report Print a complete JSON report for AI coding agent refactor planning.
capabilities Print a machine-readable catalog of every command, flag, default, and enum value.
outline Print top-level forms with paths, spans, and definition hints.
form Report one selected form with local structure for refactor planning.
find-symbol Find exact atom occurrences without touching strings or comments.
symbols Report exact atom occurrences across explicit files for rename planning.
calls Report list-head call sites across explicit files for arity refactor planning.
signature Compare callable definitions and call-site arity across explicit files.
call-graph Report internal and optional external call graph edges.
impact Report refactoring impact risks for one symbol across explicit files.
workspace Discover Lisp sources under roots and report parse/refactor inventory.
dependencies Report package, system, load, and qualified-symbol dependencies.
packages Report Common Lisp package declarations across explicit files.
definitions Report definition-like top-level forms across explicit files.
unused-definitions Report definitions with no external exact atom references.
duplicates Report repeated structural S-expression shapes across explicit files.
similarity Report structurally similar S-expression forms across explicit files.
lets Report local let bindings and inline safety for refactor planning.
complexity Report per-definition nesting depth and size metrics for refactor prioritization.
naming Report definition names that deviate from idiomatic kebab-case Lisp naming.
reachability Report callable definitions unreachable from any entry point in the internal call graph.
unused-parameters Report declared function parameters with no unshadowed reference in their body.
shadowed-bindings Report let-family bindings that shadow an enclosing parameter or let binding.
unused-local-callables Report flet/labels local callables never called anywhere in their visible scope.
package-boundaries Report package::symbol references that reach into another package's internal symbols.
call-cycles Report strongly connected cycles of two or more definitions in the internal call graph.
package-cycles Report defpackage :use/:import-from cycles across two or more packages.
system-cycles Report ASDF defsystem :depends-on cycles across two or more systems.
unused-packages Report defpackage declarations never used, imported-from, or reached by a qualified symbol.
unused-exports Report defpackage :export symbols never reached by a qualified symbol reference.
duplicate-exports Report defpackage forms that export the same symbol more than once.
unused-nicknames Report defpackage :nicknames never used as a qualifier anywhere.
package-conflicts Report distinct defpackage forms that claim the same package name or nickname.
redefinitions Report top-level definitions of the same category and name declared more than once.
undefined-packages Report in-package forms naming a package no analyzed defpackage declares.
class-cycles Report CLOS defclass/define-condition superclass inheritance cycles across two or more classes.
struct-cycles Report defstruct :include cycles across two or more structs.
system-conflicts Report distinct asdf:defsystem forms that claim the same system name.
duplicate-slots Report defclass/define-condition/defstruct forms declaring the same slot name more than once.
duplicate-methods Report defmethod forms with the same name, qualifier, and specializers declared more than once.
duplicate-parameters Report callable definitions whose lambda list names the same parameter more than once.
redundant-quote Report self-evaluating literals (numbers, strings, characters, keywords) that are quoted redundantly.
redundant-progn Report progn forms that are redundant — empty ((progn) is nil) or wrapping a single form ((progn X) is just X).
redundant-prog1 Report a (prog1 x) wrapping a single form; prog1 returns its first form's value, so with one form it is just x. Auto-fixable (unwraps to x). A multi-form (prog1 x y) (which sequences side effects and cannot become progn) and an empty (prog1) are left alone.
negated-when-unless Report when/unless forms whose test is a (not X)/(null X) negation; flipping the macro (whenunless) and dropping the negation reads more directly.
nested-progn Report a multi-form progn nested directly inside another progn; its forms splice into the outer one, so (progn a (progn b c) d) is just (progn a b c d).
redundant-body-progn Report a multi-form progn used as the body of a form that already has an implicit progn (when, unless, let, defun, lambda, …), so (when c (progn a b)) is just (when c a b).
empty-let Report a let whose binding list is empty ((let () body) or (let nil body)); with no bindings, it is just (progn body). Auto-fixable (rewrites (let () as (progn). A body that leads with (declare …) is left alone (invalid in progn), as is (let* () …) — the province of redundant-let-star.
redundant-if-nil Report a three-argument if whose else branch is a literal nil; a two-argument if already returns nil, so (if c x nil) is just (if c x).
redundant-let-star Report a let* whose binding list holds zero or one binding: with no earlier binding to depend on, the sequential scope is unused, so (let* ((x e)) body) is just (let ((x e)) body). Auto-fixable (rewrites the head to let).
single-clause-cond Report a cond with exactly one clause that has a body and a non-t test; with nothing to fall through to, (cond (test a b)) is just (when test a b). Auto-fixable (rewrites to when). A t/otherwise catch-all clause (a progn) and a test-only clause (returns the test value) are left alone.
cond-t-clause Report a cond with exactly one clause whose test is the literal t and which has a body; since t always holds and there is nothing to fall through to, (cond (t a b)) is just (progn a b). Auto-fixable (rewrites to progn). The t-clause complement of single-clause-cond. A multi-clause cond (a trailing (t …) else is idiomatic), a test-only (cond (t)) (returns t), and a non-t test are left alone; otherwise is not special in cond and is not treated as a catch-all.
explicit-step-delta Report an incf/decf whose delta operand is the literal 1; since 1 is the default step, (incf x 1) is just (incf x) (and (decf x 1) is (decf x)). Auto-fixable (drops the delta). Only the bare integer 1 is matched — a float 1.0 can coerce the place's type, so it is left alone.
negated-step-delta Report an incf/decf whose delta is a negative numeric literal; adding a negative delta is subtracting, so (incf x -1) is (decf x 1) and (decf n -5) is (incf n 5). Auto-fixable (flips the operator and drops the sign); a resulting (decf x 1) is then reduced to (decf x) by explicit-step-delta. A variable delta and a positive literal are left alone.
explicit-nil-return Report a return or return-from whose result form is the literal nil; nil is the default result, so (return nil) is just (return) and (return-from foo nil) is (return-from foo). Auto-fixable (drops the nil). (return-from nil) is left alone — there the nil is the block name, not a result.
funcall-lambda Report a funcall whose first argument is a literal (lambda …) form; a lambda form is directly applicable, so (funcall (lambda (x) …) a) is just ((lambda (x) …) a). Auto-fixable (drops funcall). The #'symbol case belongs to redundant-funcall; a #'(lambda …) first argument is left alone (its reader prefix cannot sit in operator position).
if-to-or Report a three-argument if whose test and then-branch are the same bare atom; (if x x y) returns x or the else, which is (or x y) — evaluated once instead of twice. Auto-fixable (rewrites to or). Only an atom test/then pair is matched (a compound test would be evaluated twice); a literal t/nil test belongs to constant-if-test.
if-not Report a three-argument if whose then-branch is the literal nil and else-branch is the literal t; (if test nil t) yields nil when test holds and t otherwise, which is exactly (not test). Auto-fixable (rewrites to (not test), copying the test verbatim). The dual (if test t nil) is a boolean coercion with no clearer builtin and is left alone, as is (if test nil nil) (identical-if-branches) and any non-literal branch.
if-to-unless Report a three-argument (if c nil e) whose then-branch is the literal nil; it yields nil when c holds and e otherwise, which is exactly (unless c e). Auto-fixable (rewrites to (unless c e)). To avoid overlap, else = t is left to if-not, else = nil to identical-if-branches, and a constant t/nil test to constant-if-test.
prog2-to-progn Report a two-form (prog2 a b); prog2 returns its second form's value, which for exactly two forms is also the last, so it equals (progn a b). Auto-fixable (rewrites the prog2 operator to progn). A three-or-more-form prog2 (which returns the second form, not the last) and a one-form prog2 are left alone.
handler-case-no-clauses Report a (handler-case expr) with no handler clauses; it establishes no handlers and just returns expr. Auto-fixable (unwraps to expr). A handler-case with any clause is left alone.
unwind-protect-no-cleanup Report an (unwind-protect x) with no cleanup forms; with nothing to run on exit it just returns x. Auto-fixable (unwraps to x). An unwind-protect with any cleanup form is left alone.
one-step-arithmetic Report a two-argument +/- of the literal 1, which has a unary shorthand: (+ x 1) and (+ 1 x) are (1+ x), and (- x 1) is (1- x). Auto-fixable (rewrites to 1+/1-). (- 1 x) has no shorthand and is left alone; only the bare integer 1 matches (a float 1.0 would coerce the result type).
case-nil-key Report a case/ecase/ccase clause whose key is the bare atom nil. case keys designate a list of objects, and nil designates the empty list, so (case x (nil …)) can never match — to match the value nil you must write ((nil) …). Report-only (an error-severity likely bug). ((nil) …) and a quoted 'nil (see quoted-case-key) are left alone.
typecase-nil-key Report a typecase/etypecase/ctypecase clause whose head is the bare atom nil. In typecase a clause head is a type specifier, and nil is the empty type — no object is of it — so (typecase x (nil …)) is a dead clause. To match the nil value use the null type. Report-only (error-severity). The t catch-all, the null type, and a quoted 'nil are left alone. The typecase-family analog of case-nil-key.
sharp-quoted-lambda Report a (lambda …) form with a redundant #' prefix. lambda already expands to (function (lambda …)), so #'(lambda (x) …) is exactly (lambda (x) …) in every position. Auto-fixable (strips the #'). A #'foo symbol reference and a bare (lambda …) are left alone.
redundant-eql-test Report an explicit :test #'eql on an operator whose :test already defaults to eql (member, assoc, find, position, count, remove, the set operations, …), so (find x list :test #'eql) is just (find x list). Auto-fixable (deletes the :test #'eql pair). Recognizes #'eql, 'eql, and (function eql); a custom test, :test-not, and non-eql-defaulting operators are left alone.
redundant-start-zero Report an explicit :start 0 on a bounded-sequence operator whose :start defaults to 0 (find, position, count, remove, substitute, the -if/-if-not variants, fill, reduce, parse-integer, the string-case functions, …), so (find x seq :start 0) is (find x seq). Auto-fixable (deletes the :start 0 pair). Two-sequence operators (search/mismatch/replace, which use :start1/:start2) and a non-zero start are left alone.
redundant-end-nil Report an explicit :end nil (:end defaults to nil = end of sequence) on the same bounded-sequence operators, so (find x seq :end nil) is (find x seq). Auto-fixable (deletes the :end nil pair). A non-nil end is left alone.
redundant-from-end-nil Report an explicit :from-end nil (the default) on a :from-end-taking operator (find/position/count/remove/delete/substitute families, remove-duplicates, reduce, search, mismatch), so (find x seq :from-end nil) is (find x seq). Auto-fixable. A :from-end t is left alone.
redundant-count-nil Report an explicit :count nil (the default, meaning unlimited) on remove/delete/substitute/nsubstitute and their -if/-if-not variants, so (remove x seq :count nil) is (remove x seq). Auto-fixable. A numeric :count is left alone.
make-hash-table-test Report a make-hash-table with an explicit :test 'eql; the :test argument defaults to eql, so (make-hash-table :test 'eql) is (make-hash-table). Auto-fixable (deletes the :test 'eql pair, keeping any other keyword arguments). The make-hash-table sibling of redundant-eql-test; a custom test ('equal/'equalp/'eq) is left alone.
gethash-default Report a three-argument (gethash key table nil) whose default value is the literal nil; gethash's default already defaults to nil (and the second, present-p, value is unchanged), so it is (gethash key table). Auto-fixable (deletes the nil). A non-nil default is left alone.
typep-predicate Report a (typep x 'TYPE) whose type is a CL type name with a dedicated total predicate (null, symbol, atom, cons, list, integer, string, hash-table, function, …), which is exactly (PRED x) — e.g. (typep x 'string)(stringp x). Auto-fixable. A compound type spec, a type without a dedicated predicate (fixnum), the always-true t, and the three-argument (typep x type env) form are left alone.
coerce-to-t Report a (coerce x t); coercing to type t returns the object unchanged, so it is just x. Auto-fixable (unwraps to x). A real coercion ((coerce x 'list), (coerce x 'double-float)) is left alone.
redundant-identity-key Report an explicit :key #'identity (or :key nil) on an operator that accepts :key (find, position, remove, sort, merge, reduce, the -if variants, …); :key defaults to nil (the element itself), so (sort xs #'< :key #'identity) is just (sort xs #'<). Auto-fixable (deletes the pair). Recognizes #'identity, 'identity, (function identity), and nil; a custom key and non-:key operators (e.g. tree-equal) are left alone.
single-value-bind Report a multiple-value-bind that binds exactly one variable; let captures the form's primary value, which is all a one-variable bind uses, so (multiple-value-bind (x) f body) is just (let ((x f)) body). Auto-fixable (rewrites to let). Two-or-more variables (which capture secondary values) and an empty variable list (a progn) are left alone.
nested-boolean Report an and/or nested directly inside a same-operator and/or; both operators are associative with identical short-circuiting, so (or a (or b c) d) is just (or a b c d) (and likewise for and). Auto-fixable (splices the inner operands in). The inner form must have two or more operands — the single-operand (or x) collapse belongs to single-operand-boolean.
nested-when Report a when whose only body form is another when; the two tests combine, so (when a (when b body)) is (when (and a b) body). Auto-fixable (merges the tests with and). An outer when with additional body forms after the inner when is left alone — those forms are not guarded by the inner test.
nested-unless Report an unless whose only body form is another unless; the body runs only when both tests are nil, so (unless a (unless b body)) is (unless (or a b) body). Auto-fixable (merges the tests with or). The or-combining mirror of nested-when; an outer unless with extra body forms is left alone.
empty-body Report a when/unless/dolist/dotimes form that has its test/spec but no body ((when ready), (dolist (x items))); the test/spec runs and then nothing happens — usually a forgotten body.
verbose-negation Report negation written the long way: (- 0 x) (zero minus x), (* x -1) and (* -1 x) (times minus one) are all (- x). Auto-fixable (rewrites to unary (- x)). Only bare integer 0/-1 are matched (a float -1.0 would coerce the result type); the trailing (- x 0) is identity-arithmetic's job.
identity-arithmetic Report an arithmetic form with a redundant integer identity operand ((+ x 0), (* x 1), (- x 0), (/ x 1)); adding 0, multiplying by 1, etc. does nothing. Float 0.0/1.0 (which coerce) and the leading operand of -// are not flagged.
redundant-divisor Report a two-argument floor/ceiling/truncate/round (or the float variants ffloor/fceiling/ftruncate/fround) whose divisor is the literal integer 1; the divisor defaults to 1, so (floor x 1) is exactly (floor x) — same quotient and remainder. Auto-fixable (drops the divisor, preserving operator casing). A float 1.0 (which changes the remainder type), a non-1 divisor, and mod/rem (no defaultable divisor) are left alone.
single-operand-list-op Report a single-argument append, nconc, or list*, each of which returns its argument unchanged, so (append x) is just x. Auto-fixable (replaces the form with the argument). Scoped to these three because their single-argument identity is unconditional; numeric ops like (max x) (which require a real and would signal a type error) are excluded. Two-or-zero-argument forms are left alone.
single-operand-arithmetic Report a single-operand +/* form ((+ x), (* x)); a one-argument +/* returns its operand verbatim, so the wrapper is pure noise. Auto-fixable (unwraps to the operand). Unary (- x) (negation) and (/ x) (reciprocal) are meaningful and not flagged; the zero-operand identities (+)/(*) are left alone.
redundant-funcall Report a funcall of a sharp-quoted symbol ((funcall #'foo a b)); it resolves foo through the same lexical function namespace as a direct (foo a b), so the funcall/#' is ceremony. Auto-fixable (rewrites to the direct call). A variable argument ((funcall fn …)), a sharp-quoted lambda (#'(lambda …)), and an ordinary-quoted symbol ('foo) are not flagged.
redundant-the Report a (the t form) type declaration; the type t matches every object, so the assertion is vacuous and the form is exactly form (the passes all values through, so this holds for multiple values too). Auto-fixable (replaces the form with its inner form). A specific type ((the fixnum x)), a compound type, and a wrong-arity (the t)/(the t a b) are not flagged.
nil-comparison Report an eq/eql/equal/equalp comparison against a bare nil ((eq x nil), (equal nil y)); each is exactly the idiomatic (null x) nil test. Auto-fixable (rewrites to (null X)). Numeric = (a type error on nil), a degenerate (eq nil nil), and a quoted 'nil are not flagged.
one-armed-if Report a two-argument if with no else branch ((if test then)); the mainstream style guides recommend (when test then) for a conditional with a single arm. Auto-fixable (swaps the if head for when; a progn then-branch is then spliced by redundant-body-progn). A two-armed (if test a b), an argument-short (if test), and a reader-conditional operand are not flagged.
t-comparison Report an eq/eql/equal/equalp comparison against the literal t ((eq x t)); it matches only the symbol T, so as a boolean test it silently fails for any other true value (a generalized-boolean mistake). Report-only — the right rewrite (drop the comparison, or keep an intentional symbol test) depends on intent. The symmetric partner of nil-comparison. Numeric =, a degenerate (eq t t), and a quoted 't are not flagged.
manual-incf Report a setf/setq that manually increments or decrements a variable ((setf x (1+ x)), (setq n (+ n 2)), (setf i (- i 1))), which is exactly what incf/decf express. Auto-fixable (rewrites to (incf x) / (incf n 2) / (decf i 1)). Only bare-variable places are matched (so incf's single evaluation of a compound place never changes side-effect timing); a compound place, a non-commuting (- d v), a different variable, and a multi-pair setf are not flagged.
manual-push Report a setf/setq that manually conses an element onto a variable ((setf stack (cons item stack))), which is exactly what push expresses. Auto-fixable (rewrites to (push item stack)). Because cons is (cons element list), only a form whose second cons operand is the place counts; consing the place as the element ((cons x other)), a compound place, and a multi-pair setf are not flagged.
cons-to-list Report a cons whose tail is nil/() or a list literal, which is really a list construction: (cons a nil) is (list a), (cons a (list b c)) is (list a b c). Auto-fixable (rewrites to list; a spelled-out cons chain like (cons a (cons b nil)) converges to (list a b) one layer per fixpoint pass). A cons onto a variable or an improper pair (cons a b) is a genuine cons and not flagged.
double-reverse Report a (reverse (reverse x)); reverse returns a fresh sequence of the same kind with the order flipped, so reversing twice yields a fresh sequence equal to x — exactly (copy-seq x), a wasteful obfuscated copy. Auto-fixable (rewrites to (copy-seq x), copying the inner argument verbatim). The destructive nreverse (on either level) and a mixed reverse/nreverse nesting are left alone — they cannot be reasoned about as a plain copy.
append-list-to-cons Report a two-argument (append (list x) rest) whose first argument is a one-element (list x); append conses the copied singleton onto the shared tail, which is exactly (cons x rest) — same single fresh cons, same sharing of rest, same left-to-right evaluation. Auto-fixable (rewrites to (cons x rest)). A multi-element first list ((append (list x y) rest) is (list* x y rest)), a non-list first argument, and a different argument count are left alone.
list-star-to-cons Report a two-argument (list* a b); by definition list* of exactly two arguments builds one cons, so it is exactly (cons a b). Auto-fixable (rewrites to (cons a b)). A single-argument (list* x) (which is x, single-operand-list-op's concern) and a three-or-more-argument list* (nested conses) are left alone.
values-list-of-list Report a (values-list (list a b)) whose sole argument is a list construction; building a fresh list only to spread it is exactly (values a b) — same values, order, and evaluation. Auto-fixable (rewrites to (values a b); an empty (list) becomes (values)). A quoted list ('(a b), whose elements are data, not forms), a variable argument, and a non-list constructor are left alone.
multiple-value-list-of-values Report a (multiple-value-list (values a b)); collecting the values of a literal (values …) into a list is exactly (list a b) — the inverse of values-list-of-list. Auto-fixable (rewrites to (list a b); an empty (values) becomes (list)). A variable or non-values argument is left alone.
append-nil Report a two-argument (append x nil); append copies all but its last argument, so with a nil tail the result is a fresh top-level copy of x — exactly (copy-list x). Both append (non-last arg must be a proper list) and copy-list reject a non-list identically, so the type-check domain is preserved. Auto-fixable. A non-nil tail and a differing argument count are left alone. ((nconc x nil) is deliberately not handled — rewriting to bare x would drop nconc's list type-check.)
manual-pushnew Report a setf/setq that manually adjoins an element onto a variable ((setf set (adjoin item set)), (setf seen (adjoin k seen :test #'equal))), which is exactly what pushnew expresses. Auto-fixable (rewrites to (pushnew item set), passing any :test/:key keyword arguments through unchanged). Only a form whose second adjoin operand is the place counts; a compound place and a multi-pair setf are not flagged.
nested-cxr Report nested car/cdr-family accessors that the standard combines into one ((car (cdr x)) is (cadr x), (cdr (cdr x)) is (cddr x)). Auto-fixable (collapses to the combined cXr; deeper nestings converge one layer per fixpoint pass, so (car (cdr (cdr x))) becomes (caddr x)). Only combinations that stay within the four-letter standard accessors are flagged; first/rest spellings and a nesting that would exceed cddddr are not.
redundant-identity Report an (identity x) call, which returns its argument unchanged — so it is exactly x. Auto-fixable (replaces the form with its argument). Composes with redundant-funcall ((funcall #'identity x)(identity x)x). A #'identity function reference (e.g. a :key argument) and an argument-mismatched (identity)/(identity a b) are not flagged.
nthcdr-zero Report (nthcdr 0 list), which returns the list unchanged, so it is just list. Auto-fixable (replaces the whole form with the list operand). Only the bare integer 0 count is matched; a non-zero index, a float 0.0, and a variable count are left alone.
subseq-zero Report a two-argument (subseq seq 0); a subseq from index 0 with no end is a fresh copy of the whole sequence, exactly (copy-seq seq). Auto-fixable (rewrites to (copy-seq seq)). A present end argument ((subseq seq 0 n), a genuine slice), a non-zero start, a float 0.0, and a variable start are left alone.
car-nthcdr Report a (car (nthcdr n x)); the standard defines nth as the car of the nthcdr, so this is exactly (nth n x) — same element and nil-on-overrun. Auto-fixable (rewrites to (nth n x)). A cdr/other outer accessor and a wrong nthcdr arity are left alone.
car-reverse Report a (car (reverse x)) / (first (reverse x)); the first element of the reversed list is the last of the original, but reverse builds a whole fresh copy to read one element. (car (last x)) yields the same element without the O(n) allocation. Auto-fixable (rewrites to (car (last x)), keeping the outer accessor). The destructive nreverse (which mutates x) is left alone.
nthcdr-small-index Report (nthcdr n list) with a literal count 14, for which the standard defines a named cdr accessor: (nthcdr 1 x) is (cdr x), (nthcdr 2 x) is (cddr x), (nthcdr 3 x) is (cdddr x), (nthcdr 4 x) is (cddddr x). Auto-fixable (rewrites to the accessor, copying the list operand). The count 0 belongs to nthcdr-zero; 5+ (no cdddddr), a float, and a variable count are left alone.
nth-constant-index Report an nth with a small literal index that has a named ordinal accessor ((nth 0 x) is (first x), … (nth 9 x) is (tenth x)). Auto-fixable (rewrites to the ordinal). Only literal indices 0–9 are flagged (there is no eleventh); a variable index and index 10+ are not.
redundant-apply Report an apply of a sharp-quoted symbol to a literal list ((apply #'foo (list a b))); spreading a literal (list …) is exactly the direct call (foo a b). Auto-fixable (rewrites to the direct call; an empty (list) yields a zero-argument call). The sibling of redundant-funcall. A variable list argument ((apply #'foo args)), leading fixed arguments, an ordinary-quoted 'foo, and a sharp-quoted lambda are not flagged.
sign-comparison Report a two-argument =/>/< comparison against the literal 0, which has a dedicated predicate: (= x 0) is (zerop x), (> x 0) is (plusp x), (< x 0) is (minusp x). Auto-fixable (rewrites to the predicate, accounting for which side the 0 is on — (> 0 x) becomes (minusp x)). >=/<=//= (no single-word predicate), a 0.0 spelling, a three-argument comparison, and (= 0 0) are not flagged.
negated-comparison Report a not/null wrapping a two-argument numeric comparison, which has an exact complement: (not (= a b)) is (/= a b), (not (< a b)) is (>= a b), (not (> a b)) is (<= a b) (and their inverses). Auto-fixable (rewrites to the complementary operator). Only the two-operand shape is flagged — (not (= a b c)) (all-equal vs pairwise-distinct) and a reader-conditional operand are not.
de-morgan Report an and/or whose operands are all single-argument negations, which collapses to one outer negation: (and (not a) (not b)) is (not (or a b)), (or (not a) (not b)) is (not (and a b)). Auto-fixable (rewrites via De Morgan — exact down to short-circuit order). Requires at least two operands, all not/null; a mix of negated and non-negated operands is not flagged.
redundant-boolean-identity Report an and/or containing its identity element, which contributes nothing: t in an and ((and a t b) is (and a b)) or nil in an or ((or a nil b) is (or a b)). Auto-fixable (drops the identity operand; collapses to the bare t/nil if all operands were removed). The complement of dead-boolean-operand (which handles the dominant nil-in-and/t-in-or). A trailing t in and (its return value) and a single-operand form are not flagged.
constant-if-test Report an if whose test is the literal constant t or nil, so one branch is dead code: (if t a b) is a, (if nil a b) is b, (if nil a) is nil. Auto-fixable (dead-code category — replaces the form with the live branch). A truthy non-t literal ((if 5 a b)) and a variable test are not flagged.
constant-when-test Report a when/unless whose test is the literal constant t or nil, so the body is statically decided: (when t body…) and (unless nil body…) always run (they are (progn body…)), while (when nil body…) and (unless t body…) never run (they are nil, dead code). Auto-fixable (dead-code category — splices the always-true form to progn and collapses the dead form to nil; the discarded body is never evaluated, so no side effects are lost). A truthy non-t literal and a variable test are not flagged. The if sibling is constant-if-test.
negated-if Report a three-argument if whose test is a (not X)/(null X) negation ((if (not ready) a b)); negating the test just flips the branches, so it is exactly (if X B A). Auto-fixable (drops the negation and swaps the then/else branches). A one-armed (if (not c) a) (no else to swap — the when/unless idiom's job) and a reader-conditional branch are not flagged.
duplicate-setf-places Report a setf/setq/psetf/psetq that assigns the same variable more than once in one form ((setf a 1 a 2), (setq x 1 y 2 x 3)); the earlier assignment is dead — almost always a copy-paste slip or a typo. Report-only (error severity, duplicate category). Only symbol places are compared by name (case-insensitively); a compound setf place and a malformed odd-arity form are not flagged.
single-operand-boolean Report single-operand and/or forms; a one-argument and/or returns its operand unchanged, so (and X) and (or X) are just X.
single-arg-comparison Report numeric comparisons (< > <= >= = /=) called with a single argument; these are vacuously true ((< x) is always t), usually a missing operand.
format-missing-destination Report format calls whose first argument is a string literal; the first argument is the destination (nil/t/stream), so (format "~a" x) is missing it.
format-to-string Report a (format nil "~A" x) / (format nil "~S" x) whose control string is exactly one directive; with a nil destination format returns the string, and a lone ~A/~S is princ/prin1 semantics, so these are exactly (princ-to-string x) / (prin1-to-string x). Auto-fixable (rewrites to the string function). Any surrounding text or extra directive, a non-nil destination (t/stream return nil, not the string), and an argument count other than one are left alone.
format-newline Report a (format t "~%"); with the t destination format writes to *standard-output* and returns nil, and ~% emits one newline — exactly (terpri). Auto-fixable (rewrites to (terpri)). Only the t destination is matched (an arbitrary destination could be a fill-pointer string, not a valid terpri argument); ~& (fresh-line, whose return value differs) and any call carrying format arguments are left alone.
literal-place Report incf/decf/push/pop/pushnew/setf/psetf whose place is a self-evaluating literal ((incf 5), (push x 3), (setf 5 x)); a literal is not a modifiable place, so the form fails at macroexpansion.
zero-divisor Report a division-family form with a literal 0 in a divisor position — (/ x 0), (/ 0) (reciprocal), (mod x 0), (rem x 0), (floor x 0), … — which always signals division-by-zero at run time. Report-only (there is no meaningful rewrite). A 0 numerator ((/ 0 x)), a float 0.0, and a single-argument quotient with no divisor are not flagged.
duplicate-keyword Report a make-* constructor call (make-instance, make-hash-table, make-array, make-string, make-condition, make-pathname, make-string-output-stream) that passes the same keyword argument twice, e.g. (make-instance 'c :x 1 :x 2); the leftmost value wins and the rest are silently ignored. Report-only. Scope is gated to operators with a fixed positional prefix so a positional argument that is itself a keyword is never mistaken for a keyword-argument name.
defpackage-quoted Report a quoted or sharp-quoted designator inside a defpackage clause that takes symbol/package designators (:export, :shadow, :intern, :import-from, :shadowing-import-from, :use, :nicknames), e.g. (:export 'foo); defpackage does not evaluate its options, so the quote is almost always a bug. Report-only.
step-zero Report an (incf place 0) / (decf place 0) whose step is the literal 0 — a no-op that evaluates the place but changes nothing, usually a forgotten step. Report-only (dropping it could discard the place's side effects). A float 0.0, a non-zero step, and the default-step (incf x) are not flagged.
unreachable-cond-clause Report cond forms with clauses after a t catch-all clause that can never run.
malformed-let-binding Report let/let* bindings that are neither a symbol nor a (var value) pair.
if-arity Report if forms with the wrong number of arguments (Common Lisp if takes 2 or 3).
malformed-cond-clause Report cond clauses that are not a non-empty list (a bare atom or empty clause).
malformed-case-clause Report case/typecase-family clauses that are not a non-empty list (a bare atom or empty clause).
unreachable-case-clause Report case/typecase clauses after a t/otherwise catch-all clause that can never run.
malformed-iteration-spec Report dolist/dotimes specs that are not a (var form [result]) list.
duplicate-lambda-list-keyword Report lambda lists that repeat a lambda-list keyword (&optional, &rest, &key, ...).
lambda-list-keyword-order Report lambda lists whose keywords are out of the canonical &optional/&rest/&key/&aux order.
modify-macro-arity Report incf/decf/push/pop calls with the wrong number of arguments.
binds-constant Report let/let/do/do bindings whose variable is a constant (nil, t, or a keyword).
quoted-case-key Report case/ecase/ccase clauses with a quoted key ('a matches quote and a, not a).
the-arity Report the special forms without exactly two arguments (a type and a form).
equality-arity Report eq/eql/equal/equalp calls without exactly two arguments.
accessor-arity Report nth/elt/gethash/getf/... accessors with the wrong number of arguments.
setq-non-variable Report setq/psetq places that are not variables (a list, literal, or constant).
eval-when-situation Report eval-when forms with an invalid situation (not :compile-toplevel/:load-toplevel/:execute).
exhaustive-case-otherwise Report ecase/ccase/etypecase/ctypecase forms with a forbidden t/otherwise clause.
duplicate-case-keys Report case/ecase/ccase forms with the same key in more than one clause.
self-assignments Report setq/setf/psetq/psetf pairs that assign a place to itself.
identical-if-branches Report if forms whose then and else branches are structurally identical.
duplicate-cond-tests Report cond forms with the same test expression in more than one clause.
duplicate-let-bindings Report parallel let forms that bind the same variable more than once.
duplicate-boolean-operands Report and/or forms that list the same operand more than once.
eql-string-comparison Report eq/eql calls that compare against a string literal (never reliably eql).
self-comparison Report comparison calls whose two operands are structurally identical (always true/false).
dead-boolean-operand Report and/or forms whose non-final constant operand makes later operands dead.
eq-number-comparison Report eq calls that compare against a number literal (eq on numbers is unreliable).
eq-char-comparison Report eq calls that compare against a character literal ((eq c #\a)); eq on characters is unreliable — use eql or char=.
char-op-string Report character functions (char=, char<, char-code, char-upcase, alpha-char-p, …) applied to a string literal ((char= "a" c)); these require a character, so a string literal is a type error.
string-case-fold Report a string= whose two operands are each case-folded the same way — (string= (string-downcase a) (string-downcase b)) (or both string-upcase); folding both sides then comparing case-sensitively is a case-insensitive comparison, exactly (string-equal a b). Auto-fixable. A mixed pair (one downcase, one upcase) and a :start/:end-keyworded comparison are left alone.
char-case-fold Report a char= whose two operands are each case-folded the same way — (char= (char-downcase a) (char-downcase b)) (or both char-upcase), which is exactly (char-equal a b). Auto-fixable. A mixed pair and a three-or-more-argument comparison are left alone.
nested-string-case Report a nested pair of string-upcase/string-downcase/string-capitalize(string-upcase (string-downcase s)); the outer case operation fully determines the result (case ops change case but not letter identity or word boundaries), so the inner one is dead work and the form is (string-upcase s). Auto-fixable (keeps the outer op). The destructive nstring-* variants are excluded (dropping them would drop their mutation).
code-char-char-code Report a (code-char (char-code c)); char-code maps a character to its code and code-char maps it back, so the round-trip is just c. Auto-fixable (unwraps to c). The reverse (char-code (code-char n)) is not flagged (code-char can return nil for an unsupported code).
last-default-count Report a (last x 1) whose explicit trailing count restates last's default of 1, so it is exactly (last x). Auto-fixable (drops the 1). A non-1 count ((last x 2)) is meaningful and left alone.
butlast-default-count Report a (butlast x 1) (or destructive (nbutlast x 1)) whose explicit trailing count restates the default of 1, so it is exactly (butlast x). Auto-fixable (drops the 1); nbutlast's mutation is preserved. A non-1 count is left alone.
make-list-default-element Report a (make-list n :initial-element nil) whose keyword restates make-list's default of nil, so it is exactly (make-list n). Auto-fixable (drops the :initial-element nil pair). A non-nil element (:initial-element 0) is meaningful and left alone.
parse-integer-default-radix Report a (parse-integer s :radix 10) whose keyword restates parse-integer's default of 10, so it is exactly (parse-integer s). Auto-fixable (drops the :radix 10 pair, preserving any other keywords). A non-10 radix is left alone.
getf-default-nil Report a (getf p k nil) whose explicit default restates getf's default of nil, so it is exactly (getf p k). Auto-fixable (drops the trailing nil). A non-nil default is meaningful and left alone.
make-array-default-keyword Report a make-array call with an explicit :adjustable nil or :fill-pointer nil, restating the default. Auto-fixable (drops the redundant keyword pair, preserving other keywords). A non-nil value, and :initial-element nil (which is not redundant for make-array), are left alone.
nested-char-case Report a nested pair of char-upcase/char-downcase(char-upcase (char-downcase c)); the outer case operation fully determines the result, so the inner one is dead work and the form is (char-upcase c). Auto-fixable (keeps the outer op).
list-star-nil Report a (list* a … nil) whose final argument is the literal nil; with a nil tail list* builds a proper list, exactly what list builds, so the form is (list a …). Auto-fixable (rewrites the head to list and drops the trailing nil). A non-nil tail is a genuine list* and is left alone.
destructive-literal Report destructive calls (nreverse, sort, delete, nsubstitute, nunion, nconc, rplaca, …) whose modified argument is a quoted list literal ((sort '(3 1 2) #'<), (delete x '(1 2))); modifying a constant is undefined behavior. Each function's sequence argument position is known, so a literal item or a last-nconc argument is not flagged.
eql-list-comparison Report eq/eql calls that compare against a quoted list literal (never reliably eql).
eql-search-literal Report member/assoc/find/position/count/remove/delete/adjoin/pushnew (item first) and substitute/nsubstitute/subst/nsubst (item second) searching for a string or quoted-list literal with no :test; the default eql never matches a string/list literal — add :test #'equal.
setf-arity Report setq/setf/psetq/psetf forms with an odd argument count (a place missing its value).
lint Run every within-file logic-bug lint at once and report all findings, tagged by rule and category. Each finding is self-describing — it carries its severity, category, and a fixable flag inline (so an agent can triage and decide whether to run --fix without cross-referencing --list-rules). --list-rules prints the rule catalog with categories, descriptions, a severity (error for likely/certain bugs, warning for redundant/non-idiomatic style), and a fixable flag marking the rules --fix can repair — and it honors the same --rule/--exclude/--category selectors, so --list-rules --category dead-code lists just that group; --rule/--exclude select rules; --category selects a whole group (arity, dead-code, duplicate, malformed, suspicious); --sarif emits a SARIF 2.1.0 log for CI code scanning (with stable fingerprints and one-click fixes for every rule --list-rules marks fixable); --github emits GitHub Actions ::error:: annotations for inline PR review; --fix applies those auto-fixes in place, iterating to a fixpoint (so nested redundancies collapse fully) and reporting the per-file/per-rule counts; add --diff to preview the changes as a unified diff without writing, or --check to write nothing and exit 3 when any auto-fix is still pending (a CI gate that stays green only when fixable lint has been cleaned up — distinct from --fail-on-finding, which also gates on report-only findings). --check and --diff combine (show the diff and fail). --fix-plan instead emits the machine-readable fix plan — each fixable finding's exact byte-region replacements as JSON (or tab-separated text) — without writing, so an editor or agent can preview or apply fixes one at a time (honoring the same suppressions and --baseline as --fix). Findings can be silenced in source with an inline ; paredit:ignore [rule…] comment: on its own line it suppresses the next line, trailing after code it suppresses that line, and with no rule names it suppresses every rule — honored uniformly across the report, SARIF, GitHub, and --fix outputs. --fail-on <error\|warning> gates only on findings at or above a severity (so CI can block on bugs while still reporting style warnings), and SARIF level reflects each finding's severity. --stats prints a lint-debt rollup instead of individual findings — finding counts by severity, by category, and by rule, plus files-scanned/files-with-findings — honoring the same --rule/--category/--baseline filters. --report-unused-suppressions instead reports any ; paredit:ignore that silences no finding (a stale ignore or a typo'd rule name) and exits 3 if any are found, keeping the ignore list honest in CI. For adopting the linter on an existing codebase, --write-baseline <file> snapshots today's findings and --baseline <file> then suppresses those known findings (matched by rule and trimmed-line content, so they survive line shifts) — reporting and gating only on new findings, across the default, --sarif, and --github outputs.

Most reports accept --output json for machine-readable results.

Edit

paredit edit makes one structural transformation on the form selected by --path or --at (see Selecting forms). By default the rewritten document is printed to standard output and the file is untouched. Mutating commands also accept:

  • --diff — print a unified diff against the input instead of the whole rewritten document.
  • --write — persist the result back to --file. The write is refused if the rewritten document no longer parses, and file writes are staged with automatic rollback.
Command Purpose
format Print a canonical, indentation-based rendering.
repair-unclosed-lists Append matching delimiters for parser-detected unclosed lists; refuse all other parse errors.
select Print the S-expression selected by --path or --at.
replace Replace the selected S-expression with replacement text.
kill Remove the selected S-expression.
wrap Wrap the selected S-expression in a new list. --delimiter paren\|bracket\|brace chooses the delimiter (default paren).
splice Remove one list pair while keeping its children.
split Split the enclosing list in two immediately before the selected expression.
join Join the selected list with its next sibling list, or two adjacent string literals, into one form.
splice-killing-backward Splice the enclosing list, keeping the selection and following siblings while removing preceding ones.
splice-killing-forward Splice the enclosing list, keeping the preceding siblings while removing the selection and following ones.
convolute Reverse the nesting of the two lists enclosing the selected list.
raise Replace the selected expression's parent list with the selection.
transpose-forward Exchange the selected expression with its next sibling while keeping trivia in place.
transpose-backward Exchange the selected expression with its previous sibling while keeping trivia in place.
slurp-forward Pull the next sibling into the selected list.
slurp-backward Pull the previous sibling into the selected list.
barf-forward Push the last child out of the selected list.
barf-backward Push the first child out of the selected list.

For example, preview then apply a wrap of the third child of the first top-level form:

paredit edit wrap --file source.lisp --path 0.2 --diff
paredit edit wrap --file source.lisp --path 0.2 --write

Refactor

paredit refactor contains the reviewable workflow commands and the semantic refactorings they gate. See Refactor workflow for the plan/preview/verify/apply lifecycle.

Workflow commands

Command Purpose
plan Produce an ordered, gated refactoring plan for AI coding agents.
verify Verify pre/post refactoring invariants for agents and CI gates.
preview Preview exact refactoring rewrites without modifying files.
check Validate a refactor preview manifest without writing files.
status Summarize a preview manifest into agent-safe next actions.
apply Apply a previously generated preview manifest with hash guards.
diff Render a verified diff from a preview manifest without writing files.
workspace-plan Discover Lisp sources under roots and build a gated refactor plan.
workspace-preview Discover sources and preview exact refactoring rewrites.
workspace-execute Execute a workspace refactor with preview gates and post-write verification.

Definition and file layout

Command Purpose
remove-definition Plan or remove a top-level definition from one file.
remove-unused-definitions Plan or remove unused top-level definitions across files.
move-definition Plan or move a top-level definition between files.
split-file Plan or split multiple top-level definitions into another file.
sort-definitions Plan or sort contiguous top-level definition blocks in one file.
move-form Plan or move any top-level form between files.
insert-top-level Insert exactly one top-level S-expression before, after, or at the end of a file.
replacement-plan Convert duplicate groups into reviewed replace-forms batches.
replace-forms Plan or replace multiple reviewed forms in one file.

Packages

Command Purpose
add-export Plan or add a symbol to a Common Lisp defpackage :export option.
sort-package-exports Plan or sort defpackage :export symbol designators.
sort-package-options Plan or sort defpackage option forms.
merge-package-options Plan or merge duplicate defpackage option forms.
rename-package Plan or rename package designators and qualified prefixes.

Renames

Command Purpose
rename-at Rename whatever symbol occupies a byte offset, dispatching to the owning namespace and scope.
rename-symbol Rename exact atom occurrences without touching strings or comments.
rename-in-form Rename exact atom occurrences inside one selected form.
rename-binding Rename one local binding and only the references in its lexical scope.
rename-symbols Plan or apply an exact atom rename across explicit files.
rename-function Plan or apply a Common Lisp callable definition and designator rename.
rename-macrolet Plan or apply a macrolet/compiler-macrolet binding and call-site rename.
rename-symbol-macro Plan or apply a define-symbol-macro binding and value-reference rename.
rename-local-function Plan or apply a flet/labels local function binding and call-site rename.

Calls and functions

Command Purpose
replace-function-calls Plan or replace callable call-site heads across explicit files.
wrap-function-calls Plan or wrap callable call sites in another function or macro call.
unwrap-function-calls Plan or remove a unary wrapper around callable call sites.
unwrap-call Replace one selected wrapper call with one selected argument.
thread-expression Convert a nested call chain into a thread-first or thread-last pipeline.
unthread-expression Convert a threading pipeline back into nested calls.
extract-function Extract the selected expression into a top-level function with inferred parameters.
extract-local-function Extract the selected expression into a Common Lisp flet or labels binding.
extract-constant Extract the selected expression into a top-level constant.
inline-function Inline one selected function call using a selected function definition.
inline-lambda Replace a safe, immediately invoked Common Lisp lambda with a parallel let.
inline-local-function Inline the sole direct call in a safe, single-binding Common Lisp flet form.
inline-symbol-macro Expand a conservative single-binding Common Lisp symbol-macrolet form.
inline-literal-constant Inline an immutable self-evaluating Common Lisp defconstant value.
convert-labels-to-flet Convert a non-recursive Common Lisp labels form into flet.
convert-flet-to-labels Convert a Common Lisp flet form into labels when definition references cannot be captured.
rename-block Rename a selected Common Lisp block and matching return-from references.
rename-tag Rename one tag in a selected Common Lisp tagbody and matching go references.
remove-unused-block Remove a selected Common Lisp block with no matching return-from.
remove-unused-tag Remove an unreferenced tag from a selected Common Lisp tagbody.

Parameters and bindings

Command Purpose
add-function-parameter Add a parameter to a selected function and explicit call sites.
move-function-parameter Move one positional parameter in a function and its call sites.
swap-function-parameters Swap two positional parameters in a function and its call sites.
reorder-function-parameters Reorder all positional parameters in a function and its call sites.
remove-function-parameter Remove one positional parameter from a function and its call sites.
introduce-let Replace the selected expression with a local binding in the enclosing list.
inline-let Inline a single local let binding into its body.
convert-let-to-let-star Convert a Common Lisp or Emacs Lisp let to let* when later initializers do not reference earlier bindings.
convert-let-star-to-let Convert a Common Lisp let* to let when later initializers do not reference earlier bindings.
convert-do-star-to-do Convert a Common Lisp do* to do when later initializers and step expressions do not reference earlier bindings.
convert-prog-star-to-prog Convert a Common Lisp prog* to prog when later initializers do not reference earlier bindings.
merge-nested-let-star Merge a directly nested Common Lisp or Emacs Lisp let* into one sequential binding form.
split-let-star Split a Common Lisp or Emacs Lisp let* into nested sequential binding forms at --binding-index.
merge-nested-let Merge directly nested Common Lisp or Emacs Lisp parallel let forms when inner initializers are independent.
merge-nested-flet Merge directly nested Common Lisp flet forms when inner definitions do not reference outer local functions.
split-let Split a Common Lisp or Emacs Lisp parallel let at --binding-index without capturing initializer references.
eliminate-empty-binding-form Remove an empty Common Lisp or Emacs Lisp let or let* from a known expression position.
flatten-progn Flatten directly nested Common Lisp or Emacs Lisp progn forms in a safe expression context.
convert-if-to-cond Convert a Common Lisp or Emacs Lisp (if test then [else]) form to cond.
convert-cond-to-if Convert simple Common Lisp or Emacs Lisp cond clauses to nested if forms.
convert-when-to-if Convert a Common Lisp or Emacs Lisp when form to if.
convert-unless-to-if Convert a Common Lisp or Emacs Lisp unless form to if.
convert-if-to-when Convert a Common Lisp or Emacs Lisp if without a meaningful else to when.
convert-if-to-unless Convert a Common Lisp or Emacs Lisp if with a literal nil then branch to unless.
remove-unused-binding Plan or remove one unused local let binding.