install-agent-skill 1.2.3
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/README.bigtech.md +191 -0
- package/README.md +271 -0
- package/bin/add-skill.js +889 -0
- package/bin/add-skill.v1.backup.js +1904 -0
- package/bin/add-skill.v2.js +512 -0
- package/bin/lib/commands/analyze.js +70 -0
- package/bin/lib/commands/cache.js +65 -0
- package/bin/lib/commands/doctor.js +75 -0
- package/bin/lib/commands/help.js +42 -0
- package/bin/lib/commands/info.js +38 -0
- package/bin/lib/commands/init.js +39 -0
- package/bin/lib/commands/install.js +188 -0
- package/bin/lib/commands/list.js +43 -0
- package/bin/lib/commands/lock.js +57 -0
- package/bin/lib/commands/uninstall.js +48 -0
- package/bin/lib/commands/update.js +58 -0
- package/bin/lib/commands/validate.js +69 -0
- package/bin/lib/commands/verify.js +56 -0
- package/bin/lib/config.js +80 -0
- package/bin/lib/helpers.js +155 -0
- package/bin/lib/skills.js +114 -0
- package/bin/lib/types.js +82 -0
- package/bin/lib/ui.js +77 -0
- package/package.json +56 -0
- package/specs/ADD_SKILL_SPEC.md +333 -0
- package/specs/REGISTRY_V2_SPEC.md +334 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Verify command - Checksum verification
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { resolveScope, merkleHash } from "../helpers.js";
|
|
8
|
+
import { step, stepLine, S, c } from "../ui.js";
|
|
9
|
+
import { STRICT } from "../config.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Verify skill checksums
|
|
13
|
+
*/
|
|
14
|
+
export async function run() {
|
|
15
|
+
const scope = resolveScope();
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(scope)) {
|
|
18
|
+
stepLine();
|
|
19
|
+
step("No skills directory found", S.diamond);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
stepLine();
|
|
24
|
+
step(c.bold("Verifying Skills"), S.diamondFilled, "cyan");
|
|
25
|
+
stepLine();
|
|
26
|
+
|
|
27
|
+
let issues = 0;
|
|
28
|
+
|
|
29
|
+
for (const name of fs.readdirSync(scope)) {
|
|
30
|
+
const dir = path.join(scope, name);
|
|
31
|
+
if (!fs.statSync(dir).isDirectory()) continue;
|
|
32
|
+
|
|
33
|
+
const mf = path.join(dir, ".skill-source.json");
|
|
34
|
+
if (!fs.existsSync(mf)) {
|
|
35
|
+
step(`${name}: ${c.red("missing metadata")}`, S.cross, "red");
|
|
36
|
+
issues++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const m = JSON.parse(fs.readFileSync(mf, "utf-8"));
|
|
41
|
+
const actual = merkleHash(dir);
|
|
42
|
+
|
|
43
|
+
if (actual !== m.checksum) {
|
|
44
|
+
step(`${name}: ${c.red("checksum mismatch")}`, S.cross, "red");
|
|
45
|
+
issues++;
|
|
46
|
+
} else {
|
|
47
|
+
step(`${name}: ${c.green("OK")}`, S.check, "green");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
stepLine();
|
|
52
|
+
console.log(` ${c.gray(S.branch)} ${issues ? c.red(issues + " issue(s)") : c.green("All verified")}`);
|
|
53
|
+
stepLine();
|
|
54
|
+
|
|
55
|
+
if (issues && STRICT) process.exit(1);
|
|
56
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration and argument parsing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
|
|
8
|
+
/** Current working directory */
|
|
9
|
+
export const cwd = process.cwd();
|
|
10
|
+
|
|
11
|
+
/** Local workspace skills directory */
|
|
12
|
+
export const WORKSPACE = path.join(cwd, ".agent", "skills");
|
|
13
|
+
|
|
14
|
+
/** Global skills directory */
|
|
15
|
+
export const GLOBAL_DIR = path.join(os.homedir(), ".gemini", "antigravity", "skills");
|
|
16
|
+
|
|
17
|
+
/** Cache root directory */
|
|
18
|
+
export const CACHE_ROOT = process.env.ADD_SKILL_CACHE_DIR || path.join(os.homedir(), ".cache", "add-skill");
|
|
19
|
+
|
|
20
|
+
/** Registry cache directory */
|
|
21
|
+
export const REGISTRY_CACHE = path.join(CACHE_ROOT, "registries");
|
|
22
|
+
|
|
23
|
+
/** Registries file path */
|
|
24
|
+
export const REGISTRIES_FILE = path.join(CACHE_ROOT, "registries.json");
|
|
25
|
+
|
|
26
|
+
/** Backup directory */
|
|
27
|
+
export const BACKUP_DIR = path.join(CACHE_ROOT, "backups");
|
|
28
|
+
|
|
29
|
+
// --- Argument Parsing ---
|
|
30
|
+
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
|
|
33
|
+
/** Command name (first non-flag argument) */
|
|
34
|
+
export const command = args[0] || "help";
|
|
35
|
+
|
|
36
|
+
/** All flags (starting with --) */
|
|
37
|
+
export const flags = new Set(args.filter((a) => a.startsWith("--")));
|
|
38
|
+
|
|
39
|
+
/** Command parameters (non-flag arguments after command) */
|
|
40
|
+
export const params = args.filter((a) => !a.startsWith("--")).slice(1);
|
|
41
|
+
|
|
42
|
+
// --- Flag Shortcuts ---
|
|
43
|
+
|
|
44
|
+
/** Use global scope */
|
|
45
|
+
export const GLOBAL = flags.has("--global") || flags.has("-g");
|
|
46
|
+
|
|
47
|
+
/** Verbose output */
|
|
48
|
+
export const VERBOSE = flags.has("--verbose") || flags.has("-v");
|
|
49
|
+
|
|
50
|
+
/** JSON output mode */
|
|
51
|
+
export const JSON_OUTPUT = flags.has("--json");
|
|
52
|
+
|
|
53
|
+
/** Force operation */
|
|
54
|
+
export const FORCE = flags.has("--force") || flags.has("-f");
|
|
55
|
+
|
|
56
|
+
/** Dry run mode */
|
|
57
|
+
export const DRY = flags.has("--dry-run");
|
|
58
|
+
|
|
59
|
+
/** Strict mode */
|
|
60
|
+
export const STRICT = flags.has("--strict");
|
|
61
|
+
|
|
62
|
+
/** Auto-fix mode */
|
|
63
|
+
export const FIX = flags.has("--fix");
|
|
64
|
+
|
|
65
|
+
/** Locked mode */
|
|
66
|
+
export const LOCKED = flags.has("--locked");
|
|
67
|
+
|
|
68
|
+
/** Offline mode */
|
|
69
|
+
export const OFFLINE = flags.has("--offline");
|
|
70
|
+
|
|
71
|
+
// --- Package Info ---
|
|
72
|
+
|
|
73
|
+
import { createRequire } from "module";
|
|
74
|
+
const require = createRequire(import.meta.url);
|
|
75
|
+
|
|
76
|
+
/** Package version */
|
|
77
|
+
export const VERSION = (() => {
|
|
78
|
+
try { return require("../../package.json").version; }
|
|
79
|
+
catch { return "5.0.0"; }
|
|
80
|
+
})();
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utility helper functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import crypto from "crypto";
|
|
8
|
+
import { BACKUP_DIR, DRY, cwd, GLOBAL, WORKSPACE, GLOBAL_DIR, REGISTRIES_FILE } from "./config.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get directory size recursively
|
|
12
|
+
* @param {string} dir - Directory path
|
|
13
|
+
* @returns {number} Size in bytes
|
|
14
|
+
*/
|
|
15
|
+
export function getDirSize(dir) {
|
|
16
|
+
let size = 0;
|
|
17
|
+
try {
|
|
18
|
+
const walk = (p) => {
|
|
19
|
+
for (const f of fs.readdirSync(p)) {
|
|
20
|
+
const full = path.join(p, f);
|
|
21
|
+
const stat = fs.statSync(full);
|
|
22
|
+
if (stat.isDirectory()) walk(full);
|
|
23
|
+
else size += stat.size;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
walk(dir);
|
|
27
|
+
} catch { }
|
|
28
|
+
return size;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Format bytes to human readable
|
|
33
|
+
* @param {number} bytes
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
export function formatBytes(bytes) {
|
|
37
|
+
if (bytes < 1024) return bytes + " B";
|
|
38
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
39
|
+
return (bytes / 1024 / 1024).toFixed(1) + " MB";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Format ISO date string
|
|
44
|
+
* @param {string} [iso]
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export function formatDate(iso) {
|
|
48
|
+
return iso ? new Date(iso).toLocaleDateString() : "unknown";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Calculate merkle hash of directory
|
|
53
|
+
* @param {string} dir - Directory path
|
|
54
|
+
* @returns {string} SHA256 hash
|
|
55
|
+
*/
|
|
56
|
+
export function merkleHash(dir) {
|
|
57
|
+
const files = [];
|
|
58
|
+
const walk = (p) => {
|
|
59
|
+
for (const f of fs.readdirSync(p)) {
|
|
60
|
+
if (f === ".skill-source.json") continue;
|
|
61
|
+
const full = path.join(p, f);
|
|
62
|
+
const stat = fs.statSync(full);
|
|
63
|
+
if (stat.isDirectory()) walk(full);
|
|
64
|
+
else {
|
|
65
|
+
const h = crypto.createHash("sha256").update(fs.readFileSync(full)).digest("hex");
|
|
66
|
+
files.push(`${path.relative(dir, full)}:${h}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
walk(dir);
|
|
71
|
+
files.sort();
|
|
72
|
+
return crypto.createHash("sha256").update(files.join("|")).digest("hex");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse skill spec string
|
|
77
|
+
* @param {string} spec - Spec like org/repo#skill@ref
|
|
78
|
+
* @returns {import('./types.js').ParsedSpec}
|
|
79
|
+
*/
|
|
80
|
+
export function parseSkillSpec(spec) {
|
|
81
|
+
const [repoPart, skillPart] = spec.split("#");
|
|
82
|
+
const [org, repo] = repoPart.split("/");
|
|
83
|
+
const [skill, ref] = (skillPart || "").split("@");
|
|
84
|
+
return { org, repo, skill, ref };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve scope based on flags and cwd
|
|
89
|
+
* @returns {string} Skills directory path
|
|
90
|
+
*/
|
|
91
|
+
export function resolveScope() {
|
|
92
|
+
if (GLOBAL) return GLOBAL_DIR;
|
|
93
|
+
if (fs.existsSync(path.join(cwd, ".agent"))) return WORKSPACE;
|
|
94
|
+
return GLOBAL_DIR;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create backup of skill directory
|
|
99
|
+
* @param {string} skillDir - Source directory
|
|
100
|
+
* @param {string} skillName - Skill name for backup naming
|
|
101
|
+
* @returns {string|null} Backup path or null if dry run
|
|
102
|
+
*/
|
|
103
|
+
export function createBackup(skillDir, skillName) {
|
|
104
|
+
if (DRY) return null;
|
|
105
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
106
|
+
const bp = path.join(BACKUP_DIR, `${skillName}_${ts}`);
|
|
107
|
+
fs.mkdirSync(BACKUP_DIR, { recursive: true });
|
|
108
|
+
fs.cpSync(skillDir, bp, { recursive: true });
|
|
109
|
+
return bp;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* List backups for a skill
|
|
114
|
+
* @param {string} [skillName] - Filter by skill name
|
|
115
|
+
* @returns {import('./types.js').Backup[]}
|
|
116
|
+
*/
|
|
117
|
+
export function listBackups(skillName = null) {
|
|
118
|
+
if (!fs.existsSync(BACKUP_DIR)) return [];
|
|
119
|
+
const backups = [];
|
|
120
|
+
for (const name of fs.readdirSync(BACKUP_DIR)) {
|
|
121
|
+
if (skillName && !name.startsWith(skillName + "_")) continue;
|
|
122
|
+
const bp = path.join(BACKUP_DIR, name);
|
|
123
|
+
backups.push({ name, path: bp, createdAt: fs.statSync(bp).mtime, size: getDirSize(bp) });
|
|
124
|
+
}
|
|
125
|
+
return backups.sort((a, b) => b.createdAt - a.createdAt);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Load skill lock file
|
|
130
|
+
* @returns {import('./types.js').SkillLock}
|
|
131
|
+
*/
|
|
132
|
+
export function loadSkillLock() {
|
|
133
|
+
const f = path.join(cwd, ".agent", "skill-lock.json");
|
|
134
|
+
if (!fs.existsSync(f)) throw new Error("skill-lock.json not found");
|
|
135
|
+
return JSON.parse(fs.readFileSync(f, "utf-8"));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Load registries list
|
|
140
|
+
* @returns {string[]}
|
|
141
|
+
*/
|
|
142
|
+
export function loadRegistries() {
|
|
143
|
+
try { return fs.existsSync(REGISTRIES_FILE) ? JSON.parse(fs.readFileSync(REGISTRIES_FILE, "utf-8")) : []; }
|
|
144
|
+
catch { return []; }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Save registries list
|
|
149
|
+
* @param {string[]} regs
|
|
150
|
+
*/
|
|
151
|
+
export function saveRegistries(regs) {
|
|
152
|
+
fs.mkdirSync(path.dirname(REGISTRIES_FILE), { recursive: true });
|
|
153
|
+
fs.writeFileSync(REGISTRIES_FILE, JSON.stringify(regs, null, 2));
|
|
154
|
+
}
|
|
155
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Skill detection and parsing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { resolveScope } from "./helpers.js";
|
|
8
|
+
import { getDirSize } from "./helpers.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse SKILL.md YAML frontmatter
|
|
12
|
+
* @param {string} p - Path to SKILL.md
|
|
13
|
+
* @returns {import('./types.js').SkillMeta}
|
|
14
|
+
*/
|
|
15
|
+
export function parseSkillMdFrontmatter(p) {
|
|
16
|
+
try {
|
|
17
|
+
const content = fs.readFileSync(p, "utf-8");
|
|
18
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
19
|
+
if (!match) return {};
|
|
20
|
+
|
|
21
|
+
/** @type {import('./types.js').SkillMeta} */
|
|
22
|
+
const meta = {};
|
|
23
|
+
|
|
24
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
25
|
+
const i = line.indexOf(":");
|
|
26
|
+
if (i === -1) continue;
|
|
27
|
+
const key = line.substring(0, i).trim();
|
|
28
|
+
const val = line.substring(i + 1).trim();
|
|
29
|
+
if (key === "tags") meta.tags = val.split(",").map(t => t.trim()).filter(Boolean);
|
|
30
|
+
else if (key && val) meta[key] = val;
|
|
31
|
+
}
|
|
32
|
+
return meta;
|
|
33
|
+
} catch { return {}; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Detect skill directory structure
|
|
38
|
+
* @param {string} dir - Skill directory
|
|
39
|
+
* @returns {import('./types.js').SkillStructure}
|
|
40
|
+
*/
|
|
41
|
+
export function detectSkillStructure(dir) {
|
|
42
|
+
/** @type {import('./types.js').SkillStructure} */
|
|
43
|
+
const s = {
|
|
44
|
+
hasResources: false,
|
|
45
|
+
hasExamples: false,
|
|
46
|
+
hasScripts: false,
|
|
47
|
+
hasConstitution: false,
|
|
48
|
+
hasDoctrines: false,
|
|
49
|
+
hasEnforcement: false,
|
|
50
|
+
hasProposals: false,
|
|
51
|
+
directories: [],
|
|
52
|
+
files: []
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
for (const item of fs.readdirSync(dir)) {
|
|
57
|
+
const full = path.join(dir, item);
|
|
58
|
+
if (fs.statSync(full).isDirectory()) {
|
|
59
|
+
s.directories.push(item);
|
|
60
|
+
const l = item.toLowerCase();
|
|
61
|
+
if (l === "resources") s.hasResources = true;
|
|
62
|
+
if (l === "examples") s.hasExamples = true;
|
|
63
|
+
if (l === "scripts") s.hasScripts = true;
|
|
64
|
+
if (l === "constitution") s.hasConstitution = true;
|
|
65
|
+
if (l === "doctrines") s.hasDoctrines = true;
|
|
66
|
+
if (l === "enforcement") s.hasEnforcement = true;
|
|
67
|
+
if (l === "proposals") s.hasProposals = true;
|
|
68
|
+
} else {
|
|
69
|
+
s.files.push(item);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch { }
|
|
73
|
+
return s;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get all installed skills
|
|
78
|
+
* @returns {import('./types.js').Skill[]}
|
|
79
|
+
*/
|
|
80
|
+
export function getInstalledSkills() {
|
|
81
|
+
const scope = resolveScope();
|
|
82
|
+
/** @type {import('./types.js').Skill[]} */
|
|
83
|
+
const skills = [];
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(scope)) return skills;
|
|
86
|
+
|
|
87
|
+
for (const name of fs.readdirSync(scope)) {
|
|
88
|
+
const dir = path.join(scope, name);
|
|
89
|
+
if (!fs.statSync(dir).isDirectory()) continue;
|
|
90
|
+
|
|
91
|
+
const metaFile = path.join(dir, ".skill-source.json");
|
|
92
|
+
const skillFile = path.join(dir, "SKILL.md");
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(metaFile) || fs.existsSync(skillFile)) {
|
|
95
|
+
const meta = fs.existsSync(metaFile) ? JSON.parse(fs.readFileSync(metaFile, "utf-8")) : {};
|
|
96
|
+
const hasSkillMd = fs.existsSync(skillFile);
|
|
97
|
+
const skillMeta = hasSkillMd ? parseSkillMdFrontmatter(skillFile) : {};
|
|
98
|
+
|
|
99
|
+
skills.push({
|
|
100
|
+
name,
|
|
101
|
+
path: dir,
|
|
102
|
+
...meta,
|
|
103
|
+
hasSkillMd,
|
|
104
|
+
description: skillMeta.description || meta.description || "",
|
|
105
|
+
tags: skillMeta.tags || [],
|
|
106
|
+
author: skillMeta.author || meta.publisher || "",
|
|
107
|
+
version: skillMeta.version || meta.ref || "unknown",
|
|
108
|
+
structure: detectSkillStructure(dir),
|
|
109
|
+
size: getDirSize(dir)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return skills;
|
|
114
|
+
}
|
package/bin/lib/types.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview JSDoc Type Definitions for add-skill CLI
|
|
3
|
+
* Provides IDE autocomplete support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} SkillStructure
|
|
8
|
+
* @property {boolean} hasResources
|
|
9
|
+
* @property {boolean} hasExamples
|
|
10
|
+
* @property {boolean} hasScripts
|
|
11
|
+
* @property {boolean} hasConstitution
|
|
12
|
+
* @property {boolean} hasDoctrines
|
|
13
|
+
* @property {boolean} hasEnforcement
|
|
14
|
+
* @property {boolean} hasProposals
|
|
15
|
+
* @property {string[]} directories
|
|
16
|
+
* @property {string[]} files
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} Skill
|
|
21
|
+
* @property {string} name - Skill folder name
|
|
22
|
+
* @property {string} path - Absolute path to skill
|
|
23
|
+
* @property {boolean} hasSkillMd - Has SKILL.md file
|
|
24
|
+
* @property {string} description - From SKILL.md frontmatter
|
|
25
|
+
* @property {string[]} tags - From SKILL.md frontmatter
|
|
26
|
+
* @property {string} author - Author or publisher
|
|
27
|
+
* @property {string} version - Version or ref
|
|
28
|
+
* @property {SkillStructure} structure - Directory structure
|
|
29
|
+
* @property {number} size - Total size in bytes
|
|
30
|
+
* @property {string} [repo] - Source repository
|
|
31
|
+
* @property {string} [skill] - Skill name in repo
|
|
32
|
+
* @property {string} [ref] - Git ref
|
|
33
|
+
* @property {string} [checksum] - Merkle hash
|
|
34
|
+
* @property {string} [installedAt] - ISO timestamp
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {Object} SkillMeta
|
|
39
|
+
* @property {string} [name]
|
|
40
|
+
* @property {string} [description]
|
|
41
|
+
* @property {string} [version]
|
|
42
|
+
* @property {string} [author]
|
|
43
|
+
* @property {string[]} [tags]
|
|
44
|
+
* @property {string} [type]
|
|
45
|
+
* @property {string} [authority]
|
|
46
|
+
* @property {string} [parent]
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {Object} ParsedSpec
|
|
51
|
+
* @property {string} org - GitHub org
|
|
52
|
+
* @property {string} repo - GitHub repo
|
|
53
|
+
* @property {string} [skill] - Skill name
|
|
54
|
+
* @property {string} [ref] - Git ref
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {Object} Backup
|
|
59
|
+
* @property {string} name
|
|
60
|
+
* @property {string} path
|
|
61
|
+
* @property {Date} createdAt
|
|
62
|
+
* @property {number} size
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @typedef {Object} SkillLock
|
|
67
|
+
* @property {number} lockVersion
|
|
68
|
+
* @property {string} generatedAt
|
|
69
|
+
* @property {string} generator
|
|
70
|
+
* @property {Object.<string, SkillLockEntry>} skills
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {Object} SkillLockEntry
|
|
75
|
+
* @property {string} repo
|
|
76
|
+
* @property {string} skill
|
|
77
|
+
* @property {string} ref
|
|
78
|
+
* @property {string} checksum
|
|
79
|
+
* @property {string} [publisher]
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
export { };
|
package/bin/lib/ui.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview UI components - Vercel-style theme
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import kleur from "kleur";
|
|
6
|
+
|
|
7
|
+
// --- Symbols ---
|
|
8
|
+
|
|
9
|
+
/** UI symbols for tree structure */
|
|
10
|
+
export const S = {
|
|
11
|
+
branch: "│",
|
|
12
|
+
diamond: "◇",
|
|
13
|
+
diamondFilled: "◆",
|
|
14
|
+
check: "✓",
|
|
15
|
+
cross: "x",
|
|
16
|
+
arrow: "→"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// --- Colors ---
|
|
20
|
+
|
|
21
|
+
/** Color helper functions */
|
|
22
|
+
export const c = {
|
|
23
|
+
cyan: kleur.cyan,
|
|
24
|
+
gray: kleur.gray,
|
|
25
|
+
green: kleur.green,
|
|
26
|
+
red: kleur.red,
|
|
27
|
+
yellow: kleur.yellow,
|
|
28
|
+
magenta: kleur.magenta,
|
|
29
|
+
bold: kleur.bold,
|
|
30
|
+
dim: kleur.dim
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// --- UI Functions ---
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Print a step in the tree
|
|
37
|
+
* @param {string} text - Step text
|
|
38
|
+
* @param {string} [icon] - Icon to use
|
|
39
|
+
* @param {keyof typeof c} [color] - Color name
|
|
40
|
+
*/
|
|
41
|
+
export function step(text, icon = S.diamond, color = "gray") {
|
|
42
|
+
const colorFn = c[color] || c.gray;
|
|
43
|
+
console.log(` ${c.gray(S.branch)} ${colorFn(icon)} ${text}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Print empty branch line
|
|
48
|
+
*/
|
|
49
|
+
export function stepLine() {
|
|
50
|
+
console.log(` ${c.gray(S.branch)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Print fatal error and exit
|
|
55
|
+
* @param {string} msg - Error message
|
|
56
|
+
*/
|
|
57
|
+
export function fatal(msg) {
|
|
58
|
+
console.log(` ${c.gray(S.branch)} ${c.red(S.cross)} ${c.red(msg)}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Print success message
|
|
64
|
+
* @param {string} msg - Success message
|
|
65
|
+
*/
|
|
66
|
+
export function success(msg) {
|
|
67
|
+
console.log(` ${c.gray(S.branch)} ${c.green(S.check)} ${c.green(msg)}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Output JSON if JSON_OUTPUT mode
|
|
72
|
+
* @param {any} data - Data to output
|
|
73
|
+
* @param {boolean} jsonMode - Whether to output JSON
|
|
74
|
+
*/
|
|
75
|
+
export function outputJSON(data, jsonMode) {
|
|
76
|
+
if (jsonMode) console.log(JSON.stringify(data, null, 2));
|
|
77
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "install-agent-skill",
|
|
3
|
+
"version": "1.2.3",
|
|
4
|
+
"description": "Enterprise-grade Agent Skill Manager with Antigravity Skills support, Progressive Disclosure detection, and semantic routing validation",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "DataGuruIn <contact@dataguruin.com>",
|
|
7
|
+
"homepage": "https://github.com/dataguruin/add-skill",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/dataguruin/add-skill.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/dataguruin/add-skill/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"add-skill": "./bin/add-skill.v2.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin/",
|
|
21
|
+
"specs/",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"agent",
|
|
30
|
+
"ai",
|
|
31
|
+
"skills",
|
|
32
|
+
"cli",
|
|
33
|
+
"tooling",
|
|
34
|
+
"registry",
|
|
35
|
+
"security",
|
|
36
|
+
"devops",
|
|
37
|
+
"automation",
|
|
38
|
+
"antigravity"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"lint": "echo \"No lint configured\"",
|
|
42
|
+
"test": "echo \"No tests yet\"",
|
|
43
|
+
"ci": "node bin/add-skill.js verify --strict && node bin/add-skill.js doctor --strict"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@clack/prompts": "^0.9.1",
|
|
50
|
+
"boxen": "^8.0.1",
|
|
51
|
+
"chalk": "^5.4.1",
|
|
52
|
+
"kleur": "^4.1.5",
|
|
53
|
+
"ora": "^9.1.0",
|
|
54
|
+
"prompts": "^2.4.2"
|
|
55
|
+
}
|
|
56
|
+
}
|