Skip to content

v4.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 25 Sep 12:20

clean-code.zip below is the skill for Claude Desktop / claude.ai upload. In a project, re-run the installer (scripts/install.sh or scripts/install.ps1) to update the skill and the managed block.

Full entry from CHANGELOG.md:

Stack-aware release. The skill now carries a short, strict pack for each of 21 languages and 27
frameworks, loads only the packs a project needs, and maps every file's functions and classes to
show misplaced, mixed, duplicated, badly named, and badly organized code in one place. The core
shrank, and was then rewritten terser still, so the context an agent must read before its first
edit stays small on any model.

Breaking

  • Layer rules are framework-first. The skill follows each framework's own idiomatic structure,
    and the strict Clean Architecture layer rules apply only when .clean/architecture.md declares
    layers. Projects that relied on layers being assumed should declare them; /clean-code init
    asks, and proposes an ordering from the detected layer candidates.
  • questions is now init. references/questions.md became references/init.md; the
    questions command still works as an alias. init detects the stack, runs the interview, maps
    the structure, and writes .clean/.
  • The managed block changed: placement is role-based and the layer section is framework-first.
    Re-run the installer to update it.

Added

  • Language and framework packs under references/languages/ and references/frameworks/:
    TypeScript, JavaScript, Python, Java, C, C++, C#, PHP, Go, Rust, Swift, Objective-C, Kotlin, Ruby,
    Shell, PowerShell, R, Dart, Scala, CSS, Sass; React, Next.js, Vue and Nuxt, Angular, Svelte and
    SvelteKit, Tailwind CSS, Django, Flask, FastAPI, Express, NestJS, Strapi, Spring, ASP.NET Core
    with EF Core, Laravel, Symfony, Drupal, WordPress, Ruby on Rails, Gin and Beego, Ktor, Jetpack
    Compose, SwiftUI and UIKit, Flutter, Unity, TensorFlow, PyTorch. Language packs cover names,
    functions and types, errors, modules, placement, tests, layers, enforcement, and smells; framework
    packs cover structure, roles, rules, layers, tests, enforcement, and smells; every pack's Names
    section now names its stack's concrete naming anti-patterns. Each stays under 2,000 tokens.
    Detection covers Tailwind CSS (tailwindcss in package.json), Drupal (drupal/core in
    composer.json), WordPress (wp-config.php, wp-content/, or WordPress core in
    composer.json), and Unity (ProjectSettings/ProjectVersion.txt or com.unity. packages); the
    project walker skips each framework's generated folders (Unity's Library/, Temp/, Logs/,
    UserSettings/; Drupal's core/, contrib/; WordPress's wp-admin/, wp-includes/; Flutter's
    ephemeral build output). Laravel's pack states each major version's support status, re-verified
    against current documentation.
  • Pack routing. framework-map.md holds the pack index; detect_stack.py records the selected
    packs in context.json as packs and prints them under Read next. It also detects SvelteKit,
    Strapi, Beego, Spring, the ASP.NET Core SDK, Jetpack Compose, Ktor, SwiftUI, and UIKit.
  • map_structure.py, the structure map: one table of every file with its purpose, symbols,
    and roles, plus findings for misplaced symbols (middleware declared in auth.ts is reported with
    a move to middleware/), files mixing several roles, duplicated bodies, clashing names, synonym
    verbs, component coupling metrics (Ca, Ce, I, A, D), and folder cycles; C#, JVM, and PHP
    dependencies follow the type names code uses, not namespace imports. --write saves
    .clean/structure.md and .clean/structure.json; the SessionStart hook prints the findings.
    Symbol extraction covers all 21 languages with the standard library only.
  • Naming findings: a names kind reports classes, functions, methods, and (in Python,
    JavaScript, and TypeScript) variables and parameters that break a Clean Code naming rule — vague,
    encoded (Hungarian prefixes, a stray I), numbered (_v2, data2), a noise word (Manager,
    Helper), a verb-named class, too short, off the language's own casing convention (with the
    project's own majority casing overriding it where established), or a file whose one public type
    is named for something else — each citing N1, N3, N4, N5, N6, G17, or G24 and judged per project
    in a monorepo.
  • Organization findings: family (three or more files sharing a name that import each other,
    proposed as a folder named for it — Common Closure Principle, Screaming Architecture),
    junk_drawer (a folder named for no concept holding production files, split by family and by
    role, or renamed when it holds one concept), flat_folder (more than fifteen production files in
    one folder with no grouping), unreferenced (a production file nothing imports and no role,
    entry point, or entry line explains — "possibly unused", G9), and comment_heavy (a file whose
    comments are at least 40% of its non-blank lines and at least twenty in number — routed to the
    new comment-cleanup workflow). structure.md gains a Proposed moves section
    (source -> destination) the clean-up campaign's placement batch consumes.
    map_structure.py --changed limits every finding, including these, to files git reports as
    changed, for the new review command. An entry <glob> line in a clean-roles block names
    files a framework loads without an import: never unreferenced, and a folder holding only them is
    no junk drawer. Files a manifest runs (pyproject [project.scripts], [project.gui-scripts],
    and [project.entry-points.*] targets; package.json bin, main, module, and exports)
    are entries without one. A junk-drawer name a convention chose is not reported: the folder-glob
    home of the role it is named for (Rails' app/helpers/) or a shared/ folder of components,
    directives, and pipes (Angular). accept and ignore-name now also silence naming and
    organization findings, not only placement ones.
  • skills/clean-code/scripts/ reorganized into source/ (file walking, the lexer, import
    parsing and resolution), symbols/ (symbol extraction per language), and structure/ (roles,
    findings, naming, organization, metrics, and the report), with the five CLIs
    (detect_stack.py, scan_repo.py, check_boundaries.py, map_structure.py,
    check_compression.py) staying at the top level so every documented command and manual
    equivalent keeps working; behavior-preserving, verified by the unit tests before and after.
  • Role conventions in fenced clean-roles blocks (homes by glob, roles by name or declaration
    signal), overridable per project in .clean/roles.md. allow lets a home hold other roles (a
    React context file keeps its Provider and hook; an Express controller may take next), and
    accept <glob> [= symbols] records a deliberate exception so the map stops flagging it. In a
    monorepo, a framework pack speaks only for the project whose manifest named its framework
    (pack_scopes in context.json), supersede applies within one manifest, so a Strapi CMS
    never hides the React app beside it, and move suggestions and name clashes stay inside one
    project.
  • An eval harness: evals/grade.py grades a run against a case's deterministic expectations
    and exports evals/evals.json in skill-creator's layout; 49 cases (eleven core, 38 covering 37
    packs, the sixteen listed below included), and evals/triggers.json for description tuning.
    evals/README.md records the first benchmark on ten cases: every expectation passed with the
    skill, 95.5% without it, at about 48% more tokens per task.
  • A unit test suite (tests/, standard library unittest), run by validate.sh and CI.
  • docs/install.md, docs/configuration.md, and docs/pack-sources.md, the sources each pack was
    checked against.
  • scripts/check_originality.py for contributors: it compares shipped Markdown with the local,
    gitignored books for runs of eight or more shared words. Its first run led to rewording sixteen
    such runs in references that predate this release.
  • Agent smells A1-A10 (hallucinated API, unverified dependency, context loss, scope creep,
    duplicate implementation, wrong-file gravity, phantom success, test weakening, speculative
    abstraction, silent architecture drift) replace the untagged Agent Failure Modes table in
    review-checklist.md, each with a signal and a response, cited beside the book's G, N, and T
    IDs in chapter-map.md and smell-triage.md.
  • /clean-code plan <task> (references/plan.md): reads project context, finds existing
    implementations before proposing new ones, verifies every API the plan relies on against the
    installed version, places the change, and writes a plan under fixed headings; edits nothing.
  • /clean-code review [files]: reviews the working tree, then staged changes, then named
    files, with map_structure.py --changed, check_boundaries.py, and the checklist; findings
    first, ranked P0-P3, each with a smell ID; edits nothing.
  • Risk-based verification: every change is rated LOW, MEDIUM, or HIGH from its scope, blast
    radius, uncertainty, and reversibility, each level naming the checks it owes before completion
    (review-checklist.md, Risk Levels); the operating loop's verify step points to it.
    (kyuna0312's PR #6 proposed the agent
    smells, the plan and review commands, risk-based verification, and planted-flaw evals; all
    four landed in this release.)
  • references/patterns.md: architecture styles (DI, IoC, MVC, MVVM, CQRS, Clean, Hexagonal,
    Microservices, Event-Driven), design patterns (Repository, Unit of Work, Factory, Singleton,
    Strategy, Adapter, Decorator, Observer, Command), and data-access approaches (ORM, Micro ORM,
    Query Builder, Data Mapper, Active Record) — each with its intent, when it earns its place, when
    it is over-engineering, its link to the books, and which packs' frameworks embody it.
  • /clean-code compress [files] (references/compress.md) and
    scripts/check_compression.py: rewrite a project's own instruction files (AGENTS.md,
    CLAUDE.md, GEMINI.md, DESIGN.md, ARCHITECTURE.md, CONTRIBUTING.md, .cursor/.github
    rule files) tersely, keeping a <name>.original.md backup and never touching the managed block;
    the checker exits 1 when a rewrite lost a heading, a code block, an inline code span, a URL, a
    rule ID, or a number, and reports the token reduction.
  • Comment cleanup (references/comments.md, routed from the Load Plan and clean-up):
    classifies each comment by Clean Code's kept and deleted kinds, turns "what" comments into names,
    and rewrites surviving "why" comments tersely; the comment_heavy finding picks the files.
  • The minimal-code ladder, an always-on SKILL.md rule: before writing code, ask in order
    whether it needs to exist, whether the codebase already has it, whether the standard library or
    an installed dependency does it, whether it is one line; safety is never cut.
  • Six new eval cases graded by planted-flaw and stack-specific expectations (hallucinated-api,
    unverified-success, unsafe-refactor, frontend-data-fetch, naming-cleanup, comment-cleanup), plus
    eight new pack cases; grade.py gained the command_passes expectation (runs a command in the
    workspace, passes on exit 0) and new_file now accepts a list of globs. evals/README.md records
    a second benchmark iteration across the new cases and two of the first iteration's, with and
    without the skill, on Sonnet and Haiku.
  • After the second benchmark:
    • An explicit request widens the surgical scope. When the request names an outcome that needs an
      out-of-scope fix ("make the suite pass"), the agent makes the smallest fix and reports it
      separately, never by weakening a test (SKILL.md Scope Modes and the managed block).
    • The context gate tells the agent to open each listed pack before the first edit; naming a pack
      is not reading it. The completion checklist checks it.
    • references/tools.md holds what each script answers, its flags, exit codes, and finding keys;
      SKILL.md keeps one line pointing there.
    • The walker lists files through git ls-files --cached --others --exclude-standard, so a
      project's ignored folders drop out. It falls back to os.walk outside git or when git ignores
      the root, and still walks submodules and nested repositories.
    • Poetry scripts and plugins, inline [project] script tables, setup.cfg [options.entry_points],
      and extension-less package.json targets count as entry points.
    • Two harder planted-flaw cases (secret-logging, swallowed-error); unverified-success checks the
      suite. A third, targeted benchmark iteration is recorded in evals/README.md.

Terse Content

The always-loaded managed block and SKILL.md, and every reference and pack, were rewritten in a
caveman-derived terse style — full words, imperative verbs, no filler or hedging — cutting the
top-level references by about 21% (30,263 to about 24,000 words) with no technical loss, verified
by the new check_compression.py against every rewrite. The minimal-code ladder is credited to the
ponytail plugin and the terse style to
caveman; this release adopts both as skill rules and
workflows rather than depending on either plugin, so the skill keeps working with no tooling in any
host.

Changed

  • SKILL.md is a lean router, terse-rewritten to about 1,500 tokens, that opens with the
    context gate: read .clean/, run the detector, read the named packs, then edit; a trivial edit
    skips the gate. validate.sh enforces the budgets: SKILL.md at most 1,500 tokens, the managed
    block 750, each pack 2,000. The security rule stays in the always-on rules, and the Load Plan
    routes security, trust boundaries, and performance questions to principles.md.
  • The README is short; install and configuration details moved to docs/.
  • The agent failure-mode and anti-loophole tables moved from SKILL.md to review-checklist.md.
  • Import parsing moved into source/imports.py and now reads Shell, PowerShell, R, and
    Objective-C imports; scan_repo.py shares the generated-file detection of the new map, which
    also recognizes generated files by name (*.Designer.cs, *.g.cs, *_pb2.py, *.pb.go, and
    minified or bundled *.min.js, *.min.mjs, *.min.css, *.bundle.js), so they no longer count
    as large files or flood the naming findings.
  • Every script prints UTF-8, so non-ASCII text reaches an agent intact through a Windows pipe.
  • A file counts as generated only when the comment that opens it says so, not when its prose
    mentions a generated report.
  • detect_stack.py detects JavaScript frameworks by exact package.json dependency keys, so
    next-themes in a Vite app no longer reads as Next.js.
  • The SessionStart hook resolves pack paths against the installed skill and says nothing about
    layers in a project without .clean/.

Fixed

  • check_boundaries.py layer globs: a leading **/ now also matches a top-level folder, and
    folders that only hold sources (src/main/java, include/, R's R/) no longer become namespace
    tokens, which had classified every java.* import into the first declared layer.
  • check_boundaries.py sees an import on the first line of a file that starts with a
    byte-order mark
    , as Visual Studio writes by default; such a file could pass while breaking the
    declared layers.
  • No script crashes on piped output in Windows when a path, name, or summary holds a
    character outside the console code page; detect_stack.py --write saves its file first.
  • check_boundaries.py resolves tsconfig and jsconfig paths aliases and baseUrl imports, so
    a domain file importing @/lib/db in a Next.js app no longer passes. An import that resolves to
    a project file outside every declared layer prints a WARN line, and --json lists it under
    unlayered_imports.
  • Installs and the release zip carry no Python bytecode; the installers skip __pycache__,
    and the release job fails if its archive holds any.