@cellajs/cli 0.3.0 → 0.3.2

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.
package/README.md CHANGED
@@ -18,10 +18,10 @@ pnpm cella audit
18
18
  | Service | Description |
19
19
  |---------|-------------|
20
20
  | `analyze` | Dry run to see what would change on sync. It leaves your files and branches alone but updates git metadata: it fetches upstream and writes `refs/cella/last-sync` and a graft for the sync base |
21
- | `sync` | Merge upstream changes onto a fresh branch and open a squash-merge PR into `main` |
21
+ | `sync` | Merge upstream changes onto a fresh branch, sync package.json and open a squash-merge PR into `main` |
22
22
  | `migrate` | List the upstream migration notes this app has not handled yet, read them, run their codemods, record them |
23
- | `audit` | Check for outdated packages & vulnerabilities |
24
- | `stats` | Count files by category and workspace package |
23
+ | `audit` | Check for outdated packages and vulnerabilities |
24
+ | `stats` | Count files by category and workspace package; with `--since <ref>`, count what the current branch changed, by kind of file |
25
25
  | `forks` * | Run normal sync inside local fork repositories |
26
26
  | `contributions` * | Pull and adopt changes from local forks |
27
27
 
@@ -37,26 +37,31 @@ Per-service help: `pnpm cella <service> --help`.
37
37
 
38
38
  | Service | Useful options |
39
39
  |---------|----------------|
40
- | analyze | `--log`, `--list`, `--json`, `--scope <all\|risk\|protected>`, `--diff <path>`, `--open-diff <path>` |
41
- | sync | `--log`, `--hard`, `--unpinned`, `--track <release\|branch>` |
42
- | migrate | `--all`, `--json`, `--show <id>`, `--extract <id>`, `--mark <ids...>` |
40
+ | analyze | `--log`, `--list`, `--json`, `--scope <all\|risk\|protected>`, `--track <release\|branch>`, `--ref <ref>`, `--diff <path>`, `--open-diff <path>` |
41
+ | sync | `--log`, `--hard`, `--unpinned`, `--track <release\|branch>`, `--ref <ref>`, `--keep-config` |
42
+ | migrate | `--all`, `--json`, `--show <id>`, `--extract <id>`, `--run <id> [-- <codemod args>]`, `--script <file>`, `--mark <ids...>` |
43
43
  | audit | `--list`, `--force`, `--check-overrides` |
44
- | forks | `--fork <name>`, `--log`, `--hard`, `-V, --verbose` |
44
+ | forks | `--fork <name>`, `--log`, `--hard`, `--keep-config`, `-V, --verbose` |
45
45
  | contributions | `--fork <name>`, `--list`, `--json`, `--diff <path>` |
46
- | stats | `--coverage`, `-V, --verbose` |
46
+ | stats | `--coverage`, `--since <ref>`, `--md`, `-V, --verbose` |
47
47
 
48
48
  | Global flag | Description |
49
49
  |-------------|-------------|
50
50
  | `-v, --version` | Output the current version |
51
- | `-h, --help` | Display help message |
51
+ | `-h, --help` | Display help for a command |
52
52
 
53
53
  ## Configuration
54
54
 
55
55
  Sync behavior lives in `cella.config.ts` at your monorepo root (a sensible default ships with new
56
56
  apps). To deviate files or folders from the template:
57
57
 
58
- - **`ignored`** — files completely excluded from sync (existing and new)
59
- - **`pinned`** — full fork control: existing, modified, or deleted files are preserved
58
+ - **`ignored`**: files completely excluded from sync (existing and new)
59
+ - **`pinned`**: full fork control, existing, modified, or deleted files are preserved
60
+
61
+ An `ignored` entry may name a path your app does not have, to keep it from arriving (a file upstream
62
+ has and your app deleted). Every run warns `ignored entry not found` only when neither your app nor
63
+ upstream has the path. Upstream is read at `settings.upstreamBranch` as last fetched; before the
64
+ first fetch, every `ignored` entry missing from your app warns.
60
65
 
61
66
  ## Upstream tracking
62
67
 
@@ -64,7 +69,7 @@ The sync CLI tracks upstream cella one of two ways, set via `settings.upstreamTr
64
69
 
65
70
  | Mode | Behavior | For |
66
71
  |------|----------|-----|
67
- | `release` (default) | Sync to a last cella release tag. Stable and reviewable — each bump maps to a changelog. | Most forks |
72
+ | `release` (default) | Sync to a last cella release tag. Stable and reviewable: each bump maps to a changelog. | Most forks |
68
73
  | `branch` | Follow the bleeding-edge tip of `settings.upstreamBranch`. | cella maintainers, active development |
69
74
 
70
75
  For a one-off run that ignores the configured mode, pass `--track`:
@@ -73,6 +78,20 @@ For a one-off run that ignores the configured mode, pass `--track`:
73
78
  pnpm cella sync --track branch # follow the tip once, without editing config
74
79
  ```
75
80
 
81
+ To sync to one specific upstream point, pin it with `--ref` (it wins over `--track`): a commit sha,
82
+ a release tag or an upstream branch name, resolved on the upstream remote (`main` is upstream's
83
+ `main`, not yours). The ref must be on `settings.upstreamBranch` or in an upstream release; a
84
+ release tag syncs as that release, anything else is recorded like branch tracking.
85
+
86
+ ```bash
87
+ pnpm cella sync --ref 4f7d87c # sync up to this upstream commit
88
+ ```
89
+
90
+ A run never syncs to a point behind the last sync: when an earlier `--ref` or `--track branch` run
91
+ went past the latest release, release tracking ends with a `nothing to sync` message until a newer
92
+ release exists, instead of reverting what the app already has. A `--ref` behind the last sync is
93
+ refused. Either way the run changes nothing and leaves you on the branch you started from.
94
+
76
95
  ## Sync workflow
77
96
 
78
97
  `pnpm cella sync` never commits to `main` directly.
@@ -82,27 +101,39 @@ main ──▶ cella/sync/<stamp> ──(3-way merge)──▶ PR ──(squash)
82
101
  ```
83
102
 
84
103
  It runs a real git 3-way merge. `sync` is **idempotent and staged**: each run advances the sync
85
- one stage, and the run that commits never ships — the pause on the committed branch is where
104
+ one stage, and the run that commits never ships: the pause on the committed branch is where
86
105
  drift triage (`pnpm cella analyze` diffs committed HEAD) and follow-up commits happen.
87
106
 
88
107
  1. **First run** cuts the branch and merges. A clean merge is committed right away: dependencies
89
108
  are reconciled (`pnpm install` + `pnpm check`), everything is staged, and the delta is
90
- committed — then the run stops on the branch. A conflicted merge stops earlier so you can
109
+ committed; then the run stops on the branch. A conflicted merge stops earlier so you can
91
110
  resolve in your IDE (`git add` the resolved files) and re-run to commit.
92
111
  2. **Final re-run `pnpm cella sync`** on the committed branch ships it: pushes to `origin`,
93
112
  opens a PR into `main` (via `gh`), and switches you back to `main`.
94
113
 
114
+ Before the merge starts, a first run checks that it can go ahead, and stops without changing
115
+ anything when it cannot: the temporary branch is removed again and you are back on the branch you
116
+ started from. It stops when upstream needs a newer `@cellajs/cli` than the one running, and when
117
+ upstream changed its own sync config in ways yours does not follow (see
118
+ [Upstream changes that never sync](#upstream-changes-that-never-sync)).
119
+
120
+ `sync` also runs from a linked git worktree while another worktree has `main` checked out: it
121
+ never switches to `main`. It compares `main` with `origin/main` by ref and cuts the sync branch
122
+ from `main`; when `main` is behind and checked out elsewhere, it cuts from `origin/main` instead
123
+ and leaves `main` as it is. After shipping, the worktree detaches at `main` instead of switching
124
+ to it.
125
+
95
126
  When the commit stage runs, the in-progress merge state (`MERGE_HEAD`) is discarded, so the staged delta
96
127
  collapses into a **single-parent commit** (`chore: sync upstream cella <sha>`). This keeps the PR
97
- to one clean commit with the incremental diff — a two-parent merge commit would instead list the
128
+ to one clean commit with the incremental diff; a two-parent merge commit would instead list the
98
129
  upstream branch's entire history, because the fork doesn't share pushed ancestry with upstream
99
130
  and the local `git replace` graft that makes merges incremental is never pushed. Ancestry lives
100
131
  in `refs/cella/last-sync` (and the committed `cella.manifest.json` for fresh clones), so
101
- `git merge-base` keeps working across throwaway branches — each is safe to delete once its PR
132
+ `git merge-base` keeps working across throwaway branches; each is safe to delete once its PR
102
133
  lands. The three-segment name can't collide with git's ref namespacing.
103
134
 
104
135
  If conflicts remain when you re-run, `sync` lists them and stops (never starting a second cycle
105
- mid-merge). If `pnpm check`, the push, or `gh` fails, it degrades gracefully — reporting the
136
+ mid-merge). If `pnpm check`, the push, or `gh` fails, it degrades gracefully, reporting the
106
137
  issue and printing the remaining manual steps:
107
138
 
108
139
  ```bash
@@ -134,10 +165,26 @@ listed until marked.
134
165
  ```bash
135
166
  pnpm cella migrate # open notes, with summary and links
136
167
  pnpm cella migrate --show <id> # one note's README
137
- pnpm cella migrate --extract <id> # its folder under node_modules/.cache/cella/migrations/<id>/, to run the codemod
168
+ pnpm cella migrate --run <id> # run its codemod, report only
169
+ pnpm cella migrate --run <id> -- rewrite frontend/src # run it with your own arguments
170
+ pnpm cella migrate --extract <id> # its folder under node_modules/.cache/cella/migrations/<id>/
138
171
  pnpm cella migrate --mark <id> [...] # record notes as handled
139
172
  ```
140
173
 
174
+ `--run` extracts the note and runs its codemod with `tsx` from your app root. Everything after `--`
175
+ goes to the codemod as given; without it the codemod gets `inventory` and the note's `roots`, the
176
+ report-only run. The codemod is the one script in the note folder; when a folder holds several,
177
+ name it with `--script <file>`.
178
+
179
+ A codemod's roots cover template-owned files too, and a file that is identical to upstream already
180
+ is the way upstream wants it: upstream may have changed it after writing the codemod, so a rewrite
181
+ there only creates drift. Before the codemod starts, `--run` notes which files are byte-identical
182
+ to the last synced upstream commit (`upstream.commit` in `cella/cella.manifest.json`, during a
183
+ staged sync the incoming commit). After the run, each of those files the codemod changed gets its
184
+ upstream content back, and the run prints how many it restored. Files your app changed keep the
185
+ codemod's rewrite. The codemod's own report still counts the restored files, in a report-only run
186
+ too. A codemod run by hand from the `--extract` folder has no such protection.
187
+
141
188
  ## Sync rules
142
189
 
143
190
  For each file, `sync` compares fork and upstream **content** (blob comparison) and resolves it
@@ -158,7 +205,7 @@ customize). Unconfigured = syncs automatically. Run `pnpm cella analyze` first t
158
205
 
159
206
  ### Aggressive sync flags
160
207
 
161
- Two opt-in flags make `sync` more aggressive. Both resurface full upstream history (natural merge-base, not the last-sync point), so expect a larger diff and a post-run warning — cherry-pick deliberately. They compose (`--hard --unpinned`).
208
+ Two opt-in flags make `sync` more aggressive. Both resurface full upstream history (natural merge-base, not the last-sync point), so expect a larger diff and a post-run warning: cherry-pick deliberately. They compose (`--hard --unpinned`).
162
209
 
163
210
  | Flag | Effect |
164
211
  |------|--------|
@@ -178,7 +225,7 @@ During analysis and sync, files are displayed with status indicators:
178
225
  | ! | `drifted` | Fork changed, not protected | At risk, consider pinning |
179
226
  | ↓ | `behind` | Upstream has changes | Will sync from upstream |
180
227
  | ⇅ | `diverged` | Both sides changed | Will merge from upstream |
181
- | ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork — review, see below |
228
+ | ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork: review, see below |
182
229
  | + | `local` | Only in fork, never in upstream | No action needed |
183
230
 
184
231
  ### Protected but behind upstream
@@ -192,6 +239,22 @@ missing new upstream utilities that synced components rely on), so both commands
192
239
  `--scope all` and `--scope protected` (`--json` adds `upstreamChanged` and `upstreamChangedLines`).
193
240
  - `sync` prints the same list at the end of its summary, right when the drop happens.
194
241
 
242
+ Both lists end with one `git diff <last-sync>..<upstream> -- <entry>` line per `pinned` or
243
+ `ignored` entry that holds a listed file, to paste. The range is what makes it useful:
244
+ `git diff HEAD <upstream> -- shared/config` shows your whole config against the template's, while
245
+ `<last-sync>..<upstream>` shows only what upstream changed since your last sync.
246
+
247
+ ```
248
+ ⚠ 3 protected files kept the fork version, dropping upstream changes:
249
+ ⨀ frontend/src/styling/tailwind.css · 21 lines changed upstream
250
+ ⨂ shared/config/default.ts · 4 lines changed upstream
251
+ ⨂ shared/config/staging.ts · 2 lines changed upstream
252
+ pinned/ignored files where upstream also changed since the last sync; the fork side wins on conflict, so diff each against upstream (analyze --open-diff <path>) and adopt what you need.
253
+ what upstream changed since the last sync, per pinned/ignored entry:
254
+ git diff e9a8d485e..a81e3353b -- frontend/src/styling/tailwind.css
255
+ git diff e9a8d485e..a81e3353b -- shared/config
256
+ ```
257
+
195
258
  Diff each against upstream (`cella analyze --open-diff <path>`) and adopt what you need. The
196
259
  check is relative to the last sync point: a drop that happened in an earlier sync only shows up
197
260
  as plain `ahead` afterwards. For that case the `↑ protected in fork` section annotates pinned
@@ -211,12 +274,12 @@ its summary, and `sync` after its merge summary:
211
274
  paste. Files both sides changed stay in the section above, and generated output the fork
212
275
  regenerates itself (`sdk/gen`, `*.gen.*` files) is left out. `--list`/`--json` include these
213
276
  files in `--scope all` and `--scope protected` (`--json`: `upstreamOnly`).
214
- - **Upstream override changes.** `cella/cella.config.ts` never syncs, so entries upstream adds to
215
- its own `overrides.pinned` or `overrides.ignored` (the template your config started from) never
216
- arrive. The report names each entry upstream added that your config lacks (`+`) and each entry
217
- upstream dropped that your config still has (`−`). It reads upstream's config without running it
218
- and never edits yours. `--json` puts the lists on the `cella/cella.config.ts` entry
219
- (`upstreamOverrides`).
277
+ - **Upstream sync config changes.** `cella/cella.config.ts` never syncs, so entries upstream adds to
278
+ its own `overrides.pinned` or `overrides.ignored`, and keys it adds to `settings.packageJsonSync`
279
+ (the template your config started from), never arrive. The report names each entry upstream added
280
+ that your config lacks (`+`) and each entry upstream dropped that your config still has (`−`). It
281
+ reads upstream's config without running it and never edits yours. `--json` puts the lists on the
282
+ `cella/cella.config.ts` entry (`upstreamOverrides`).
220
283
 
221
284
  ```
222
285
  ⚠ ignored paths changed upstream · 1 file under 1 entry
@@ -224,11 +287,24 @@ its summary, and `sync` after its merge summary:
224
287
  git diff e9a8d485e..a81e3353b -- frontend/src/modules/marketing
225
288
  the fork left these untouched and ignored paths never sync: diff and adopt what you need.
226
289
 
227
- ⚠ upstream changed its sync overrides · 1 entry to review
290
+ ⚠ upstream changed its sync config · 2 entries to review
228
291
  + pinned: backend/src/bundle-config.ts
292
+ + packageJsonSync: exports
229
293
  cella/cella.config.ts never syncs: add or drop these by hand where they fit your app.
230
294
  ```
231
295
 
296
+ `analyze` only reports these. `sync` checks the sync config before a new merge and stops when it
297
+ finds entries: the merge runs on your config, so with a list that is behind, a path upstream now
298
+ ignores or pins would arrive as an ordinary synced file (upstream renamed an ignored folder, your
299
+ list still names the old one). Nothing has changed at that point. Add or drop the entries that fit
300
+ your app in `cella/cella.config.ts`, commit and rerun, or pass `--keep-config` to merge with your
301
+ config as it stands; the report then follows the merge summary as before. The rerun that commits an
302
+ already staged merge never stops for this.
303
+
304
+ ```bash
305
+ pnpm cella sync --keep-config # merge although upstream changed its sync config
306
+ ```
307
+
232
308
  ## Package.json sync
233
309
 
234
310
  `packageJsonSync` controls which package.json sections sync from upstream:
@@ -252,6 +328,10 @@ already defines is never rewritten, and `exports` only merges when both sides ar
252
328
  (`{ ".": …, "./config": … }`). A workspace upstream added since your last sync arrives with its
253
329
  package.json copied verbatim.
254
330
 
331
+ `type` always syncs, whatever `packageJsonSync` lists: it follows upstream where your fork kept the
332
+ previous value, and a package.json without `type` gets upstream's. Set it yourself (for example
333
+ `"commonjs"`) to keep your own.
334
+
255
335
  ## Contributions (pull from forks)
256
336
 
257
337
  Upstream can pull modifications from local forks and selectively adopt them.
@@ -266,6 +346,46 @@ forks: [
266
346
 
267
347
  ### Pulling contributions
268
348
 
269
- Run `pnpm cella contributions` (or pick **contributions** from the menu). Select one or more
270
- forks; cella fetches each fork's `pullBranch`, builds a clean local `contrib/<fork>` branch with
271
- only that fork's contributed files. Accepted files are checked out from the contrib branch and staged for review.
349
+ Run `pnpm cella contributions` (or pick **contributions** from the menu). Select a fork; cella
350
+ fetches its `pullBranch` and builds a clean local `contrib/<fork>` branch with only that fork's
351
+ contributed files. Accepted files are checked out from the contrib branch and staged for review.
352
+ `--list` prints the result as tab-separated rows (fork, status, kind, changedAt, path) for tooling.
353
+
354
+ ## Stats of a branch
355
+
356
+ `pnpm cella stats` counts the whole repository. With `--since <ref>` it counts what the current
357
+ branch changed since it left that ref, by kind of file, which is what a pull request into the ref
358
+ shows. Only committed work counts.
359
+
360
+ ```bash
361
+ pnpm cella stats --since origin/main # tables in the terminal, -V lists every file
362
+ pnpm cella stats --since origin/main --md # a markdown table on stdout, for a PR description
363
+ ```
364
+
365
+ ```
366
+ files added removed net
367
+ 8 +438 -6 +432 tests
368
+ 14 +229 -109 +120 source
369
+ 1 +44 0 +44 docs
370
+ 2 +11 -6 +5 json
371
+ 25 +722 -121 +601 total
372
+
373
+ source lines by content
374
+ +152 -87 +65 code
375
+ +49 -9 +40 comments
376
+ +28 -13 +15 blank
377
+ ```
378
+
379
+ | Kind | Files it holds |
380
+ |------|----------------|
381
+ | source | Code and styles that are none of the kinds below |
382
+ | tests | `*.test.*`, `*.spec.*` and anything under a `tests/`, `test/` or `__tests__/` folder |
383
+ | stories | `*.stories.*` |
384
+ | generated | `*.gen.*`, `gen/` folders, `drizzle/`, `*.d.ts` and lock files |
385
+ | json | `*.json` and `*.jsonc` that are not generated: config and translations |
386
+ | docs | `*.md` and `*.mdx` |
387
+ | other | Everything else, such as workflows and Dockerfiles |
388
+
389
+ Source lines are also split by what they hold. A diff shows changed lines only, so a comment is
390
+ recognized by how the line starts (`//`, `/*`, `*`, `{/*`): a line of a template literal that starts
391
+ that way counts as a comment too.
package/dist/config.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Sync CLI v2 - Configuration Types
3
- *
4
- * Simplified configuration for worktree-based merge approach.
2
+ * Configuration types: the public cella/cella.config.ts surface, its runtime
3
+ * schema, and the internal types the services share.
5
4
  */
6
5
 
6
+ declare const PACKAGE_JSON_SYNC_KEYS: readonly ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies", "scripts", "engines", "packageManager", "overrides", "exports", "pnpm"];
7
7
  /** Valid package.json keys that can be synced */
8
- type PackageJsonSyncKey = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'scripts' | 'engines' | 'packageManager' | 'overrides' | 'exports' | 'pnpm';
8
+ type PackageJsonSyncKey = (typeof PACKAGE_JSON_SYNC_KEYS)[number];
9
9
  /**
10
- * Sync settings - all configurable options for the sync CLI.
10
+ * Sync settings: all configurable options for the sync CLI.
11
11
  * Hover over each property for documentation.
12
12
  */
13
13
  interface SyncSettings {
@@ -21,7 +21,7 @@ interface SyncSettings {
21
21
  /**
22
22
  * How to track upstream cella. Defaults to 'release'.
23
23
  * - 'release' (default): sync to a published cella release tag (`v*`). Stable and
24
- * reviewable — each bump maps to a changelog. Uses the latest release.
24
+ * reviewable: each bump maps to a changelog. Uses the latest release.
25
25
  * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella
26
26
  * maintainers and forks doing active development on top of unreleased changes.
27
27
  */
@@ -32,14 +32,9 @@ interface SyncSettings {
32
32
  * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.
33
33
  */
34
34
  releaseBase?: string;
35
- /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */
35
+ /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']). `type` always syncs. */
36
36
  packageJsonSync?: PackageJsonSyncKey[];
37
- /**
38
- * Automatically run packages sync after the sync service completes.
39
- * When true (default), the packages service is hidden from the menu
40
- * and runs automatically as part of sync.
41
- * Set to false to keep packages as a separate manual service.
42
- */
37
+ /** Automatic package.json sync after the sync service completes (default: true). Set to false to turn it off. */
43
38
  syncWithPackages?: boolean;
44
39
  /**
45
40
  * How to link files in CLI output.
@@ -64,18 +59,15 @@ interface ForkConfig {
64
59
  /**
65
60
  * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').
66
61
  * When set, the contributions service fetches the fork's `pullBranch` from this
67
- * remote (the authoritative committed ref) instead of the local clone, so the
68
- * comparison no longer depends on the local checkout being up to date. The local
69
- * clone is still used to read the fork's owned-folder territory when available.
62
+ * remote (the authoritative committed ref), so the comparison does not depend on
63
+ * the local checkout being up to date. The local clone is still used to read the
64
+ * fork's owned-folder territory when available.
70
65
  */
71
66
  remoteUrl?: string;
72
67
  /** Fork branch that cella pulls contributions from (contributions service) */
73
68
  pullBranch: string;
74
69
  }
75
- /**
76
- * User-configurable sync options for cella/cella.config.ts.
77
- * Simplified for v2 - no long-lived sync branch or squash options.
78
- */
70
+ /** User-configurable sync options for cella/cella.config.ts. */
79
71
  interface CellaCliConfig {
80
72
  /** Core sync settings */
81
73
  settings: SyncSettings;
@@ -84,14 +76,14 @@ interface CellaCliConfig {
84
76
  */
85
77
  overrides?: {
86
78
  /**
87
- * Paths the fork fully owns — never synced (existing or new).
79
+ * Paths the fork fully owns: never synced (existing or new).
88
80
  * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and
89
81
  * everything under it, 'README.md' matches that exact file.
90
82
  * Local territory: upstream cannot add, modify, or delete anything under these.
91
83
  */
92
84
  ignored?: string[];
93
85
  /**
94
- * Paths pinned to fork — fork wins on conflicts.
86
+ * Paths pinned to the fork: fork wins on conflicts.
95
87
  * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and
96
88
  * everything under it, 'README.md' matches that exact file.
97
89
  * Non-conflicting upstream changes merge normally.
package/dist/config.js CHANGED
@@ -1,5 +1,17 @@
1
1
  // src/config/types.ts
2
2
  import { z } from "zod";
3
+ var PACKAGE_JSON_SYNC_KEYS = [
4
+ "dependencies",
5
+ "devDependencies",
6
+ "peerDependencies",
7
+ "optionalDependencies",
8
+ "scripts",
9
+ "engines",
10
+ "packageManager",
11
+ "overrides",
12
+ "exports",
13
+ "pnpm"
14
+ ];
3
15
  function defineConfig(config) {
4
16
  return config;
5
17
  }
@@ -9,20 +21,7 @@ var cellaConfigSchema = z.object({
9
21
  upstreamBranch: z.string().min(1).optional(),
10
22
  upstreamTrack: z.enum(["release", "branch"]).optional(),
11
23
  releaseBase: z.string().min(1).optional(),
12
- packageJsonSync: z.array(
13
- z.enum([
14
- "dependencies",
15
- "devDependencies",
16
- "peerDependencies",
17
- "optionalDependencies",
18
- "scripts",
19
- "engines",
20
- "packageManager",
21
- "overrides",
22
- "exports",
23
- "pnpm"
24
- ])
25
- ).optional(),
24
+ packageJsonSync: z.array(z.enum(PACKAGE_JSON_SYNC_KEYS)).optional(),
26
25
  syncWithPackages: z.boolean().optional(),
27
26
  fileLinkMode: z.enum(["commit", "file", "local"]).optional()
28
27
  }).strict(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/types.ts"],"sourcesContent":["/**\n * Sync CLI v2 - Configuration Types\n *\n * Simplified configuration for worktree-based merge approach.\n */\n\nimport { z } from 'zod';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC API - for cella/cella.config.ts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Valid package.json keys that can be synced */\nexport type PackageJsonSyncKey =\n | 'dependencies'\n | 'devDependencies'\n | 'peerDependencies'\n | 'optionalDependencies'\n | 'scripts'\n | 'engines'\n | 'packageManager'\n | 'overrides'\n | 'exports'\n | 'pnpm';\n\n/**\n * Sync settings - all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable — each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /**\n * Automatically run packages sync after the sync service completes.\n * When true (default), the packages service is hidden from the menu\n * and runs automatically as part of sync.\n * Set to false to keep packages as a separate manual service.\n */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref) instead of the local clone, so the\n * comparison no longer depends on the local checkout being up to date. The local\n * clone is still used to read the fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/**\n * User-configurable sync options for cella/cella.config.ts.\n * Simplified for v2 - no long-lived sync branch or squash options.\n */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns — never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to fork — fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella/cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` instead of `ignored` fails closed\n * instead of silently dropping sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z\n .array(\n z.enum([\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n 'exports',\n 'pnpm',\n ]),\n )\n .optional(),\n syncWithPackages: z.boolean().optional(),\n fileLinkMode: z.enum(['commit', 'file', 'local']).optional(),\n })\n .strict(),\n overrides: z\n .object({\n ignored: z.array(z.string()).optional(),\n pinned: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n forks: z\n .array(\n z\n .object({\n name: z.string().min(1),\n localPath: z.string().min(1),\n remoteUrl: z.string().optional(),\n pullBranch: z.string().min(1),\n })\n .strict(),\n )\n .optional(),\n })\n .strict() satisfies z.ZodType<CellaCliConfig>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// INTERNAL TYPES - for sync modules\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Sync services available in the CLI */\nexport type SyncService = 'analyze' | 'sync' | 'migrate' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for a single contributed file, then exit (contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella/cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n\n /** List every upstream migration note, not only the open ones (migrate service) */\n all?: boolean;\n\n /** Print one migration note's README (migrate service) */\n show?: string;\n\n /** Write one migration note's folder where its codemod can run (migrate service) */\n extract?: string;\n\n /** Migration note ids to record as handled (migrate service) */\n mark?: string[];\n}\n\n/** File status after analysis */\nexport type FileStatus =\n | 'identical' // No changes needed\n | 'ahead' // Fork is ahead, protected (pinned/ignored)\n | 'local' // Local file (never existed upstream)\n | 'drifted' // Fork is ahead, NOT protected (at risk)\n | 'behind' // Upstream has changes to sync\n | 'diverged' // Both changed, will merge\n | 'pinned' // Fork wins on conflict\n | 'ignored' // Excluded from sync entirely\n | 'deleted' // Fork deleted, will stay deleted\n | 'renamed'; // Upstream renamed file\n\n/** Analyzed file with status and metadata */\nexport interface AnalyzedFile {\n path: string;\n status: FileStatus;\n /** True if file is in ignored list */\n isIgnored: boolean;\n /** True if file is in pinned list */\n isPinned: boolean;\n /** True if file exists in fork */\n existsInFork: boolean;\n /** True if file exists in upstream */\n existsInUpstream: boolean;\n /** True if file has merge conflict */\n hasConflict?: boolean;\n /** Relative date when file was last changed (since merge-base) */\n changedAt?: string;\n /** Unix epoch seconds of the last change, for sorting */\n changedTs?: number;\n /** Short commit hash of the last change */\n changedCommit?: string;\n /** For diverged files: relative date of upstream change */\n upstreamChangedAt?: string;\n /** For diverged files: Unix epoch seconds of the upstream change, for sorting */\n upstreamChangedTs?: number;\n /** For diverged files: short commit hash of the upstream change */\n upstreamCommit?: string;\n /** For renamed files: the original path before rename */\n renamedFrom?: string;\n /**\n * Protected (pinned/ignored) files only: both sides changed since the merge-base. The fork\n * side wins whole-file, so upstream's changes to this path are dropped by the sync.\n */\n upstreamChanged?: boolean;\n /** For `upstreamChanged` files: lines upstream changed since the merge-base (undefined for binary) */\n upstreamChangedLines?: number;\n /**\n * Ignored files only: upstream changed, added or deleted the file since the merge-base and the\n * fork did not touch it. Ignored paths never sync, so without this flag the change stays unseen\n * (new config keys, version bumps). Managed files and generated output (`isGeneratedFile`) are\n * never flagged.\n */\n upstreamOnly?: boolean;\n /**\n * Pinned `ahead` files only: lines present upstream but absent from the fork (undefined for\n * binary). Independent of the sync point, so it also shows upstream content dropped by an\n * earlier sync — or removed on purpose; only a diff can tell.\n */\n upstreamLinesAbsent?: number;\n}\n\n/** Summary counts by status */\nexport interface AnalysisSummary {\n /** Changed package/config files that are handled separately from normal sync categories */\n managed: number;\n identical: number;\n ahead: number;\n local: number;\n drifted: number;\n behind: number;\n diverged: number;\n pinned: number;\n ignored: number;\n deleted: number;\n renamed: number;\n total: number;\n}\n\n/** Merge result from merge-engine */\nexport interface MergeResult {\n success: boolean;\n /** Upstream branch name for file links */\n upstreamBranch?: string;\n /** Resolved concrete upstream ref that was merged (branch ref or release tag ref) */\n upstreamRef?: string;\n /** Upstream release tag synced (when tracking releases) */\n upstreamTag?: string;\n files: AnalyzedFile[];\n summary: AnalysisSummary;\n conflicts: string[];\n /** Upstream GitHub URL base for commit links */\n upstreamGitHubUrl?: string;\n /** Upstream commit info */\n upstreamCommit?: {\n hash: string;\n message: string;\n date: string;\n };\n /** Commits included in this sync range (oldest-first when rendered) */\n upstreamCommits?: Array<{\n hash: string;\n message: string;\n date: string;\n }>;\n /** Files that were auto-merged by git (diverged without remaining conflicts) */\n autoMergedFiles?: string[];\n /**\n * Protected (pinned/ignored) files resolved to the fork side although upstream also changed\n * them: upstream's hunks were dropped, not merged. Review each against upstream.\n */\n protectedConflicts?: string[];\n /** Pasteable `<merge-base>..<upstream>` range of short SHAs, for `git diff` hints. */\n upstreamDiffRange?: string;\n /** How upstream's own `overrides` changed since the merge-base, where the fork config does not follow. */\n upstreamOverrides?: UpstreamOverridesReport;\n /** Files flagged `upstreamOnly`, grouped by the `ignored` entry they fall under (config order). */\n ignoredUpstreamChanges?: IgnoredUpstreamGroup[];\n /**\n * Upstream migration notes: how many the incoming ref holds, which ones upstream added since the\n * sync point, and how many stay open in the fork once those are added.\n */\n migrationNotes?: { total: number; arrived: string[]; open: number };\n}\n\n/** Entries of one override list (`pinned` or `ignored`) that the fork config does not mirror. */\nexport interface OverrideListChanges {\n /** Entries upstream added since the merge-base that the fork list lacks. */\n added: string[];\n /** Entries upstream removed since the merge-base that the fork list still carries. */\n removed: string[];\n}\n\n/**\n * Upstream override changes the fork should review. The sync config is managed (never merged),\n * so new upstream `pinned`/`ignored` entries only reach the fork by hand. `unreadable` means\n * upstream's config was missing or unparsable at one side (`base` = merge-base, `incoming` =\n * upstream ref), so nothing was compared.\n */\nexport type UpstreamOverridesReport =\n | { kind: 'changes'; pinned: OverrideListChanges; ignored: OverrideListChanges }\n | { kind: 'unreadable'; side: 'base' | 'incoming' };\n\n/** Upstream-only changes under one `ignored` entry. */\nexport interface IgnoredUpstreamGroup {\n /** The `ignored` entry (configured or derived from an app-owned module) the files fall under. */\n entry: string;\n /** Changed paths under the entry, sorted. */\n paths: string[];\n /** Paths the fork lacks (new upstream files). */\n added: number;\n /** Paths upstream deleted (the fork keeps them). */\n deleted: number;\n}\n"],"mappings":";AAMA,SAAS,SAAS;AAqIX,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;AAaO,IAAM,oBAAoB,EAC9B,OAAO;AAAA,EACN,UAAU,EACP,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC7B,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IAC3C,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IACtD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,iBAAiB,EACd;AAAA,MACC,EAAE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACC,SAAS;AAAA,IACZ,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACvC,cAAc,EAAE,KAAK,CAAC,UAAU,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EAC7D,CAAC,EACA,OAAO;AAAA,EACV,WAAW,EACR,OAAO;AAAA,IACN,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACtC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,OAAO,EACJ;AAAA,IACC,EACG,OAAO;AAAA,MACN,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACtB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MAC3B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO;","names":[]}
1
+ {"version":3,"sources":["../src/config/types.ts"],"sourcesContent":["/**\n * Configuration types: the public cella/cella.config.ts surface, its runtime\n * schema, and the internal types the services share.\n */\n\nimport { z } from 'zod';\n\n// Public API for cella/cella.config.ts.\n\nconst PACKAGE_JSON_SYNC_KEYS = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n 'exports',\n 'pnpm',\n] as const;\n\n/** Valid package.json keys that can be synced */\nexport type PackageJsonSyncKey = (typeof PACKAGE_JSON_SYNC_KEYS)[number];\n\n/**\n * Sync settings: all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable: each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']). `type` always syncs. */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /** Automatic package.json sync after the sync service completes (default: true). Set to false to turn it off. */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref), so the comparison does not depend on\n * the local checkout being up to date. The local clone is still used to read the\n * fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/** User-configurable sync options for cella/cella.config.ts. */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns: never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to the fork: fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella/cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` for `ignored` fails closed; a silent\n * fallback would drop sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z.array(z.enum(PACKAGE_JSON_SYNC_KEYS)).optional(),\n syncWithPackages: z.boolean().optional(),\n fileLinkMode: z.enum(['commit', 'file', 'local']).optional(),\n })\n .strict(),\n overrides: z\n .object({\n ignored: z.array(z.string()).optional(),\n pinned: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n forks: z\n .array(\n z\n .object({\n name: z.string().min(1),\n localPath: z.string().min(1),\n remoteUrl: z.string().optional(),\n pullBranch: z.string().min(1),\n })\n .strict(),\n )\n .optional(),\n })\n .strict() satisfies z.ZodType<CellaCliConfig>;\n\n// Internal types for the services.\n\n/** Sync services available in the CLI */\nexport type SyncService = 'analyze' | 'sync' | 'migrate' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for one file, then exit (analyze and contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella/cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /**\n * Pin the upstream ref for this run (analyze/sync), overriding `track`: a commit sha, a release\n * tag or an upstream branch name. Must be on the upstream branch or in an upstream release.\n */\n ref?: string;\n\n /**\n * Merge with the fork's sync config as it stands (sync service). Without it, a new merge stops\n * before anything changes when upstream changed its own `overrides` or `packageJsonSync` in ways\n * the fork config does not follow.\n */\n keepConfig?: boolean;\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n\n /** Count what the current branch changed since it left this ref, by kind of file (stats service) */\n since?: string;\n\n /** Print the `since` table as markdown for a pull request description (stats service) */\n md?: boolean;\n\n /** List every upstream migration note, not only the open ones (migrate service) */\n all?: boolean;\n\n /** Print one migration note's README (migrate service) */\n show?: string;\n\n /** Write one migration note's folder where its codemod can run (migrate service) */\n extract?: string;\n\n /** Run one migration note's codemod, keeping files identical to upstream as they are (migrate service) */\n run?: string;\n\n /** The script to run with `run`, when the note folder holds more than one (migrate service) */\n script?: string;\n\n /** Arguments after `--`, passed to the codemod as given (migrate service, with `run`) */\n runArgs?: string[];\n\n /** Migration note ids to record as handled (migrate service) */\n mark?: string[];\n}\n\n/** File status after analysis */\nexport type FileStatus =\n | 'identical' // No changes needed\n | 'ahead' // Fork is ahead, protected (pinned/ignored)\n | 'local' // Local file (never existed upstream)\n | 'drifted' // Fork is ahead, NOT protected (at risk)\n | 'behind' // Upstream has changes to sync\n | 'diverged' // Both changed, will merge\n | 'pinned' // Fork wins on conflict\n | 'ignored' // Excluded from sync entirely\n | 'deleted' // Fork deleted, will stay deleted\n | 'renamed'; // Upstream renamed file\n\n/** Statuses the sync applies changes for: a staged sync holds at least one file with one of these. */\nexport const SYNC_APPLIED_STATUSES: readonly FileStatus[] = ['behind', 'diverged', 'renamed', 'ignored', 'pinned'];\n\n/** Commit metadata for a single log entry */\nexport interface CommitRangeEntry {\n hash: string;\n message: string;\n date: string;\n}\n\n/** Analyzed file with status and metadata */\nexport interface AnalyzedFile {\n path: string;\n status: FileStatus;\n /** True if file is in ignored list */\n isIgnored: boolean;\n /** True if file is in pinned list */\n isPinned: boolean;\n /** True if file exists in fork */\n existsInFork: boolean;\n /** True if file exists in upstream */\n existsInUpstream: boolean;\n /** Relative date when file was last changed (since merge-base) */\n changedAt?: string;\n /** Unix epoch seconds of the last change, for sorting */\n changedTs?: number;\n /** Short commit hash of the last change */\n changedCommit?: string;\n /** For diverged files: relative date of upstream change */\n upstreamChangedAt?: string;\n /** For diverged files: Unix epoch seconds of the upstream change, for sorting */\n upstreamChangedTs?: number;\n /** For diverged files: short commit hash of the upstream change */\n upstreamCommit?: string;\n /** For renamed files: the original path before rename */\n renamedFrom?: string;\n /**\n * Protected (pinned/ignored) files only: both sides changed since the merge-base. The fork\n * side wins whole-file, so upstream's changes to this path are dropped by the sync.\n */\n upstreamChanged?: boolean;\n /** For `upstreamChanged` files: lines upstream changed since the merge-base (undefined for binary) */\n upstreamChangedLines?: number;\n /**\n * Ignored files only: upstream changed, added or deleted the file since the merge-base and the\n * fork did not touch it. Ignored paths never sync, so without this flag the change stays unseen\n * (new config keys, version bumps). Managed files and generated output (`isGeneratedFile`) are\n * never flagged.\n */\n upstreamOnly?: boolean;\n /**\n * Pinned `ahead` files only: lines present upstream but absent from the fork (undefined for\n * binary). Independent of the sync point, so it also shows upstream content dropped by an\n * earlier sync, or removed on purpose; only a diff can tell.\n */\n upstreamLinesAbsent?: number;\n}\n\n/** Summary counts by status */\nexport interface AnalysisSummary {\n /** Changed package/config files that are handled separately from normal sync categories */\n managed: number;\n identical: number;\n ahead: number;\n local: number;\n drifted: number;\n behind: number;\n diverged: number;\n pinned: number;\n ignored: number;\n deleted: number;\n renamed: number;\n}\n\n/** Merge result from merge-engine */\nexport interface MergeResult {\n success: boolean;\n /** Upstream branch name for file links */\n upstreamBranch?: string;\n /** Resolved concrete upstream ref that was merged (branch ref or release tag ref) */\n upstreamRef?: string;\n /** Upstream release tag synced (when tracking releases) */\n upstreamTag?: string;\n files: AnalyzedFile[];\n summary: AnalysisSummary;\n conflicts: string[];\n /** Upstream GitHub URL base for commit links */\n upstreamGitHubUrl?: string;\n /** Upstream commit info */\n upstreamCommit?: CommitRangeEntry;\n /** Commits included in this sync range (oldest-first when rendered) */\n upstreamCommits?: CommitRangeEntry[];\n /** Files that were auto-merged by git (diverged without remaining conflicts) */\n autoMergedFiles?: string[];\n /**\n * Protected (pinned/ignored) files resolved to the fork side although upstream also changed\n * them: upstream's hunks were dropped, not merged. Review each against upstream.\n */\n protectedConflicts?: string[];\n /** `protectedConflicts` grouped by the `pinned` or `ignored` entry they fall under, for one diff hint per entry. */\n protectedUpstreamChanges?: ProtectedUpstreamGroup[];\n /** Pasteable `<merge-base>..<upstream>` range of short SHAs, for `git diff` hints. */\n upstreamDiffRange?: string;\n /** How upstream's own sync config changed since the merge-base, where the fork config does not follow. */\n upstreamOverrides?: UpstreamOverridesReport;\n /** Files flagged `upstreamOnly`, grouped by the `ignored` entry they fall under (config order). */\n ignoredUpstreamChanges?: IgnoredUpstreamGroup[];\n /**\n * Upstream migration notes: how many the incoming ref holds, which ones upstream added since the\n * sync point, and how many stay open in the fork once those are added.\n */\n migrationNotes?: { total: number; arrived: string[]; open: number };\n}\n\n/** Entries of one sync config list (`pinned`, `ignored` or `packageJsonSync`) that the fork config does not mirror. */\nexport interface OverrideListChanges {\n /** Entries upstream added since the merge-base that the fork list lacks. */\n added: string[];\n /** Entries upstream removed since the merge-base that the fork list still carries. */\n removed: string[];\n}\n\n/**\n * Upstream sync config changes the fork should review. The sync config is managed (never merged),\n * so new upstream `pinned`/`ignored` entries and `packageJsonSync` keys only reach the fork by\n * hand. `unreadable` means upstream's config was missing or unparsable at one side (`base` =\n * merge-base, `incoming` = upstream ref), so nothing was compared.\n */\nexport type UpstreamOverridesReport =\n | {\n kind: 'changes';\n pinned: OverrideListChanges;\n ignored: OverrideListChanges;\n packageJsonSync: OverrideListChanges;\n }\n | { kind: 'unreadable'; side: 'base' | 'incoming' };\n\n/** Protected paths upstream also changed, under one `pinned` or `ignored` entry. */\nexport interface ProtectedUpstreamGroup {\n /** The `pinned` or `ignored` entry the paths fall under; the path itself when no entry covers it (a managed file). */\n entry: string;\n /** Paths under the entry, sorted. */\n paths: string[];\n}\n\n/** Upstream-only changes under one `ignored` entry. */\nexport interface IgnoredUpstreamGroup {\n /** The `ignored` entry (configured or derived from an app-owned module) the files fall under. */\n entry: string;\n /** Changed paths under the entry, sorted. */\n paths: string[];\n /** Paths the fork lacks (new upstream files). */\n added: number;\n /** Paths upstream deleted (the fork keeps them). */\n deleted: number;\n}\n"],"mappings":";AAKA,SAAS,SAAS;AAIlB,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+GO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;AAaO,IAAM,oBAAoB,EAC9B,OAAO;AAAA,EACN,UAAU,EACP,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC7B,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IAC3C,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IACtD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,iBAAiB,EAAE,MAAM,EAAE,KAAK,sBAAsB,CAAC,EAAE,SAAS;AAAA,IAClE,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACvC,cAAc,EAAE,KAAK,CAAC,UAAU,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EAC7D,CAAC,EACA,OAAO;AAAA,EACV,WAAW,EACR,OAAO;AAAA,IACN,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACtC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,OAAO,EACJ;AAAA,IACC,EACG,OAAO;AAAA,MACN,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACtB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MAC3B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO;","names":[]}