sidecarsync 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,7 +86,7 @@ Sidecar takes over branch management there —
86
86
  - [Standalone repos](docs/standalone.md) — a repo that is its own sidecar
87
87
  - [Redaction](docs/redaction.md) — what's stripped from pushes, reviewing it, opting out
88
88
  - [Editor search visibility](docs/editors.md) — making gitignored files searchable
89
- - [Global vs local installs](docs/install.md) — pinning sidecar per-repo
89
+ - [Global vs local installs](docs/install.md) — the newest install wins
90
90
  - [All commands](docs/commands.md)
91
91
  - [Uninstall & troubleshooting](docs/uninstall.md) — removing the daemon, service, and state
92
92
 
package/dist/cli.js CHANGED
@@ -238,6 +238,15 @@ function projectDependsOnSidecar(projectRoot) {
238
238
  return false;
239
239
  }
240
240
  }
241
+ function installedPackageVersion(projectRoot) {
242
+ const manifestPath = path2.join(projectRoot, "node_modules", PACKAGE_NAME, "package.json");
243
+ try {
244
+ const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
245
+ return manifest.name === PACKAGE_NAME ? manifest.version : undefined;
246
+ } catch {
247
+ return;
248
+ }
249
+ }
241
250
  function shouldUseGlobalRegistry() {
242
251
  return process.env[GLOBAL_EXEC_ENV] === "1" || !findDependencyRoot(process.cwd());
243
252
  }
@@ -3696,7 +3705,7 @@ function cmdStatus(args) {
3696
3705
  statusLine("main branch", config.branch);
3697
3706
  statusLine("inbox branch", inbox);
3698
3707
  if (!checkoutPresent) {
3699
- statusLine("checkout", "missing", "bad");
3708
+ statusLine("checkout", "missing — run `sidecar init`", "bad");
3700
3709
  printDaemonLine();
3701
3710
  printLastSyncLine(root);
3702
3711
  return 0;
@@ -3737,6 +3746,7 @@ function cmdStatusJson() {
3737
3746
  branch: config.branch,
3738
3747
  inbox,
3739
3748
  checkout: checkoutPresent ? "present" : "missing",
3749
+ globalInstall: shouldUseGlobalRegistry() || Boolean(findGlobalSidecarExecutable()),
3740
3750
  currentBranch: branch || undefined,
3741
3751
  dirty: checkoutPresent ? Boolean(git(sidecarPath, ["status", "--porcelain"]).stdout.trim()) : undefined,
3742
3752
  daemon: daemonHealth().text,
@@ -3752,8 +3762,12 @@ function pendingStatusInboxBranches(sidecarPath, config) {
3752
3762
  return pendingInboxBranches(sidecarPath, config).filter((remoteBranch) => !isAncestor(sidecarPath, remoteBranch, base));
3753
3763
  }
3754
3764
  function daemonHealth() {
3755
- if (!shouldUseGlobalRegistry())
3756
- return { text: "no global install", role: "quiet" };
3765
+ if (!shouldUseGlobalRegistry()) {
3766
+ if (!findGlobalSidecarExecutable()) {
3767
+ return { text: `no global install — nothing syncs; \`npm install -g ${PACKAGE_SPEC}\``, role: "bad" };
3768
+ }
3769
+ return { text: "owned by the global install", role: "quiet" };
3770
+ }
3757
3771
  const service = daemonServiceStatus();
3758
3772
  if (!service.available)
3759
3773
  return { text: service.message ?? "unavailable", role: "quiet" };
@@ -4175,10 +4189,9 @@ function localSidecarCliPath(root) {
4175
4189
  const candidate = path9.join(root, "node_modules", PACKAGE_NAME, "dist", "cli.js");
4176
4190
  if (!isFile(candidate))
4177
4191
  return;
4178
- try {
4179
- if (fs11.realpathSync(candidate) === fs11.realpathSync(currentCliPath()))
4180
- return;
4181
- } catch {}
4192
+ const localVersion = installedPackageVersion(root);
4193
+ if (localVersion === undefined || compareVersions(localVersion, packageVersion()) <= 0)
4194
+ return;
4182
4195
  return candidate;
4183
4196
  }
4184
4197
  function currentCliPath() {
@@ -5098,12 +5111,11 @@ var GLOBAL_EXEC_ENV3 = "SIDECAR_GLOBAL_EXEC";
5098
5111
  var PACKAGE_NAME2 = "sidecarsync";
5099
5112
  var GLOBAL_ONLY_COMMANDS = new Set(["daemon", "deinit", "register-install", "set-install-source", "update"]);
5100
5113
  if (!process.env[SKIP_LOCAL_EXEC_ENV3]) {
5101
- const localExecutable = findLocalExecutable(process.cwd(), fileURLToPath4(import.meta.url));
5102
- if (localExecutable) {
5103
- if (GLOBAL_ONLY_COMMANDS.has(process.argv[2])) {
5104
- process.env[GLOBAL_EXEC_ENV3] = "1";
5105
- } else {
5106
- const result = spawnSync5(process.execPath, [localExecutable, ...process.argv.slice(2)], {
5114
+ const local = findLocalInstall(process.cwd(), fileURLToPath4(import.meta.url));
5115
+ if (local) {
5116
+ process.env[GLOBAL_EXEC_ENV3] = "1";
5117
+ if (local.newer && !GLOBAL_ONLY_COMMANDS.has(process.argv[2])) {
5118
+ const result = spawnSync5(process.execPath, [local.executable, ...process.argv.slice(2)], {
5107
5119
  stdio: "inherit",
5108
5120
  env: {
5109
5121
  ...process.env,
@@ -5119,13 +5131,13 @@ if (!process.env[SKIP_LOCAL_EXEC_ENV3]) {
5119
5131
  }
5120
5132
  }
5121
5133
  process.exit(await main());
5122
- function findLocalExecutable(start, self) {
5134
+ function findLocalInstall(start, self) {
5123
5135
  let current = path11.resolve(start);
5124
5136
  while (true) {
5125
5137
  if (projectDependsOnSidecar2(current)) {
5126
5138
  const candidate = path11.join(current, "node_modules", PACKAGE_NAME2, "dist", "cli.js");
5127
5139
  if (isFile2(candidate) && !sameFile(candidate, self)) {
5128
- return candidate;
5140
+ return { executable: candidate, newer: localIsNewer(current) };
5129
5141
  }
5130
5142
  }
5131
5143
  const parent = path11.dirname(current);
@@ -5134,6 +5146,10 @@ function findLocalExecutable(start, self) {
5134
5146
  current = parent;
5135
5147
  }
5136
5148
  }
5149
+ function localIsNewer(projectRoot) {
5150
+ const localVersion = installedPackageVersion(projectRoot);
5151
+ return localVersion !== undefined && compareVersions(localVersion, packageVersion()) > 0;
5152
+ }
5137
5153
  function projectDependsOnSidecar2(projectRoot) {
5138
5154
  const manifestPath = path11.join(projectRoot, "package.json");
5139
5155
  if (!isFile2(manifestPath))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidecarsync",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A Git-backed sidecar inbox for repo-local development metadata.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,8 +14,7 @@ try {
14
14
  enableDaemon(packageRoot);
15
15
  recordInstallSource(packageRoot);
16
16
  } else {
17
- cloneIfMissing(packageRoot);
18
- registerWithGlobalSidecar(packageRoot);
17
+ setUpLocalInstall(packageRoot);
19
18
  }
20
19
  } catch (error) {
21
20
  console.warn(`sidecar: postinstall failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -61,10 +60,36 @@ function recordInstallSource(packageRoot) {
61
60
  });
62
61
  }
63
62
 
64
- function cloneIfMissing(packageRoot) {
63
+ // The global install owns the daemon, so it is what makes a cloned checkout
64
+ // actually sync. Without it a clone here would leave the user with a sidecar
65
+ // directory that silently never updates — worse than no checkout at all.
66
+ function setUpLocalInstall(packageRoot) {
65
67
  const projectRoot = findInstallProjectRoot();
66
68
  if (!projectRoot || !fs.existsSync(path.join(projectRoot, ".sidecar"))) return;
67
69
 
70
+ const globalSidecar = findGlobalSidecar(packageRoot);
71
+ if (!globalSidecar) {
72
+ warnMissingGlobalSidecar();
73
+ return;
74
+ }
75
+
76
+ cloneIfMissing(packageRoot, projectRoot);
77
+ registerWithGlobalSidecar(globalSidecar, projectRoot);
78
+ }
79
+
80
+ function warnMissingGlobalSidecar() {
81
+ console.warn(
82
+ [
83
+ "sidecar: no global install found, so this repo's sidecar checkout was not cloned.",
84
+ "sidecar: without the global install there is no daemon, and nothing would sync.",
85
+ "sidecar: install it, then set this repo up:",
86
+ "sidecar: npm install -g sidecarsync",
87
+ "sidecar: sidecar init",
88
+ ].join("\n"),
89
+ );
90
+ }
91
+
92
+ function cloneIfMissing(packageRoot, projectRoot) {
68
93
  const cliPath = path.join(packageRoot, "dist", "cli.js");
69
94
  if (!fs.existsSync(cliPath)) return;
70
95
 
@@ -80,13 +105,7 @@ function cloneIfMissing(packageRoot) {
80
105
  if (output) console.warn(output);
81
106
  }
82
107
 
83
- function registerWithGlobalSidecar(packageRoot) {
84
- const projectRoot = findInstallProjectRoot();
85
- if (!projectRoot || !fs.existsSync(path.join(projectRoot, ".sidecar"))) return;
86
-
87
- const globalSidecar = findGlobalSidecar(packageRoot);
88
- if (!globalSidecar) return;
89
-
108
+ function registerWithGlobalSidecar(globalSidecar, projectRoot) {
90
109
  const result = spawnSync(globalSidecar, ["register-install"], {
91
110
  cwd: projectRoot,
92
111
  encoding: "utf8",