@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 +236 -4
- package/package.json +6 -2
- package/skills/terse/SKILL.md +20 -2
- package/skills/terse/skill.json +7 -2
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
|
|
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(
|
|
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.
|
|
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",
|
package/skills/terse/SKILL.md
CHANGED
|
@@ -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
|
-
-
|
|
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."
|
package/skills/terse/skill.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terse",
|
|
3
|
-
"version": "1.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": [
|
|
7
|
+
"tools": [
|
|
8
|
+
"claude",
|
|
9
|
+
"cursor",
|
|
10
|
+
"codex",
|
|
11
|
+
"gemini"
|
|
12
|
+
],
|
|
8
13
|
"files": {
|
|
9
14
|
"prompt": "SKILL.md"
|
|
10
15
|
},
|