harnessed 3.9.8 → 3.9.9
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/cli.mjs +77 -24
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/manifests/skill-packs/mattpocock-skills.yaml +4 -3
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1222,7 +1222,7 @@ var init_auto_install = __esm({
|
|
|
1222
1222
|
|
|
1223
1223
|
// package.json
|
|
1224
1224
|
var package_default = {
|
|
1225
|
-
version: "3.9.
|
|
1225
|
+
version: "3.9.9"};
|
|
1226
1226
|
|
|
1227
1227
|
// src/manifest/errors.ts
|
|
1228
1228
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -4379,6 +4379,9 @@ var installCcHookAdd = async (ctx) => {
|
|
|
4379
4379
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
4380
4380
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
|
|
4381
4381
|
};
|
|
4382
|
+
|
|
4383
|
+
// src/installers/lib/idempotent.ts
|
|
4384
|
+
init_readClaudeConfig();
|
|
4382
4385
|
function expandTildeForWindows(cmd) {
|
|
4383
4386
|
const home = homedir().replace(/\\/g, "/");
|
|
4384
4387
|
return cmd.replace(/(^|[\s"'`(])~\//g, `$1${home}/`);
|
|
@@ -4466,18 +4469,83 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4466
4469
|
|
|
4467
4470
|
// src/installers/lib/idempotent.ts
|
|
4468
4471
|
var IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
|
|
4472
|
+
function extractSkillName(cmd, fallback) {
|
|
4473
|
+
const m = cmd.match(/\bskills(?:@\S+)?\s+add\s+(\S+)/i);
|
|
4474
|
+
if (!m?.[1]) return fallback;
|
|
4475
|
+
const seg = m[1].split("/");
|
|
4476
|
+
return seg[seg.length - 1] ?? fallback;
|
|
4477
|
+
}
|
|
4478
|
+
function extractGitCloneTarget(cmd) {
|
|
4479
|
+
const idx = cmd.indexOf("git clone");
|
|
4480
|
+
if (idx < 0) return null;
|
|
4481
|
+
const tail = cmd.slice(idx + "git clone".length).trim();
|
|
4482
|
+
const tokens = tail.split(/\s+/);
|
|
4483
|
+
let i = 0;
|
|
4484
|
+
while (i < tokens.length && tokens[i]?.startsWith("-")) {
|
|
4485
|
+
i += tokens[i]?.includes("=") ? 1 : 2;
|
|
4486
|
+
}
|
|
4487
|
+
const dest = tokens[i + 1];
|
|
4488
|
+
if (!dest || dest === "&&" || dest === ";" || dest === "|") return null;
|
|
4489
|
+
if (dest.startsWith("~/")) return join(homedir(), dest.slice(2));
|
|
4490
|
+
if (dest.startsWith("/") || /^[A-Z]:[\\/]/i.test(dest)) return dest;
|
|
4491
|
+
return null;
|
|
4492
|
+
}
|
|
4493
|
+
async function detectNative(ctx) {
|
|
4494
|
+
const method = ctx.manifest.spec.install.method;
|
|
4495
|
+
const cmd = ctx.manifest.spec.install.cmd;
|
|
4496
|
+
const name = ctx.manifest.metadata.name;
|
|
4497
|
+
if (method === "cc-plugin-marketplace") {
|
|
4498
|
+
const m = cmd.match(/(?:claude\s+)?plugin\s+install\s+(\S+)/i);
|
|
4499
|
+
const pluginName = m?.[1]?.split("@")[0] ?? name;
|
|
4500
|
+
try {
|
|
4501
|
+
return await isPluginRegistered(pluginName);
|
|
4502
|
+
} catch {
|
|
4503
|
+
return false;
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
if (method === "npx-skill-installer") {
|
|
4507
|
+
const skillName = extractSkillName(cmd, name);
|
|
4508
|
+
const skillMd = join(homedir(), ".claude", "skills", skillName, "SKILL.md");
|
|
4509
|
+
try {
|
|
4510
|
+
await access(skillMd);
|
|
4511
|
+
return true;
|
|
4512
|
+
} catch {
|
|
4513
|
+
return false;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
if (method === "git-clone-with-setup") {
|
|
4517
|
+
const target = extractGitCloneTarget(cmd);
|
|
4518
|
+
if (!target) return false;
|
|
4519
|
+
try {
|
|
4520
|
+
await access(target);
|
|
4521
|
+
return true;
|
|
4522
|
+
} catch {
|
|
4523
|
+
return false;
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4526
|
+
if (method === "npm-cli") {
|
|
4527
|
+
const skillDir = join(homedir(), ".claude", "skills", name);
|
|
4528
|
+
try {
|
|
4529
|
+
await access(skillDir);
|
|
4530
|
+
return true;
|
|
4531
|
+
} catch {
|
|
4532
|
+
return false;
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
return false;
|
|
4536
|
+
}
|
|
4469
4537
|
async function isAlreadyInstalled(ctx, opts = {}) {
|
|
4470
4538
|
const honorUpdateFlag = opts.honorUpdateFlag !== false;
|
|
4471
4539
|
if (honorUpdateFlag && ctx.opts.updateInstalled === true) return false;
|
|
4472
4540
|
if (ctx.opts.dryRun) return false;
|
|
4541
|
+
const native = await detectNative(ctx);
|
|
4542
|
+
if (native) return true;
|
|
4473
4543
|
const idempotentCmd = ctx.manifest.spec.install.idempotent_check;
|
|
4474
4544
|
if (typeof idempotentCmd !== "string" || idempotentCmd.length === 0) {
|
|
4475
4545
|
return false;
|
|
4476
4546
|
}
|
|
4477
4547
|
const r = await spawnCmd(ctx, idempotentCmd, [], IDEMPOTENT_CHECK_TIMEOUT_MS);
|
|
4478
|
-
if (!("exitCode" in r))
|
|
4479
|
-
return false;
|
|
4480
|
-
}
|
|
4548
|
+
if (!("exitCode" in r)) return false;
|
|
4481
4549
|
return r.exitCode === 0;
|
|
4482
4550
|
}
|
|
4483
4551
|
|
|
@@ -4615,7 +4683,7 @@ ${newEntry}
|
|
|
4615
4683
|
const r1 = await runArgs(["plugin", "marketplace", "add", parsed.marketplaceRef], spawnCwd);
|
|
4616
4684
|
stepOneStderr = r1.stderr;
|
|
4617
4685
|
}
|
|
4618
|
-
const r2 = await runArgs(installArgs, spawnCwd);
|
|
4686
|
+
const r2 = await runArgs(installArgs, spawnCwd, 6e4);
|
|
4619
4687
|
if (r2.exitCode !== 0) {
|
|
4620
4688
|
return {
|
|
4621
4689
|
ok: false,
|
|
@@ -5221,7 +5289,7 @@ var installNpmCli = async (ctx) => {
|
|
|
5221
5289
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, install.npm_version, "");
|
|
5222
5290
|
return { ok: true, backupId: bk.backupId, appliedFiles: [] };
|
|
5223
5291
|
};
|
|
5224
|
-
function
|
|
5292
|
+
function extractSkillName2(cmd, fallback) {
|
|
5225
5293
|
const m = cmd.match(/\bskills(?:@\S+)?\s+add\s+(\S+)/i);
|
|
5226
5294
|
if (!m || m[1] === void 0) return fallback;
|
|
5227
5295
|
const ref = m[1];
|
|
@@ -5252,21 +5320,6 @@ var installNpxSkillInstaller = async (ctx) => {
|
|
|
5252
5320
|
if (await isAlreadyInstalled(ctx)) {
|
|
5253
5321
|
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
5254
5322
|
}
|
|
5255
|
-
if (!/\bskills@(?!latest\b)\S+/.test(install.cmd)) {
|
|
5256
|
-
return {
|
|
5257
|
-
ok: false,
|
|
5258
|
-
phase: "preflight",
|
|
5259
|
-
error: {
|
|
5260
|
-
...err(
|
|
5261
|
-
ctx,
|
|
5262
|
-
"/spec/install/cmd",
|
|
5263
|
-
`npx-skill-installer cmd must reference a pinned skills@<version> (got: '${install.cmd.slice(0, 100)}'); @latest is forbidden for reproducibility (ADR 0001)`,
|
|
5264
|
-
"skills-pin-required"
|
|
5265
|
-
),
|
|
5266
|
-
suggest: "change `skills@latest` \u2192 `skills@1.5.7` (current research-pinned stable)"
|
|
5267
|
-
}
|
|
5268
|
-
};
|
|
5269
|
-
}
|
|
5270
5323
|
if (!/\B--copy\b/.test(install.cmd) || !/\B--global\b/.test(install.cmd)) {
|
|
5271
5324
|
return {
|
|
5272
5325
|
ok: false,
|
|
@@ -5283,7 +5336,7 @@ var installNpxSkillInstaller = async (ctx) => {
|
|
|
5283
5336
|
};
|
|
5284
5337
|
}
|
|
5285
5338
|
const name = ctx.manifest.metadata.name;
|
|
5286
|
-
const skillSegment =
|
|
5339
|
+
const skillSegment = extractSkillName2(install.cmd, name);
|
|
5287
5340
|
const skillDir = join(homedir(), ".claude", "skills", skillSegment);
|
|
5288
5341
|
const skillMdPath = join(skillDir, "SKILL.md");
|
|
5289
5342
|
const plan = {
|
|
@@ -6647,7 +6700,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
6647
6700
|
}
|
|
6648
6701
|
return { ok: true, removedPaths: [pkg] };
|
|
6649
6702
|
};
|
|
6650
|
-
function
|
|
6703
|
+
function extractSkillName3(cmd, fallback) {
|
|
6651
6704
|
const m = cmd.match(/\bskills(?:@\S+)?\s+add\s+(\S+)/i);
|
|
6652
6705
|
if (!m || m[1] === void 0) return fallback;
|
|
6653
6706
|
const ref = m[1];
|
|
@@ -6662,7 +6715,7 @@ var uninstallNpxSkillInstaller = async (ctx) => {
|
|
|
6662
6715
|
const abort = dryRunGate(ctx);
|
|
6663
6716
|
if (abort) return abort;
|
|
6664
6717
|
const name = ctx.manifest.metadata.name;
|
|
6665
|
-
const skillName =
|
|
6718
|
+
const skillName = extractSkillName3(install.cmd, name);
|
|
6666
6719
|
const skillDir = join(homedir(), ".claude", "skills", skillName);
|
|
6667
6720
|
await rm(skillDir, { recursive: true, force: true, maxRetries: 3 });
|
|
6668
6721
|
return { ok: true, removedPaths: [skillDir] };
|