claudeup 6.2.0 → 6.2.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/package.json +4 -4
- package/src/__tests__/go-module.test.ts +43 -0
- package/src/__tests__/plugin-requires.test.ts +40 -0
- package/src/__tests__/plugin-setup.test.ts +102 -0
- package/src/services/content-drift.ts +1 -1
- package/src/services/local-marketplace.ts +7 -5
- package/src/services/plugin-requires.ts +7 -2
- package/src/services/plugin-setup.ts +25 -6
- package/src/utils/go-module.ts +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeup",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.1",
|
|
4
4
|
"description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.tsx",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"typescript": "^5.6.3"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"claudeup-darwin-arm64": "6.2.
|
|
68
|
-
"claudeup-darwin-x64": "6.2.
|
|
69
|
-
"claudeup-linux-x64": "6.2.
|
|
67
|
+
"claudeup-darwin-arm64": "6.2.1",
|
|
68
|
+
"claudeup-darwin-x64": "6.2.1",
|
|
69
|
+
"claudeup-linux-x64": "6.2.1"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* goBinaryName: the one rule for turning a Go module path into the binary
|
|
3
|
+
* `go install` produces. Both consumers (plugin-setup's Go path and
|
|
4
|
+
* plugin-requires' `setup.go` branch) are tested through their own surfaces;
|
|
5
|
+
* this file pins the rule itself.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
import { goBinaryName } from "../utils/go-module.js";
|
|
10
|
+
|
|
11
|
+
describe("goBinaryName", () => {
|
|
12
|
+
test("takes the last segment of a v0/v1 module path", () => {
|
|
13
|
+
expect(goBinaryName("github.com/MadAppGang/tmux-mcp")).toBe("tmux-mcp");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("strips a trailing /v2 major-version suffix before taking the name", () => {
|
|
17
|
+
expect(goBinaryName("github.com/MadAppGang/tmux-mcp/v2")).toBe("tmux-mcp");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("strips multi-digit suffixes (/v10, /v42)", () => {
|
|
21
|
+
expect(goBinaryName("github.com/x/tool/v10")).toBe("tool");
|
|
22
|
+
expect(goBinaryName("github.com/x/tool/v42")).toBe("tool");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("a segment that merely starts with v2 is a name, not a suffix", () => {
|
|
26
|
+
expect(goBinaryName("github.com/x/v2tool")).toBe("v2tool");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("v0 and v1 are never path suffixes, so they stay as names", () => {
|
|
30
|
+
// Nobody names a binary this, but the rule must not invent an exception.
|
|
31
|
+
expect(goBinaryName("github.com/x/v1")).toBe("v1");
|
|
32
|
+
expect(goBinaryName("github.com/x/v0")).toBe("v0");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("a suffix in the middle of the path is not trailing and is kept", () => {
|
|
36
|
+
expect(goBinaryName("github.com/x/repo/v2/cmd/cli")).toBe("cli");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("single-segment and empty inputs pass through", () => {
|
|
40
|
+
expect(goBinaryName("mytool")).toBe("mytool");
|
|
41
|
+
expect(goBinaryName("")).toBe("");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -69,6 +69,46 @@ describe("parsePluginRequires — setup block (the established convention)", ()
|
|
|
69
69
|
expect(tmux.formula).toBe("tmux");
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
// Negative control for the /vN fix: basename(base) yields "v2" here, so
|
|
73
|
+
// without the go-module helper this test FAILS on `name`.
|
|
74
|
+
test("terminal's v2 pin: /v2 is the module's major version, not the binary", () => {
|
|
75
|
+
const r = parsePluginRequires({
|
|
76
|
+
name: "terminal",
|
|
77
|
+
setup: { go: ["github.com/MadAppGang/tmux-mcp/v2@v2.0.0"] },
|
|
78
|
+
});
|
|
79
|
+
expect(r.bin).toEqual([
|
|
80
|
+
{
|
|
81
|
+
name: "tmux-mcp",
|
|
82
|
+
via: "go",
|
|
83
|
+
module: "github.com/MadAppGang/tmux-mcp/v2",
|
|
84
|
+
version: "v2.0.0",
|
|
85
|
+
check: "tmux-mcp --version",
|
|
86
|
+
},
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// One manifest per spec: entries are keyed by binary name, so a v1 and a v2
|
|
91
|
+
// pin of the same module in ONE setup would (correctly) collapse to one.
|
|
92
|
+
test.each([
|
|
93
|
+
[
|
|
94
|
+
"github.com/MadAppGang/tmux-mcp@v1.7.1",
|
|
95
|
+
{ name: "tmux-mcp", module: "github.com/MadAppGang/tmux-mcp", version: "v1.7.1" },
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"github.com/MadAppGang/tmux-mcp/v2",
|
|
99
|
+
{ name: "tmux-mcp", module: "github.com/MadAppGang/tmux-mcp/v2", version: undefined },
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"github.com/x/v2tool@v1.0.0",
|
|
103
|
+
{ name: "v2tool", module: "github.com/x/v2tool", version: "v1.0.0" },
|
|
104
|
+
],
|
|
105
|
+
])("go spec %s → name/module/version", (spec, expected) => {
|
|
106
|
+
const r = parsePluginRequires({ setup: { go: [spec] } });
|
|
107
|
+
expect(
|
|
108
|
+
r.bin!.map((b) => ({ name: b.name, module: b.module, version: b.version })),
|
|
109
|
+
).toEqual([expected]);
|
|
110
|
+
});
|
|
111
|
+
|
|
72
112
|
test("browser-use's setup: pip packages", () => {
|
|
73
113
|
const r = parsePluginRequires({
|
|
74
114
|
name: "browser-use",
|
|
@@ -86,6 +86,7 @@ const {
|
|
|
86
86
|
extractGoVersion,
|
|
87
87
|
extractBinaryName,
|
|
88
88
|
parseAtSpec,
|
|
89
|
+
parseGoSpec,
|
|
89
90
|
parsePipSpec,
|
|
90
91
|
goDepNeedsInstall,
|
|
91
92
|
goDepDecision,
|
|
@@ -127,6 +128,89 @@ describe("extractGoBinaryName", () => {
|
|
|
127
128
|
it("handles empty string gracefully", () => {
|
|
128
129
|
expect(extractGoBinaryName("")).toBe("");
|
|
129
130
|
});
|
|
131
|
+
|
|
132
|
+
// Negative control for the /vN fix: a plain basename returns "v2" here.
|
|
133
|
+
// Without the go-module helper wired in, this test FAILS.
|
|
134
|
+
it("names the binary after the module, not its /v2 major-version suffix", () => {
|
|
135
|
+
expect(
|
|
136
|
+
extractGoBinaryName("github.com/MadAppGang/tmux-mcp/v2@v2.0.0"),
|
|
137
|
+
).toBe("tmux-mcp");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("strips the /v2 suffix from an unpinned module path too", () => {
|
|
141
|
+
expect(extractGoBinaryName("github.com/MadAppGang/tmux-mcp/v2")).toBe(
|
|
142
|
+
"tmux-mcp",
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("keeps a name that merely starts with v2", () => {
|
|
147
|
+
expect(extractGoBinaryName("github.com/x/v2tool@v1.0.0")).toBe("v2tool");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// 1b. parseGoSpec — the Go-specific view over parseAtSpec
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
describe("parseGoSpec", () => {
|
|
156
|
+
it("v1 module: name and pinned version", () => {
|
|
157
|
+
expect(parseGoSpec("github.com/MadAppGang/tmux-mcp@v1.7.1")).toEqual({
|
|
158
|
+
spec: "github.com/MadAppGang/tmux-mcp@v1.7.1",
|
|
159
|
+
name: "tmux-mcp",
|
|
160
|
+
version: "v1.7.1",
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("v2 module: the /v2 suffix is not the name, the version survives", () => {
|
|
165
|
+
expect(parseGoSpec("github.com/MadAppGang/tmux-mcp/v2@v2.0.0")).toEqual({
|
|
166
|
+
spec: "github.com/MadAppGang/tmux-mcp/v2@v2.0.0",
|
|
167
|
+
name: "tmux-mcp",
|
|
168
|
+
version: "v2.0.0",
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("v2 module without a pin: name resolved, version null", () => {
|
|
173
|
+
expect(parseGoSpec("github.com/MadAppGang/tmux-mcp/v2")).toEqual({
|
|
174
|
+
spec: "github.com/MadAppGang/tmux-mcp/v2",
|
|
175
|
+
name: "tmux-mcp",
|
|
176
|
+
version: null,
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("a segment that starts with v2 is a real name", () => {
|
|
181
|
+
expect(parseGoSpec("github.com/x/v2tool@v1.0.0")).toEqual({
|
|
182
|
+
spec: "github.com/x/v2tool@v1.0.0",
|
|
183
|
+
name: "v2tool",
|
|
184
|
+
version: "v1.0.0",
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("@latest is unpinned, same as parseAtSpec", () => {
|
|
189
|
+
expect(parseGoSpec("github.com/MadAppGang/tmux-mcp/v2@latest")).toEqual({
|
|
190
|
+
spec: "github.com/MadAppGang/tmux-mcp/v2@latest",
|
|
191
|
+
name: "tmux-mcp",
|
|
192
|
+
version: null,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Terminal 5.0.0 contract cases (test plan Area 3, C5 and C6). They live
|
|
197
|
+
// here rather than in their own file because any test file that imports
|
|
198
|
+
// plugin-setup.js ahead of the mock.module seam above bakes the real
|
|
199
|
+
// execFile into promisify() and fails the installPluginDeps suites.
|
|
200
|
+
it("C5: a /v10 suffix is stripped in full, not only its first digit", () => {
|
|
201
|
+
const { name, version } = parseGoSpec("github.com/x/tool/v10@v10.1.0");
|
|
202
|
+
expect({ name, version }).toEqual({ name: "tool", version: "v10.1.0" });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("C6: /v1 is never a Go major-version suffix, so it stays the name", () => {
|
|
206
|
+
const { name, version } = parseGoSpec("github.com/x/tool/v1@v1.0.0");
|
|
207
|
+
expect({ name, version }).toEqual({ name: "v1", version: "v1.0.0" });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("C6 control: the same shape at /v2 is a suffix and is stripped", () => {
|
|
211
|
+
const { name, version } = parseGoSpec("github.com/x/tool/v2@v2.0.0");
|
|
212
|
+
expect({ name, version }).toEqual({ name: "tool", version: "v2.0.0" });
|
|
213
|
+
});
|
|
130
214
|
});
|
|
131
215
|
|
|
132
216
|
// ---------------------------------------------------------------------------
|
|
@@ -295,6 +379,14 @@ describe("extractGoVersion", () => {
|
|
|
295
379
|
it("returns null when unpinned", () => {
|
|
296
380
|
expect(extractGoVersion("github.com/user/tool")).toBeNull();
|
|
297
381
|
});
|
|
382
|
+
it("reads the pin behind a /v2 module path", () => {
|
|
383
|
+
expect(
|
|
384
|
+
extractGoVersion("github.com/MadAppGang/tmux-mcp/v2@v2.0.0"),
|
|
385
|
+
).toBe("v2.0.0");
|
|
386
|
+
});
|
|
387
|
+
it("a /v2 module path with no pin is unpinned, not pinned to v2", () => {
|
|
388
|
+
expect(extractGoVersion("github.com/MadAppGang/tmux-mcp/v2")).toBeNull();
|
|
389
|
+
});
|
|
298
390
|
});
|
|
299
391
|
|
|
300
392
|
describe("goDepNeedsInstall — version awareness", () => {
|
|
@@ -666,6 +758,16 @@ describe("parseAtSpec", () => {
|
|
|
666
758
|
expect(parseAtSpec("tool@latest").version).toBeNull();
|
|
667
759
|
});
|
|
668
760
|
|
|
761
|
+
it("does NOT apply the Go /vN rule — an npm scope may be named v2", () => {
|
|
762
|
+
// The Go major-version strip lives in parseGoSpec only; folding it in
|
|
763
|
+
// here would turn this scoped npm package into "@scope".
|
|
764
|
+
expect(parseAtSpec("@scope/v2@1.0.0")).toEqual({
|
|
765
|
+
spec: "@scope/v2@1.0.0",
|
|
766
|
+
name: "v2",
|
|
767
|
+
version: "1.0.0",
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
|
|
669
771
|
it("treats a bare name as unpinned", () => {
|
|
670
772
|
expect(parseAtSpec("tool")).toEqual({
|
|
671
773
|
spec: "tool",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* invisible. No update is offered, and the only cure is a reinstall — which is
|
|
8
8
|
* exactly the "plugin says installed but its skills are missing" report.
|
|
9
9
|
*
|
|
10
|
-
* This is not a hypothetical. `publish-dist.sh`
|
|
10
|
+
* This is not a hypothetical. `publish-dist.sh` pushes a rebuilt tree, so
|
|
11
11
|
* same-version-different-content is a normal outcome of the release process.
|
|
12
12
|
* Measured on 2026-08-06: `cache/magus/dev/3.0.1/skills` held
|
|
13
13
|
* {audit, tui-progress} while `marketplaces/magus/plugins/dev/skills` held
|
|
@@ -113,11 +113,13 @@ function getGitRemote(marketplacePath: string): string | undefined {
|
|
|
113
113
|
* Names of the skills under `skillsDir`, recursing through grouping directories.
|
|
114
114
|
*
|
|
115
115
|
* A skill is a directory holding a SKILL.md, and it may sit one or more levels
|
|
116
|
-
* down: dev
|
|
117
|
-
* and so on, and mattpocock groups its 37 under
|
|
118
|
-
* only the top level counted the *groups* — dev
|
|
119
|
-
* disk, mattpocock 5 against 37 — and that
|
|
120
|
-
* panel prints.
|
|
116
|
+
* down: dev nests its skills by category under `skills/frontend/`,
|
|
117
|
+
* `skills/backend/` and so on, and mattpocock groups its 37 under
|
|
118
|
+
* `skills/engineering/`. Reading only the top level counted the *groups* — dev
|
|
119
|
+
* once reported 9 skills against 43 on disk, mattpocock 5 against 37 — and that
|
|
120
|
+
* count is what the plugin detail panel prints. dev also ships a `knowledge/`
|
|
121
|
+
* tree beside `skills/`: reference manuals reached by path, holding no SKILL.md
|
|
122
|
+
* and registering nothing, so this scan correctly never sees them.
|
|
121
123
|
*
|
|
122
124
|
* Depth is capped because this runs over every plugin of every cloned
|
|
123
125
|
* marketplace on each list load, and a skill nested four levels below `skills/`
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
BinRequirement,
|
|
16
16
|
PluginRequires,
|
|
17
17
|
} from "../types/index.js";
|
|
18
|
+
import { goBinaryName } from "../utils/go-module.js";
|
|
18
19
|
import { parsePluginId } from "../utils/string-utils.js";
|
|
19
20
|
import { getLocalMarketplacesInfo } from "./plugin-manager.js";
|
|
20
21
|
|
|
@@ -98,15 +99,19 @@ function parseSetup(setup: unknown): BinRequirement[] {
|
|
|
98
99
|
pkgManager("npm");
|
|
99
100
|
pkgManager("pip");
|
|
100
101
|
|
|
102
|
+
// Not basename(): a Go module's trailing /vN is its major version, not the
|
|
103
|
+
// binary. `tmux-mcp/v2` installs `tmux-mcp`; naming it `v2` here made every
|
|
104
|
+
// downstream presence check look for a binary that never exists.
|
|
101
105
|
for (const spec of (s.go as string[] | undefined) ?? []) {
|
|
102
106
|
if (typeof spec !== "string") continue;
|
|
103
107
|
const { base, version } = splitVersion(spec);
|
|
108
|
+
const name = goBinaryName(base);
|
|
104
109
|
bins.push({
|
|
105
|
-
name
|
|
110
|
+
name,
|
|
106
111
|
via: "go",
|
|
107
112
|
module: base,
|
|
108
113
|
version,
|
|
109
|
-
check: `${
|
|
114
|
+
check: `${name} --version`,
|
|
110
115
|
});
|
|
111
116
|
}
|
|
112
117
|
for (const spec of (s.brew as string[] | undefined) ?? []) {
|
|
@@ -33,6 +33,7 @@ import { constants as fsConstants } from "node:fs";
|
|
|
33
33
|
import path from "node:path";
|
|
34
34
|
import os from "node:os";
|
|
35
35
|
import { which } from "../utils/command-utils.js";
|
|
36
|
+
import { goBinaryName } from "../utils/go-module.js";
|
|
36
37
|
|
|
37
38
|
const execFileAsync = promisify(execFile);
|
|
38
39
|
|
|
@@ -392,14 +393,32 @@ async function installCargoPackages(
|
|
|
392
393
|
}
|
|
393
394
|
}
|
|
394
395
|
|
|
396
|
+
/**
|
|
397
|
+
* {@link parseAtSpec} for a Go module spec.
|
|
398
|
+
*
|
|
399
|
+
* Same split, but the name comes from {@link goBinaryName}: Go's `/vN`
|
|
400
|
+
* major-version suffix is part of the module path, not the binary, so
|
|
401
|
+
* "github.com/MadAppGang/tmux-mcp/v2@v2.0.0" installs `tmux-mcp`, not `v2`.
|
|
402
|
+
* The generic parser cannot do this — an npm scope may legitimately be `v2`.
|
|
403
|
+
*/
|
|
404
|
+
export function parseGoSpec(spec: string): ParsedSpec {
|
|
405
|
+
const parsed = parseAtSpec(spec);
|
|
406
|
+
// Same separator rule as parseAtSpec: a leading `@` is never a version.
|
|
407
|
+
const at = spec.lastIndexOf("@");
|
|
408
|
+
const modulePath = at > 0 ? spec.slice(0, at) : spec;
|
|
409
|
+
return { ...parsed, name: goBinaryName(modulePath) };
|
|
410
|
+
}
|
|
411
|
+
|
|
395
412
|
/**
|
|
396
413
|
* Extract binary name from a Go module path.
|
|
397
|
-
* The binary name is the last path segment before @version
|
|
398
|
-
*
|
|
399
|
-
*
|
|
414
|
+
* The binary name is the last path segment before @version, minus any /vN
|
|
415
|
+
* major-version suffix.
|
|
416
|
+
* e.g., "github.com/MadAppGang/tmux-mcp@latest" → "tmux-mcp"
|
|
417
|
+
* "github.com/MadAppGang/tmux-mcp/v2@v2.0.0" → "tmux-mcp"
|
|
418
|
+
* "github.com/user/tool" → "tool"
|
|
400
419
|
*/
|
|
401
420
|
export function extractGoBinaryName(pkg: string): string {
|
|
402
|
-
return
|
|
421
|
+
return parseGoSpec(pkg).name;
|
|
403
422
|
}
|
|
404
423
|
|
|
405
424
|
/**
|
|
@@ -409,7 +428,7 @@ export function extractGoBinaryName(pkg: string): string {
|
|
|
409
428
|
* "github.com/user/tool" → null
|
|
410
429
|
*/
|
|
411
430
|
export function extractGoVersion(pkg: string): string | null {
|
|
412
|
-
return
|
|
431
|
+
return parseGoSpec(pkg).version;
|
|
413
432
|
}
|
|
414
433
|
|
|
415
434
|
/** Normalize a semver-ish string for comparison: trim and drop a leading "v". */
|
|
@@ -641,7 +660,7 @@ export async function binaryDepDecision(
|
|
|
641
660
|
|
|
642
661
|
/** {@link binaryDepDecision} for a Go module path. */
|
|
643
662
|
export async function goDepDecision(pkg: string): Promise<GoDepDecision> {
|
|
644
|
-
return binaryDepDecision(
|
|
663
|
+
return binaryDepDecision(parseGoSpec(pkg), getGoInstallDir);
|
|
645
664
|
}
|
|
646
665
|
|
|
647
666
|
/**
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go module path → installed binary name.
|
|
3
|
+
*
|
|
4
|
+
* `go install` names the binary after the last path element of the module
|
|
5
|
+
* path, EXCEPT when that element is a major-version suffix: the module
|
|
6
|
+
* `github.com/MadAppGang/tmux-mcp/v2` installs `tmux-mcp`, not `v2`. Taking a
|
|
7
|
+
* plain basename therefore points every later check at a binary that never
|
|
8
|
+
* exists — presence probes fail, so the module is reinstalled on every run,
|
|
9
|
+
* doctor reports it missing, and the pin comparison never runs at all.
|
|
10
|
+
*
|
|
11
|
+
* Only the Go rule lives here. npm and cargo share the `name@version` syntax
|
|
12
|
+
* but not the suffix convention — an npm scoped package can legitimately be
|
|
13
|
+
* called `@scope/v2` — so this must not be folded into the generic spec parser.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Trailing major-version suffix per the Go modules spec: `/vN` with N >= 2.
|
|
18
|
+
* Anchored to the END of the path with nothing after `vN`, so a segment that
|
|
19
|
+
* merely starts with "v2" (`github.com/x/v2tool`) is a real name, not a suffix.
|
|
20
|
+
* v0 and v1 are never written as a path suffix. The alternation, not a plain
|
|
21
|
+
* `[2-9][0-9]*`, because that shorthand rejects v10–v19 — a first digit of 1
|
|
22
|
+
* is only excluded when it is the ONLY digit.
|
|
23
|
+
*/
|
|
24
|
+
const MAJOR_VERSION_SUFFIX = /\/v(?:[2-9]|[1-9][0-9]+)$/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The binary `go install <modulePath>` produces.
|
|
28
|
+
*
|
|
29
|
+
* Takes the MODULE PATH — the part before any `@version` — so callers that
|
|
30
|
+
* hold a full `path@version` spec split it first. An empty or single-segment
|
|
31
|
+
* input is returned unchanged.
|
|
32
|
+
*/
|
|
33
|
+
export function goBinaryName(modulePath: string): string {
|
|
34
|
+
const withoutSuffix = modulePath.replace(MAJOR_VERSION_SUFFIX, "");
|
|
35
|
+
return withoutSuffix.split("/").pop() || withoutSuffix;
|
|
36
|
+
}
|