Cleaner is a local-first, project-level static-analysis CLI for JavaScript and TypeScript. It uses one shared TypeScript analysis program to map modules and references, find unnecessary code, and apply only validated safe cleanup edits.
Available in this milestone:
- load project files, Cleaner configuration, ignore rules, and TypeScript configuration
- build a project graph of modules, imports, exports, symbols, references, dependencies, and resolved calls
- detect unused imports, local variables, parameters, functions, and exports
- flag and safely remove standalone console/debugger statements
- report unreachable-statement and legacy-file candidates
- attach static evidence, confidence, and source locations to findings
- preview structured edits and validate proposed output in a temporary copy before applying it
- create a cleaned copy by default; modify source only in explicit write mode
Duplicate-code detection, artifact analysis, runtime observation and runtime evidence, automated test execution during validation, and advanced simplification are not part of this release. These and the broader Cleaner 2.0 vision remain future work; this release provides the static-analysis foundation.
It prioritizes correctness and transparency over aggressive cleanup.
Developer tooling should help people understand what may be unnecessary, not guess and delete. Cleaner favors:
- static evidence
- explicit review
- dry-run by default
- local execution
- no cloud dependency
- no telemetry
npm install --foreground-scripts @coachlogic/cleanerThe install prints the available starter commands. npm normally buffers output from dependency lifecycle scripts, so a plain install may not display the message. To install without foreground lifecycle output, then view help:
npm install @coachlogic/cleaner
npx cleaner --helpIf Cleaner is already installed and you want to print the install message again:
npm rebuild @coachlogic/cleaner --foreground-scriptsnpm's lifecycle banner shows the command node postinstall.cjs, followed by
Cleaner's instructions. It does not print the script source inline.
Preview findings without writing output:
npx cleaner . --diffTo create a cleaned copy, run npx cleaner .; Cleaner writes to dist/ and
leaves the source untouched. You can pin common commands in your project's
package.json:
{
"scripts": {
"cleaner:preview": "cleaner . --diff",
"cleaner:check": "cleaner . --check",
"cleaner:copy": "cleaner ."
}
}Then run npm run cleaner:preview, npm run cleaner:check, or
npm run cleaner:copy.
For repository development:
npm install
npm run build
npm link
cleaner .Create a build artifact locally:
npm packThis creates a versioned archive, for example coachlogic-cleaner-0.1.5.tgz.
Share the generated file; another user can install it with:
npm install ./coachlogic-cleaner-0.1.5.tgz
npx cleaner .Public publishing is handled by the tag-driven GitHub Actions release workflow,
not from developer workstations. After publishing, users can install it with
npm install @coachlogic/cleaner and run it with npx cleaner ..
Update package.json and package-lock.json to the intended release version,
commit that change, then create and push the matching tag:
npm version patch --no-git-tag-version
git add package.json package-lock.json
git commit -m "chore: prepare release"
git tag vX.Y.Z
git push origin HEAD
git push origin vX.Y.ZThe tag must exactly match the package version. GitHub Actions validates the
tag, runs tests and build/package checks, creates a GitHub Release with the
.tgz and its SHA256 checksum, then publishes @coachlogic/cleaner to npm via
OIDC. See docs/releasing.md for one-time setup and details.
For local development cleanup, use the confirmation-protected reset command:
npm run reset-projectIt removes only known disposable build, test, temporary, and local tarball artifacts.
Use npm run reset-project -- --yes only in an automated job that intentionally
wants those listed artifacts removed.
import jwt from "jsonwebtoken";
const value = 42;
console.log("debug");
debugger;
export function run() {
return value;
}Cleaner reports findings like:
SAFE unused-imports src/example.ts:1 - Unused import candidates detected during static scan.
SAFE console src/example.ts:3 - Console debug call(s) detected: 1.
SAFE debugger src/example.ts:4 - Debugger statement found.
Cleaner follows a strict safety rule:
When uncertain, do not modify.
Cleaner reads your source and writes a cleaned copy by default. Source files are never modified during ordinary usage. Only clearly safe findings are applied to the generated copy; warning-level findings remain review items.
cleaner srcThis produces dist/src, preserving the source directory structure. A custom
output base works the same way:
cleaner src --output cleanedThis produces cleaned/src. Existing unrelated files in the output directory
are preserved. Cleaner rejects output paths inside the input tree to prevent
recursive output such as dist/dist.
cleaner .
cleaner . --diff
cleaner . --write
cleaner . --check
cleaner . --json
cleaner . --output cleaned
cleaner . --config cleaner.config.json
cleaner --help
cleaner --version--diffanalyzes and displays safe proposed changes without creating output.--checkanalyzes without creating output and returns1when findings exist.--writeexplicitly requests source modification and requires confirmation.--forcebypasses that confirmation only when source modification was requested.--output <directory>chooses the output base; use-oas the short form.
Selecting the source directory as the output is blocked unless you confirm with
y, or explicitly use --force. Non-interactive source modification fails
instead of hanging or proceeding implicitly.
For the complete command reference, development scripts, packaging workflow, and exit behavior, see docs/cli.md.
{
"rules": {
"unused-imports": true,
"unused-variables": true,
"unused-functions": true,
"unused-parameters": true,
"unused-exports": true,
"console": true,
"debugger": true,
"dead-code": true,
"dead-files": true
},
"ignore": ["node_modules/**", "dist/**", "coverage/**"]
}Cleaner also accepts duplicate-code and artifacts as planned rule IDs, but
they are disabled by default and do not produce findings. Console calls are
removed only when they are standalone statements in a safe statement list;
calls embedded in expressions or unbraced control-flow bodies are preserved
and reported as warnings. Dead-code, unused-function, and unused-export
findings are review-only and are never automatically removed. Unused-export
results are project-local candidates; Cleaner cannot know every external entry
point or consumer.
- JavaScript
- TypeScript
- JSX / TSX support is included in the scanning model, with conservative behavior.
This is the first project-level 0.2.x milestone, not a universal code-quality
suite or semantic optimizer. Some warning-level rules remain heuristic and
non-fixable; automatic transformations are limited to findings Cleaner can
prove safe. Cleaner does not run project tests during transformation validation.
cleaner/
├── src/
│ ├── cli/
│ ├── config/
│ ├── core/
│ └── ...
├── tests/
├── fixtures/
├── docs/
├── package.json
├── cleaner.config.json
├── README.md
├── LICENSE
└── .gitignore
Contributions are welcome. A good contribution usually includes:
- a small, focused rule or detection change
- fixture coverage
- tests for positive and negative cases
- documentation updates
MIT