vskill 0.2.26 → 0.2.28
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/dist/commands/__tests__/eval-router.test.d.ts +1 -0
- package/dist/commands/__tests__/eval-router.test.js +60 -0
- package/dist/commands/__tests__/eval-router.test.js.map +1 -0
- package/dist/commands/add.js +113 -19
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/eval/__tests__/coverage.test.d.ts +1 -0
- package/dist/commands/eval/__tests__/coverage.test.js +122 -0
- package/dist/commands/eval/__tests__/coverage.test.js.map +1 -0
- package/dist/commands/eval/__tests__/generate-all.test.d.ts +1 -0
- package/dist/commands/eval/__tests__/generate-all.test.js +133 -0
- package/dist/commands/eval/__tests__/generate-all.test.js.map +1 -0
- package/dist/commands/eval/__tests__/init.test.d.ts +1 -0
- package/dist/commands/eval/__tests__/init.test.js +116 -0
- package/dist/commands/eval/__tests__/init.test.js.map +1 -0
- package/dist/commands/eval/__tests__/run.test.d.ts +1 -0
- package/dist/commands/eval/__tests__/run.test.js +149 -0
- package/dist/commands/eval/__tests__/run.test.js.map +1 -0
- package/dist/commands/eval/coverage.d.ts +1 -0
- package/dist/commands/eval/coverage.js +79 -0
- package/dist/commands/eval/coverage.js.map +1 -0
- package/dist/commands/eval/generate-all.d.ts +1 -0
- package/dist/commands/eval/generate-all.js +64 -0
- package/dist/commands/eval/generate-all.js.map +1 -0
- package/dist/commands/eval/init.d.ts +1 -0
- package/dist/commands/eval/init.js +38 -0
- package/dist/commands/eval/init.js.map +1 -0
- package/dist/commands/eval/run.d.ts +1 -0
- package/dist/commands/eval/run.js +107 -0
- package/dist/commands/eval/run.js.map +1 -0
- package/dist/commands/eval.d.ts +4 -0
- package/dist/commands/eval.js +48 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/eval/__tests__/benchmark.test.d.ts +1 -0
- package/dist/eval/__tests__/benchmark.test.js +65 -0
- package/dist/eval/__tests__/benchmark.test.js.map +1 -0
- package/dist/eval/__tests__/judge.test.d.ts +1 -0
- package/dist/eval/__tests__/judge.test.js +45 -0
- package/dist/eval/__tests__/judge.test.js.map +1 -0
- package/dist/eval/__tests__/llm.test.d.ts +1 -0
- package/dist/eval/__tests__/llm.test.js +85 -0
- package/dist/eval/__tests__/llm.test.js.map +1 -0
- package/dist/eval/__tests__/prompt-builder.test.d.ts +1 -0
- package/dist/eval/__tests__/prompt-builder.test.js +72 -0
- package/dist/eval/__tests__/prompt-builder.test.js.map +1 -0
- package/dist/eval/__tests__/schema.test.d.ts +1 -0
- package/dist/eval/__tests__/schema.test.js +209 -0
- package/dist/eval/__tests__/schema.test.js.map +1 -0
- package/dist/eval/__tests__/skill-scanner.test.d.ts +1 -0
- package/dist/eval/__tests__/skill-scanner.test.js +78 -0
- package/dist/eval/__tests__/skill-scanner.test.js.map +1 -0
- package/dist/eval/benchmark.d.ts +22 -0
- package/dist/eval/benchmark.js +24 -0
- package/dist/eval/benchmark.js.map +1 -0
- package/dist/eval/judge.d.ts +9 -0
- package/dist/eval/judge.js +40 -0
- package/dist/eval/judge.js.map +1 -0
- package/dist/eval/llm.d.ts +5 -0
- package/dist/eval/llm.js +34 -0
- package/dist/eval/llm.js.map +1 -0
- package/dist/eval/prompt-builder.d.ts +3 -0
- package/dist/eval/prompt-builder.js +155 -0
- package/dist/eval/prompt-builder.js.map +1 -0
- package/dist/eval/schema.d.ts +26 -0
- package/dist/eval/schema.js +128 -0
- package/dist/eval/schema.js.map +1 -0
- package/dist/eval/skill-scanner.d.ts +8 -0
- package/dist/eval/skill-scanner.js +44 -0
- package/dist/eval/skill-scanner.js.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/marketplace/index.d.ts +2 -2
- package/dist/marketplace/index.js +1 -1
- package/dist/marketplace/index.js.map +1 -1
- package/dist/marketplace/marketplace.d.ts +13 -0
- package/dist/marketplace/marketplace.js +35 -0
- package/dist/marketplace/marketplace.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Mocks
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
const mocks = vi.hoisted(() => ({
|
|
6
|
+
runEvalInit: vi.fn(),
|
|
7
|
+
runEvalRun: vi.fn(),
|
|
8
|
+
runEvalCoverage: vi.fn(),
|
|
9
|
+
runEvalGenerateAll: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("../eval/init.js", () => ({ runEvalInit: mocks.runEvalInit }));
|
|
12
|
+
vi.mock("../eval/run.js", () => ({ runEvalRun: mocks.runEvalRun }));
|
|
13
|
+
vi.mock("../eval/coverage.js", () => ({
|
|
14
|
+
runEvalCoverage: mocks.runEvalCoverage,
|
|
15
|
+
}));
|
|
16
|
+
vi.mock("../eval/generate-all.js", () => ({
|
|
17
|
+
runEvalGenerateAll: mocks.runEvalGenerateAll,
|
|
18
|
+
}));
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Import module under test AFTER mocks
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const { evalCommand } = await import("../eval.js");
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Tests
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
describe("evalCommand router", () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.resetAllMocks();
|
|
29
|
+
});
|
|
30
|
+
it("routes init subcommand to runEvalInit", async () => {
|
|
31
|
+
await evalCommand("init", "marketing/social-media-posting", {
|
|
32
|
+
root: "/tmp/test",
|
|
33
|
+
});
|
|
34
|
+
expect(mocks.runEvalInit).toHaveBeenCalledWith(expect.stringContaining("marketing/skills/social-media-posting"), false);
|
|
35
|
+
});
|
|
36
|
+
it("routes run subcommand to runEvalRun", async () => {
|
|
37
|
+
await evalCommand("run", "marketing/social-media-posting", {
|
|
38
|
+
root: "/tmp/test",
|
|
39
|
+
});
|
|
40
|
+
expect(mocks.runEvalRun).toHaveBeenCalledWith(expect.stringContaining("marketing/skills/social-media-posting"));
|
|
41
|
+
});
|
|
42
|
+
it("routes coverage subcommand to runEvalCoverage", async () => {
|
|
43
|
+
await evalCommand("coverage", undefined, { root: "/tmp/test" });
|
|
44
|
+
expect(mocks.runEvalCoverage).toHaveBeenCalledWith("/tmp/test");
|
|
45
|
+
});
|
|
46
|
+
it("routes generate-all subcommand to runEvalGenerateAll", async () => {
|
|
47
|
+
await evalCommand("generate-all", undefined, {
|
|
48
|
+
root: "/tmp/test",
|
|
49
|
+
force: true,
|
|
50
|
+
});
|
|
51
|
+
expect(mocks.runEvalGenerateAll).toHaveBeenCalledWith("/tmp/test", true);
|
|
52
|
+
});
|
|
53
|
+
it("prints error for unknown subcommand", async () => {
|
|
54
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
55
|
+
await evalCommand("unknown", undefined, { root: "/tmp/test" });
|
|
56
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Unknown subcommand"));
|
|
57
|
+
consoleSpy.mockRestore();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=eval-router.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval-router.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/eval-router.test.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/add.js
CHANGED
|
@@ -7,12 +7,12 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { execSync } from "node:child_process";
|
|
8
8
|
import os from "node:os";
|
|
9
9
|
import { resolveTilde } from "../utils/paths.js";
|
|
10
|
-
import { reportInstall, reportInstallBatch } from "../api/client.js";
|
|
10
|
+
import { reportInstall, reportInstallBatch, submitSkill } from "../api/client.js";
|
|
11
11
|
import { filterAgents } from "../utils/agent-filter.js";
|
|
12
12
|
import { detectInstalledAgents, AGENTS_REGISTRY } from "../agents/agents-registry.js";
|
|
13
13
|
import { ensureLockfile, writeLockfile, readLockfile } from "../lockfile/index.js";
|
|
14
14
|
import { runTier1Scan } from "../scanner/index.js";
|
|
15
|
-
import { getAvailablePlugins, getPluginSource, getPluginVersion, hasPlugin } from "../marketplace/index.js";
|
|
15
|
+
import { getAvailablePlugins, getPluginSource, getPluginVersion, hasPlugin, discoverUnregisteredPlugins } from "../marketplace/index.js";
|
|
16
16
|
import { checkInstallSafety } from "../blocklist/blocklist.js";
|
|
17
17
|
import { getSkill } from "../api/client.js";
|
|
18
18
|
import { checkPlatformSecurity } from "../security/index.js";
|
|
@@ -104,6 +104,24 @@ export async function detectMarketplaceRepo(owner, repo) {
|
|
|
104
104
|
}
|
|
105
105
|
return { isMarketplace: false };
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Offer to submit a marketplace repo for re-scanning on the platform.
|
|
109
|
+
* Uses the existing submitSkill() API. Non-throwing — prints fallback URL on failure.
|
|
110
|
+
*/
|
|
111
|
+
async function triggerResubmission(owner, repo) {
|
|
112
|
+
const repoUrl = `https://github.com/${owner}/${repo}`;
|
|
113
|
+
try {
|
|
114
|
+
const result = await submitSkill({ repoUrl });
|
|
115
|
+
console.log(green(" Submitted for re-scanning!"));
|
|
116
|
+
if (result.trackingUrl) {
|
|
117
|
+
console.log(dim(` Track progress: ${result.trackingUrl}`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
console.log(yellow(" Could not submit automatically."));
|
|
122
|
+
console.log(dim(` Submit manually: https://verified-skill.com/submit?repo=${owner}/${repo}`));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
107
125
|
/**
|
|
108
126
|
* Install plugins from a Claude Code plugin marketplace repo.
|
|
109
127
|
*
|
|
@@ -118,8 +136,20 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
118
136
|
console.error(red("No plugins found in marketplace.json"));
|
|
119
137
|
process.exit(1);
|
|
120
138
|
}
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
// Discover plugin directories not yet in marketplace.json
|
|
140
|
+
const unregistered = await discoverUnregisteredPlugins(owner, repo, manifestContent);
|
|
141
|
+
const headerParts = [
|
|
142
|
+
`\n${bold("Claude Code Plugin Marketplace")} detected: ${cyan(`${owner}/${repo}`)}\n`,
|
|
143
|
+
dim(`Marketplace: ${marketplaceName || "unknown"} — ${plugins.length} registered plugin${plugins.length === 1 ? "" : "s"}`),
|
|
144
|
+
];
|
|
145
|
+
if (unregistered.length > 0) {
|
|
146
|
+
headerParts.push(dim(`, ${unregistered.length} unregistered`));
|
|
147
|
+
}
|
|
148
|
+
headerParts.push("\n");
|
|
149
|
+
if (unregistered.length > 0) {
|
|
150
|
+
headerParts.push(yellow(` ${unregistered.length} new plugin${unregistered.length === 1 ? "" : "s"} not yet in marketplace.json\n`));
|
|
151
|
+
}
|
|
152
|
+
console.log(headerParts.join(""));
|
|
123
153
|
// Check lockfile for already-installed plugins
|
|
124
154
|
const lockDir = lockfileRoot(opts);
|
|
125
155
|
const lock = readLockfile(lockDir);
|
|
@@ -133,6 +163,7 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
133
163
|
}
|
|
134
164
|
// Select plugins
|
|
135
165
|
let selectedPlugins;
|
|
166
|
+
let selectedUnregistered = [];
|
|
136
167
|
if (!isTTY() && !opts.yes && !opts.all) {
|
|
137
168
|
// Non-TTY: list plugins and exit with guidance
|
|
138
169
|
console.log("Available plugins:\n");
|
|
@@ -140,6 +171,13 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
140
171
|
const installed = installedSet.has(p.name) ? dim(" (installed)") : "";
|
|
141
172
|
console.log(` ${bold(p.name)}${installed}${p.description ? dim(` — ${p.description}`) : ""}`);
|
|
142
173
|
}
|
|
174
|
+
if (unregistered.length > 0) {
|
|
175
|
+
console.log("\nUnregistered plugins (not in marketplace.json):\n");
|
|
176
|
+
for (const u of unregistered) {
|
|
177
|
+
console.log(` ${yellow(u.name)} ${dim("(new — not in marketplace.json)")}`);
|
|
178
|
+
}
|
|
179
|
+
console.log(dim("\nUse --force --plugin <name> to install unregistered plugins."));
|
|
180
|
+
}
|
|
143
181
|
console.error(red("\nNon-interactive mode. Use --plugin <name> or --yes to select all."));
|
|
144
182
|
process.exit(1);
|
|
145
183
|
}
|
|
@@ -158,9 +196,14 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
158
196
|
selectedPlugins = plugins;
|
|
159
197
|
console.log(dim(`Auto-selecting all ${plugins.length} plugins (--yes/--all)`));
|
|
160
198
|
}
|
|
199
|
+
if (unregistered.length > 0) {
|
|
200
|
+
console.log(dim(` Skipping ${unregistered.length} unregistered plugin${unregistered.length === 1 ? "" : "s"}: `) +
|
|
201
|
+
dim(unregistered.map((u) => u.name).join(", ")) +
|
|
202
|
+
dim(" (use --force to include)"));
|
|
203
|
+
}
|
|
161
204
|
}
|
|
162
|
-
else if (plugins.length === 1) {
|
|
163
|
-
// Single plugin — show details and ask for confirmation
|
|
205
|
+
else if (plugins.length === 1 && unregistered.length === 0) {
|
|
206
|
+
// Single plugin, no unregistered — show details and ask for confirmation
|
|
164
207
|
const p = plugins[0];
|
|
165
208
|
const isInstalled = installedSet.has(p.name);
|
|
166
209
|
const versionTag = p.version ? ` v${p.version}` : "";
|
|
@@ -185,17 +228,55 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
185
228
|
selectedPlugins = plugins;
|
|
186
229
|
}
|
|
187
230
|
else {
|
|
231
|
+
// Build combined picker: registered plugins first, then unregistered
|
|
232
|
+
const combinedItems = [
|
|
233
|
+
...plugins.map((p) => ({
|
|
234
|
+
label: p.name + (installedSet.has(p.name) ? dim(" (installed)") : ""),
|
|
235
|
+
description: p.description,
|
|
236
|
+
checked: preSelected ? preSelected.includes(p.name) : installedSet.has(p.name),
|
|
237
|
+
})),
|
|
238
|
+
...unregistered.map((u) => ({
|
|
239
|
+
label: u.name + yellow(" (new — not in marketplace.json)"),
|
|
240
|
+
description: undefined,
|
|
241
|
+
checked: false,
|
|
242
|
+
})),
|
|
243
|
+
];
|
|
188
244
|
const prompter = createPrompter();
|
|
189
|
-
const indices = await prompter.promptCheckboxList(plugins
|
|
190
|
-
label: p.name + (installedSet.has(p.name) ? dim(" (installed)") : ""),
|
|
191
|
-
description: p.description,
|
|
192
|
-
checked: preSelected ? preSelected.includes(p.name) : installedSet.has(p.name),
|
|
193
|
-
})), { title: "Select plugins to install" });
|
|
245
|
+
const indices = await prompter.promptCheckboxList(combinedItems, { title: "Select plugins to install" });
|
|
194
246
|
if (indices.length === 0) {
|
|
195
247
|
console.log(dim("No plugins selected. Aborting."));
|
|
196
248
|
return;
|
|
197
249
|
}
|
|
198
|
-
|
|
250
|
+
// Partition selections into registered and unregistered
|
|
251
|
+
selectedPlugins = [];
|
|
252
|
+
selectedUnregistered = [];
|
|
253
|
+
for (const i of indices) {
|
|
254
|
+
if (i < plugins.length) {
|
|
255
|
+
selectedPlugins.push(plugins[i]);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
selectedUnregistered.push(unregistered[i - plugins.length]);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Gate: unregistered plugins require --force
|
|
263
|
+
if (selectedUnregistered.length > 0 && !opts.force) {
|
|
264
|
+
console.log(yellow(`\n ${selectedUnregistered.length} unregistered plugin${selectedUnregistered.length === 1 ? "" : "s"} selected but --force not set.`) + "\n" +
|
|
265
|
+
dim(" Unregistered plugins have not been scanned or verified.") + "\n" +
|
|
266
|
+
dim(" Use --force to install them anyway, or submit the repo for re-scanning.") + "\n");
|
|
267
|
+
// Offer resubmission in interactive mode
|
|
268
|
+
if (isTTY()) {
|
|
269
|
+
const prompter = createPrompter();
|
|
270
|
+
const resubmit = await prompter.promptConfirm(`Submit ${bold(`${owner}/${repo}`)} for re-scanning?`, true);
|
|
271
|
+
if (resubmit) {
|
|
272
|
+
await triggerResubmission(owner, repo);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
console.log(dim(` Submit manually: https://verified-skill.com/submit?repo=${owner}/${repo}`));
|
|
277
|
+
}
|
|
278
|
+
// Clear unregistered selection — only install registered plugins
|
|
279
|
+
selectedUnregistered = [];
|
|
199
280
|
}
|
|
200
281
|
// Attempt native Claude Code install
|
|
201
282
|
const hasClaude = !opts.copy && isClaudeCliAvailable();
|
|
@@ -220,7 +301,7 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
220
301
|
}
|
|
221
302
|
}
|
|
222
303
|
}
|
|
223
|
-
// Install
|
|
304
|
+
// Install registered plugins
|
|
224
305
|
const results = [];
|
|
225
306
|
for (const plugin of selectedPlugins) {
|
|
226
307
|
if (hasClaude && marketplaceRegistered && marketplaceName) {
|
|
@@ -256,15 +337,28 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
256
337
|
}
|
|
257
338
|
}
|
|
258
339
|
}
|
|
340
|
+
// Install unregistered plugins via extraction (--force only)
|
|
341
|
+
for (const unreg of selectedUnregistered) {
|
|
342
|
+
console.log(yellow(` Installing unregistered plugin: ${bold(unreg.name)} (--force)`));
|
|
343
|
+
try {
|
|
344
|
+
await installRepoPlugin(`${owner}/${repo}`, unreg.name, opts, unreg.source);
|
|
345
|
+
results.push({ name: unreg.name, installed: true, method: "extraction-unregistered" });
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
console.error(red(` ✗ ${unreg.name}: ${err.message}`));
|
|
349
|
+
results.push({ name: unreg.name, installed: false, method: "failed" });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
259
352
|
// Update lockfile
|
|
260
353
|
const lockForWrite = ensureLockfile(lockDir);
|
|
261
354
|
for (const r of results) {
|
|
262
355
|
if (r.installed) {
|
|
263
|
-
const
|
|
356
|
+
const isUnregistered = r.method === "extraction-unregistered";
|
|
357
|
+
const pluginVersion = isUnregistered ? "0.0.0" : (getPluginVersion(r.name, manifestContent) || "0.0.0");
|
|
264
358
|
lockForWrite.skills[r.name] = {
|
|
265
359
|
version: pluginVersion,
|
|
266
360
|
sha: "",
|
|
267
|
-
tier: "VERIFIED",
|
|
361
|
+
tier: isUnregistered ? "UNSCANNED" : "VERIFIED",
|
|
268
362
|
installedAt: new Date().toISOString(),
|
|
269
363
|
source: `marketplace:${owner}/${repo}#${r.name}`,
|
|
270
364
|
marketplace: marketplaceName || undefined,
|
|
@@ -1028,7 +1122,7 @@ async function installAllRepoPlugins(ownerRepo, opts) {
|
|
|
1028
1122
|
// ---------------------------------------------------------------------------
|
|
1029
1123
|
// Remote plugin installation from a GitHub repository with marketplace.json
|
|
1030
1124
|
// ---------------------------------------------------------------------------
|
|
1031
|
-
async function installRepoPlugin(ownerRepo, pluginName, opts) {
|
|
1125
|
+
async function installRepoPlugin(ownerRepo, pluginName, opts, overrideSource) {
|
|
1032
1126
|
const [owner, repo] = ownerRepo.split("/");
|
|
1033
1127
|
if (!owner || !repo) {
|
|
1034
1128
|
throw new Error("--repo must be in owner/repo format (e.g. anton-abyzov/vskill)");
|
|
@@ -1054,14 +1148,14 @@ async function installRepoPlugin(ownerRepo, pluginName, opts) {
|
|
|
1054
1148
|
throw err;
|
|
1055
1149
|
throw new Error(`Network error: ${err.message}`);
|
|
1056
1150
|
}
|
|
1057
|
-
// Find the plugin in the marketplace
|
|
1058
|
-
const pluginSource = getPluginSource(pluginName, manifestContent);
|
|
1151
|
+
// Find the plugin in the marketplace (or use override for unregistered plugins)
|
|
1152
|
+
const pluginSource = overrideSource || getPluginSource(pluginName, manifestContent);
|
|
1059
1153
|
if (!pluginSource) {
|
|
1060
1154
|
const available = getAvailablePlugins(manifestContent).map((p) => p.name);
|
|
1061
1155
|
throw new Error(`Plugin "${pluginName}" not found in marketplace.json. ` +
|
|
1062
1156
|
`Available plugins: ${available.join(", ")}`);
|
|
1063
1157
|
}
|
|
1064
|
-
const pluginVersion = getPluginVersion(pluginName, manifestContent) || "0.0.0";
|
|
1158
|
+
const pluginVersion = overrideSource ? "0.0.0" : (getPluginVersion(pluginName, manifestContent) || "0.0.0");
|
|
1065
1159
|
const pluginPath = pluginSource.replace(/^\.\//, "");
|
|
1066
1160
|
// Blocklist + rejection check BEFORE fetching content
|
|
1067
1161
|
const safety = await checkInstallSafety(pluginName);
|