@ruyfranca/myskills 1.0.33 → 1.0.35
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.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: sdd-spec-writer
|
|
3
|
-
description: Specification writer for Spec-Driven Development (SDD)
|
|
4
|
-
tools: Read, Grep, Glob, Bash
|
|
5
|
-
model: inherit
|
|
6
|
-
skills: sdd-spec-writer, clean-code, writing-plans
|
|
2
|
+
name: "sdd-spec-writer"
|
|
3
|
+
description: "Specification writer for Spec-Driven Development (SDD) - creates executable specifications that serve as unambiguous contracts."
|
|
4
|
+
tools: "Read, Grep, Glob, Bash"
|
|
5
|
+
model: "inherit"
|
|
6
|
+
skills: "sdd-spec-writer, clean-code, writing-plans"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# SDD Spec Writer
|
package/index.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import fs from 'fs
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import { existsSync, statSync } from 'fs';
|
|
6
7
|
import path from 'path';
|
|
7
8
|
import { fileURLToPath } from 'url';
|
|
8
|
-
import
|
|
9
|
+
import * as readline from 'readline/promises';
|
|
9
10
|
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const program = new Command();
|
|
@@ -16,7 +17,39 @@ const AGENTS_DIR = path.join(__dirname, '.agent', 'agents');
|
|
|
16
17
|
program
|
|
17
18
|
.name('myskills')
|
|
18
19
|
.description('CLI para gerenciar e instalar skills e agents do Antigravity')
|
|
19
|
-
.version('1.0.
|
|
20
|
+
.version('1.0.34');
|
|
21
|
+
|
|
22
|
+
// Helper para copiar pastas recursivamente
|
|
23
|
+
async function copyRecursively(src, dest) {
|
|
24
|
+
const stat = await fs.stat(src);
|
|
25
|
+
if (stat.isDirectory()) {
|
|
26
|
+
await fs.mkdir(dest, { recursive: true });
|
|
27
|
+
const entries = await fs.readdir(src);
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
await copyRecursively(path.join(src, entry), path.join(dest, entry));
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
// Para simplificar, substitui se existir
|
|
33
|
+
await fs.copyFile(src, dest);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper para substituir inquirer.prompt
|
|
38
|
+
async function promptList(message, choices) {
|
|
39
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
40
|
+
console.log(chalk.bold(`\n${message}`));
|
|
41
|
+
choices.forEach((c, idx) => console.log(` ${chalk.cyan(idx + 1)}) ${c}`));
|
|
42
|
+
|
|
43
|
+
while (true) {
|
|
44
|
+
const answer = await rl.question(chalk.yellow('\nDigite o número da opção desejada: '));
|
|
45
|
+
const num = parseInt(answer.trim(), 10);
|
|
46
|
+
if (!isNaN(num) && num > 0 && num <= choices.length) {
|
|
47
|
+
rl.close();
|
|
48
|
+
return choices[num - 1];
|
|
49
|
+
}
|
|
50
|
+
console.log(chalk.red('❌ Opção inválida, tente novamente digitando o número correspondente.'));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
20
53
|
|
|
21
54
|
program
|
|
22
55
|
.command('list')
|
|
@@ -25,7 +58,7 @@ program
|
|
|
25
58
|
const skills = await fs.readdir(SKILLS_DIR);
|
|
26
59
|
console.log(chalk.cyan.bold('\n🚀 Skills Disponíveis:\n'));
|
|
27
60
|
skills.forEach(skill => {
|
|
28
|
-
if (
|
|
61
|
+
if (statSync(path.join(SKILLS_DIR, skill)).isDirectory()) {
|
|
29
62
|
console.log(` - ${chalk.green(skill)}`);
|
|
30
63
|
}
|
|
31
64
|
});
|
|
@@ -36,7 +69,7 @@ program
|
|
|
36
69
|
.command('list-agents')
|
|
37
70
|
.description('Lista todos os agents disponíveis na biblioteca')
|
|
38
71
|
.action(async () => {
|
|
39
|
-
if (!
|
|
72
|
+
if (!existsSync(AGENTS_DIR)) {
|
|
40
73
|
console.log(chalk.yellow('\n⚠️ Nenhum agent disponível ainda.\n'));
|
|
41
74
|
return;
|
|
42
75
|
}
|
|
@@ -61,18 +94,16 @@ program
|
|
|
61
94
|
console.log(chalk.cyan('\n🚀 Inicializando kit completo do Antigravity...\n'));
|
|
62
95
|
|
|
63
96
|
try {
|
|
64
|
-
|
|
65
|
-
await
|
|
66
|
-
await fs.copy(sourceAgentDir, destAgentDir);
|
|
97
|
+
await fs.mkdir(destAgentDir, { recursive: true });
|
|
98
|
+
await copyRecursively(sourceAgentDir, destAgentDir);
|
|
67
99
|
console.log(chalk.green(' ✅ Pasta .agent instalada!'));
|
|
68
100
|
|
|
69
|
-
// Copy root rule files so Antigravity picks them up automatically
|
|
70
101
|
const rootRuleFiles = ['AGENTS.md', 'GEMINI.md'];
|
|
71
102
|
for (const ruleFile of rootRuleFiles) {
|
|
72
103
|
const src = path.join(__dirname, ruleFile);
|
|
73
104
|
const dest = path.join(destRoot, ruleFile);
|
|
74
|
-
if (
|
|
75
|
-
await
|
|
105
|
+
if (existsSync(src)) {
|
|
106
|
+
await copyRecursively(src, dest);
|
|
76
107
|
console.log(chalk.green(` ✅ ${ruleFile} instalado na raiz!`));
|
|
77
108
|
}
|
|
78
109
|
}
|
|
@@ -93,7 +124,7 @@ program
|
|
|
93
124
|
.action(async (name, options) => {
|
|
94
125
|
if (options.all) {
|
|
95
126
|
const skills = (await fs.readdir(SKILLS_DIR)).filter(s =>
|
|
96
|
-
|
|
127
|
+
statSync(path.join(SKILLS_DIR, s)).isDirectory()
|
|
97
128
|
);
|
|
98
129
|
console.log(chalk.cyan(`\n📦 Instalando todas as ${skills.length} skills...\n`));
|
|
99
130
|
|
|
@@ -102,8 +133,8 @@ program
|
|
|
102
133
|
const destDir = path.join(process.cwd(), '.agent', 'skills', s);
|
|
103
134
|
|
|
104
135
|
try {
|
|
105
|
-
await fs.
|
|
106
|
-
await
|
|
136
|
+
await fs.mkdir(path.dirname(destDir), { recursive: true });
|
|
137
|
+
await copyRecursively(sourceDir, destDir);
|
|
107
138
|
console.log(` - ${chalk.green(s)}: ${chalk.gray('OK')}`);
|
|
108
139
|
} catch (err) {
|
|
109
140
|
console.error(chalk.red(` - ${s}: Erro - ${err.message}`));
|
|
@@ -119,15 +150,7 @@ program
|
|
|
119
150
|
const agentNames = agents.map(a => a.replace('.md', ''));
|
|
120
151
|
|
|
121
152
|
if (!name) {
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
type: 'list',
|
|
125
|
-
name: 'selectedAgent',
|
|
126
|
-
message: 'Qual agent você deseja adicionar?',
|
|
127
|
-
choices: agentNames
|
|
128
|
-
}
|
|
129
|
-
]);
|
|
130
|
-
name = answers.selectedAgent;
|
|
153
|
+
name = await promptList('📋 Qual agent você deseja adicionar?', agentNames);
|
|
131
154
|
}
|
|
132
155
|
|
|
133
156
|
if (!agentNames.includes(name)) {
|
|
@@ -139,8 +162,8 @@ program
|
|
|
139
162
|
const destFile = path.join(process.cwd(), '.agent', 'agents', `${name}.md`);
|
|
140
163
|
|
|
141
164
|
try {
|
|
142
|
-
await fs.
|
|
143
|
-
await
|
|
165
|
+
await fs.mkdir(path.dirname(destFile), { recursive: true });
|
|
166
|
+
await copyRecursively(sourceFile, destFile);
|
|
144
167
|
console.log(chalk.green(`\n✅ Agent "${name}" instalado com sucesso em .agent/agents/${name}.md!\n`));
|
|
145
168
|
} catch (err) {
|
|
146
169
|
console.error(chalk.red(`\n❌ Erro ao copiar agent: ${err.message}\n`));
|
|
@@ -150,19 +173,11 @@ program
|
|
|
150
173
|
|
|
151
174
|
// Default: Skill
|
|
152
175
|
const skills = (await fs.readdir(SKILLS_DIR)).filter(s =>
|
|
153
|
-
|
|
176
|
+
statSync(path.join(SKILLS_DIR, s)).isDirectory()
|
|
154
177
|
);
|
|
155
178
|
|
|
156
179
|
if (!name) {
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
type: 'list',
|
|
160
|
-
name: 'selectedSkill',
|
|
161
|
-
message: 'Qual skill você deseja adicionar?',
|
|
162
|
-
choices: skills
|
|
163
|
-
}
|
|
164
|
-
]);
|
|
165
|
-
name = answers.selectedSkill;
|
|
180
|
+
name = await promptList('📋 Qual skill você deseja adicionar?', skills);
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
if (!skills.includes(name)) {
|
|
@@ -174,8 +189,8 @@ program
|
|
|
174
189
|
const destDir = path.join(process.cwd(), '.agent', 'skills', name);
|
|
175
190
|
|
|
176
191
|
try {
|
|
177
|
-
await fs.
|
|
178
|
-
await
|
|
192
|
+
await fs.mkdir(path.dirname(destDir), { recursive: true });
|
|
193
|
+
await copyRecursively(sourceDir, destDir);
|
|
179
194
|
console.log(chalk.green(`\n✅ Skill "${name}" instalada com sucesso em .agent/skills/${name}!\n`));
|
|
180
195
|
} catch (err) {
|
|
181
196
|
console.error(chalk.red(`\n❌ Erro ao copiar skill: ${err.message}\n`));
|
|
@@ -209,13 +224,13 @@ program
|
|
|
209
224
|
}
|
|
210
225
|
|
|
211
226
|
for (const task of tasks) {
|
|
212
|
-
if (!
|
|
227
|
+
if (!existsSync(task.src)) {
|
|
213
228
|
console.log(chalk.yellow(` ⚠️ Fonte não encontrada: ${task.label} (${task.src})`));
|
|
214
229
|
continue;
|
|
215
230
|
}
|
|
216
231
|
try {
|
|
217
|
-
await fs.
|
|
218
|
-
await
|
|
232
|
+
await fs.mkdir(task.dest, { recursive: true });
|
|
233
|
+
await copyRecursively(task.src, task.dest);
|
|
219
234
|
console.log(chalk.green(` ✅ ${task.label}: copiado`));
|
|
220
235
|
} catch (err) {
|
|
221
236
|
console.error(chalk.red(` ❌ ${task.label}: erro na cópia — ${err.message}`));
|
|
@@ -223,7 +238,6 @@ program
|
|
|
223
238
|
}
|
|
224
239
|
}
|
|
225
240
|
|
|
226
|
-
// Rewrite hardcoded paths (file:///...mySkills...) to point to the dest project
|
|
227
241
|
if (destRoot !== srcRoot) {
|
|
228
242
|
console.log(chalk.cyan('\n🔧 Corrigindo paths nos arquivos copiados...\n'));
|
|
229
243
|
|
|
@@ -240,7 +254,7 @@ program
|
|
|
240
254
|
let fixedFiles = 0;
|
|
241
255
|
|
|
242
256
|
for (const dir of dirsToFix) {
|
|
243
|
-
if (!
|
|
257
|
+
if (!existsSync(dir)) continue;
|
|
244
258
|
|
|
245
259
|
const walk = async (currentDir) => {
|
|
246
260
|
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
@@ -257,7 +271,7 @@ program
|
|
|
257
271
|
fixedFiles++;
|
|
258
272
|
}
|
|
259
273
|
} catch {
|
|
260
|
-
// ignorar
|
|
274
|
+
// ignorar
|
|
261
275
|
}
|
|
262
276
|
}
|
|
263
277
|
}
|
|
@@ -273,15 +287,14 @@ program
|
|
|
273
287
|
}
|
|
274
288
|
}
|
|
275
289
|
|
|
276
|
-
// Update root rule files (AGENTS.md, GEMINI.md)
|
|
277
290
|
if (updateAll || options.rules) {
|
|
278
291
|
const rootRuleFiles = ['AGENTS.md', 'GEMINI.md'];
|
|
279
292
|
for (const ruleFile of rootRuleFiles) {
|
|
280
293
|
const src = path.join(srcRoot, ruleFile);
|
|
281
294
|
const dest = path.join(destRoot, ruleFile);
|
|
282
|
-
if (
|
|
295
|
+
if (existsSync(src)) {
|
|
283
296
|
try {
|
|
284
|
-
await
|
|
297
|
+
await copyRecursively(src, dest);
|
|
285
298
|
console.log(chalk.green(` ✅ ${ruleFile}: atualizado na raiz`));
|
|
286
299
|
} catch (err) {
|
|
287
300
|
console.error(chalk.red(` ❌ ${ruleFile}: ${err.message}`));
|
|
@@ -290,20 +303,19 @@ program
|
|
|
290
303
|
}
|
|
291
304
|
}
|
|
292
305
|
|
|
293
|
-
// Instalar workflows globalmente para aparecerem no / do Antigravity
|
|
294
306
|
if (updateAll || options.workflows) {
|
|
295
307
|
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
296
308
|
const globalWorkflowsDir = path.join(homeDir, '.gemini', 'antigravity', 'global_workflows');
|
|
297
309
|
const workflowsSrc = path.join(srcRoot, '.agent', 'workflows');
|
|
298
310
|
|
|
299
|
-
if (
|
|
311
|
+
if (existsSync(workflowsSrc)) {
|
|
300
312
|
try {
|
|
301
|
-
await fs.
|
|
313
|
+
await fs.mkdir(globalWorkflowsDir, { recursive: true });
|
|
302
314
|
const files = await fs.readdir(workflowsSrc);
|
|
303
315
|
let copied = 0;
|
|
304
316
|
for (const file of files) {
|
|
305
317
|
if (file.endsWith('.md')) {
|
|
306
|
-
await
|
|
318
|
+
await copyRecursively(path.join(workflowsSrc, file), path.join(globalWorkflowsDir, file));
|
|
307
319
|
copied++;
|
|
308
320
|
}
|
|
309
321
|
}
|
|
@@ -315,7 +327,6 @@ program
|
|
|
315
327
|
}
|
|
316
328
|
|
|
317
329
|
console.log(chalk.cyan.bold('\n✨ Atualização concluída!\n'));
|
|
318
|
-
console.log(chalk.gray('💡 Dica: reinicie o Antigravity para que o / exiba os workflows.\n'));
|
|
319
330
|
});
|
|
320
331
|
|
|
321
332
|
program
|
|
@@ -329,12 +340,12 @@ program
|
|
|
329
340
|
console.log(chalk.cyan('\n🌐 Instalando workflows globalmente...\n'));
|
|
330
341
|
|
|
331
342
|
try {
|
|
332
|
-
await fs.
|
|
343
|
+
await fs.mkdir(globalWorkflowsDir, { recursive: true });
|
|
333
344
|
const files = await fs.readdir(workflowsSrc);
|
|
334
345
|
let copied = 0;
|
|
335
346
|
for (const file of files) {
|
|
336
347
|
if (file.endsWith('.md')) {
|
|
337
|
-
await
|
|
348
|
+
await copyRecursively(path.join(workflowsSrc, file), path.join(globalWorkflowsDir, file));
|
|
338
349
|
copied++;
|
|
339
350
|
}
|
|
340
351
|
}
|
|
@@ -345,7 +356,4 @@ program
|
|
|
345
356
|
}
|
|
346
357
|
});
|
|
347
358
|
|
|
348
|
-
|
|
349
|
-
|
|
350
359
|
program.parse();
|
|
351
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruyfranca/myskills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Biblioteca de skills customizadas para Antigravity / Claude Code",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,8 +21,6 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"chalk": "^5.3.0",
|
|
24
|
-
"commander": "^12.0.0"
|
|
25
|
-
"fs-extra": "^11.2.0",
|
|
26
|
-
"inquirer": "^9.2.15"
|
|
24
|
+
"commander": "^12.0.0"
|
|
27
25
|
}
|
|
28
26
|
}
|
package/test-yaml.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
function checkDir(dir) {
|
|
5
|
+
let files = fs.readdirSync(dir, { withFileTypes: true });
|
|
6
|
+
for (let f of files) {
|
|
7
|
+
let full = path.join(dir, f.name);
|
|
8
|
+
if (f.isDirectory()) {
|
|
9
|
+
checkDir(full);
|
|
10
|
+
} else if (f.name.endsWith('.md')) {
|
|
11
|
+
let content = fs.readFileSync(full, 'utf8');
|
|
12
|
+
if (content.startsWith('---')) {
|
|
13
|
+
let closing = content.indexOf('\n---', 3);
|
|
14
|
+
if (closing === -1) {
|
|
15
|
+
console.log('Error: No closing --- in', full);
|
|
16
|
+
} else {
|
|
17
|
+
// extract
|
|
18
|
+
let yamlStr = content.substring(4, closing);
|
|
19
|
+
// check if there's another block immediately following
|
|
20
|
+
let rest = content.substring(closing + 4).trimStart();
|
|
21
|
+
if (rest.startsWith('---')) {
|
|
22
|
+
console.log('Error: Duplicate frontmatter block in', full);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
checkDir('.agent');
|
|
31
|
+
console.log('Done checking.');
|