@agentskillkit/agent-skills 3.2.2 → 3.2.5
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/.agent/skills/mobile-design/scripts/mobile_audit.js +333 -0
- package/.agent/skills/typescript-expert/scripts/ts_diagnostic.js +227 -0
- package/README.md +197 -720
- package/package.json +4 -4
- package/packages/cli/lib/audit.js +2 -2
- package/packages/cli/lib/auto-learn.js +8 -8
- package/packages/cli/lib/eslint-fix.js +1 -1
- package/packages/cli/lib/fix.js +5 -5
- package/packages/cli/lib/hooks/install-hooks.js +4 -4
- package/packages/cli/lib/hooks/lint-learn.js +4 -4
- package/packages/cli/lib/knowledge-index.js +4 -4
- package/packages/cli/lib/knowledge-metrics.js +2 -2
- package/packages/cli/lib/knowledge-retention.js +3 -3
- package/packages/cli/lib/knowledge-validator.js +3 -3
- package/packages/cli/lib/learn.js +10 -10
- package/packages/cli/lib/recall.js +1 -1
- package/packages/cli/lib/skill-learn.js +2 -2
- package/packages/cli/lib/stats.js +3 -3
- package/packages/cli/lib/ui/dashboard-ui.js +222 -0
- package/packages/cli/lib/ui/help-ui.js +41 -18
- package/packages/cli/lib/ui/index.js +57 -5
- package/packages/cli/lib/ui/settings-ui.js +292 -14
- package/packages/cli/lib/ui/stats-ui.js +93 -43
- package/packages/cli/lib/watcher.js +2 -2
- package/packages/kit/kit.js +89 -0
- package/packages/kit/lib/agents.js +208 -0
- package/packages/kit/lib/commands/analyze.js +70 -0
- package/packages/kit/lib/commands/cache.js +65 -0
- package/packages/kit/lib/commands/doctor.js +75 -0
- package/packages/kit/lib/commands/help.js +155 -0
- package/packages/kit/lib/commands/info.js +38 -0
- package/packages/kit/lib/commands/init.js +39 -0
- package/packages/kit/lib/commands/install.js +803 -0
- package/packages/kit/lib/commands/list.js +43 -0
- package/packages/kit/lib/commands/lock.js +57 -0
- package/packages/kit/lib/commands/uninstall.js +307 -0
- package/packages/kit/lib/commands/update.js +55 -0
- package/packages/kit/lib/commands/validate.js +69 -0
- package/packages/kit/lib/commands/verify.js +56 -0
- package/packages/kit/lib/config.js +81 -0
- package/packages/kit/lib/helpers.js +196 -0
- package/packages/kit/lib/helpers.test.js +60 -0
- package/packages/kit/lib/installer.js +164 -0
- package/packages/kit/lib/skills.js +119 -0
- package/packages/kit/lib/skills.test.js +109 -0
- package/packages/kit/lib/types.js +82 -0
- package/packages/kit/lib/ui.js +329 -0
- package/.agent/skills/mobile-design/scripts/mobile_audit.py +0 -670
- package/.agent/skills/requirements-python.txt +0 -25
- package/.agent/skills/requirements.txt +0 -96
- package/.agent/skills/typescript-expert/scripts/ts_diagnostic.py +0 -203
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview List command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getInstalledSkills } from "../skills.js";
|
|
6
|
+
import { resolveScope, formatBytes } from "../helpers.js";
|
|
7
|
+
import { step, stepLine, S, c, outputJSON } from "../ui.js";
|
|
8
|
+
import { VERBOSE, JSON_OUTPUT } from "../config.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List installed skills
|
|
12
|
+
*/
|
|
13
|
+
export async function run() {
|
|
14
|
+
stepLine();
|
|
15
|
+
step(c.bold("Installed Skills"), S.diamondFilled, "cyan");
|
|
16
|
+
console.log(`${c.gray(S.branch)} ${c.dim("Location: " + resolveScope())}`);
|
|
17
|
+
stepLine();
|
|
18
|
+
|
|
19
|
+
const skills = getInstalledSkills();
|
|
20
|
+
|
|
21
|
+
if (skills.length === 0) {
|
|
22
|
+
step(c.dim("No skills installed."), S.diamond);
|
|
23
|
+
stepLine();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (JSON_OUTPUT) {
|
|
28
|
+
outputJSON({ skills }, true);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const s of skills) {
|
|
33
|
+
const icon = s.hasSkillMd ? c.green(S.check) : c.yellow(S.diamond);
|
|
34
|
+
console.log(`${c.gray(S.branch)} ${icon} ${c.bold(s.name)} ${c.dim("v" + s.version)} ${c.dim("(" + formatBytes(s.size) + ")")}`);
|
|
35
|
+
if (s.description && VERBOSE) {
|
|
36
|
+
console.log(`${c.gray(S.branch)} ${c.dim(s.description.substring(0, 60))}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
stepLine();
|
|
41
|
+
console.log(`${c.gray(S.branch)} ${c.dim("Total: " + skills.length + " skill(s)")}`);
|
|
42
|
+
stepLine();
|
|
43
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Lock command - Generate skill-lock.json
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { step, stepLine, success, fatal, outputJSON } from "../ui.js";
|
|
8
|
+
import { WORKSPACE, DRY, cwd, VERSION } from "../config.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate skill-lock.json
|
|
12
|
+
*/
|
|
13
|
+
export async function run() {
|
|
14
|
+
if (!fs.existsSync(WORKSPACE)) {
|
|
15
|
+
fatal("No .agent/skills directory");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
stepLine();
|
|
20
|
+
|
|
21
|
+
const skills = {};
|
|
22
|
+
for (const name of fs.readdirSync(WORKSPACE)) {
|
|
23
|
+
const dir = path.join(WORKSPACE, name);
|
|
24
|
+
if (!fs.statSync(dir).isDirectory()) continue;
|
|
25
|
+
|
|
26
|
+
const mf = path.join(dir, ".skill-source.json");
|
|
27
|
+
if (!fs.existsSync(mf)) continue;
|
|
28
|
+
|
|
29
|
+
const m = JSON.parse(fs.readFileSync(mf, "utf-8"));
|
|
30
|
+
skills[name] = {
|
|
31
|
+
repo: m.repo,
|
|
32
|
+
skill: m.skill,
|
|
33
|
+
ref: m.ref,
|
|
34
|
+
checksum: `sha256:${m.checksum}`,
|
|
35
|
+
publisher: m.publisher || null
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const lock = {
|
|
40
|
+
lockVersion: 1,
|
|
41
|
+
generatedAt: new Date().toISOString(),
|
|
42
|
+
generator: `@dataguruin/add-skill@${VERSION}`,
|
|
43
|
+
skills
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (DRY) {
|
|
47
|
+
step("Would generate skill-lock.json");
|
|
48
|
+
outputJSON(lock, true);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fs.mkdirSync(path.join(cwd, ".agent"), { recursive: true });
|
|
53
|
+
fs.writeFileSync(path.join(cwd, ".agent", "skill-lock.json"), JSON.stringify(lock, null, 2));
|
|
54
|
+
|
|
55
|
+
success("skill-lock.json generated");
|
|
56
|
+
stepLine();
|
|
57
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Uninstall command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import os from "os";
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
import { resolveScope, createBackup } from "../helpers.js";
|
|
10
|
+
import { step, stepLine, success, fatal, c, select, isCancel, cancel } from "../ui.js";
|
|
11
|
+
import { DRY } from "../config.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Remove a skill or all skills
|
|
15
|
+
* @param {string} skillName - Skill name or "all" to remove everything
|
|
16
|
+
*/
|
|
17
|
+
export async function run(skillName) {
|
|
18
|
+
const scope = resolveScope();
|
|
19
|
+
|
|
20
|
+
// Check if skills directory exists
|
|
21
|
+
if (!fs.existsSync(scope)) {
|
|
22
|
+
fatal("No skills directory found");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Get list of installed skills
|
|
26
|
+
const skills = fs.readdirSync(scope)
|
|
27
|
+
.filter(item => {
|
|
28
|
+
const itemPath = path.join(scope, item);
|
|
29
|
+
return fs.statSync(itemPath).isDirectory() && fs.existsSync(path.join(itemPath, "SKILL.md"));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (skills.length === 0) {
|
|
33
|
+
step("No skills installed");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// CASE 1: kit uninstall all → Auto-remove everything
|
|
38
|
+
if (skillName === "all") {
|
|
39
|
+
await removeAllAutomatic(scope, skills);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// CASE 2: kit uninstall (no params) → Interactive menu
|
|
44
|
+
if (!skillName) {
|
|
45
|
+
await interactiveRemove(scope, skills);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// CASE 3: kit uninstall <skill> → Remove specific skill
|
|
50
|
+
await removeSingleSkill(scope, skillName);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Interactive menu for skill removal
|
|
55
|
+
*/
|
|
56
|
+
async function interactiveRemove(scope, skills) {
|
|
57
|
+
stepLine();
|
|
58
|
+
step(`Found ${skills.length} installed skill(s)`);
|
|
59
|
+
stepLine();
|
|
60
|
+
|
|
61
|
+
const choice = await select({
|
|
62
|
+
message: "What would you like to do?",
|
|
63
|
+
options: [
|
|
64
|
+
{ value: "select", label: "Select specific skill", hint: "Choose one skill to remove" },
|
|
65
|
+
{ value: "all", label: "Remove all skills", hint: "Remove everything (with confirmations)" },
|
|
66
|
+
{ value: "cancel", label: "Cancel", hint: "Go back" }
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (isCancel(choice) || choice === "cancel") {
|
|
71
|
+
cancel("Cancelled");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (choice === "all") {
|
|
76
|
+
await removeAllWithConfirmation(scope, skills);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Select specific skill
|
|
81
|
+
const skillChoice = await select({
|
|
82
|
+
message: "Select skill to remove",
|
|
83
|
+
options: skills.map(s => ({ value: s, label: s }))
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (isCancel(skillChoice)) {
|
|
87
|
+
cancel("Cancelled");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await removeSingleSkill(scope, skillChoice);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Remove all skills automatically (kit uninstall all)
|
|
96
|
+
*/
|
|
97
|
+
async function removeAllAutomatic(scope, skills) {
|
|
98
|
+
stepLine();
|
|
99
|
+
step(c.yellow("AUTOMATIC COMPLETE REMOVAL"));
|
|
100
|
+
step(c.dim(`Removing ${skills.length} skill(s) + .agent folder + npm dependencies`));
|
|
101
|
+
stepLine();
|
|
102
|
+
|
|
103
|
+
if (DRY) {
|
|
104
|
+
step(`Would remove ${skills.length} skill(s) from: ${scope}`);
|
|
105
|
+
step("Would remove .agent folder");
|
|
106
|
+
step("Would remove npm dependencies");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 1. Remove all skills with backup
|
|
111
|
+
for (const skill of skills) {
|
|
112
|
+
const targetDir = path.join(scope, skill);
|
|
113
|
+
createBackup(targetDir, skill);
|
|
114
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
115
|
+
step(`✓ Removed: ${skill}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 2. Remove .agent folder
|
|
119
|
+
const agentDir = path.dirname(scope);
|
|
120
|
+
if (fs.existsSync(agentDir) && path.basename(agentDir) === ".agent") {
|
|
121
|
+
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
122
|
+
step("✓ Removed: .agent folder");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 3. Remove npm dependencies
|
|
126
|
+
const cwd = process.cwd();
|
|
127
|
+
const nodeModules = path.join(cwd, "node_modules");
|
|
128
|
+
const packageJson = path.join(cwd, "package.json");
|
|
129
|
+
const packageLock = path.join(cwd, "package-lock.json");
|
|
130
|
+
|
|
131
|
+
if (fs.existsSync(nodeModules)) {
|
|
132
|
+
fs.rmSync(nodeModules, { recursive: true, force: true });
|
|
133
|
+
step("✓ Removed: node_modules/");
|
|
134
|
+
}
|
|
135
|
+
if (fs.existsSync(packageJson)) {
|
|
136
|
+
fs.rmSync(packageJson);
|
|
137
|
+
step("✓ Removed: package.json");
|
|
138
|
+
}
|
|
139
|
+
if (fs.existsSync(packageLock)) {
|
|
140
|
+
fs.rmSync(packageLock);
|
|
141
|
+
step("✓ Removed: package-lock.json");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 4. For global installs, ask about ~/.gemini/ cleanup
|
|
145
|
+
if (scope.includes(".gemini")) {
|
|
146
|
+
stepLine();
|
|
147
|
+
step(c.yellow("Global Configuration Cleanup"));
|
|
148
|
+
step(c.dim("The entire ~/.gemini/ folder can be removed:"));
|
|
149
|
+
step(c.dim(" • GEMINI.md (global rules)"));
|
|
150
|
+
step(c.dim(" • All Antigravity configs"));
|
|
151
|
+
step(c.dim(" • Cache and backups"));
|
|
152
|
+
stepLine();
|
|
153
|
+
|
|
154
|
+
const geminiRoot = path.join(require("os").homedir(), ".gemini");
|
|
155
|
+
if (fs.existsSync(geminiRoot)) {
|
|
156
|
+
// Just remove it automatically in "all" mode
|
|
157
|
+
fs.rmSync(geminiRoot, { recursive: true, force: true });
|
|
158
|
+
step("✓ Removed: ~/.gemini/ (complete cleanup)");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
stepLine();
|
|
163
|
+
success("Complete cleanup done - everything removed");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Remove all skills with step-by-step confirmations (kit uninstall → all)
|
|
168
|
+
*/
|
|
169
|
+
async function removeAllWithConfirmation(scope, skills) {
|
|
170
|
+
stepLine();
|
|
171
|
+
step(`Found ${skills.length} skill(s) to remove:`);
|
|
172
|
+
skills.forEach(s => step(` • ${s}`, "", "dim"));
|
|
173
|
+
stepLine();
|
|
174
|
+
|
|
175
|
+
const confirmSkills = await prompts({
|
|
176
|
+
type: "confirm",
|
|
177
|
+
name: "value",
|
|
178
|
+
message: `Remove all ${skills.length} skill(s)?`,
|
|
179
|
+
initial: false
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (!confirmSkills.value) {
|
|
183
|
+
step("Cancelled");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Remove each skill with backup
|
|
188
|
+
for (const skill of skills) {
|
|
189
|
+
const targetDir = path.join(scope, skill);
|
|
190
|
+
createBackup(targetDir, skill);
|
|
191
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
192
|
+
step(`Removed: ${skill}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
success(`All ${skills.length} skill(s) removed successfully`);
|
|
196
|
+
stepLine();
|
|
197
|
+
|
|
198
|
+
// Ask about .agent folder
|
|
199
|
+
const agentDir = path.dirname(scope);
|
|
200
|
+
if (fs.existsSync(agentDir) && path.basename(agentDir) === ".agent") {
|
|
201
|
+
stepLine();
|
|
202
|
+
step(c.yellow("Complete Cleanup"));
|
|
203
|
+
step(c.dim("The following will also be removed:"));
|
|
204
|
+
step(c.dim(" • Agents"));
|
|
205
|
+
step(c.dim(" • Workflows"));
|
|
206
|
+
step(c.dim(" • Knowledge & Lessons"));
|
|
207
|
+
step(c.dim(" • All configuration files"));
|
|
208
|
+
stepLine();
|
|
209
|
+
|
|
210
|
+
const confirmAgent = await prompts({
|
|
211
|
+
type: "confirm",
|
|
212
|
+
name: "value",
|
|
213
|
+
message: "Remove entire .agent folder?",
|
|
214
|
+
initial: false
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (confirmAgent.value) {
|
|
218
|
+
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
219
|
+
success("Complete cleanup done - .agent folder removed");
|
|
220
|
+
|
|
221
|
+
// Ask about npm dependencies
|
|
222
|
+
stepLine();
|
|
223
|
+
step(c.yellow("npm Dependencies Cleanup"));
|
|
224
|
+
step(c.dim("The following npm files will also be removed:"));
|
|
225
|
+
step(c.dim(" • node_modules/"));
|
|
226
|
+
step(c.dim(" • package.json"));
|
|
227
|
+
step(c.dim(" • package-lock.json"));
|
|
228
|
+
stepLine();
|
|
229
|
+
|
|
230
|
+
const confirmNpm = await prompts({
|
|
231
|
+
type: "confirm",
|
|
232
|
+
name: "value",
|
|
233
|
+
message: "Remove npm dependencies?",
|
|
234
|
+
initial: false
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (confirmNpm.value) {
|
|
238
|
+
const cwd = process.cwd();
|
|
239
|
+
const nodeModules = path.join(cwd, "node_modules");
|
|
240
|
+
const packageJson = path.join(cwd, "package.json");
|
|
241
|
+
const packageLock = path.join(cwd, "package-lock.json");
|
|
242
|
+
|
|
243
|
+
if (fs.existsSync(nodeModules)) {
|
|
244
|
+
fs.rmSync(nodeModules, { recursive: true, force: true });
|
|
245
|
+
step("Removed: node_modules/");
|
|
246
|
+
}
|
|
247
|
+
if (fs.existsSync(packageJson)) {
|
|
248
|
+
fs.rmSync(packageJson);
|
|
249
|
+
step("Removed: package.json");
|
|
250
|
+
}
|
|
251
|
+
if (fs.existsSync(packageLock)) {
|
|
252
|
+
fs.rmSync(packageLock);
|
|
253
|
+
step("Removed: package-lock.json");
|
|
254
|
+
}
|
|
255
|
+
success("npm dependencies removed");
|
|
256
|
+
} else {
|
|
257
|
+
step(c.dim("Kept npm dependencies"));
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
step(c.dim("Kept .agent folder (agents, workflows, etc.)"));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
stepLine();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Remove a single skill
|
|
269
|
+
*/
|
|
270
|
+
async function removeSingleSkill(scope, skillName) {
|
|
271
|
+
const targetDir = path.join(scope, skillName);
|
|
272
|
+
|
|
273
|
+
if (!fs.existsSync(targetDir)) {
|
|
274
|
+
fatal(`Skill not found: ${skillName}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!fs.existsSync(path.join(targetDir, "SKILL.md"))) {
|
|
278
|
+
fatal(`Not a valid skill: ${skillName}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
stepLine();
|
|
282
|
+
step(`Removing skill: ${c.cyan(skillName)}`);
|
|
283
|
+
|
|
284
|
+
const confirm = await prompts({
|
|
285
|
+
type: "confirm",
|
|
286
|
+
name: "value",
|
|
287
|
+
message: "Confirm removal?",
|
|
288
|
+
initial: false
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!confirm.value) {
|
|
292
|
+
step("Cancelled");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (DRY) {
|
|
297
|
+
step(`Would remove: ${targetDir}`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const backup = createBackup(targetDir, skillName);
|
|
302
|
+
if (backup) step(`Backup created: ${backup}`);
|
|
303
|
+
|
|
304
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
305
|
+
success(`Removed: ${skillName}`);
|
|
306
|
+
stepLine();
|
|
307
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Update command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
import { resolveScope, createBackup } from "../helpers.js";
|
|
9
|
+
import { step, stepLine, S, c, fatal, spinner } from "../ui.js";
|
|
10
|
+
import { DRY } from "../config.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Update a skill
|
|
14
|
+
* @param {string} skillName
|
|
15
|
+
*/
|
|
16
|
+
export async function run(skillName) {
|
|
17
|
+
if (!skillName) fatal("Missing skill name");
|
|
18
|
+
|
|
19
|
+
const scope = resolveScope();
|
|
20
|
+
const targetDir = path.join(scope, skillName);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(targetDir)) fatal(`Skill not found: ${skillName}`);
|
|
23
|
+
|
|
24
|
+
const metaFile = path.join(targetDir, ".skill-source.json");
|
|
25
|
+
if (!fs.existsSync(metaFile)) fatal("Skill metadata not found");
|
|
26
|
+
|
|
27
|
+
const meta = JSON.parse(fs.readFileSync(metaFile, "utf-8"));
|
|
28
|
+
if (!meta.repo || meta.repo === "local") fatal("Cannot update local skill");
|
|
29
|
+
|
|
30
|
+
stepLine();
|
|
31
|
+
|
|
32
|
+
const s = spinner();
|
|
33
|
+
s.start(`Updating ${skillName}`);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
if (!DRY) {
|
|
37
|
+
createBackup(targetDir, skillName);
|
|
38
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const spec = `${meta.repo}#${meta.skill}${meta.ref ? "@" + meta.ref : ""}`;
|
|
42
|
+
|
|
43
|
+
if (DRY) {
|
|
44
|
+
s.stop("Dry run analysis complete");
|
|
45
|
+
step(`Would update: ${skillName}`);
|
|
46
|
+
} else {
|
|
47
|
+
s.stop("Preparing update...");
|
|
48
|
+
// Dynamically import install command
|
|
49
|
+
const { run: install } = await import("./install.js");
|
|
50
|
+
await install(spec);
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
s.fail(`Failed: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Validate command - Antigravity spec validation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { getInstalledSkills, parseSkillMdFrontmatter } from "../skills.js";
|
|
8
|
+
import { resolveScope } from "../helpers.js";
|
|
9
|
+
import { step, stepLine, S, c, fatal } from "../ui.js";
|
|
10
|
+
import { VERBOSE, STRICT } from "../config.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate skills against Antigravity spec
|
|
14
|
+
* @param {string} [skillName] - Optional specific skill
|
|
15
|
+
*/
|
|
16
|
+
export async function run(skillName) {
|
|
17
|
+
const scope = resolveScope();
|
|
18
|
+
let skillsToValidate = [];
|
|
19
|
+
|
|
20
|
+
if (skillName) {
|
|
21
|
+
const sd = path.join(scope, skillName);
|
|
22
|
+
if (!fs.existsSync(sd)) fatal(`Skill not found: ${skillName}`);
|
|
23
|
+
skillsToValidate = [{ name: skillName, path: sd }];
|
|
24
|
+
} else {
|
|
25
|
+
skillsToValidate = getInstalledSkills();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (skillsToValidate.length === 0) {
|
|
29
|
+
stepLine();
|
|
30
|
+
step("No skills to validate", S.diamond);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
stepLine();
|
|
35
|
+
step(c.bold("Antigravity Validation"), S.diamondFilled, "cyan");
|
|
36
|
+
stepLine();
|
|
37
|
+
|
|
38
|
+
let totalErrors = 0, totalWarnings = 0;
|
|
39
|
+
|
|
40
|
+
for (const skill of skillsToValidate) {
|
|
41
|
+
const errors = [], warnings = [];
|
|
42
|
+
const smp = path.join(skill.path, "SKILL.md");
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(smp)) {
|
|
45
|
+
errors.push("Missing SKILL.md");
|
|
46
|
+
} else {
|
|
47
|
+
const m = parseSkillMdFrontmatter(smp);
|
|
48
|
+
if (!m.description) errors.push("Missing description");
|
|
49
|
+
else if (m.description.length < 50) warnings.push("Description too short");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const status = errors.length > 0 ? c.red("FAIL") : warnings.length > 0 ? c.yellow("WARN") : c.green("PASS");
|
|
53
|
+
console.log(`${c.gray(S.branch)} ${status} ${c.bold(skill.name)}`);
|
|
54
|
+
|
|
55
|
+
if (VERBOSE || errors.length || warnings.length) {
|
|
56
|
+
errors.forEach(e => console.log(`${c.gray(S.branch)} ${c.red("ERROR: " + e)}`));
|
|
57
|
+
warnings.forEach(w => console.log(`${c.gray(S.branch)} ${c.yellow("WARN: " + w)}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
totalErrors += errors.length;
|
|
61
|
+
totalWarnings += warnings.length;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
stepLine();
|
|
65
|
+
console.log(`${c.gray(S.branch)} Total: ${skillsToValidate.length}, Errors: ${totalErrors}, Warnings: ${totalWarnings}`);
|
|
66
|
+
stepLine();
|
|
67
|
+
|
|
68
|
+
if (STRICT && totalErrors > 0) process.exit(1);
|
|
69
|
+
}
|
|
@@ -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,81 @@
|
|
|
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 (configurable via ADD_SKILL_WORKSPACE) */
|
|
12
|
+
export const WORKSPACE = process.env.ADD_SKILL_WORKSPACE || path.join(cwd, ".agent", "skills");
|
|
13
|
+
|
|
14
|
+
/** Global skills directory (configurable via ADD_SKILL_GLOBAL_DIR) */
|
|
15
|
+
export const GLOBAL_DIR = process.env.ADD_SKILL_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", "agentskillskit");
|
|
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
|
+
/** @type {boolean} Use global scope */
|
|
45
|
+
export const GLOBAL = flags.has("--global") || flags.has("-g");
|
|
46
|
+
|
|
47
|
+
/** @type {boolean} Verbose output */
|
|
48
|
+
export const VERBOSE = flags.has("--verbose") || flags.has("-v");
|
|
49
|
+
|
|
50
|
+
/** @type {boolean} JSON output mode */
|
|
51
|
+
export const JSON_OUTPUT = flags.has("--json");
|
|
52
|
+
|
|
53
|
+
/** @type {boolean} Force operation */
|
|
54
|
+
export const FORCE = flags.has("--force") || flags.has("-f");
|
|
55
|
+
|
|
56
|
+
/** @type {boolean} Dry run mode */
|
|
57
|
+
export const DRY = flags.has("--dry-run");
|
|
58
|
+
|
|
59
|
+
/** @type {boolean} Auto-fix mode */
|
|
60
|
+
export const FIX = flags.has("--fix");
|
|
61
|
+
|
|
62
|
+
/** @type {boolean} Strict mode */
|
|
63
|
+
export const STRICT = flags.has("--strict");
|
|
64
|
+
|
|
65
|
+
/** @type {boolean} Locked mode (install from lockfile) */
|
|
66
|
+
export const LOCKED = flags.has("--locked");
|
|
67
|
+
|
|
68
|
+
/** @type {boolean} Offline mode (skip network operations) */
|
|
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
|
+
/** @type {string} Package version */
|
|
77
|
+
export const VERSION = (() => {
|
|
78
|
+
try { return require("../../package.json").version; }
|
|
79
|
+
catch { return "1.2.0"; }
|
|
80
|
+
})();
|
|
81
|
+
|