@cellajs/cli 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -144,13 +144,31 @@ During analysis and sync, files are displayed with status indicators:
144
144
  | ◇ | `managed` | Package/config file changed | Handled separately by cella |
145
145
  | ⨂ | `ignored` | Protected by ignored config | Excluded from sync |
146
146
  | ✓ | `identical` | Fork matches upstream | No action needed |
147
- | ↑ | `ahead` | Fork changed (pinned) | Protected, keeping fork |
147
+ | ↑ | `ahead` | Fork changed (pinned), upstream did not | Protected, keeping fork |
148
148
  | ! | `drifted` | Fork changed, not protected | At risk, consider pinning |
149
149
  | ↓ | `behind` | Upstream has changes | Will sync from upstream |
150
150
  | ⇅ | `diverged` | Both sides changed | Will merge from upstream |
151
- | ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork |
151
+ | ⨀ | `pinned` | Both changed, fork wins | Protected, keeping fork — review, see below |
152
152
  | + | `local` | Only in fork, never in upstream | No action needed |
153
153
 
154
+ ### Protected but behind upstream
155
+
156
+ A pinned or ignored file wins whole-file: when upstream also changed it since the last sync,
157
+ upstream's hunks are dropped, not merged. That is easy to miss (a pinned stylesheet quietly
158
+ missing new upstream utilities that synced components rely on), so both commands call it out:
159
+
160
+ - `analyze` lists them in a `⚠ protected but behind upstream` section (pinned files as ⨀,
161
+ ignored as ⨂) with the number of lines upstream changed; `--list`/`--json` include them in
162
+ `--scope all` and `--scope protected` (`--json` adds `upstreamChanged` and `upstreamChangedLines`).
163
+ - `sync` prints the same list at the end of its summary, right when the drop happens.
164
+
165
+ Diff each against upstream (`cella analyze --open-diff <path>`) and adopt what you need. The
166
+ check is relative to the last sync point: a drop that happened in an earlier sync only shows up
167
+ as plain `ahead` afterwards. For that case the `↑ protected in fork` section annotates pinned
168
+ files with `· N upstream lines absent` (lines upstream has that the fork lacks, compared at the
169
+ tips, `--json`: `upstreamLinesAbsent`). That is upstream content the fork never received,
170
+ deliberately or not: diff and decide. Ignored files are not annotated.
171
+
154
172
  ## Package.json sync
155
173
 
156
174
  `packageJsonSync` controls which package.json sections sync from upstream:
@@ -159,10 +177,20 @@ During analysis and sync, files are displayed with status indicators:
159
177
  packageJsonSync: ['dependencies', 'devDependencies', 'scripts']
160
178
  ```
161
179
 
162
- **Supported keys:** `dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies`, `scripts`, `engines`, `packageManager`, `overrides`
163
-
164
- It **adds** new keys and **updates** existing ones to match upstream, but never **removes** keys
165
- that only exist in your fork — so extra dependencies and scripts survive each sync.
180
+ **Supported keys:** `dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies`, `scripts`, `engines`, `packageManager`, `overrides`, `exports`, `pnpm`
181
+
182
+ Each section merges three-way against the merge-base package.json (upstream as of your last
183
+ sync). An entry your fork never touched follows upstream: it is **added**, **updated** (a rewritten
184
+ script, a changed range) or **removed** when upstream dropped it. An entry your fork added or
185
+ changed stays as it is; only a strictly higher upstream version still bumps it, and never for
186
+ `scripts`. An entry your fork removed is not re-added. A dependency or script upstream dropped
187
+ stays while your own code still uses it: files that differ from upstream and scripts your fork
188
+ added or changed are searched for an import of the package, a run of its CLI, or a package manager
189
+ command that runs the script. The sync lists every change and warns about each entry it kept this
190
+ way, so you can remove it once your code no longer needs it. `exports` are add-only: an entry your fork
191
+ already defines is never rewritten, and `exports` only merges when both sides are subpath maps
192
+ (`{ ".": …, "./config": … }`). A workspace upstream added since your last sync arrives with its
193
+ package.json copied verbatim.
166
194
 
167
195
  ## Contributions (pull from forks)
168
196
 
package/dist/config.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  /** Valid package.json keys that can be synced */
8
- type PackageJsonSyncKey = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'scripts' | 'engines' | 'packageManager' | 'overrides' | 'pnpm';
8
+ type PackageJsonSyncKey = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'scripts' | 'engines' | 'packageManager' | 'overrides' | 'exports' | 'pnpm';
9
9
  /**
10
10
  * Sync settings - all configurable options for the sync CLI.
11
11
  * Hover over each property for documentation.
package/dist/config.js CHANGED
@@ -19,6 +19,7 @@ var cellaConfigSchema = z.object({
19
19
  "engines",
20
20
  "packageManager",
21
21
  "overrides",
22
+ "exports",
22
23
  "pnpm"
23
24
  ])
24
25
  ).optional(),
@@ -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 | 'pnpm';\n\n/**\n * Sync settings - all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable — each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /**\n * Automatically run packages sync after the sync service completes.\n * When true (default), the packages service is hidden from the menu\n * and runs automatically as part of sync.\n * Set to false to keep packages as a separate manual service.\n */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref) instead of the local clone, so the\n * comparison no longer depends on the local checkout being up to date. The local\n * clone is still used to read the fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/**\n * User-configurable sync options for cella/cella.config.ts.\n * Simplified for v2 - no long-lived sync branch or squash options.\n */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns — never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to fork — fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella/cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` instead of `ignored` fails closed\n * instead of silently dropping sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z\n .array(\n z.enum([\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n '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' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for a single contributed file, then exit (contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella/cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n\n /**\n * After opening the sync PR, enable GitHub auto-merge (squash) so it merges itself once\n * required checks pass (sync service). Opt-in per run via `--direct-merge`; needs auto-merge\n * enabled on the repo. Not the advised default — a sync PR is usually worth a human look.\n */\n directMerge?: boolean;\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\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"],"mappings":";AAMA,SAAS,SAAS;AAoIX,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,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 * Sync CLI v2 - Configuration Types\n *\n * Simplified configuration for worktree-based merge approach.\n */\n\nimport { z } from 'zod';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC API - for cella/cella.config.ts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Valid package.json keys that can be synced */\nexport type PackageJsonSyncKey =\n | 'dependencies'\n | 'devDependencies'\n | 'peerDependencies'\n | 'optionalDependencies'\n | 'scripts'\n | 'engines'\n | 'packageManager'\n | 'overrides'\n | 'exports'\n | 'pnpm';\n\n/**\n * Sync settings - all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable — each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /**\n * Automatically run packages sync after the sync service completes.\n * When true (default), the packages service is hidden from the menu\n * and runs automatically as part of sync.\n * Set to false to keep packages as a separate manual service.\n */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref) instead of the local clone, so the\n * comparison no longer depends on the local checkout being up to date. The local\n * clone is still used to read the fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/**\n * User-configurable sync options for cella/cella.config.ts.\n * Simplified for v2 - no long-lived sync branch or squash options.\n */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns — never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to fork — fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella/cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` instead of `ignored` fails closed\n * instead of silently dropping sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z\n .array(\n z.enum([\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n 'exports',\n 'pnpm',\n ]),\n )\n .optional(),\n syncWithPackages: z.boolean().optional(),\n fileLinkMode: z.enum(['commit', 'file', 'local']).optional(),\n })\n .strict(),\n overrides: z\n .object({\n ignored: z.array(z.string()).optional(),\n pinned: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n forks: z\n .array(\n z\n .object({\n name: z.string().min(1),\n localPath: z.string().min(1),\n remoteUrl: z.string().optional(),\n pullBranch: z.string().min(1),\n })\n .strict(),\n )\n .optional(),\n })\n .strict() satisfies z.ZodType<CellaCliConfig>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// INTERNAL TYPES - for sync modules\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Sync services available in the CLI */\nexport type SyncService = 'analyze' | 'sync' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for a single contributed file, then exit (contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella/cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n\n /**\n * After opening the sync PR, enable GitHub auto-merge (squash) so it merges itself once\n * required checks pass (sync service). Opt-in per run via `--direct-merge`; needs auto-merge\n * enabled on the repo. Not the advised default — a sync PR is usually worth a human look.\n */\n directMerge?: boolean;\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 * 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}\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":[]}