@cellajs/cli 0.3.3 → 0.4.1

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
@@ -20,7 +20,7 @@ pnpm cella audit
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
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 and vulnerabilities |
23
+ | `audit` | Update outdated packages and runtime pins, then rebuild the security overrides from a fresh audit |
24
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 |
@@ -37,13 +37,13 @@ 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>`, `--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
- | audit | `--list`, `--force`, `--check-overrides` |
44
- | forks | `--fork <name>`, `--log`, `--hard`, `--keep-config`, `-V, --verbose` |
45
- | contributions | `--fork <name>`, `--list`, `--json`, `--diff <path>` |
46
- | stats | `--coverage`, `--since <ref>`, `--md`, `-V, --verbose` |
40
+ | analyze | `--list`, `--json`, `--scope <all\|risk\|protected>`, `--track <release\|branch>`, `--ref <ref>`, `--unpinned` |
41
+ | sync | `--track <release\|branch>`, `--ref <ref>`, `--keep-config`, `--unpinned` |
42
+ | migrate | `--all`, `--json`, `--show <id>`, `--extract <id>`, `--run <id> [-- <codemod args>]`, `--mark <ids...>` |
43
+ | audit | `--list` |
44
+ | forks | `--fork <name>`, `--keep-config` |
45
+ | contributions | `--fork <name>`, `--json` |
46
+ | stats | `--since <ref>`, `--md`, `-V, --verbose` |
47
47
 
48
48
  | Global flag | Description |
49
49
  |-------------|-------------|
@@ -63,6 +63,10 @@ has and your app deleted). Every run warns `ignored entry not found` only when n
63
63
  upstream has the path. Upstream is read at `settings.upstreamBranch` as last fetched; before the
64
64
  first fetch, every `ignored` entry missing from your app warns.
65
65
 
66
+ Every run also warns about a `pinned` entry that does nothing: one inside an `ignored` path (app
67
+ modules included), one upstream no longer has (the file is your app's own now), and one identical
68
+ to upstream, which only drops upstream's next change to it.
69
+
66
70
  ## Upstream tracking
67
71
 
68
72
  The sync CLI tracks upstream cella one of two ways, set via `settings.upstreamTrack`:
@@ -109,7 +113,8 @@ drift triage (`pnpm cella analyze` diffs committed HEAD) and follow-up commits h
109
113
  committed; then the run stops on the branch. A conflicted merge stops earlier so you can
110
114
  resolve in your IDE (`git add` the resolved files) and re-run to commit.
111
115
  2. **Final re-run `pnpm cella sync`** on the committed branch ships it: pushes to `origin`,
112
- opens a PR into `main` (via `gh`), and switches you back to `main`.
116
+ opens a PR into `main` (via `gh`) titled like the sync commit, even with follow-up commits on
117
+ top, and switches you back to `main`.
113
118
 
114
119
  Before the merge starts, a first run checks that it can go ahead, and stops without changing
115
120
  anything when it cannot: the temporary branch is removed again and you are back on the branch you
@@ -173,8 +178,7 @@ pnpm cella migrate --mark <id> [...] # record notes as handled
173
178
 
174
179
  `--run` extracts the note and runs its codemod with `tsx` from your app root. Everything after `--`
175
180
  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>`.
181
+ report-only run.
178
182
 
179
183
  A codemod's roots cover template-owned files too, and a file that is identical to upstream already
180
184
  is the way upstream wants it: upstream may have changed it after writing the codemod, so a rewrite
@@ -203,15 +207,6 @@ out:
203
207
  assets, config you own). `pinned` = your version always wins but stays visible (files you
204
208
  customize). Unconfigured = syncs automatically. Run `pnpm cella analyze` first to preview.
205
209
 
206
- ### Aggressive sync flags
207
-
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`).
209
-
210
- | Flag | Effect |
211
- |------|--------|
212
- | `--hard` | Overwrites `drifted` files with upstream (local-only changes are replaced) |
213
- | `--unpinned` | Ignores `pinned` files so upstream surfaces as `behind`/`diverged`; managed files stay pinned |
214
-
215
210
  ## Status indicators
216
211
 
217
212
  During analysis and sync, files are displayed with status indicators:
@@ -239,28 +234,26 @@ missing new upstream utilities that synced components rely on), so both commands
239
234
  `--scope all` and `--scope protected` (`--json` adds `upstreamChanged` and `upstreamChangedLines`).
240
235
  - `sync` prints the same list at the end of its summary, right when the drop happens.
241
236
 
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
237
  ```
248
238
  ⚠ 3 protected files kept the fork version, dropping upstream changes:
249
239
  ⨀ frontend/src/styling/tailwind.css · 21 lines changed upstream
250
240
  ⨂ shared/config/default.ts · 4 lines changed upstream
251
241
  ⨂ 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
242
+ pinned/ignored files where upstream also changed since the last sync; the fork side wins on conflict, so review what upstream changed and adopt what you need.
256
243
  ```
257
244
 
258
- Diff each against upstream (`cella analyze --open-diff <path>`) and adopt what you need. The
259
- check is relative to the last sync point: a drop that happened in an earlier sync only shows up
260
- as plain `ahead` afterwards. For that case the `↑ protected in fork` section annotates pinned
245
+ The [review page](#review-page) shows what upstream changed in each of them since the last sync.
246
+ The check is relative to the last sync point: a drop that happened in an earlier sync only shows
247
+ up as plain `ahead` afterwards. For that case the `↑ protected in fork` section annotates pinned
261
248
  files with `· N upstream lines absent` (lines upstream has that the fork lacks, compared at the
262
249
  tips, `--json`: `upstreamLinesAbsent`). That is upstream content the fork never received,
263
- deliberately or not: diff and decide. Ignored files are not annotated.
250
+ deliberately or not: the review page shows the difference, so you can decide. Ignored files are
251
+ not annotated.
252
+
253
+ To pull upstream into every pinned file at once, run `sync --unpinned`: pins are lifted for that
254
+ run (managed files stay pinned) and the merge uses the full upstream history, so changes earlier
255
+ syncs dropped come back too. Review the staged merge in your editor and keep what you want;
256
+ `analyze --unpinned` previews it.
264
257
 
265
258
  ### Upstream changes that never sync
266
259
 
@@ -270,9 +263,9 @@ its summary, and `sync` after its merge summary:
270
263
  - **Ignored paths changed upstream.** Upstream changed, added or deleted a file under an `ignored`
271
264
  entry (app-owned module folders included) and the fork left it untouched. No conflict marks it,
272
265
  so this is where new config keys and version bumps under `shared/config` show up. Each `ignored`
273
- entry gets one line with a file count and a `git diff <last-sync>..<upstream> -- <entry>` line to
274
- paste. Files both sides changed stay in the section above, and generated output the fork
275
- regenerates itself (`sdk/gen`, `*.gen.*` files) is left out. `--list`/`--json` include these
266
+ entry gets one line with a file count, and the [review page](#review-page) shows the diffs. Files
267
+ both sides changed stay in the section above, and generated output the fork regenerates itself
268
+ (`sdk/gen`, `*.gen.*` files) is left out. `--list`/`--json` include these
276
269
  files in `--scope all` and `--scope protected` (`--json`: `upstreamOnly`).
277
270
  - **Upstream sync config changes.** `cella/cella.config.ts` never syncs, so entries upstream adds to
278
271
  its own `overrides.pinned` or `overrides.ignored`, and keys it adds to `settings.packageJsonSync`
@@ -284,8 +277,7 @@ its summary, and `sync` after its merge summary:
284
277
  ```
285
278
  ⚠ ignored paths changed upstream · 1 file under 1 entry
286
279
  ⨂ frontend/src/modules/marketing · 1 file
287
- git diff e9a8d485e..a81e3353b -- frontend/src/modules/marketing
288
- the fork left these untouched and ignored paths never sync: diff and adopt what you need.
280
+ the fork left these untouched and ignored paths never sync: review and adopt what you need.
289
281
 
290
282
  ⚠ upstream changed its sync config · 2 entries to review
291
283
  + pinned: backend/src/bundle-config.ts
@@ -305,6 +297,31 @@ already staged merge never stops for this.
305
297
  pnpm cella sync --keep-config # merge although upstream changed its sync config
306
298
  ```
307
299
 
300
+ ### Review page
301
+
302
+ The terminal lists files; the review page shows their diffs, side by side and highlighted, on one
303
+ page in your browser under the same section titles. `analyze` and `sync` write it whenever a list
304
+ has files, and print its path last:
305
+
306
+ ```
307
+ ⧈ review page with 3 diffs, opened in the browser:
308
+ /Users/you/app/.git/cella-diff/sync-review.html
309
+ ```
310
+
311
+ | Section | `analyze` | `sync` | The diff shows |
312
+ | --- | --- | --- | --- |
313
+ | protected but behind upstream (sync: protected files kept the fork version) | ✓ | ✓ | what upstream changed since the last sync |
314
+ | ignored paths changed upstream | ✓ | ✓ | what upstream changed since the last sync |
315
+ | pinned files that match the previous upstream | ✓ | ✓ | what upstream changed since the last sync |
316
+ | protected in fork, upstream lines absent | ✓ | | the fork against upstream, both as they are now |
317
+ | drifted from upstream | ✓ | | what the fork changed since the last sync |
318
+
319
+ `sync` opens the page in your browser when it runs in a terminal: the files on it are upstream
320
+ changes the merge did not bring in, and the committed sync branch is where you adopt them.
321
+ `analyze` only prints the path, since you run it often and pinned files keep their absent lines.
322
+ The page lives in the git dir, so it never shows up in `git status`, and each run overwrites it. It
323
+ holds 50 diffs at most; the rest, and files without a text diff, are listed by path.
324
+
308
325
  ## Package.json sync
309
326
 
310
327
  `packageJsonSync` controls which package.json sections sync from upstream:
@@ -326,12 +343,81 @@ command that runs the script. The sync lists every change and warns about each e
326
343
  way, so you can remove it once your code no longer needs it. `exports` are add-only: an entry your fork
327
344
  already defines is never rewritten, and `exports` only merges when both sides are subpath maps
328
345
  (`{ ".": …, "./config": … }`). A workspace upstream added since your last sync arrives with its
329
- package.json copied verbatim.
346
+ package.json copied verbatim. A merged section is sorted alphabetically, except `scripts`: they
347
+ keep upstream's order, and a script only your fork has stays after the one it follows.
330
348
 
331
349
  `type` always syncs, whatever `packageJsonSync` lists: it follows upstream where your fork kept the
332
350
  previous value, and a package.json without `type` gets upstream's. Set it yourself (for example
333
351
  `"commonjs"`) to keep your own.
334
352
 
353
+ ## Audit
354
+
355
+ `pnpm cella audit` works in two layers, in this order:
356
+
357
+ 1. **Updates.** It lists the outdated packages and runtime pins in one table and asks once which to
358
+ update. Rows with a high or critical advisory start ticked; submitting with nothing ticked skips
359
+ this layer.
360
+ 2. **Security.** It audits the workspace as the picked updates leave it and brings three settings
361
+ in `pnpm-workspace.yaml` in line with that audit.
362
+
363
+ Nothing is written until the end: the updates and the security settings land together, with a
364
+ single `pnpm install`. Cancelling any prompt leaves the workspace as it was.
365
+
366
+ ### Runtime pins
367
+
368
+ `pnpm audit` only sees the lockfile, so the audit also checks the runtimes the workspace pins to an
369
+ exact version, against their own release feeds:
370
+
371
+ | Pin | Feed | Dot |
372
+ |-----|------|-----|
373
+ | root `engines.node` | nodejs.org release index | red when a newer release in the line is a security release |
374
+ | root `packageManager` (pnpm) | npm registry and its advisory database | red when the newer version fixes an advisory on yours |
375
+ | `FROM image:2.8-suffix` in tracked Dockerfiles | Docker Hub tags | yellow when your tag has had no push for 6 months while a newer one has |
376
+
377
+ A pin moves within its major, to the newest version older than `minimumReleaseAge`. A Node bump
378
+ waits until Docker Hub has the `node:<version><suffix>` images the Dockerfiles build from, and the
379
+ audit prints what is left to do on your machine: switching its Node, and building each changed image.
380
+
381
+ ### Holding a package back
382
+
383
+ To keep a package off the update list, use pnpm's own setting, with the reason as a comment.
384
+ `pnpm outdated` skips it, so the audit does too, and `pnpm audit` still checks it:
385
+
386
+ ```yaml
387
+ updateConfig:
388
+ ignoreDependencies:
389
+ - typescript # 7 drops the JS compiler API that SDK codegen needs
390
+ ```
391
+
392
+ The security layer treats its overrides as output, not as configuration. Every run it drops them,
393
+ resolves the workspace without them in a temporary copy, and puts back only the ones a current
394
+ advisory still needs. An override whose package now resolves to a fixed version, because you
395
+ updated a parent or the parent widened its range, is gone after the next run. The workspace
396
+ changes once, at the end, and only when the outcome differs from what is there.
397
+
398
+ | Setting | What the audit owns | What it leaves alone |
399
+ |---------|---------------------|----------------------|
400
+ | `overrides` | keys with a version selector: `pkg@<1.2.3` | keys without one (`pkg`) and parent-scoped keys (`parent>pkg`) |
401
+ | `minimumReleaseAgeExclude` | `pkg@1.2.3` entries, removed once that version is older than `minimumReleaseAge` | name-only entries |
402
+ | `auditConfig.ignoreGhsas` | every entry, removed when the advisory no longer applies or a fix arrives | |
403
+
404
+ To keep an override whatever the audit finds, write it without a version selector.
405
+
406
+ For each advisory the audit looks up the published versions itself; it does not trust the patched
407
+ version an advisory claims. Most outcomes need no answer from you:
408
+
409
+ - **A fix that fits.** The audit writes an override. When the fix lies inside the range the
410
+ parents ask for, the lockfile holds the fixed version on its own, and the following run drops
411
+ the override.
412
+ - **A breaking fix** (another major, or another minor below 1.0), or a fix for a package you
413
+ depend on directly: you choose between leaving it open, applying the override and ignoring the
414
+ advisory. An override that was in place before the run is applied again without asking.
415
+ - **A fix younger than `minimumReleaseAge`**: you choose between waiting and installing it now
416
+ with an exclude.
417
+ - **No fix published**: you are asked whether to ignore the advisory until one is.
418
+
419
+ `--list` prints the same report without prompts and writes nothing.
420
+
335
421
  ## Contributions (pull from forks)
336
422
 
337
423
  Upstream can pull modifications from local forks and selectively adopt them.
@@ -348,8 +434,8 @@ forks: [
348
434
 
349
435
  Run `pnpm cella contributions` (or pick **contributions** from the menu). Select a fork; cella
350
436
  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.
437
+ contributed files. Press `d` on a file to open its diff in your browser. Accepted files are
438
+ checked out from the contrib branch and staged for review.
353
439
 
354
440
  ## Stats of a branch
355
441
 
package/dist/config.d.ts CHANGED
@@ -40,9 +40,8 @@ interface SyncSettings {
40
40
  * How to link files in CLI output.
41
41
  * - 'commit' (default): Link to the commit that changed the file on GitHub.
42
42
  * - 'file': Link to the file in the repo at the upstream branch on GitHub.
43
- * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.
44
43
  */
45
- fileLinkMode?: 'commit' | 'file' | 'local';
44
+ fileLinkMode?: 'commit' | 'file';
46
45
  }
47
46
  /**
48
47
  * Configuration for a downstream fork repository that cella interacts with.
@@ -83,10 +82,11 @@ interface CellaCliConfig {
83
82
  */
84
83
  ignored?: string[];
85
84
  /**
86
- * Paths pinned to the fork: fork wins on conflicts.
85
+ * Paths pinned to the fork: the fork copy wins whole-file.
87
86
  * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and
88
87
  * everything under it, 'README.md' matches that exact file.
89
- * Non-conflicting upstream changes merge normally.
88
+ * Upstream changes to a pinned file are dropped, not merged: analyze lists them under
89
+ * "protected but behind upstream" to adopt by hand.
90
90
  * If fork deleted a pinned file, deletion is respected.
91
91
  */
92
92
  pinned?: string[];
package/dist/config.js CHANGED
@@ -23,7 +23,7 @@ var cellaConfigSchema = z.object({
23
23
  releaseBase: z.string().min(1).optional(),
24
24
  packageJsonSync: z.array(z.enum(PACKAGE_JSON_SYNC_KEYS)).optional(),
25
25
  syncWithPackages: z.boolean().optional(),
26
- fileLinkMode: z.enum(["commit", "file", "local"]).optional()
26
+ fileLinkMode: z.enum(["commit", "file"]).optional()
27
27
  }).strict(),
28
28
  overrides: z.object({
29
29
  ignored: z.array(z.string()).optional(),
@@ -1 +1 @@
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 (`0.18.1`, with or without `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":[]}
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 (`0.18.1`, with or without `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 */\n fileLinkMode?: 'commit' | 'file';\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: the fork copy wins whole-file.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Upstream changes to a pinned file are dropped, not merged: analyze lists them under\n * \"protected but behind upstream\" to adopt by hand.\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']).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 /** 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 /** 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 /**\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 /**\n * Lift every pinned entry for this run (analyze, sync): managed files stay pinned, and the merge\n * uses the full upstream history, so upstream changes that earlier syncs dropped come back too.\n */\n unpinned?: 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 /** 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 /** 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 /** `<merge-base>..<upstream>` range of short SHAs: what upstream changed since the last sync, for the review page. */\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/** 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,MAAM,CAAC,EAAE,SAAS;AAAA,EACpD,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":[]}