@theholocron/cli 2.0.1 → 2.1.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +50 -1
  2. package/package.json +8 -8
package/dist/cli.mjs CHANGED
@@ -3676,6 +3676,22 @@ async function runSetup(input) {
3676
3676
  summary
3677
3677
  };
3678
3678
  }
3679
+ /**
3680
+ * Fetch a single SKILL.md from its upstream GitHub source.
3681
+ * Verifies the SHA-256 hash when `computedHash` is present in the lock entry.
3682
+ */
3683
+ async function fetchExternalSkill(entry) {
3684
+ if (entry.sourceType !== "github") throw new Error(`unsupported sourceType: ${entry.sourceType}`);
3685
+ const url = `https://raw.githubusercontent.com/${entry.source}/HEAD/${entry.skillPath}`;
3686
+ const res = await fetch(url);
3687
+ if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
3688
+ const content = await res.text();
3689
+ if (entry.computedHash) {
3690
+ const actual = createHash("sha256").update(content).digest("hex");
3691
+ if (actual !== entry.computedHash) throw new Error(`hash mismatch for ${entry.source}/${entry.skillPath}: expected ${entry.computedHash}, got ${actual}`);
3692
+ }
3693
+ return content;
3694
+ }
3679
3695
  const AGENTS_SKILLS_ROOT = ".agents/skills";
3680
3696
  /** Relative path of the agent-specific symlink. undefined = unsupported agent. */
3681
3697
  const AGENT_SYMLINK_PATHS = { claude: (name) => `.claude/skills/${name}` };
@@ -3723,9 +3739,42 @@ async function installSkills({ agent, skills, repoRoot }) {
3723
3739
  await symlink(relative(dirname(symlinkPath), agentsDir).replace(/\\/g, "/"), symlinkPath);
3724
3740
  installed.push(name);
3725
3741
  }
3726
- if (installed.length > 0 || stale.length > 0 || missing.length > 0) await updateSkillsGitignore(gitignorePath, existingContent, [...installed, ...missing], symlinkFn);
3742
+ const externalFailed = [];
3743
+ if (missing.length > 0) {
3744
+ let lock = null;
3745
+ try {
3746
+ lock = JSON.parse(await readFile(join(skillsRoot, "skills-lock.json"), "utf8"));
3747
+ } catch {}
3748
+ if (lock?.skills) for (const name of [...missing]) {
3749
+ const entry = lock.skills[name];
3750
+ if (!entry) continue;
3751
+ try {
3752
+ const content = await fetchExternalSkill(entry);
3753
+ const agentsDir = join(repoRoot, AGENTS_SKILLS_ROOT, name);
3754
+ await mkdir(agentsDir, { recursive: true });
3755
+ await writeFile(join(agentsDir, "SKILL.md"), content);
3756
+ const symlinkPath = join(repoRoot, symlinkFn(name));
3757
+ await mkdir(dirname(symlinkPath), { recursive: true });
3758
+ try {
3759
+ await unlink(symlinkPath);
3760
+ } catch {}
3761
+ await symlink(relative(dirname(symlinkPath), agentsDir).replace(/\\/g, "/"), symlinkPath);
3762
+ missing.splice(missing.indexOf(name), 1);
3763
+ installed.push(name);
3764
+ } catch {
3765
+ missing.splice(missing.indexOf(name), 1);
3766
+ externalFailed.push(name);
3767
+ }
3768
+ }
3769
+ }
3770
+ if (installed.length > 0 || stale.length > 0 || missing.length > 0 || externalFailed.length > 0) await updateSkillsGitignore(gitignorePath, existingContent, [
3771
+ ...installed,
3772
+ ...missing,
3773
+ ...externalFailed
3774
+ ], symlinkFn);
3727
3775
  const parts = [`installed ${installed.length}`];
3728
3776
  if (stale.length > 0) parts.push(`pruned: ${stale.join(", ")}`);
3777
+ if (externalFailed.length > 0) parts.push(`fetch failed: ${externalFailed.join(", ")}`);
3729
3778
  if (missing.length > 0) parts.push(`unknown: ${missing.join(", ")}`);
3730
3779
  return parts.join("; ");
3731
3780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/cli",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "The Holocron CLI — a pluggable, capability-based orchestrator for spinning up and operating software projects.",
5
5
  "homepage": "https://github.com/theholocron/holocron/tree/main/packages/cli#readme",
6
6
  "bugs": "https://github.com/theholocron/holocron/issues",
@@ -34,17 +34,17 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@napi-rs/keyring": "^1.3.0",
37
- "@theholocron/github-client": "^1.1.0",
38
- "@theholocron/http-client": "^1.1.0",
37
+ "@theholocron/github-client": "^1.3.0",
38
+ "@theholocron/http-client": "^1.3.0",
39
39
  "chalk": "^5.4.1",
40
40
  "ora": "^8.2.0",
41
- "tsx": "^4.22.4",
41
+ "tsx": "4.22.4",
42
42
  "yargs": "^18.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@theholocron/eslint-config": "^7.3.0",
46
- "@theholocron/tsconfig": "^7.3.0",
47
- "@theholocron/vitest-config": "^7.3.0",
45
+ "@theholocron/eslint-config": "^7.4.1",
46
+ "@theholocron/tsconfig": "^7.4.1",
47
+ "@theholocron/vitest-config": "^7.4.1",
48
48
  "@types/node": "^26",
49
49
  "@types/yargs": "^17.0.35",
50
50
  "@vitest/coverage-v8": "^4.1.10",
@@ -52,7 +52,7 @@
52
52
  "eslint": "^10.7.0",
53
53
  "eslint-plugin-n": "^18.2.2",
54
54
  "globals": "^17.7.0",
55
- "tsdown": "^0.22.3",
55
+ "tsdown": "^0.22.13",
56
56
  "typescript": "^5.9.3",
57
57
  "vitest": "^4.1.10"
58
58
  },