@cellajs/cli 0.3.1 → 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 +128 -31
- package/dist/config.d.ts +13 -21
- package/dist/config.js +13 -14
- package/dist/config.js.map +1 -1
- package/dist/index.js +1063 -686
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
|
@@ -38,25 +38,30 @@ Per-service help: `pnpm cella <service> --help`.
|
|
|
38
38
|
| Service | Useful options |
|
|
39
39
|
|---------|----------------|
|
|
40
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
|
|
42
|
-
| migrate | `--all`, `--json`, `--show <id>`, `--extract <id>`, `--mark <ids...>` |
|
|
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
|
|
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
|
|
59
|
-
- **`pinned
|
|
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
|
|
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`:
|
|
@@ -83,8 +88,9 @@ pnpm cella sync --ref 4f7d87c # sync up to this upstream commit
|
|
|
83
88
|
```
|
|
84
89
|
|
|
85
90
|
A run never syncs to a point behind the last sync: when an earlier `--ref` or `--track branch` run
|
|
86
|
-
went past the latest release, release tracking
|
|
87
|
-
instead of reverting what the app already has.
|
|
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.
|
|
88
94
|
|
|
89
95
|
## Sync workflow
|
|
90
96
|
|
|
@@ -95,16 +101,22 @@ main ──▶ cella/sync/<stamp> ──(3-way merge)──▶ PR ──(squash)
|
|
|
95
101
|
```
|
|
96
102
|
|
|
97
103
|
It runs a real git 3-way merge. `sync` is **idempotent and staged**: each run advances the sync
|
|
98
|
-
one stage, and the run that commits never ships
|
|
104
|
+
one stage, and the run that commits never ships: the pause on the committed branch is where
|
|
99
105
|
drift triage (`pnpm cella analyze` diffs committed HEAD) and follow-up commits happen.
|
|
100
106
|
|
|
101
107
|
1. **First run** cuts the branch and merges. A clean merge is committed right away: dependencies
|
|
102
108
|
are reconciled (`pnpm install` + `pnpm check`), everything is staged, and the delta is
|
|
103
|
-
committed
|
|
109
|
+
committed; then the run stops on the branch. A conflicted merge stops earlier so you can
|
|
104
110
|
resolve in your IDE (`git add` the resolved files) and re-run to commit.
|
|
105
111
|
2. **Final re-run `pnpm cella sync`** on the committed branch ships it: pushes to `origin`,
|
|
106
112
|
opens a PR into `main` (via `gh`), and switches you back to `main`.
|
|
107
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
|
+
|
|
108
120
|
`sync` also runs from a linked git worktree while another worktree has `main` checked out: it
|
|
109
121
|
never switches to `main`. It compares `main` with `origin/main` by ref and cuts the sync branch
|
|
110
122
|
from `main`; when `main` is behind and checked out elsewhere, it cuts from `origin/main` instead
|
|
@@ -113,15 +125,15 @@ to it.
|
|
|
113
125
|
|
|
114
126
|
When the commit stage runs, the in-progress merge state (`MERGE_HEAD`) is discarded, so the staged delta
|
|
115
127
|
collapses into a **single-parent commit** (`chore: sync upstream cella <sha>`). This keeps the PR
|
|
116
|
-
to one clean commit with the incremental diff
|
|
128
|
+
to one clean commit with the incremental diff; a two-parent merge commit would instead list the
|
|
117
129
|
upstream branch's entire history, because the fork doesn't share pushed ancestry with upstream
|
|
118
130
|
and the local `git replace` graft that makes merges incremental is never pushed. Ancestry lives
|
|
119
131
|
in `refs/cella/last-sync` (and the committed `cella.manifest.json` for fresh clones), so
|
|
120
|
-
`git merge-base` keeps working across throwaway branches
|
|
132
|
+
`git merge-base` keeps working across throwaway branches; each is safe to delete once its PR
|
|
121
133
|
lands. The three-segment name can't collide with git's ref namespacing.
|
|
122
134
|
|
|
123
135
|
If conflicts remain when you re-run, `sync` lists them and stops (never starting a second cycle
|
|
124
|
-
mid-merge). If `pnpm check`, the push, or `gh` fails, it degrades gracefully
|
|
136
|
+
mid-merge). If `pnpm check`, the push, or `gh` fails, it degrades gracefully, reporting the
|
|
125
137
|
issue and printing the remaining manual steps:
|
|
126
138
|
|
|
127
139
|
```bash
|
|
@@ -153,10 +165,26 @@ listed until marked.
|
|
|
153
165
|
```bash
|
|
154
166
|
pnpm cella migrate # open notes, with summary and links
|
|
155
167
|
pnpm cella migrate --show <id> # one note's README
|
|
156
|
-
pnpm cella migrate --
|
|
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>/
|
|
157
171
|
pnpm cella migrate --mark <id> [...] # record notes as handled
|
|
158
172
|
```
|
|
159
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
|
+
|
|
160
188
|
## Sync rules
|
|
161
189
|
|
|
162
190
|
For each file, `sync` compares fork and upstream **content** (blob comparison) and resolves it
|
|
@@ -177,7 +205,7 @@ customize). Unconfigured = syncs automatically. Run `pnpm cella analyze` first t
|
|
|
177
205
|
|
|
178
206
|
### Aggressive sync flags
|
|
179
207
|
|
|
180
|
-
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
|
|
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`).
|
|
181
209
|
|
|
182
210
|
| Flag | Effect |
|
|
183
211
|
|------|--------|
|
|
@@ -197,7 +225,7 @@ During analysis and sync, files are displayed with status indicators:
|
|
|
197
225
|
| ! | `drifted` | Fork changed, not protected | At risk, consider pinning |
|
|
198
226
|
| ↓ | `behind` | Upstream has changes | Will sync from upstream |
|
|
199
227
|
| ⇅ | `diverged` | Both sides changed | Will merge from upstream |
|
|
200
|
-
| ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork
|
|
228
|
+
| ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork: review, see below |
|
|
201
229
|
| + | `local` | Only in fork, never in upstream | No action needed |
|
|
202
230
|
|
|
203
231
|
### Protected but behind upstream
|
|
@@ -211,6 +239,22 @@ missing new upstream utilities that synced components rely on), so both commands
|
|
|
211
239
|
`--scope all` and `--scope protected` (`--json` adds `upstreamChanged` and `upstreamChangedLines`).
|
|
212
240
|
- `sync` prints the same list at the end of its summary, right when the drop happens.
|
|
213
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
|
+
|
|
214
258
|
Diff each against upstream (`cella analyze --open-diff <path>`) and adopt what you need. The
|
|
215
259
|
check is relative to the last sync point: a drop that happened in an earlier sync only shows up
|
|
216
260
|
as plain `ahead` afterwards. For that case the `↑ protected in fork` section annotates pinned
|
|
@@ -230,12 +274,12 @@ its summary, and `sync` after its merge summary:
|
|
|
230
274
|
paste. Files both sides changed stay in the section above, and generated output the fork
|
|
231
275
|
regenerates itself (`sdk/gen`, `*.gen.*` files) is left out. `--list`/`--json` include these
|
|
232
276
|
files in `--scope all` and `--scope protected` (`--json`: `upstreamOnly`).
|
|
233
|
-
- **Upstream
|
|
234
|
-
its own `overrides.pinned` or `overrides.ignored
|
|
235
|
-
arrive. The report names each entry upstream added
|
|
236
|
-
upstream dropped that your config still has (`−`). It
|
|
237
|
-
and never edits yours. `--json` puts the lists on the
|
|
238
|
-
(`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`).
|
|
239
283
|
|
|
240
284
|
```
|
|
241
285
|
⚠ ignored paths changed upstream · 1 file under 1 entry
|
|
@@ -243,11 +287,24 @@ its summary, and `sync` after its merge summary:
|
|
|
243
287
|
git diff e9a8d485e..a81e3353b -- frontend/src/modules/marketing
|
|
244
288
|
the fork left these untouched and ignored paths never sync: diff and adopt what you need.
|
|
245
289
|
|
|
246
|
-
⚠ upstream changed its sync
|
|
290
|
+
⚠ upstream changed its sync config · 2 entries to review
|
|
247
291
|
+ pinned: backend/src/bundle-config.ts
|
|
292
|
+
+ packageJsonSync: exports
|
|
248
293
|
cella/cella.config.ts never syncs: add or drop these by hand where they fit your app.
|
|
249
294
|
```
|
|
250
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
|
+
|
|
251
308
|
## Package.json sync
|
|
252
309
|
|
|
253
310
|
`packageJsonSync` controls which package.json sections sync from upstream:
|
|
@@ -289,6 +346,46 @@ forks: [
|
|
|
289
346
|
|
|
290
347
|
### Pulling contributions
|
|
291
348
|
|
|
292
|
-
Run `pnpm cella contributions` (or pick **contributions** from the menu). Select
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
*
|
|
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 =
|
|
8
|
+
type PackageJsonSyncKey = (typeof PACKAGE_JSON_SYNC_KEYS)[number];
|
|
9
9
|
/**
|
|
10
|
-
* Sync settings
|
|
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
|
|
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
|
*/
|
|
@@ -34,12 +34,7 @@ interface SyncSettings {
|
|
|
34
34
|
releaseBase?: string;
|
|
35
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)
|
|
68
|
-
*
|
|
69
|
-
*
|
|
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
|
|
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
|
|
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(),
|
package/dist/config.js.map
CHANGED
|
@@ -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']). `type` always syncs. */\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 /**\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 /** 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":[]}
|