@codefast/cli 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +596 -0
  2. package/LICENSE +1 -1
  3. package/README.md +335 -129
  4. package/dist/arrange/analyze.d.ts +10 -0
  5. package/dist/arrange/cli-schema.d.ts +50 -0
  6. package/dist/arrange/command.d.ts +7 -0
  7. package/dist/arrange/domain/analyze-service.d.ts +18 -0
  8. package/dist/arrange/domain/ast/ast-node.d.ts +394 -0
  9. package/dist/arrange/domain/ast/collectors-cn.d.ts +26 -0
  10. package/dist/arrange/domain/ast/collectors-jsx.d.ts +8 -0
  11. package/dist/arrange/domain/ast/collectors-tv.d.ts +34 -0
  12. package/dist/arrange/domain/ast/helpers.d.ts +36 -0
  13. package/dist/arrange/domain/ast/helpers.js +1 -0
  14. package/dist/arrange/domain/ast/simplify-targets.d.ts +22 -0
  15. package/dist/arrange/domain/ast/targets.d.ts +20 -0
  16. package/dist/arrange/domain/constants.d.ts +111 -0
  17. package/dist/arrange/domain/grouping-service.d.ts +100 -0
  18. package/dist/arrange/domain/grouping.d.ts +21 -0
  19. package/dist/arrange/domain/imports.d.ts +14 -0
  20. package/dist/arrange/domain/source-text-formatters.d.ts +33 -0
  21. package/dist/arrange/domain/tailwind-token.d.ts +24 -0
  22. package/dist/arrange/domain/token-classifier.d.ts +47 -0
  23. package/dist/arrange/domain/types.d.ts +208 -0
  24. package/dist/arrange/output.d.ts +26 -0
  25. package/dist/arrange/process-file.d.ts +11 -0
  26. package/dist/arrange/resolve-target.d.ts +10 -0
  27. package/dist/arrange/resolve-target.js +3 -16
  28. package/dist/arrange/scan-target.d.ts +7 -0
  29. package/dist/arrange/simplify-process-file.d.ts +11 -0
  30. package/dist/arrange/simplify-sync.d.ts +13 -0
  31. package/dist/arrange/source-parse.d.ts +7 -0
  32. package/dist/arrange/suggest.d.ts +8 -0
  33. package/dist/arrange/sync.d.ts +11 -0
  34. package/dist/arrange/typescript-ast-translator.d.ts +31 -0
  35. package/dist/arrange/workspace.d.ts +13 -0
  36. package/dist/arrange/workspace.js +2 -2
  37. package/dist/audit/cli-schema.d.ts +93 -0
  38. package/dist/audit/cli-schema.js +14 -3
  39. package/dist/audit/command.d.ts +8 -0
  40. package/dist/audit/command.js +52 -12
  41. package/dist/audit/domain/audit-file.d.ts +7 -0
  42. package/dist/audit/domain/comment-content.d.ts +26 -0
  43. package/dist/audit/domain/comment-dividers.d.ts +62 -0
  44. package/dist/audit/domain/comment-dividers.js +48 -20
  45. package/dist/audit/domain/display-names.d.ts +11 -0
  46. package/dist/audit/domain/display-names.js +71 -0
  47. package/dist/audit/domain/import-policy.d.ts +34 -0
  48. package/dist/audit/domain/import-policy.js +147 -0
  49. package/dist/audit/domain/link-references.d.ts +40 -0
  50. package/dist/audit/domain/mappings.d.ts +45 -0
  51. package/dist/audit/domain/markdown-links.d.ts +44 -0
  52. package/dist/audit/domain/since-versions.d.ts +26 -0
  53. package/dist/audit/domain/tokenize.d.ts +14 -0
  54. package/dist/audit/domain/tsdoc-syntax.d.ts +20 -0
  55. package/dist/audit/domain/types.d.ts +171 -0
  56. package/dist/audit/output.d.ts +91 -0
  57. package/dist/audit/output.js +52 -11
  58. package/dist/audit/prepare.d.ts +70 -0
  59. package/dist/audit/prepare.js +41 -10
  60. package/dist/audit/run-comments.d.ts +17 -0
  61. package/dist/audit/run-comments.js +22 -18
  62. package/dist/audit/run-display-names.d.ts +14 -0
  63. package/dist/audit/run-display-names.js +60 -0
  64. package/dist/audit/run-imports.d.ts +14 -0
  65. package/dist/audit/{run-react.js → run-imports.js} +15 -5
  66. package/dist/audit/run-links.d.ts +14 -0
  67. package/dist/audit/run.d.ts +14 -0
  68. package/dist/bin.d.ts +2 -0
  69. package/dist/cli.d.ts +6 -0
  70. package/dist/core/cli/format-error.d.ts +7 -0
  71. package/dist/core/cli/global-options.d.ts +15 -0
  72. package/dist/core/cli/positional.d.ts +6 -0
  73. package/dist/core/cli/result-handle.d.ts +19 -0
  74. package/dist/core/config/define-config.d.ts +7 -0
  75. package/dist/core/config/define-config.js +8 -0
  76. package/dist/core/config/loader.d.ts +18 -0
  77. package/dist/core/config/loader.js +2 -7
  78. package/dist/core/config/schema.d.ts +99 -0
  79. package/dist/core/config/schema.js +7 -75
  80. package/dist/core/config/warnings.d.ts +6 -0
  81. package/dist/core/config.d.ts +12 -0
  82. package/dist/core/errors.d.ts +25 -0
  83. package/dist/core/exit-codes.d.ts +18 -0
  84. package/dist/core/filesystem/node.d.ts +7 -0
  85. package/dist/core/filesystem/node.js +1 -0
  86. package/dist/core/filesystem/port.d.ts +44 -0
  87. package/dist/core/glob.d.ts +19 -0
  88. package/dist/core/logger.d.ts +9 -0
  89. package/dist/core/result.d.ts +30 -0
  90. package/dist/core/schema-parse.d.ts +9 -0
  91. package/dist/core/source-text-edit.d.ts +33 -0
  92. package/dist/core/verbose-diagnostics.d.ts +6 -0
  93. package/dist/core/workspace/ancestor-directories.d.ts +12 -0
  94. package/dist/core/workspace/ancestor-directories.js +30 -0
  95. package/dist/core/workspace/markdown-walk.d.ts +7 -0
  96. package/dist/core/workspace/markdown-walk.js +2 -20
  97. package/dist/core/workspace/package-version.d.ts +9 -0
  98. package/dist/core/workspace/package-version.js +8 -12
  99. package/dist/core/workspace/resolver.d.ts +39 -0
  100. package/dist/core/workspace/resolver.js +58 -75
  101. package/dist/core/workspace/skip-directories.d.ts +6 -0
  102. package/dist/core/workspace/source-walk.d.ts +16 -0
  103. package/dist/core/workspace/source-walk.js +14 -20
  104. package/dist/core/workspace/typescript-walk.d.ts +7 -0
  105. package/dist/core/workspace/typescript-walk.js +2 -23
  106. package/dist/core/workspace/walk-files.d.ts +7 -0
  107. package/dist/core/workspace/walk-files.js +27 -0
  108. package/dist/core/workspace/well-known-files.d.ts +18 -0
  109. package/dist/core/workspace/well-known-files.js +18 -0
  110. package/dist/index.d.ts +6 -0
  111. package/dist/index.js +5 -0
  112. package/dist/mirror/cli-result.d.ts +13 -0
  113. package/dist/mirror/cli-schema.d.ts +8 -0
  114. package/dist/mirror/command.d.ts +7 -0
  115. package/dist/mirror/dist-filesystem-impl.d.ts +8 -0
  116. package/dist/mirror/domain/constants.d.ts +18 -0
  117. package/dist/mirror/domain/constants.js +0 -12
  118. package/dist/mirror/domain/dirent-guard.d.ts +10 -0
  119. package/dist/mirror/domain/dist-filesystem.d.ts +9 -0
  120. package/dist/mirror/domain/errors.d.ts +24 -0
  121. package/dist/mirror/domain/exports.d.ts +36 -0
  122. package/dist/mirror/domain/package-display-name.d.ts +8 -0
  123. package/dist/mirror/domain/path-normalizer.d.ts +6 -0
  124. package/dist/mirror/domain/types.d.ts +131 -0
  125. package/dist/mirror/output.d.ts +23 -0
  126. package/dist/mirror/package-path.d.ts +19 -0
  127. package/dist/mirror/prepare.d.ts +15 -0
  128. package/dist/mirror/prepare.js +2 -2
  129. package/dist/mirror/supplement-exports.d.ts +27 -0
  130. package/dist/mirror/supplement-exports.js +2 -2
  131. package/dist/mirror/sync-reporter.d.ts +60 -0
  132. package/dist/mirror/sync-reporter.js +4 -0
  133. package/dist/mirror/sync-types.d.ts +43 -0
  134. package/dist/mirror/sync-workspace-package.d.ts +9 -0
  135. package/dist/mirror/sync-workspace-package.js +3 -3
  136. package/dist/mirror/sync.d.ts +12 -0
  137. package/dist/mirror/sync.js +5 -3
  138. package/dist/mirror/write-exports.d.ts +15 -0
  139. package/dist/pack-slim/cli-result.d.ts +13 -0
  140. package/dist/pack-slim/cli-schema.d.ts +17 -0
  141. package/dist/pack-slim/command.d.ts +7 -0
  142. package/dist/pack-slim/command.js +5 -4
  143. package/dist/pack-slim/domain/transform.d.ts +69 -0
  144. package/dist/pack-slim/domain/transform.js +141 -15
  145. package/dist/pack-slim/domain/types.d.ts +46 -0
  146. package/dist/pack-slim/output.d.ts +15 -0
  147. package/dist/pack-slim/output.js +10 -1
  148. package/dist/pack-slim/sync.d.ts +23 -0
  149. package/dist/pack-slim/sync.js +14 -8
  150. package/dist/pack-slim/working-tree.d.ts +20 -0
  151. package/dist/tag/cli-result.d.ts +7 -0
  152. package/dist/tag/cli-schema.d.ts +8 -0
  153. package/dist/tag/command.d.ts +7 -0
  154. package/dist/tag/domain/types.d.ts +111 -0
  155. package/dist/tag/output.d.ts +17 -0
  156. package/dist/tag/prepare.d.ts +13 -0
  157. package/dist/tag/prepare.js +2 -2
  158. package/dist/tag/resolve-target-path.d.ts +10 -0
  159. package/dist/tag/since-writer.d.ts +32 -0
  160. package/dist/tag/sync.d.ts +42 -0
  161. package/dist/tag/target-candidates.d.ts +8 -0
  162. package/dist/tag/target-candidates.js +1 -1
  163. package/dist/tag/target-runner.d.ts +8 -0
  164. package/dist/tag/version-resolver.d.ts +7 -0
  165. package/package.json +16 -33
  166. package/dist/audit/domain/react-imports.js +0 -91
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 CodeFast Labs
3
+ Copyright (c) 2024 Codefast Labs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,73 +1,109 @@
1
1
  # @codefast/cli
2
2
 
3
- The `codefast` command line for the [codefast monorepo](https://github.com/codefastlabs/codefast): `arrange` Tailwind
4
- class strings, `audit` source conventions, `mirror` export maps from `dist/`, `pack-slim` the publish artifact, and
5
- `tag` exported APIs with `@since`.
3
+ `codefast` is a small, dependency-light CLI toolkit for TypeScript projects — a **pnpm workspace** or a **single
4
+ package**. It reorders Tailwind classes, generates `package.json#exports` from a package's built `dist/`, slims the npm
5
+ tarball at publish time, stamps `@since` on your public API, and audits a handful of source and documentation
6
+ conventions.
7
+
8
+ It was built for — and is exercised daily by — the [codefast monorepo](https://github.com/codefastlabs/codefast), but
9
+ nothing here is codefast-only: run it in any pnpm workspace, or in a standalone package, and it works. A few audits
10
+ encode an opinionated house style (called out below) that you can adopt, ignore, or narrow with an allowlist.
6
11
 
7
12
  [![npm version](https://img.shields.io/npm/v/@codefast/cli)](https://www.npmjs.com/package/@codefast/cli)
8
13
  [![license](https://img.shields.io/npm/l/@codefast/cli)](./LICENSE)
9
14
 
10
- ## Overview
11
-
12
- `codefast` is the command line for the [codefast monorepo](https://github.com/codefastlabs/codefast). It has five
13
- commands: `arrange` regroups Tailwind class strings, `audit` checks source conventions, `mirror` writes
14
- `package.json#exports` from `dist/`, `pack-slim` strips the source lane from the publish artifact, and `tag` stamps
15
- exported APIs with `@since`.
15
+ ## Design principles
16
16
 
17
- This is repo tooling, published to npm. It runs in any pnpm workspace with a similar layout, but its flags and defaults
18
- follow the codefast conventions rather than aiming to be a general-purpose product.
19
-
20
- - **Safe by default.** Every writing command has `--dry-run`, and every audit is read-only except
17
+ - **Safe by default.** Every writing command supports `--dry-run`, and every audit is read-only except
21
18
  `audit comments --fix`.
22
- - **Scriptable.** `--json` prints one JSON object on stdout and suppresses human progress output.
23
- - **CI-ready.** Audits exit non-zero when findings remain, so they gate a pipeline without extra glue.
19
+ - **Scriptable.** `--json` prints one JSON object on stdout and suppresses the human progress output.
20
+ - **CI-ready.** Audits exit non-zero when findings remain, so they gate a pipeline with no extra glue.
24
21
  - **Configurable.** An optional `codefast.config.*` file, validated by a strict schema, adjusts every command.
25
22
 
26
- ## Installation and usage
23
+ ## Requirements
24
+
25
+ - **Node.js ≥ 24** (the CLI is published as ESM).
26
+ - **A project root — workspace or single package.** Commands resolve their root by walking up from the current
27
+ directory: the nearest `pnpm-workspace.yaml` marks a **workspace** (every package under it is in scope), and with no
28
+ workspace file the nearest `package.json` marks a **single package** (that one package is the whole scope). Only
29
+ `arrange group` needs no project at all — it just formats a string you paste in.
30
+ - **pnpm is not required to _run_ the CLI** — npm, npx, or a plain `node` invocation are all fine. pnpm matters only for
31
+ workspace-wide behavior, which is keyed off `pnpm-workspace.yaml`.
27
32
 
28
- Inside the codefast monorepo, the CLI runs from its built output via root `package.json` scripts:
33
+ ## Install
34
+
35
+ Install it globally, or run it once without installing:
29
36
 
30
37
  ```bash
31
- pnpm --filter @codefast/cli build # produce dist/bin.js first
38
+ # global install (pick your package manager)
39
+ pnpm add -g @codefast/cli
40
+ npm install -g @codefast/cli
32
41
 
33
- pnpm run codefast <command> # generic entry: node ./packages/cli/dist/bin.js
42
+ # one-off, no install
43
+ pnpm dlx @codefast/cli --help
44
+ npx @codefast/cli --help
45
+ ```
34
46
 
35
- # Convenience wrappers
36
- pnpm run cli:arrange # codefast arrange
37
- pnpm run cli:arrange:inspect # codefast arrange inspect
38
- pnpm run cli:arrange:preview # codefast arrange --dry-run
39
- pnpm run cli:arrange:simplify # codefast arrange simplify
40
- pnpm run cli:arrange:simplify:preview
41
- pnpm run cli:mirror # codefast mirror
42
- pnpm run cli:mirror:preview # codefast mirror --dry-run
43
- pnpm run cli:audit:rtl # codefast audit rtl
44
- pnpm run cli:audit:links # codefast audit links
45
- pnpm run cli:audit:comments # codefast audit comments
46
- pnpm run cli:audit:react # codefast audit react
47
+ As a project dev dependency:
48
+
49
+ ```bash
50
+ pnpm add -D @codefast/cli
51
+ pnpm exec codefast --help
47
52
  ```
48
53
 
49
- `pnpm run version-packages` runs `changeset version` and then `codefast tag`, so published APIs are stamped at release.
54
+ The package is published on the `0.x` line and versioned on its own track: **breaking changes ship as minor versions**,
55
+ so pin the minor (`@codefast/cli@~0.9.0`) when you need stability.
50
56
 
51
- Standalone install (Node >= 24):
57
+ ## Quick start
52
58
 
53
59
  ```bash
54
- pnpm add -g @codefast/cli
55
- # or one-off
56
- pnpm dlx @codefast/cli --help
60
+ codefast --help # list commands
61
+ codefast arrange group "flex h-10 w-full rounded-md bg-primary" # works anywhere, no project needed
62
+
63
+ # in a workspace or a single-package project:
64
+ codefast arrange inspect packages/ui/src # read-only report of arrange findings
65
+ codefast arrange --dry-run packages/ui/src # preview a class reorder
66
+ codefast mirror --dry-run # preview generated exports for each package in scope
67
+ codefast audit links # find broken markdown cross-references
57
68
  ```
58
69
 
59
- The package is published on 0.x and versioned on its own track: breaking changes ship as minor versions, so pin the
60
- minor version when you need stability.
61
-
62
- Every writing command writes by default; pass `--dry-run` to preview. The global `--no-color` flag must come before the
63
- command name (`codefast --no-color mirror`). Commands that accept `--json` print a single JSON object on stdout and
64
- suppress human progress output.
70
+ ## Global options and conventions
71
+
72
+ - `codefast --help` lists the commands, and `--help` on any command shows its usage; `codefast --version` prints the
73
+ installed version.
74
+ - **The global `--no-color` flag must come _before_ the command name** — `codefast --no-color mirror`, not
75
+ `codefast mirror --no-color`.
76
+ - **Writing commands write by default; pass `--dry-run` to preview.** The audits are read-only — the one exception is
77
+ `audit comments --fix`, which repairs section dividers in place.
78
+ - **`--json` prints a single JSON object on stdout** and suppresses the human-readable progress output, so any command
79
+ can gate a script or a CI job.
80
+
81
+ ## Commands at a glance
82
+
83
+ | Command | What it does | Writes? |
84
+ | --------------------- | -------------------------------------------------------------------------- | ----------------- |
85
+ | `arrange` | Regroup Tailwind classes in `cn()` / `tv()` calls in render-pipeline order | yes (`--dry-run`) |
86
+ | `mirror` | Write each package's `package.json#exports` from its built `dist/` | yes (`--dry-run`) |
87
+ | `pack-slim` | Strip the dev-only surface from a package right before publish | yes (`--dry-run`) |
88
+ | `tag` | Stamp `@since <version>` on exported declarations that lack one | yes (`--dry-run`) |
89
+ | `audit links` | Report markdown cross-references that resolve to nothing | no |
90
+ | `audit rtl` | Report physical-direction Tailwind classes that should be logical | no |
91
+ | `audit imports` | Enforce the import policy (React by-name, Zod namespace in front-end) | no (report only) |
92
+ | `audit comments` | Check doc-comment conventions; repair section dividers | `--fix` only |
93
+ | `audit display-names` | Enforce the `namespace:Name` display-name convention | no |
94
+
95
+ **Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`, and `audit links` are general-purpose — they
96
+ work for any pnpm workspace or single package that builds with `tsc`. The other four audits encode codefast's own house
97
+ style (logical Tailwind directions, named React imports, a specific comment/divider grammar, a `namespace:Name` scheme
98
+ for `@codefast/di` tokens). Adopt them if they fit your project; otherwise skip them, or use an allowlist to narrow
99
+ their scope.
65
100
 
66
101
  ## `arrange`
67
102
 
68
103
  Rewrites Tailwind class strings inside `cn()` and `tv()` calls, regrouping utilities in render-pipeline order —
69
104
  existence, position, layout, sizing, spacing, shape, background, shadow, typography, composite, motion, starting,
70
- behavior, state, selector — instead of alphabetically.
105
+ behavior, state, selector — rather than alphabetically. (This is codefast's ordering, deliberately different from the
106
+ official Prettier Tailwind plugin's sort.)
71
107
 
72
108
  ```bash
73
109
  codefast arrange inspect packages/ui/src # read-only report
@@ -75,9 +111,14 @@ codefast arrange --dry-run packages/ui/src # preview the rewrite
75
111
  codefast arrange packages/ui/src # write
76
112
  ```
77
113
 
78
- When `[target]` is omitted, `arrange` uses the nearest directory with a `package.json` found by walking up from the
79
- current working directory. Directory scans skip test files (`*.test.*` / `*.spec.*`), because a `cn()` inside an
80
- assertion is intentional; pass such a file explicitly to process it.
114
+ When `[target]` is omitted, `arrange` uses the nearest directory with a `package.json`, walking up from the current
115
+ directory. Directory scans skip test files (`*.test.*` / `*.spec.*`), because a `cn()` inside an assertion is
116
+ intentional; pass such a file explicitly to process it.
117
+
118
+ `arrange` rewrites a `cn()` / `tv()` call only when its binding is imported from a recognized module — `clsx`,
119
+ `class-variance-authority`, `tailwind-variants`, `@codefast/tailwind-variants`, a `@/lib/utils` / `~/lib/utils` /
120
+ `#lib/utils` re-export, any `…/utils` path, or a dedicated `cn.ts` module — so an unrelated local `cn` is left alone.
121
+ Long static JSX `className` strings are regrouped regardless of where `cn` comes from.
81
122
 
82
123
  | Flag | Description |
83
124
  | -------------------- | ----------------------------------------------------------------------------- |
@@ -99,7 +140,8 @@ Accepts `--dry-run` and `--json`.
99
140
 
100
141
  ### `arrange group <tokens...>`
101
142
 
102
- Groups a pasted class string without touching the filesystem — useful for checking how classes would be bucketed:
143
+ Groups a pasted class string without touching the filesystem — the one command that needs no workspace, useful for
144
+ checking how classes would be bucketed:
103
145
 
104
146
  ```bash
105
147
  codefast arrange group "relative flex h-10 w-full items-center rounded-md bg-primary"
@@ -114,10 +156,10 @@ codefast arrange group --tv "flex items-center gap-2"
114
156
 
115
157
  ## `mirror`
116
158
 
117
- Scans each workspace package's built `dist/` tree and writes its `package.json#exports` map, plus top-level `main`,
118
- `module`, and `types` mirrored from the root export and a `files` entry for `dist`. The workspace root is the directory
119
- holding `pnpm-workspace.yaml`, so it runs from anywhere inside the repo. Build first — `mirror` reads `dist/`, and stale
120
- output produces stale exports.
159
+ Scans each package's built `dist/` tree and writes its `package.json#exports` map, plus top-level `main`, `module`, and
160
+ `types` mirrored from the root export and a `files` entry for `dist`. In a workspace it processes every package under
161
+ `pnpm-workspace.yaml`; in a single-package project it processes that one package. **Build first** — `mirror` reads
162
+ `dist/`, and stale output produces stale exports.
121
163
 
122
164
  ```bash
123
165
  codefast mirror # all workspace packages
@@ -133,19 +175,45 @@ codefast mirror --dry-run # report changes without writing
133
175
 
134
176
  Exits `1` when any package fails, `0` otherwise.
135
177
 
178
+ ### Per-package `mirror` configuration
179
+
180
+ The `mirror` config is a record keyed by package name, set under `mirror` in `codefast.config.*`. Set a package to
181
+ `false` to skip it entirely; omit a package to process it with defaults. For a package you do configure, these keys
182
+ apply (see the [Configuration](#configuration) example for their shape):
183
+
184
+ - **`source`** (`boolean | string`, default `true`) — emit a `source` condition pointing at the original `.ts` so a
185
+ consumer using the `source` condition resolves your `src/`. A string overrides the root-export source path explicitly.
186
+ - **`types`** (`boolean`, default `true`) — emit the `types` condition when a matching `.d.ts` exists.
187
+ - **`import`** (`boolean`, default `true`) — emit the `import` condition.
188
+ - **`preserve`** (`boolean`) — keep the existing `package.json#exports` map as written and only fill in the missing
189
+ `source` / `types` / `import` conditions; no `dist/` scan runs, so the public surface stays exactly what you declared.
190
+ - **`strip`** (`string`) — a `dist/` path prefix to flatten out of the generated specifiers, so `./components/button` is
191
+ published as `./button` rather than leaking the internal folder.
192
+ - **`exclude`** (`string[]`) — specifiers to leave out of the generated map, making a package's public surface a
193
+ decision rather than a consequence of its `dist/` layout. Matched against the specifier as it appears in `exports`
194
+ (after `strip`); a trailing `/*` excludes a whole subtree. The root export and `./package.json` are never excluded.
195
+ - **`exports`** (`Record<string, string>`) — extra or overriding entries merged into the generated map, for specifiers
196
+ the `dist/` scan does not produce (for example a raw CSS source path).
197
+ - **`css`** (`boolean | { enabled?, forceExportFiles?, customExports? }`) — how CSS files in `dist/` become exports:
198
+ `true` enables the default wildcard handling, and the object form tunes it (`enabled` toggles it, `forceExportFiles`
199
+ adds them to `files`, `customExports` sets explicit per-file CSS entries).
200
+
201
+ `source`, `types`, and `import` default to `true`, so an empty config object still emits all three.
202
+
136
203
  ## `pack-slim`
137
204
 
138
- Strips the source lane from published packages so the npm tarball ships `dist` runtime and types only. Where `mirror`
139
- writes the full exports — including the `source` condition — for repo dev, `pack-slim` removes it for publish: it drops
140
- `src` from `files`, every `source` condition from `exports`/`imports`, and the `dist` source maps plus their dangling
141
- `sourceMappingURL` directives. Private packages are skipped, since `changeset publish` never publishes them. It is meant
142
- to run on an ephemeral CI checkout right before publish (the release workflow runs it as its publish step), so it is
143
- never committed.
205
+ Slims a published package down to what a consumer's `tsc` and Node actually read, so the npm tarball ships `dist`
206
+ runtime and types only. Where `mirror` writes the full exports — including the `source` condition — for local
207
+ development, `pack-slim` removes that development lane for publish: it drops `src` from `files`, every `source`
208
+ condition from `exports`/`imports`, every `imports` entry left pointing outside `files`, every script that is not an
209
+ install or publish lifecycle hook, `devDependencies`, and the `dist` source maps plus their dangling `sourceMappingURL`
210
+ directives. Private packages are skipped. It is meant to run on an ephemeral CI checkout right before publish, so its
211
+ result is **never committed**.
144
212
 
145
- Because its result must never be committed, `pack-slim` refuses to write when the git working tree has uncommitted
146
- tracked changes — guarding against an accidental local run landing on real work. `--dry-run` is exempt (it writes
147
- nothing) and `--force` overrides the guard. In CI the check is transparent: `dist` is gitignored, so the tree is clean
148
- when the release workflow runs it.
213
+ Because that result must never be committed, `pack-slim` refuses to write when the git working tree has uncommitted
214
+ tracked changes — a guard against an accidental local run landing on real work. `--dry-run` is exempt (it writes
215
+ nothing) and `--force` overrides the guard. In CI the guard is invisible: `dist` is gitignored, so the tree is already
216
+ clean when the release workflow runs `pack-slim`.
149
217
 
150
218
  ```bash
151
219
  codefast pack-slim # every published package
@@ -161,33 +229,37 @@ codefast pack-slim --dry-run # report what would be stripped without touch
161
229
 
162
230
  Exits `1` when any package fails, `0` otherwise.
163
231
 
164
- ## `audit rtl`
232
+ ## `tag`
165
233
 
166
- Read-only scan for physical-direction Tailwind classes (e.g. `ml-*`, `left-*`, `text-left`) that should use logical
167
- equivalents (`ms-*`, `start-*`, `text-start`) or an `rtl:` companion (`translate-x`, `space-x`, resize cursors). Exits
168
- non-zero when violations remain so it can gate CI.
234
+ Adds `@since <version>` tags to the doc comments of exported declarations that lack one, creating the doc block when
235
+ there is none. The version comes from the nearest `package.json` above each target file, and declarations that already
236
+ carry `@since` are left alone. Run it at release time so published APIs carry accurate version metadata — never
237
+ hand-write `@since`.
169
238
 
170
239
  ```bash
171
- codefast audit rtl # uses audit.rtl.target from config
172
- codefast audit rtl packages/ui/src # explicit target
173
- codefast audit rtl --json # machine-readable summary
240
+ codefast tag # auto-discover packages from cwd (or the single package)
241
+ codefast tag packages/ui/src # tag one directory or file
242
+ codefast tag --dry-run # summary only, no writes
174
243
  ```
175
244
 
176
- | Flag | Description |
177
- | -------- | --------------------------------- |
178
- | `--json` | Print one JSON summary on stdout. |
245
+ | Flag | Description |
246
+ | ----------- | ------------------------------------------------------------- |
247
+ | `--dry-run` | Show summary without writing files. |
248
+ | `--json` | Print one JSON summary on stdout (suppresses human progress). |
249
+
250
+ Exits `1` when no target is selected, when any target fails, or when the `tag.onAfterWrite` hook fails.
179
251
 
180
- With no `[target]`, the scan root is `audit.rtl.target` from the config; when neither is set the command fails.
181
- Configure intentional exceptions via `audit.rtl.allowlist` — each entry is a bare class token or
182
- `repo/relative/path.tsx:token`.
252
+ ## `audit`
253
+
254
+ Every audit is read-only, exits non-zero when findings remain (so it gates a CI pipeline with no extra glue), and takes
255
+ an optional `[target]` plus `--json`. Each also reads an `allowlist` from configuration for intentional exceptions.
183
256
 
184
- ## `audit links`
257
+ ### `audit links`
185
258
 
186
- Read-only scan for markdown cross-references that point at nothing: a relative path that does not exist, an in-document
187
- anchor with no matching heading or `<a id>`, and an anchor into another document that the target does not offer. That
188
- last one is the reason this exists — a browser fails it silently by scrolling to the top. External URLs are not checked,
189
- and links inside fenced code are treated as examples rather than references. Exits non-zero when breakages remain so it
190
- can gate CI.
259
+ _General-purpose._ Scans markdown for cross-references that point at nothing: a relative path that does not exist, an
260
+ in-document anchor with no matching heading or `<a id>`, and an anchor into another document that the target does not
261
+ offer. That last case is the reason this exists — a browser fails it silently by scrolling to the top. External URLs are
262
+ not checked, and links inside fenced code are treated as examples rather than references.
191
263
 
192
264
  ```bash
193
265
  codefast audit links # whole repo
@@ -195,20 +267,48 @@ codefast audit links packages/di # explicit target
195
267
  codefast audit links --json # machine-readable summary
196
268
  ```
197
269
 
198
- | Flag | Description |
199
- | -------- | --------------------------------- |
200
- | `--json` | Print one JSON summary on stdout. |
270
+ Configure exceptions via `audit.links.allowlist` — each entry is a bare link target or `repo/relative/doc.md:target`.
271
+
272
+ ### `audit rtl`
273
+
274
+ _House style._ Scans for physical-direction Tailwind classes (e.g. `ml-*`, `left-*`, `text-left`) that should use
275
+ logical equivalents (`ms-*`, `start-*`, `text-start`) or an `rtl:` companion (`translate-x`, `space-x`, resize cursors).
276
+
277
+ ```bash
278
+ codefast audit rtl # uses audit.rtl.target from config
279
+ codefast audit rtl packages/ui/src # explicit target
280
+ codefast audit rtl --json # machine-readable summary
281
+ ```
282
+
283
+ With no `[target]`, the scan root is `audit.rtl.target` from the config; when neither is set the command fails.
284
+ Configure exceptions via `audit.rtl.allowlist` — each entry is a bare class token or `repo/relative/path.tsx:token`.
201
285
 
202
- Configure intentional exceptions via `audit.links.allowlist` — each entry is a bare link target or
203
- `repo/relative/doc.md:target`.
286
+ ### `audit imports`
204
287
 
205
- ## `audit comments`
288
+ _House style._ Enforces the monorepo's import policy over `.ts`/`.tsx` files:
206
289
 
207
- Scans source comments for the repo's comment conventions. Section dividers that are not in the one allowed form are
208
- mechanical, so `--fix` rewrites them in place. The rest is reported for a person to fix: TSDoc grammar errors, JSDoc
209
- `{type}` payloads, comments pointing at repo documents, `@param` lists that name some parameters but not all, `@param`
210
- descriptions without the `-` separator, `@since` tags out of position or naming a version the package has not reached,
211
- and comment links to missing paths. Exits non-zero when unfixed findings remain so it can gate CI.
290
+ - **React** — members must be imported by name. Flags `import * as React` and default `React` imports (type-only
291
+ included), plus an implicit `React.*` UMD-global type reference (`e: React.FormEvent` with no import) that `tsc`
292
+ accepts silently through the `export as namespace React` declaration in `@types/react`.
293
+ - **Zod** (front-end packages only) — flags a named `import { z } from "zod"`, which pins Zod's full locale set into the
294
+ bundle; `import * as z from "zod"` lets bundlers tree-shake it.
295
+
296
+ ```bash
297
+ codefast audit imports # whole repo
298
+ codefast audit imports apps/web/src # explicit target
299
+ codefast audit imports --json # machine-readable summary
300
+ ```
301
+
302
+ Configure exceptions via `audit.imports.allowlist` — each entry is the offending source text as written or
303
+ `repo/relative/path.tsx:<text>`.
304
+
305
+ ### `audit comments`
306
+
307
+ _House style._ Checks doc-comment conventions. Section dividers not in the one allowed form are mechanical, so `--fix`
308
+ rewrites them in place. The rest is reported for a person to fix: TSDoc grammar errors, JSDoc `{type}` payloads,
309
+ comments pointing at repo documents, `@param` lists that name some parameters but not all, `@param` descriptions without
310
+ the `-` separator, `@since` tags out of position or naming a version the package has not reached, and comment links to
311
+ missing paths.
212
312
 
213
313
  ```bash
214
314
  codefast audit comments # whole repo
@@ -222,57 +322,121 @@ codefast audit comments --json # machine-readable summary
222
322
  | `--fix` | Rewrite every mechanically fixable divider in place. |
223
323
  | `--json` | Print one JSON summary on stdout. |
224
324
 
225
- Configure intentional exceptions via `audit.comments.allowlist` — each entry is a divider line as written or
325
+ Configure exceptions via `audit.comments.allowlist` — each entry is a divider line as written or
226
326
  `repo/relative/path.ts:<divider>`.
227
327
 
228
- ## `audit react`
328
+ ### `audit display-names`
229
329
 
230
- Read-only scan enforcing the repo's React import policy: members are imported by name. Flags `import * as React` and
231
- default `React` imports (type-only included), plus an implicit `React.*` UMD-global type reference (`e: React.FormEvent`
232
- with no import), which `tsc` accepts silently through the `export as namespace React` declaration in `@types/react`.
233
- Exits non-zero when violations remain so it can gate CI.
330
+ _House style._ Enforces the display-name convention for every string a `token()`, `tag()`, or module factory takes: a
331
+ name is spelled like the TS symbol it stands for, under its owner's namespace — `namespace:Name`. The namespace is a
332
+ kebab-case package, app, or feature slug (or a scoped package name); a token or module name is PascalCase, a tag key is
333
+ camelCase. It scans TypeScript and markdown alike, since a doc sample is what a reader copies, and skips `tests/`,
334
+ `benchmarks/`, `.changeset/`, and `CHANGELOG.md`.
234
335
 
235
336
  ```bash
236
- codefast audit react # whole repo
237
- codefast audit react apps/ui/src # explicit target
238
- codefast audit react --json # machine-readable summary
337
+ codefast audit display-names # whole repo
338
+ codefast audit display-names packages/di/examples # explicit target
339
+ codefast audit display-names --json # machine-readable summary
239
340
  ```
240
341
 
241
- | Flag | Description |
242
- | -------- | --------------------------------- |
243
- | `--json` | Print one JSON summary on stdout. |
342
+ Configure exceptions via `audit.displayNames.allowlist` — each entry is the call as written, through its closing quote
343
+ (or parenthesis when the name is the only argument), or `repo/relative/path.ts:<call>`.
244
344
 
245
- Configure intentional exceptions via `audit.react.allowlist` — each entry is the offending source text as written or
246
- `repo/relative/path.tsx:<text>`.
345
+ ## Configuration
247
346
 
248
- ## `tag`
347
+ **You do not need a config file.** Every command has sensible defaults and works with none. Add a `codefast.config.*`
348
+ file at your project root only to change a default — and add only the sections for the commands you actually use.
249
349
 
250
- Adds `@since <version>` tags to the doc comments of exported declarations that lack one, creating the doc block when
251
- there is none. The version comes from the nearest `package.json` above each target file. Declarations that already carry
252
- `@since` are left alone.
350
+ **Where it goes and how it loads.** The CLI walks up from the working directory and uses the first match, checking
351
+ `codefast.config.mjs`, `codefast.config.js`, `codefast.config.cjs`, then `codefast.config.json` in each directory. JS
352
+ configs are loaded via [jiti](https://github.com/unjs/jiti) — so **only run the CLI in repositories you trust**, and
353
+ note that only a JS config can define `onAfterWrite` hooks (JSON can't hold functions). The schema is **strict**: an
354
+ unknown key is an error, which catches typos immediately.
253
355
 
254
- ```bash
255
- codefast tag # auto-discover workspace packages from cwd
256
- codefast tag packages/ui/src # tag one directory or file
257
- codefast tag --dry-run # summary only, no writes
356
+ ### Start small
357
+
358
+ The smallest valid config is empty. Grow it one section at a time — each top-level key configures one command:
359
+
360
+ ```js
361
+ // codefast.config.js
362
+ export default {};
258
363
  ```
259
364
 
260
- | Flag | Description |
261
- | ----------- | ------------------------------------------------------------- |
262
- | `--dry-run` | Show summary without writing files. |
263
- | `--json` | Print one JSON summary on stdout (suppresses human progress). |
365
+ | Key | Command | What it configures |
366
+ | --------- | --------- | ---------------------------------------------------------------------------------------------- |
367
+ | `mirror` | `mirror` | per-package `exports` generation — see [per-package config](#per-package-mirror-configuration) |
368
+ | `tag` | `tag` | package names to skip, and a hook to run after writing |
369
+ | `arrange` | `arrange` | a hook to run after writing |
370
+ | `audit` | `audit *` | each audit's default scan target and its `allowlist` of accepted exceptions |
264
371
 
265
- Exits `1` when no target is selected, when any target fails, or when the `tag.onAfterWrite` hook fails. In this repo,
266
- `tag` runs inside `pnpm run version-packages` so published APIs carry accurate version metadata — never hand-write
267
- `@since` tags.
372
+ ### Author it with types
268
373
 
269
- ## Configuration
374
+ Don't memorize the shape. Import `defineConfig` (or annotate with the `CodefastConfig` type) and your editor completes
375
+ every key, checks the values, and catches typos **before you run anything** — the types _are_ the reference for what's
376
+ valid, and the strict runtime schema is the backstop.
377
+
378
+ ```ts
379
+ // codefast.config.ts
380
+ import { defineConfig } from "@codefast/cli";
381
+
382
+ export default defineConfig({
383
+ mirror: { "@acme/ui": { strip: "./components/" } }, // autocomplete: strip, exclude, source, types, css, …
384
+ });
385
+ ```
386
+
387
+ A plain `.js` config gets the same help through a JSDoc type — no build step, no `.ts`:
388
+
389
+ ```js
390
+ // codefast.config.js
391
+ /** @type {import("@codefast/cli").CodefastConfig} */
392
+ export default {
393
+ mirror: { "@acme/ui": { strip: "./components/" } },
394
+ };
395
+ ```
396
+
397
+ ### Common recipes
398
+
399
+ **Run a formatter after a command rewrites files.** `tag` and `arrange` take an `onAfterWrite` hook (sync or async). It
400
+ runs only when files were actually written — never on `--dry-run`:
401
+
402
+ ```js
403
+ // codefast.config.js
404
+ import { execSync } from "node:child_process";
405
+
406
+ const format = ({ files }) => execSync(`oxfmt ${files.join(" ")}`, { stdio: "inherit" });
407
+
408
+ export default {
409
+ tag: { onAfterWrite: format },
410
+ arrange: { onAfterWrite: format },
411
+ };
412
+ ```
413
+
414
+ **Skip packages.** `tag.skipPackages` takes globs matched against package names; `mirror` skips any package set to
415
+ `false`:
416
+
417
+ ```js
418
+ export default {
419
+ tag: { skipPackages: ["@acme/internal", "@apps/*"] },
420
+ mirror: { "@acme/internal": false },
421
+ };
422
+ ```
423
+
424
+ **Accept a known audit finding.** Every audit takes an `allowlist`. An entry is the offending text exactly as it
425
+ appears, or `repo/relative/path:<text>` to scope it to a single file:
426
+
427
+ ```js
428
+ export default {
429
+ audit: {
430
+ imports: { allowlist: [`packages/legacy/src/x.ts:import { z } from "zod";`] },
431
+ rtl: { allowlist: ["packages/ui/src/variants/sheet.ts:data-open:slide-in-from-left-10"] },
432
+ },
433
+ };
434
+ ```
270
435
 
271
- An optional `codefast.config.*` file adjusts `mirror`, `tag`, `arrange`, and `audit`. The CLI walks up from the working
272
- directory and uses the first match, checking `codefast.config.mjs`, `codefast.config.js`, `codefast.config.cjs`, then
273
- `codefast.config.json` in each directory. JS configs are loaded via [jiti](https://github.com/unjs/jiti), so only run
274
- the CLI in repositories you trust; JSON configs cannot define hooks. The schema is strict: an unknown key is a
275
- configuration error.
436
+ ### Complete reference
437
+
438
+ Every section together — see [per-package `mirror` configuration](#per-package-mirror-configuration) for the `mirror`
439
+ keys:
276
440
 
277
441
  ```js
278
442
  // codefast.config.js
@@ -310,13 +474,15 @@ export default {
310
474
  },
311
475
  links: { allowlist: [] }, // bare link target, or `repo/relative/doc.md:target`
312
476
  comments: { allowlist: [] }, // divider as written, or `repo/relative/path.ts:<divider>`
313
- react: { allowlist: [] }, // offending text as written, or `repo/relative/path.tsx:<text>`
477
+ imports: { allowlist: [] }, // offending import text as written, or `repo/relative/path.tsx:<text>`
478
+ displayNames: { allowlist: [] }, // call as written, or `repo/relative/path.ts:<call>`
314
479
  },
315
480
  };
316
481
  ```
317
482
 
318
- `source`, `types`, and `import` default to `true`. The `onAfterWrite` hooks (sync or async) run only when files were
319
- actually written — never on `--dry-run`. A hook failure is reported on stderr and the command exits `1`.
483
+ `source`, `types`, and `import` default to `true`, so an empty `mirror` entry still emits all three. The `onAfterWrite`
484
+ hooks run only when files were actually written — never on `--dry-run`; a hook failure is reported on stderr and the
485
+ command exits `1`.
320
486
 
321
487
  ## Exit codes
322
488
 
@@ -326,6 +492,46 @@ actually written — never on `--dry-run`. A hook failure is reported on stderr
326
492
  | `1` | General failure (missing paths, failed packages, failed hooks). |
327
493
  | `2` | Invalid arguments or configuration. |
328
494
 
495
+ ## Programmatic use
496
+
497
+ `@codefast/cli` is importable as well as executable. `runCli` runs the same CLI in-process and resolves to the exit code
498
+ it would have exited with — the `codefast` binary is a thin wrapper around it.
499
+
500
+ ```ts
501
+ import { runCli } from "@codefast/cli";
502
+
503
+ // `argv` follows the `process.argv` layout: the first two entries are ignored,
504
+ // exactly as when Node runs the binary.
505
+ const exitCode = await runCli(["node", "codefast", "mirror", "--dry-run", "--json"]);
506
+
507
+ if (exitCode !== 0) {
508
+ throw new Error(`codefast exited with ${exitCode}`);
509
+ }
510
+ ```
511
+
512
+ The command still writes its human or `--json` output to stdout/stderr; `runCli` does not capture it. Read stdout
513
+ yourself when you need the structured summary.
514
+
515
+ ## How the codefast monorepo uses it
516
+
517
+ The tool is general; the codefast monorepo just wires convenience scripts and a release step around it — a good template
518
+ if you adopt the CLI in your own workspace. It runs from the built output via root `package.json` scripts:
519
+
520
+ ```bash
521
+ pnpm run codefast <command> # generic entry: node ./packages/cli/dist/bin.js
522
+
523
+ pnpm run cli:arrange # codefast arrange
524
+ pnpm run cli:mirror # codefast mirror
525
+ pnpm run cli:audit:links # codefast audit links
526
+ pnpm run cli:audit:rtl # codefast audit rtl
527
+ pnpm run cli:audit:comments # codefast audit comments
528
+ pnpm run cli:audit:imports # codefast audit imports
529
+ pnpm run cli:audit:display-names # codefast audit display-names
530
+ ```
531
+
532
+ `pnpm run version-packages` runs `changeset version` and then `codefast tag`, so published APIs are stamped at release,
533
+ and the release workflow runs `codefast pack-slim` as its publish step on a clean CI checkout.
534
+
329
535
  ## Documentation
330
536
 
331
537
  - [codefastlabs.com/docs/cli](https://codefastlabs.com/docs/cli) — this document, rendered.
@@ -0,0 +1,10 @@
1
+ import type { AnalyzeReport } from "#/arrange/domain/types";
2
+ import { AppError } from "#/core/errors";
3
+ import type { FilesystemPort } from "#/core/filesystem/port";
4
+ import type { Result } from "#/core/result";
5
+ /**
6
+ * Scans a directory's arrange targets and returns the accumulated analyze report.
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ export declare function analyzeDirectory(fs: FilesystemPort, analyzeRootPath: string): Result<AnalyzeReport, AppError>;