@iceinvein/agent-skills 0.1.15 → 0.1.17

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/index.js CHANGED
@@ -644,6 +644,158 @@ async function updateSkill(cwd, skillName) {
644
644
  }
645
645
  return { ok: true, from: oldVersion, to: manifestResult.manifest.version };
646
646
  }
647
+ async function updateAllSkills(cwd) {
648
+ const lockfile = await readLockfile(cwd);
649
+ const skillNames = Object.keys(lockfile.skills);
650
+ if (skillNames.length === 0)
651
+ return [];
652
+ const results = [];
653
+ for (const name of skillNames) {
654
+ const result = await updateSkill(cwd, name);
655
+ results.push({ name, ...result });
656
+ }
657
+ return results;
658
+ }
659
+
660
+ // src/cli/commands/bump.ts
661
+ import { join as join7 } from "node:path";
662
+
663
+ // src/cli/semver.ts
664
+ function bumpVersion(current, level) {
665
+ const parts = current.split(".");
666
+ if (parts.length !== 3 || parts.some((p) => !/^\d+$/.test(p))) {
667
+ throw new Error(`Invalid semver version: '${current}'`);
668
+ }
669
+ const [major, minor, patch] = parts.map(Number);
670
+ switch (level) {
671
+ case "major":
672
+ return `${major + 1}.0.0`;
673
+ case "minor":
674
+ return `${major}.${minor + 1}.0`;
675
+ case "patch":
676
+ return `${major}.${minor}.${patch + 1}`;
677
+ }
678
+ }
679
+
680
+ // src/cli/commands/bump.ts
681
+ async function bumpSkill(repoRoot, skillName, level) {
682
+ const manifestPath = join7(repoRoot, "skills", skillName, "skill.json");
683
+ const file = Bun.file(manifestPath);
684
+ if (!await file.exists()) {
685
+ return { ok: false, error: `Skill '${skillName}' not found at skills/${skillName}/skill.json` };
686
+ }
687
+ const manifest = await file.json();
688
+ const from = manifest.version;
689
+ let to;
690
+ try {
691
+ to = bumpVersion(from, level);
692
+ } catch (e) {
693
+ return { ok: false, error: `Invalid version '${from}' in skills/${skillName}/skill.json` };
694
+ }
695
+ manifest.version = to;
696
+ await Bun.write(manifestPath, JSON.stringify(manifest, null, 2) + `
697
+ `);
698
+ return { ok: true, from, to };
699
+ }
700
+ function getLatestTag(repoRoot) {
701
+ const result = Bun.spawnSync(["git", "describe", "--tags", "--abbrev=0"], {
702
+ cwd: repoRoot
703
+ });
704
+ if (result.exitCode !== 0)
705
+ return null;
706
+ return result.stdout.toString().trim();
707
+ }
708
+ var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
709
+ function getChangedSkills(repoRoot, sinceTag) {
710
+ const base = sinceTag ?? EMPTY_TREE;
711
+ const args = ["git", "diff", "--name-only", base, "HEAD", "--", "skills/"];
712
+ const result = Bun.spawnSync(args, { cwd: repoRoot });
713
+ const output = result.stdout.toString().trim();
714
+ if (!output)
715
+ return new Set;
716
+ const names = new Set;
717
+ for (const line of output.split(`
718
+ `)) {
719
+ const parts = line.split("/");
720
+ if (parts.length >= 2 && parts[0] === "skills") {
721
+ names.add(parts[1]);
722
+ }
723
+ }
724
+ return names;
725
+ }
726
+ function skillVersionChanged(repoRoot, skillName, sinceTag) {
727
+ const base = sinceTag ?? EMPTY_TREE;
728
+ const args = ["git", "diff", base, "HEAD", "--", `skills/${skillName}/skill.json`];
729
+ const result = Bun.spawnSync(args, { cwd: repoRoot });
730
+ const diff = result.stdout.toString();
731
+ const hasRemoved = diff.split(`
732
+ `).some((l) => l.startsWith("-") && !l.startsWith("---") && l.includes('"version"'));
733
+ const hasAdded = diff.split(`
734
+ `).some((l) => l.startsWith("+") && !l.startsWith("+++") && l.includes('"version"'));
735
+ return hasRemoved && hasAdded;
736
+ }
737
+ async function bumpAllChanged(repoRoot, level, dryRun) {
738
+ const tag = getLatestTag(repoRoot);
739
+ const changed = getChangedSkills(repoRoot, tag);
740
+ const results = [];
741
+ for (const skillName of changed) {
742
+ if (skillVersionChanged(repoRoot, skillName, tag))
743
+ continue;
744
+ if (dryRun) {
745
+ const manifestPath = join7(repoRoot, "skills", skillName, "skill.json");
746
+ const file = Bun.file(manifestPath);
747
+ if (!await file.exists())
748
+ continue;
749
+ const manifest = await file.json();
750
+ const from = manifest.version;
751
+ try {
752
+ const to = bumpVersion(from, level);
753
+ results.push({ name: skillName, ok: true, from, to });
754
+ } catch {
755
+ results.push({ name: skillName, ok: false, error: `Invalid version '${from}'` });
756
+ }
757
+ } else {
758
+ const result = await bumpSkill(repoRoot, skillName, level);
759
+ results.push({ name: skillName, ...result });
760
+ }
761
+ }
762
+ return results;
763
+ }
764
+
765
+ // src/cli/update-check.ts
766
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
767
+ async function checkForUpdates(cwd) {
768
+ try {
769
+ const lockfile = await readLockfile(cwd);
770
+ const skillNames = Object.keys(lockfile.skills);
771
+ if (skillNames.length === 0)
772
+ return [];
773
+ if (lockfile.lastUpdateCheck) {
774
+ const lastCheck = new Date(lockfile.lastUpdateCheck).getTime();
775
+ if (Date.now() - lastCheck < CHECK_INTERVAL_MS)
776
+ return [];
777
+ }
778
+ const results = await Promise.all(skillNames.map(async (name) => {
779
+ try {
780
+ const result = await fetchSkillManifest2(name);
781
+ if (!result.ok)
782
+ return null;
783
+ const installed = lockfile.skills[name].version;
784
+ if (result.manifest.version !== installed) {
785
+ return { name, installed, latest: result.manifest.version };
786
+ }
787
+ return null;
788
+ } catch {
789
+ return null;
790
+ }
791
+ }));
792
+ lockfile.lastUpdateCheck = new Date().toISOString();
793
+ await writeLockfile(cwd, lockfile);
794
+ return results.filter((r) => r !== null);
795
+ } catch {
796
+ return [];
797
+ }
798
+ }
647
799
 
648
800
  // src/cli/types.ts
649
801
  var TOOL_NAMES2 = ["claude", "cursor", "codex", "gemini"];
@@ -683,7 +835,7 @@ Select (comma-separated numbers, e.g. 1,3): `);
683
835
 
684
836
  // src/cli/index.ts
685
837
  import { mkdirSync as mkdirSync4 } from "fs";
686
- import { join as join7 } from "path";
838
+ import { join as join8 } from "path";
687
839
  import { homedir } from "os";
688
840
  function resolveInstallDir(flags) {
689
841
  if (flags.global !== undefined || flags.g !== undefined) {
@@ -691,7 +843,7 @@ function resolveInstallDir(flags) {
691
843
  }
692
844
  return process.cwd();
693
845
  }
694
- var BOOLEAN_FLAGS = new Set(["global", "g"]);
846
+ var BOOLEAN_FLAGS = new Set(["global", "g", "all", "dry-run"]);
695
847
  function parseArgs(argv) {
696
848
  if (argv.length === 0)
697
849
  return { command: "help", args: [], flags: {} };
@@ -724,12 +876,16 @@ Usage:
724
876
  agent-skills install <skill> [--tool <tool>] [-g] Install a skill
725
877
  agent-skills remove <skill> [-g] Remove a skill
726
878
  agent-skills update <skill> [-g] Update a skill
879
+ agent-skills update --all [-g] Update all installed skills
880
+ agent-skills bump <skill> [patch|minor|major] Bump a skill's version
881
+ agent-skills bump --all [patch|minor|major] Bump all changed skills
727
882
  agent-skills list List available skills
728
883
  agent-skills info <skill> Show skill details
729
884
 
730
885
  Flags:
731
886
  --tool <tool> Install for a specific tool (${TOOL_NAMES2.join(", ")})
732
887
  -g, --global Install to home directory (available in all projects)
888
+ --dry-run With bump --all: check without writing (exit 1 if unbumped)
733
889
  `);
734
890
  }
735
891
  function printInstalled(result, skipped) {
@@ -790,7 +946,7 @@ async function main() {
790
946
  gemini: ".gemini"
791
947
  };
792
948
  if (dirs[tool]) {
793
- mkdirSync4(join7(installDir, dirs[tool]), { recursive: true });
949
+ mkdirSync4(join8(installDir, dirs[tool]), { recursive: true });
794
950
  }
795
951
  }
796
952
  } else {
@@ -823,12 +979,31 @@ async function main() {
823
979
  break;
824
980
  }
825
981
  case "update": {
982
+ const updateDir = resolveInstallDir(flags);
983
+ if (flags.all !== undefined) {
984
+ console.log(`Updating all installed skills...
985
+ `);
986
+ const results = await updateAllSkills(updateDir);
987
+ if (results.length === 0) {
988
+ console.log("No skills installed.");
989
+ break;
990
+ }
991
+ for (const r of results) {
992
+ if (!r.ok) {
993
+ console.error(` \u2717 ${r.name}: ${r.error}`);
994
+ } else if (r.from === r.to) {
995
+ console.log(` \u2298 ${r.name} already up to date (v${r.to})`);
996
+ } else {
997
+ console.log(` \u2713 ${r.name} v${r.from} \u2192 v${r.to}`);
998
+ }
999
+ }
1000
+ break;
1001
+ }
826
1002
  const skillName = args[0];
827
1003
  if (!skillName) {
828
1004
  console.error("Error: skill name required. Usage: agent-skills update <skill>");
829
1005
  process.exit(1);
830
1006
  }
831
- const updateDir = resolveInstallDir(flags);
832
1007
  console.log(`Updating '${skillName}'...`);
833
1008
  const result = await updateSkill(updateDir, skillName);
834
1009
  if (!result.ok) {
@@ -876,11 +1051,68 @@ ${m.name} v${m.version}`);
876
1051
  console.log(` Package: ${m.mcp.package}`);
877
1052
  break;
878
1053
  }
1054
+ case "bump": {
1055
+ const BUMP_LEVELS = ["patch", "minor", "major"];
1056
+ const repoRoot = import.meta.dir.replace(/\/dist\/cli$|\/src\/cli$/, "");
1057
+ if (flags.all !== undefined) {
1058
+ const levelArg = args[0] ?? "patch";
1059
+ if (!BUMP_LEVELS.includes(levelArg)) {
1060
+ console.error(`Error: invalid bump level '${levelArg}'. Must be one of: ${BUMP_LEVELS.join(", ")}`);
1061
+ process.exit(1);
1062
+ }
1063
+ const dryRun = flags["dry-run"] !== undefined;
1064
+ const results = await bumpAllChanged(repoRoot, levelArg, dryRun);
1065
+ if (results.length === 0) {
1066
+ if (!dryRun)
1067
+ console.log("All skill versions are up to date.");
1068
+ process.exit(0);
1069
+ }
1070
+ for (const r of results) {
1071
+ if (!r.ok) {
1072
+ console.error(` \u2717 ${r.name}: ${r.error}`);
1073
+ } else if (dryRun) {
1074
+ console.log(` needs bump: ${r.name} ${r.from} \u2192 ${r.to}`);
1075
+ } else {
1076
+ console.log(` \u2713 ${r.name} ${r.from} \u2192 ${r.to}`);
1077
+ }
1078
+ }
1079
+ if (dryRun)
1080
+ process.exit(1);
1081
+ break;
1082
+ }
1083
+ const skillName = args[0];
1084
+ if (!skillName) {
1085
+ console.error("Error: skill name required. Usage: agent-skills bump <skill> [patch|minor|major]");
1086
+ process.exit(1);
1087
+ }
1088
+ const level = args[1] ?? "patch";
1089
+ if (!BUMP_LEVELS.includes(level)) {
1090
+ console.error(`Error: invalid bump level '${args[1]}'. Must be one of: ${BUMP_LEVELS.join(", ")}`);
1091
+ process.exit(1);
1092
+ }
1093
+ const result = await bumpSkill(repoRoot, skillName, level);
1094
+ if (!result.ok) {
1095
+ console.error(`Error: ${result.error}`);
1096
+ process.exit(1);
1097
+ }
1098
+ console.log(`\u2713 ${skillName} ${result.from} \u2192 ${result.to}`);
1099
+ break;
1100
+ }
879
1101
  case "help":
880
1102
  default:
881
1103
  printHelp();
882
1104
  break;
883
1105
  }
1106
+ if (command !== "update") {
1107
+ const checkDir = flags.global !== undefined || flags.g !== undefined ? homedir() : process.cwd();
1108
+ const outdated = await checkForUpdates(checkDir);
1109
+ if (outdated.length > 0) {
1110
+ const list = outdated.map((s) => `${s.name} (v${s.installed} \u2192 v${s.latest})`).join(", ");
1111
+ console.log(`
1112
+ \u26A0 Updates available: ${list}`);
1113
+ console.log(" Run: agent-skills update --all");
1114
+ }
1115
+ }
884
1116
  }
885
1117
  main().catch((err) => {
886
1118
  console.error("Fatal:", err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iceinvein/agent-skills",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Install agent skills into AI coding tools",
5
5
  "author": "iceinvein",
6
6
  "license": "MIT",
@@ -15,7 +15,11 @@
15
15
  "scripts": {
16
16
  "build": "bun build src/cli/index.ts --outdir dist/cli --target bun",
17
17
  "test": "bun test",
18
- "dev": "bun run src/cli/index.ts"
18
+ "dev": "bun run src/cli/index.ts",
19
+ "skill:bump": "bun run src/cli/index.ts bump",
20
+ "skill:bump:all": "bun run src/cli/index.ts bump --all",
21
+ "skill:bump:check": "bun run src/cli/index.ts bump --all --dry-run",
22
+ "release": "bash scripts/release.sh"
19
23
  },
20
24
  "keywords": [
21
25
  "ai",
@@ -36,9 +36,27 @@ Every response, cut these patterns:
36
36
  - Kill: [explanation] "So in summary, what we did was update the middleware to validate tokens correctly, which should fix the authentication issue."
37
37
  - Write: [explanation ends]
38
38
 
39
- **Narrating actions** — don't announce what you're about to do. Just do it.
39
+ **Narrating actions** — don't announce what you're about to do. Just do it. The tool call is the communication. Never preface a tool call with text explaining that you're about to make it.
40
40
  - Kill: "Let me take a look at the file for you. I'll read it now and analyze what's going on."
41
- - Write: [reads the file]
41
+ - Kill: "Now let me fix that issue."
42
+ - Kill: "Let me check the tests."
43
+ - Kill: "I'll update the config next."
44
+ - Kill: "We need to update the schema first."
45
+ - Kill: "First, I'll read the file to understand the structure."
46
+ - Write: [tool call — no preamble]
47
+
48
+ **Banned action-narration openers** — these phrases before a tool call are always filler. Cut them 100% of the time:
49
+ - "Let me..." / "Now let me..." / "Now I'll..."
50
+ - "I'll..." / "I need to..." / "We need to..."
51
+ - "First, let me..." / "Next, I'll..."
52
+ - "Going to..." / "I'm going to..."
53
+ - "Time to..." / "Let's..."
54
+
55
+ If context is needed between tool calls, state the *finding* or *decision*, not the action:
56
+ - Kill: "Now let me update the handler to fix this."
57
+ - Write: "The handler is missing the null check." [edits file]
58
+ - Kill: "Let me run the tests to verify."
59
+ - Write: [runs tests]
42
60
 
43
61
  **Over-explaining the obvious** — don't describe trivial operations.
44
62
  - Kill: "I'll create a new file called `utils.ts`. This file will contain utility functions that we can reuse across the project."
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "terse",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Professional output compression — proper grammar, no fluff, ~50-60% fewer tokens. Three levels: clean, tight, sharp.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
- "tools": ["claude", "cursor", "codex", "gemini"],
7
+ "tools": [
8
+ "claude",
9
+ "cursor",
10
+ "codex",
11
+ "gemini"
12
+ ],
8
13
  "files": {
9
14
  "prompt": "SKILL.md"
10
15
  },