@vibecodr/cli 1.0.0 → 1.0.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +75 -115
  2. package/MIGRATION.md +31 -2
  3. package/dist/auth/credential-broker.d.ts +28 -0
  4. package/dist/auth/credential-broker.d.ts.map +1 -0
  5. package/dist/auth/credential-broker.js +80 -0
  6. package/dist/auth/credential-broker.js.map +1 -0
  7. package/dist/bin/vc-tools.js +4 -0
  8. package/dist/bin/vc-tools.js.map +1 -1
  9. package/dist/bin/vibecodr-mcp.js +4 -0
  10. package/dist/bin/vibecodr-mcp.js.map +1 -1
  11. package/dist/clients/claude-code.d.ts.map +1 -1
  12. package/dist/clients/claude-code.js +5 -1
  13. package/dist/clients/claude-code.js.map +1 -1
  14. package/dist/core/env.d.ts +13 -0
  15. package/dist/core/env.d.ts.map +1 -0
  16. package/dist/core/env.js +62 -0
  17. package/dist/core/env.js.map +1 -0
  18. package/dist/legacy/config/store.d.ts.map +1 -1
  19. package/dist/legacy/config/store.js +19 -5
  20. package/dist/legacy/config/store.js.map +1 -1
  21. package/dist/legacy/core/version.d.ts +2 -2
  22. package/dist/legacy/core/version.js +1 -1
  23. package/dist/platform/paths.d.ts.map +1 -1
  24. package/dist/platform/paths.js +11 -1
  25. package/dist/platform/paths.js.map +1 -1
  26. package/dist/storage/config-store.d.ts.map +1 -1
  27. package/dist/storage/config-store.js +15 -3
  28. package/dist/storage/config-store.js.map +1 -1
  29. package/dist/storage/install-manifest.d.ts.map +1 -1
  30. package/dist/storage/install-manifest.js +14 -3
  31. package/dist/storage/install-manifest.js.map +1 -1
  32. package/dist/storage/migrate.d.ts +15 -0
  33. package/dist/storage/migrate.d.ts.map +1 -0
  34. package/dist/storage/migrate.js +93 -0
  35. package/dist/storage/migrate.js.map +1 -0
  36. package/dist/types/install.d.ts +4 -0
  37. package/dist/types/install.d.ts.map +1 -1
  38. package/docs/commands.md +171 -119
  39. package/docs/legacy/CHANGELOG-mcp-cli.md +85 -0
  40. package/package.json +74 -69
  41. package/preinstall-check.mjs +243 -0
@@ -0,0 +1,243 @@
1
+ // Runs as the npm `preinstall` lifecycle script before file copy. Catches two
2
+ // install-blocking scenarios for `npm install -g @vibecodr/cli` and exits
3
+ // with an actionable message instead of leaving the user staring at npm's
4
+ // bare EEXIST:
5
+ //
6
+ // 1. Legacy install: @vibecodr/vc-tools@0.1.x is globally installed. That
7
+ // package and @vibecodr/cli@1.x both register a `vc-tools` bin under
8
+ // the same path; npm refuses to overwrite a bin owned by a different
9
+ // package.
10
+ // 2. Orphan shims from a prior aborted install: an earlier `npm install
11
+ // -g @vibecodr/cli` (or @vibecodr/vc-tools) wrote some of the
12
+ // `vc-tools` / `vibecodr` / `vibecodr-mcp` shim files at the global
13
+ // bin dir then died on Windows-handle-locked file cleanup. The
14
+ // orphan files block the retry with EEXIST even though no package
15
+ // currently claims them.
16
+ //
17
+ // Bail-outs (the check is a no-op in any of these cases):
18
+ // - VIBECDR_SKIP_PREINSTALL_CHECK=1 (operator opt-out)
19
+ // - not a global install (the conflict is global-bin-only)
20
+ // - running from inside this source repo (npm install / npm ci during
21
+ // local dev shouldn't trip the check)
22
+ // - any inspection failure (`npm ls`, fs access) -- we never block a
23
+ // legitimate install on a transient diagnostic error
24
+
25
+ import { execSync } from "node:child_process";
26
+ import { existsSync, readFileSync } from "node:fs";
27
+ import path from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+
30
+ const SKIP_ENV = "VIBECDR_SKIP_PREINSTALL_CHECK";
31
+ const here = path.dirname(fileURLToPath(import.meta.url));
32
+ const repoRoot = here;
33
+
34
+ function isLocalDevInstall() {
35
+ // The npm lifecycle sets npm_config_local_prefix (or INIT_CWD) to the
36
+ // directory holding the package.json being installed FROM. For
37
+ // npm ci / npm install run inside this repo, that prefix equals our
38
+ // repo root.
39
+ const localPrefix = process.env["npm_config_local_prefix"] ?? process.env["INIT_CWD"];
40
+ if (!localPrefix) return false;
41
+ try {
42
+ return path.resolve(localPrefix) === repoRoot;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ function isGlobalInstall() {
49
+ return process.env["npm_config_global"] === "true";
50
+ }
51
+
52
+ if (process.env[SKIP_ENV] === "1") {
53
+ process.exit(0);
54
+ }
55
+ if (!isGlobalInstall()) {
56
+ process.exit(0);
57
+ }
58
+ if (isLocalDevInstall()) {
59
+ process.exit(0);
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Scenario 1: legacy @vibecodr/vc-tools@0.1.x globally installed
64
+ // ---------------------------------------------------------------------------
65
+
66
+ let collidingVersion;
67
+ try {
68
+ const output = execSync("npm ls -g --depth 0 --json @vibecodr/vc-tools", {
69
+ encoding: "utf8",
70
+ stdio: ["ignore", "pipe", "ignore"]
71
+ });
72
+ const parsed = JSON.parse(output);
73
+ const entry = parsed?.dependencies?.["@vibecodr/vc-tools"];
74
+ if (entry && typeof entry.version === "string") {
75
+ // 0.2.x is the tombstone forwarder package; it depends on @vibecodr/cli
76
+ // and is the intended migration path. Only block on the legacy 0.1.x line
77
+ // (and a defensive guard against any pre-0.2 versions we never published).
78
+ if (entry.version.startsWith("0.1.")) {
79
+ collidingVersion = entry.version;
80
+ }
81
+ }
82
+ } catch {
83
+ // npm ls exits non-zero when the package isn't installed; treat as no
84
+ // conflict.
85
+ }
86
+
87
+ if (collidingVersion) {
88
+ printLegacyConflictMessage(collidingVersion);
89
+ process.exit(1);
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Scenario 2: orphan vc-tools / vibecodr / vibecodr-mcp shims at the global
94
+ // bin dir with no @vibecodr/cli package currently installed to own them
95
+ // ---------------------------------------------------------------------------
96
+
97
+ function npmGlobalBinDir() {
98
+ // Windows: <prefix>/. POSIX: <prefix>/bin.
99
+ const prefix = process.env["npm_config_prefix"];
100
+ if (!prefix) return undefined;
101
+ return process.platform === "win32" ? prefix : path.join(prefix, "bin");
102
+ }
103
+
104
+ function npmGlobalRoot() {
105
+ const prefix = process.env["npm_config_prefix"];
106
+ if (!prefix) return undefined;
107
+ return process.platform === "win32"
108
+ ? path.join(prefix, "node_modules")
109
+ : path.join(prefix, "lib", "node_modules");
110
+ }
111
+
112
+ function vibecodrCliInstalledAndValid() {
113
+ const root = npmGlobalRoot();
114
+ if (!root) return true; // can't determine -> don't block
115
+ const pkgPath = path.join(root, "@vibecodr", "cli", "package.json");
116
+ if (!existsSync(pkgPath)) return false;
117
+ try {
118
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
119
+ return pkg?.name === "@vibecodr/cli";
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ const SHIM_NAMES_WIN = ["vc-tools", "vc-tools.cmd", "vc-tools.ps1", "vibecodr", "vibecodr.cmd", "vibecodr.ps1", "vibecodr-mcp", "vibecodr-mcp.cmd", "vibecodr-mcp.ps1"];
126
+ const SHIM_NAMES_POSIX = ["vc-tools", "vibecodr", "vibecodr-mcp"];
127
+
128
+ function detectOrphanShims() {
129
+ const dir = npmGlobalBinDir();
130
+ if (!dir) return [];
131
+ const candidates = process.platform === "win32" ? SHIM_NAMES_WIN : SHIM_NAMES_POSIX;
132
+ return candidates.filter((name) => existsSync(path.join(dir, name)));
133
+ }
134
+
135
+ const presentShims = detectOrphanShims();
136
+ if (presentShims.length > 0 && !vibecodrCliInstalledAndValid()) {
137
+ // Shim files exist at the global bin dir but no @vibecodr/cli package
138
+ // exists in global node_modules to own them. They're either from an
139
+ // earlier aborted install of @vibecodr/cli or a manually-deleted
140
+ // package that didn't clean its shims. Either way, the retry will
141
+ // EEXIST unless the user removes them first.
142
+ printOrphanShimMessage(npmGlobalBinDir(), npmGlobalRoot(), presentShims);
143
+ process.exit(1);
144
+ }
145
+
146
+ // No conflict detected; let npm proceed.
147
+ process.exit(0);
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Message helpers (separated so the test file can exercise the gate logic
151
+ // without diffing the multi-line console output)
152
+ // ---------------------------------------------------------------------------
153
+
154
+ function printLegacyConflictMessage(version) {
155
+ console.error("");
156
+ console.error("==========================================================================");
157
+ console.error(" @vibecodr/cli install blocked: legacy @vibecodr/vc-tools is in the way");
158
+ console.error("==========================================================================");
159
+ console.error("");
160
+ console.error(` You have @vibecodr/vc-tools@${version} installed globally. That`);
161
+ console.error(" package and @vibecodr/cli@1.x both claim the `vc-tools` bin name on");
162
+ console.error(" disk, and npm refuses to overwrite a bin owned by a different package");
163
+ console.error(" (you would otherwise see a cryptic EEXIST pointing at the global bin");
164
+ console.error(" dir).");
165
+ console.error("");
166
+ console.error(" The merged @vibecodr/cli replaces the legacy @vibecodr/vc-tools. The");
167
+ console.error(" legacy line is preserved on npm under explicit version pins; nothing");
168
+ console.error(" about your stored credentials, OS keychain entries, or config dirs is");
169
+ console.error(" removed by the uninstall below.");
170
+ console.error("");
171
+ console.error(" To unblock, in this order:");
172
+ console.error("");
173
+ console.error(" npm uninstall -g @vibecodr/vc-tools");
174
+ console.error(" npm install -g @vibecodr/cli");
175
+ console.error("");
176
+ console.error(" After install, all three bin names work and resolve to the same");
177
+ console.error(" dispatcher: vibecodr, vibecodr-mcp, vc-tools.");
178
+ console.error("");
179
+ console.error(" Advanced opt-out (not recommended): set VIBECDR_SKIP_PREINSTALL_CHECK=1");
180
+ console.error(" and retry. The collision still trips an EEXIST inside npm; the env var");
181
+ console.error(" only silences this preflight check.");
182
+ console.error("");
183
+ console.error("==========================================================================");
184
+ console.error("");
185
+ }
186
+
187
+ function printOrphanShimMessage(binDir, root, shims) {
188
+ console.error("");
189
+ console.error("==========================================================================");
190
+ console.error(" @vibecodr/cli install blocked: orphan bin shims from a prior install");
191
+ console.error("==========================================================================");
192
+ console.error("");
193
+ console.error(" These bin shim files exist in your global npm bin directory but no");
194
+ console.error(" @vibecodr/cli package is currently installed to own them:");
195
+ console.error("");
196
+ for (const name of shims) {
197
+ console.error(` ${path.join(binDir, name)}`);
198
+ }
199
+ console.error("");
200
+ console.error(" They are typically left behind when an earlier global install of");
201
+ console.error(" @vibecodr/cli or @vibecodr/vc-tools was aborted mid-flight (Windows");
202
+ console.error(" file-handle locks during npm cleanup are the common cause). npm");
203
+ console.error(" refuses to overwrite shim files it didn't create in the current");
204
+ console.error(" install run, so the retry trips the same EEXIST every time.");
205
+ console.error("");
206
+ console.error(" To unblock on Windows (PowerShell):");
207
+ console.error("");
208
+ for (const name of shims) {
209
+ console.error(` Remove-Item "${path.join(binDir, name)}" -Force`);
210
+ }
211
+ if (root) {
212
+ const halfInstall = path.join(root, "@vibecodr", "cli");
213
+ if (existsSync(halfInstall)) {
214
+ console.error(` Remove-Item "${halfInstall}" -Recurse -Force`);
215
+ }
216
+ }
217
+ console.error(" npm install -g @vibecodr/cli");
218
+ console.error("");
219
+ console.error(" On macOS / Linux (bash):");
220
+ console.error("");
221
+ for (const name of shims) {
222
+ console.error(` rm -f "${path.join(binDir, name)}"`);
223
+ }
224
+ if (root) {
225
+ const halfInstall = path.join(root, "@vibecodr", "cli");
226
+ if (existsSync(halfInstall)) {
227
+ console.error(` rm -rf "${halfInstall}"`);
228
+ }
229
+ }
230
+ console.error(" npm install -g @vibecodr/cli");
231
+ console.error("");
232
+ console.error(" If the rm or Remove-Item fails with EPERM/locked-file errors, close");
233
+ console.error(" any IDE / terminal that has run vibecodr, vibecodr-mcp, or vc-tools");
234
+ console.error(" recently (the OS may still hold handles on the shim files), then");
235
+ console.error(" retry.");
236
+ console.error("");
237
+ console.error(" Advanced opt-out (not recommended): set VIBECDR_SKIP_PREINSTALL_CHECK=1");
238
+ console.error(" and retry. The shim collision still trips an EEXIST inside npm; the");
239
+ console.error(" env var only silences this preflight check.");
240
+ console.error("");
241
+ console.error("==========================================================================");
242
+ console.error("");
243
+ }