The @codefast/cli package is a small Node CLI (top-level commands: arrange, audit, mirror, tag). It
intentionally avoids hexagonal/DI ceremony: behavior is wired with plain functions, Commander for argv, and
Result<T, AppError> for recoverable failures.
Layout (src/)
| Area | Role |
|---|---|
bin.ts |
Process entry: shebang, runCli, process.exit. |
cli.ts |
Composition root: Command program, global options, registers createArrangeCommand(), createAuditCommand(), createMirrorCommand(), createTagCommand(). |
core/ |
Shared primitives: Result / AppError / messageFrom, filesystem (nodeFilesystem), workspace discovery, Zod parseWithSchema, logger, consumeCliAppError, path-lesson text edits, TypeScript file walking. |
core/config/ |
Zod schema (schema.ts), config load + cache (loader.ts), warning lines (warnings.ts). core/config.ts exposes loadCodefastConfig as the public Result-based API. |
arrange/ |
Tailwind cn() / tv() tooling: Commander tree (command.ts), orchestration (analyze.ts, sync.ts, workspace.ts, …), output.ts for stdout, domain/ for pure logic + domain/ast/ for AST. |
audit/ |
Read-only audits (command.ts, domain/): audit rtl scans source for physical-direction Tailwind classes (run.ts); audit links scans markdown for cross-references that resolve to nothing (run-links.ts); audit comments checks doc-comment conventions (run-comments.ts); audit react flags namespace/default React imports and implicit React.* UMD-global type references (run-react.ts). |
mirror/ |
package.json exports sync: commands, prepare / sync, workspace package sync implementation, progress presenter, domain/exports.ts for export map generation. |
tag/ |
@since JSDoc tagging: commands, prepare / sync, target discovery, since-writer, presenters. |
Boundaries
- Domain modules (
*/domain/**) stay pure where possible: no Commander, noprocess, no ad-hoc logging—only types and algorithms (I/O lives in callers or thincore/helpers). - Commands (
*/command.ts) parse argv, callparseWithSchema/consumeCliAppError, delegate toprepare*/run*functions, setprocess.exitCode. - Config is loaded once per resolved repo root via
core/config/loader.ts; warnings go throughcore/config/warnings.ts.
Imports
Internal code uses the #/… alias (see package.json imports). Prefer #/core/…, #/arrange/…, etc., over deep
relative paths.
File naming
Rationale lives in DECISIONS.md.
- Prefer one concept per filename (
grouping.ts,grouping-service.ts,analyze-service.ts,exports.ts). ast/uses short names:ast-node.ts,helpers.ts,collectors-cn.ts,targets.ts, etc.- Zod schemas are named for what they parse, not with a reserved suffix: per-command argv schemas are
cli-schema.ts, the config schema iscore/config/schema.ts. .test.tsis reserved for Vitest. No*.port.ts,*.adapter.ts,*.domain-service.ts, or*.value-object.tsin new code.
Testing
Tests live under tests/ only (see workspace rules). The CLI Vitest profile is Node, tests/**/*.test.*,
passWithNoTests: true.
Further reading
- Product behavior and commands:
README.md. - Design decisions and their reasons:
DECISIONS.md.
License
Released under the MIT License.