myaidev-method 0.3.2 → 0.3.4
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/.claude-plugin/plugin.json +52 -48
- package/DEV_WORKFLOW_GUIDE.md +6 -6
- package/MCP_INTEGRATION.md +4 -4
- package/README.md +81 -64
- package/TECHNICAL_ARCHITECTURE.md +112 -18
- package/USER_GUIDE.md +57 -40
- package/bin/cli.js +49 -127
- package/dist/mcp/gutenberg-converter.js +667 -413
- package/dist/mcp/wordpress-server.js +1558 -1181
- package/extension.json +3 -3
- package/package.json +2 -1
- package/skills/content-writer/SKILL.md +130 -178
- package/skills/infographic/SKILL.md +191 -0
- package/skills/myaidev-analyze/SKILL.md +242 -0
- package/skills/myaidev-analyze/agents/dependency-mapper-agent.md +236 -0
- package/skills/myaidev-analyze/agents/pattern-detector-agent.md +240 -0
- package/skills/myaidev-analyze/agents/structure-scanner-agent.md +171 -0
- package/skills/myaidev-analyze/agents/tech-profiler-agent.md +291 -0
- package/skills/myaidev-architect/SKILL.md +389 -0
- package/skills/myaidev-architect/agents/compliance-checker-agent.md +287 -0
- package/skills/myaidev-architect/agents/requirements-analyst-agent.md +194 -0
- package/skills/myaidev-architect/agents/system-designer-agent.md +315 -0
- package/skills/myaidev-coder/SKILL.md +291 -0
- package/skills/myaidev-coder/agents/implementer-agent.md +185 -0
- package/skills/myaidev-coder/agents/integration-agent.md +168 -0
- package/skills/myaidev-coder/agents/pattern-scanner-agent.md +161 -0
- package/skills/myaidev-coder/agents/self-reviewer-agent.md +168 -0
- package/skills/myaidev-debug/SKILL.md +308 -0
- package/skills/myaidev-debug/agents/fix-agent-debug.md +317 -0
- package/skills/myaidev-debug/agents/hypothesis-agent.md +226 -0
- package/skills/myaidev-debug/agents/investigator-agent.md +250 -0
- package/skills/myaidev-debug/agents/symptom-collector-agent.md +231 -0
- package/skills/myaidev-documenter/SKILL.md +194 -0
- package/skills/myaidev-documenter/agents/code-reader-agent.md +172 -0
- package/skills/myaidev-documenter/agents/doc-validator-agent.md +174 -0
- package/skills/myaidev-documenter/agents/doc-writer-agent.md +379 -0
- package/skills/myaidev-migrate/SKILL.md +300 -0
- package/skills/myaidev-migrate/agents/migration-planner-agent.md +237 -0
- package/skills/myaidev-migrate/agents/migration-writer-agent.md +248 -0
- package/skills/myaidev-migrate/agents/schema-analyzer-agent.md +190 -0
- package/skills/myaidev-performance/SKILL.md +270 -0
- package/skills/myaidev-performance/agents/benchmark-agent.md +281 -0
- package/skills/myaidev-performance/agents/optimizer-agent.md +277 -0
- package/skills/myaidev-performance/agents/profiler-agent.md +252 -0
- package/skills/myaidev-refactor/SKILL.md +296 -0
- package/skills/myaidev-refactor/agents/refactor-executor-agent.md +221 -0
- package/skills/myaidev-refactor/agents/refactor-planner-agent.md +213 -0
- package/skills/myaidev-refactor/agents/regression-guard-agent.md +242 -0
- package/skills/myaidev-refactor/agents/smell-detector-agent.md +233 -0
- package/skills/myaidev-reviewer/SKILL.md +385 -0
- package/skills/myaidev-reviewer/agents/auto-fixer-agent.md +238 -0
- package/skills/myaidev-reviewer/agents/code-analyst-agent.md +220 -0
- package/skills/myaidev-reviewer/agents/security-scanner-agent.md +262 -0
- package/skills/myaidev-tester/SKILL.md +331 -0
- package/skills/myaidev-tester/agents/coverage-analyst-agent.md +163 -0
- package/skills/myaidev-tester/agents/tdd-driver-agent.md +242 -0
- package/skills/myaidev-tester/agents/test-runner-agent.md +176 -0
- package/skills/myaidev-tester/agents/test-strategist-agent.md +154 -0
- package/skills/myaidev-tester/agents/test-writer-agent.md +242 -0
- package/skills/myaidev-workflow/SKILL.md +567 -0
- package/skills/myaidev-workflow/agents/analyzer-agent.md +317 -0
- package/skills/myaidev-workflow/agents/coordinator-agent.md +253 -0
- package/skills/security-auditor/SKILL.md +1 -1
- package/skills/skill-builder/SKILL.md +417 -0
- package/src/cli/commands/addon.js +146 -135
- package/src/cli/commands/auth.js +9 -1
- package/src/config/workflows.js +11 -6
- package/src/lib/ascii-banner.js +3 -3
- package/src/lib/update-manager.js +120 -61
- package/src/mcp/gutenberg-converter.js +667 -413
- package/src/mcp/wordpress-server.js +1558 -1181
- package/src/statusline/statusline.sh +279 -0
- package/src/templates/claude/CLAUDE.md +124 -0
- package/skills/sparc-architect/SKILL.md +0 -127
- package/skills/sparc-coder/SKILL.md +0 -90
- package/skills/sparc-documenter/SKILL.md +0 -155
- package/skills/sparc-reviewer/SKILL.md +0 -138
- package/skills/sparc-tester/SKILL.md +0 -100
- package/skills/sparc-workflow/SKILL.md +0 -130
- /package/{marketplace.json → .claude-plugin/marketplace.json} +0 -0
|
@@ -17,16 +17,24 @@ import inquirer from 'inquirer';
|
|
|
17
17
|
import fs from 'fs-extra';
|
|
18
18
|
import path from 'path';
|
|
19
19
|
import os from 'os';
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
import matter from 'gray-matter';
|
|
22
22
|
import { isAuthenticated, getAuthToken, getAuthData, authFetch } from '../../lib/auth-helper.js';
|
|
23
23
|
import { validateSkill, generateSlug } from '../../lib/skill-validator.js';
|
|
24
24
|
|
|
25
25
|
const API_BASE = 'https://dev.myai1.ai/api/skills';
|
|
26
26
|
const MARKETPLACE_REPO = 'myaione/myaidev-marketplace';
|
|
27
|
+
const FETCH_TIMEOUT_MS = 10000;
|
|
27
28
|
|
|
28
29
|
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
29
30
|
|
|
31
|
+
function fetchWithTimeout(url, options = {}, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
34
|
+
return fetch(url, { ...options, signal: controller.signal })
|
|
35
|
+
.finally(() => clearTimeout(timeout));
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
function projectSkillsDir() {
|
|
31
39
|
return path.join(process.cwd(), '.claude', 'skills');
|
|
32
40
|
}
|
|
@@ -57,6 +65,7 @@ async function requireAuth() {
|
|
|
57
65
|
export function registerAddonCommand(program) {
|
|
58
66
|
const addon = program
|
|
59
67
|
.command('addon [name]')
|
|
68
|
+
.enablePositionalOptions()
|
|
60
69
|
.description('Install a skill from the MyAIDev marketplace')
|
|
61
70
|
.option('--project', 'Install to project .claude/skills/')
|
|
62
71
|
.option('--global', 'Install to global ~/.claude/skills/')
|
|
@@ -68,6 +77,16 @@ export function registerAddonCommand(program) {
|
|
|
68
77
|
await installSkill(name, options);
|
|
69
78
|
});
|
|
70
79
|
|
|
80
|
+
// ── addon install ───────────────────────────────────────────────────
|
|
81
|
+
addon
|
|
82
|
+
.command('install <name>')
|
|
83
|
+
.description('Install a skill from the marketplace')
|
|
84
|
+
.option('--project', 'Install to project .claude/skills/')
|
|
85
|
+
.option('--global', 'Install to global ~/.claude/skills/')
|
|
86
|
+
.action(async (name, opts) => {
|
|
87
|
+
await installSkill(name, opts);
|
|
88
|
+
});
|
|
89
|
+
|
|
71
90
|
// ── addon list ──────────────────────────────────────────────────────
|
|
72
91
|
addon
|
|
73
92
|
.command('list')
|
|
@@ -154,7 +173,7 @@ async function installSkill(name, options) {
|
|
|
154
173
|
|
|
155
174
|
try {
|
|
156
175
|
// Search for skill
|
|
157
|
-
const res = await
|
|
176
|
+
const res = await fetchWithTimeout(`${API_BASE}?search=${encodeURIComponent(name)}&limit=5`);
|
|
158
177
|
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
159
178
|
const body = await res.json();
|
|
160
179
|
const skills = body.skills || [];
|
|
@@ -196,11 +215,12 @@ async function installSkill(name, options) {
|
|
|
196
215
|
const { location } = await inquirer.prompt([{
|
|
197
216
|
type: 'list',
|
|
198
217
|
name: 'location',
|
|
199
|
-
message: '
|
|
218
|
+
message: 'Where do you want to install this skill?',
|
|
200
219
|
choices: [
|
|
201
|
-
{ name: `Project
|
|
202
|
-
{ name: `Global
|
|
220
|
+
{ name: `Project ${chalk.gray('— .claude/skills/ (this project only)')}`, value: 'project' },
|
|
221
|
+
{ name: `Global ${chalk.gray('— ~/.claude/skills/ (available everywhere)')}`, value: 'global' },
|
|
203
222
|
],
|
|
223
|
+
default: 'project',
|
|
204
224
|
}]);
|
|
205
225
|
targetDir = location === 'project' ? projectSkillsDir() : globalSkillsDir();
|
|
206
226
|
}
|
|
@@ -210,14 +230,23 @@ async function installSkill(name, options) {
|
|
|
210
230
|
|
|
211
231
|
// Download
|
|
212
232
|
const dlSpinner = ora(`Downloading ${chalk.cyan(skill.name)}...`).start();
|
|
213
|
-
const dlRes = await
|
|
233
|
+
const dlRes = await fetchWithTimeout(`${API_BASE}/${skill.id}/download`);
|
|
214
234
|
if (!dlRes.ok) throw new Error(`Download failed: ${dlRes.status}`);
|
|
215
|
-
const dlBody = await dlRes.json();
|
|
216
235
|
|
|
217
|
-
|
|
236
|
+
// Handle both JSON and raw markdown responses
|
|
237
|
+
const contentType = dlRes.headers.get('content-type') || '';
|
|
238
|
+
let content;
|
|
239
|
+
if (contentType.includes('application/json')) {
|
|
240
|
+
const dlBody = await dlRes.json();
|
|
241
|
+
content = dlBody.content;
|
|
242
|
+
} else {
|
|
243
|
+
content = await dlRes.text();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!content) throw new Error('Skill content is empty');
|
|
218
247
|
|
|
219
248
|
await fs.ensureDir(installPath);
|
|
220
|
-
await fs.writeFile(path.join(installPath, 'SKILL.md'),
|
|
249
|
+
await fs.writeFile(path.join(installPath, 'SKILL.md'), content, 'utf8');
|
|
221
250
|
|
|
222
251
|
dlSpinner.succeed(chalk.green(`Installed ${chalk.bold(skill.name)}`));
|
|
223
252
|
|
|
@@ -225,7 +254,13 @@ async function installSkill(name, options) {
|
|
|
225
254
|
console.log(chalk.gray(` → ${rel}/SKILL.md`));
|
|
226
255
|
console.log(chalk.cyan('\n Restart Claude Code to load the new skill.\n'));
|
|
227
256
|
} catch (err) {
|
|
228
|
-
|
|
257
|
+
if (err.name === 'AbortError') {
|
|
258
|
+
spinner.fail(chalk.red('Request timed out. Check your internet connection.'));
|
|
259
|
+
} else if (err.cause?.code === 'ENOTFOUND' || err.message?.includes('ENOTFOUND')) {
|
|
260
|
+
spinner.fail(chalk.red('Cannot reach marketplace API. Check your internet connection.'));
|
|
261
|
+
} else {
|
|
262
|
+
spinner.fail(chalk.red(`Failed to install: ${err.message}`));
|
|
263
|
+
}
|
|
229
264
|
process.exit(1);
|
|
230
265
|
}
|
|
231
266
|
}
|
|
@@ -241,7 +276,7 @@ async function listMarketplace(opts) {
|
|
|
241
276
|
if (opts.sort) params.set('sort', opts.sort);
|
|
242
277
|
params.set('limit', opts.limit || '20');
|
|
243
278
|
|
|
244
|
-
const res = await
|
|
279
|
+
const res = await fetchWithTimeout(`${API_BASE}?${params}`);
|
|
245
280
|
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
246
281
|
const body = await res.json();
|
|
247
282
|
const skills = body.skills || [];
|
|
@@ -264,15 +299,22 @@ async function listMarketplace(opts) {
|
|
|
264
299
|
for (const s of skills) {
|
|
265
300
|
const stars = String(s.stars ?? 0).padStart(3);
|
|
266
301
|
const downloads = String(s.downloads ?? 0).padStart(5);
|
|
302
|
+
const author = s.authorName || s.author || 'unknown';
|
|
267
303
|
console.log(
|
|
268
|
-
` ${chalk.green(pad(s.name, 24))} ${chalk.gray(pad(truncate(s.description, 38), 40))} ${chalk.white(pad(truncate(
|
|
304
|
+
` ${chalk.green(pad(s.name, 24))} ${chalk.gray(pad(truncate(s.description, 38), 40))} ${chalk.white(pad(truncate(author, 14), 16))} ${chalk.yellow(stars)} ${chalk.blue(downloads)}`
|
|
269
305
|
);
|
|
270
306
|
}
|
|
271
307
|
|
|
272
308
|
console.log(chalk.gray('\n Install: myaidev-method addon <name>'));
|
|
273
309
|
console.log(chalk.gray(' Search: myaidev-method addon search <query>\n'));
|
|
274
310
|
} catch (err) {
|
|
275
|
-
|
|
311
|
+
if (err.name === 'AbortError') {
|
|
312
|
+
spinner.fail(chalk.red('Request timed out. Check your internet connection.'));
|
|
313
|
+
} else if (err.cause?.code === 'ENOTFOUND' || err.message?.includes('ENOTFOUND')) {
|
|
314
|
+
spinner.fail(chalk.red('Cannot reach marketplace API. Check your internet connection.'));
|
|
315
|
+
} else {
|
|
316
|
+
spinner.fail(chalk.red(`Failed to fetch marketplace: ${err.message}`));
|
|
317
|
+
}
|
|
276
318
|
process.exit(1);
|
|
277
319
|
}
|
|
278
320
|
}
|
|
@@ -284,7 +326,7 @@ async function searchMarketplace(query, opts) {
|
|
|
284
326
|
|
|
285
327
|
try {
|
|
286
328
|
const params = new URLSearchParams({ search: query, limit: opts.limit || '10' });
|
|
287
|
-
const res = await
|
|
329
|
+
const res = await fetchWithTimeout(`${API_BASE}?${params}`);
|
|
288
330
|
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
289
331
|
const body = await res.json();
|
|
290
332
|
const skills = body.skills || [];
|
|
@@ -301,19 +343,26 @@ async function searchMarketplace(query, opts) {
|
|
|
301
343
|
for (const s of skills) {
|
|
302
344
|
const stars = s.stars ?? 0;
|
|
303
345
|
const downloads = s.downloads ?? 0;
|
|
346
|
+
const author = s.authorName || s.author || 'unknown';
|
|
304
347
|
// Highlight matching parts of the name
|
|
305
348
|
const highlighted = s.name.replace(
|
|
306
349
|
new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'),
|
|
307
350
|
(m) => chalk.yellow.bold(m)
|
|
308
351
|
);
|
|
309
352
|
|
|
310
|
-
console.log(` ${chalk.green('●')} ${highlighted} ${chalk.gray(`by ${
|
|
353
|
+
console.log(` ${chalk.green('●')} ${highlighted} ${chalk.gray(`by ${author}`)}`);
|
|
311
354
|
console.log(` ${chalk.gray(truncate(s.description, 70))}`);
|
|
312
355
|
console.log(` ${chalk.yellow(`⭐ ${stars}`)} ${chalk.blue(`⬇ ${downloads}`)} ${chalk.gray(`Install: myaidev-method addon ${s.slug || s.name.toLowerCase().replace(/\s+/g, '-')}`)}`);
|
|
313
356
|
console.log('');
|
|
314
357
|
}
|
|
315
358
|
} catch (err) {
|
|
316
|
-
|
|
359
|
+
if (err.name === 'AbortError') {
|
|
360
|
+
spinner.fail(chalk.red('Request timed out. Check your internet connection.'));
|
|
361
|
+
} else if (err.cause?.code === 'ENOTFOUND' || err.message?.includes('ENOTFOUND')) {
|
|
362
|
+
spinner.fail(chalk.red('Cannot reach marketplace API. Check your internet connection.'));
|
|
363
|
+
} else {
|
|
364
|
+
spinner.fail(chalk.red(`Search failed: ${err.message}`));
|
|
365
|
+
}
|
|
317
366
|
process.exit(1);
|
|
318
367
|
}
|
|
319
368
|
}
|
|
@@ -648,26 +697,10 @@ async function scaffoldSkill() {
|
|
|
648
697
|
return targetDir;
|
|
649
698
|
}
|
|
650
699
|
|
|
651
|
-
function ghAvailable() {
|
|
652
|
-
try {
|
|
653
|
-
execSync('gh auth status', { stdio: 'pipe' });
|
|
654
|
-
return true;
|
|
655
|
-
} catch {
|
|
656
|
-
return false;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
700
|
async function submitSkill(opts) {
|
|
661
|
-
// Step 1: Auth
|
|
701
|
+
// Step 1: Auth check
|
|
662
702
|
if (!(await requireAuth())) return;
|
|
663
703
|
|
|
664
|
-
if (!ghAvailable()) {
|
|
665
|
-
console.log(chalk.red('\n✗ GitHub CLI (gh) is required for submissions.'));
|
|
666
|
-
console.log(chalk.gray(' Install: https://cli.github.com'));
|
|
667
|
-
console.log(chalk.gray(' Then: gh auth login\n'));
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
704
|
// Step 2: Detect or scaffold SKILL.md
|
|
672
705
|
let targetPath = opts.dir ? path.resolve(opts.dir) : process.cwd();
|
|
673
706
|
let skillDir = targetPath;
|
|
@@ -699,14 +732,13 @@ async function submitSkill(opts) {
|
|
|
699
732
|
|
|
700
733
|
const scaffoldedDir = await scaffoldSkill();
|
|
701
734
|
if (!scaffoldedDir) return;
|
|
702
|
-
// User needs to edit the scaffold first
|
|
703
735
|
return;
|
|
704
736
|
}
|
|
705
737
|
|
|
706
|
-
// Step 3:
|
|
738
|
+
// Step 3: Local validation
|
|
707
739
|
const spinner = ora('Validating skill...').start();
|
|
708
740
|
const result = await validateSkill(skillDir, {
|
|
709
|
-
checkDuplicates:
|
|
741
|
+
checkDuplicates: false,
|
|
710
742
|
apiBase: API_BASE,
|
|
711
743
|
});
|
|
712
744
|
spinner.stop();
|
|
@@ -729,11 +761,11 @@ async function submitSkill(opts) {
|
|
|
729
761
|
}
|
|
730
762
|
|
|
731
763
|
// Step 4: Show summary and confirm
|
|
732
|
-
const slug = result.info.slug;
|
|
733
764
|
console.log(chalk.cyan('\n📤 Submit to MyAIDev Marketplace\n'));
|
|
734
765
|
console.log(` ${chalk.gray('Name:')} ${chalk.white.bold(result.info.name)}`);
|
|
735
|
-
console.log(` ${chalk.gray('Slug:')} ${chalk.white(slug)}`);
|
|
766
|
+
console.log(` ${chalk.gray('Slug:')} ${chalk.white(result.info.slug)}`);
|
|
736
767
|
console.log(` ${chalk.gray('Description:')} ${chalk.white(truncate(result.info.description, 60))}`);
|
|
768
|
+
console.log(` ${chalk.gray('Category:')} ${result.info.category || 'general'}`);
|
|
737
769
|
console.log(` ${chalk.gray('Sections:')} ${result.info.sectionCount}`);
|
|
738
770
|
console.log('');
|
|
739
771
|
|
|
@@ -750,116 +782,75 @@ async function submitSkill(opts) {
|
|
|
750
782
|
}
|
|
751
783
|
}
|
|
752
784
|
|
|
753
|
-
// Step 5:
|
|
754
|
-
const
|
|
755
|
-
const tmpDir = path.join(os.tmpdir(), `myaidev-submit-${Date.now()}`);
|
|
785
|
+
// Step 5: Submit via API
|
|
786
|
+
const submitSpinner = ora('Submitting skill for review...').start();
|
|
756
787
|
|
|
757
788
|
try {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
const ghUser = execSync('gh api user --jq .login', { encoding: 'utf8' }).trim();
|
|
767
|
-
|
|
768
|
-
// Clone the user's fork
|
|
769
|
-
execSync(`gh repo clone ${ghUser}/myaidev-marketplace "${tmpDir}" -- --depth 1`, { stdio: 'pipe' });
|
|
770
|
-
|
|
771
|
-
// Create branch
|
|
772
|
-
const branch = `skill/${slug}`;
|
|
773
|
-
execSync(`git -C "${tmpDir}" checkout -b ${branch}`, { stdio: 'pipe' });
|
|
774
|
-
|
|
775
|
-
// Copy skill files
|
|
776
|
-
const destDir = path.join(tmpDir, 'skills', slug);
|
|
777
|
-
await fs.ensureDir(destDir);
|
|
778
|
-
await fs.copy(skillDir, destDir, {
|
|
779
|
-
filter: (src) => {
|
|
780
|
-
const ext = path.extname(src).toLowerCase();
|
|
781
|
-
const base = path.basename(src);
|
|
782
|
-
// Copy directories and allowed file types
|
|
783
|
-
if (fs.statSync(src).isDirectory()) return true;
|
|
784
|
-
return ['.md', '.json', '.yaml', '.yml', '.sh', '.js', '.ts', '.py', '.txt'].includes(ext) || base === '.gitkeep';
|
|
785
|
-
},
|
|
789
|
+
const content = await fs.readFile(skillFile, 'utf8');
|
|
790
|
+
|
|
791
|
+
const res = await authFetch(`${API_BASE}/submissions`, {
|
|
792
|
+
method: 'POST',
|
|
793
|
+
body: JSON.stringify({
|
|
794
|
+
content,
|
|
795
|
+
category: result.info.category || undefined,
|
|
796
|
+
}),
|
|
786
797
|
});
|
|
787
798
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
prSpinner.succeed(chalk.green('Pull request created!'));
|
|
814
|
-
|
|
815
|
-
// Extract PR URL from output
|
|
816
|
-
const prUrl = prOutput.match(/https:\/\/github\.com\S+/)?.[0] || prOutput;
|
|
817
|
-
console.log(chalk.gray(` PR: ${prUrl}`));
|
|
818
|
-
|
|
819
|
-
// Step 6: Record submission
|
|
820
|
-
try {
|
|
821
|
-
await authFetch(`${API_BASE}/submissions`, {
|
|
822
|
-
method: 'POST',
|
|
823
|
-
body: JSON.stringify({
|
|
824
|
-
skillName: result.info.name,
|
|
825
|
-
slug,
|
|
826
|
-
description: result.info.description || '',
|
|
827
|
-
prUrl,
|
|
828
|
-
}),
|
|
829
|
-
});
|
|
830
|
-
} catch {
|
|
831
|
-
// Non-fatal: submission tracking is optional
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
console.log(chalk.cyan('\n Status: awaiting review'));
|
|
835
|
-
console.log(chalk.gray(' Check: myaidev-method addon status\n'));
|
|
799
|
+
if (!res.ok) {
|
|
800
|
+
const body = await res.json().catch(() => ({}));
|
|
801
|
+
if (body.validation?.errors?.length) {
|
|
802
|
+
submitSpinner.fail(chalk.red('Server validation failed:'));
|
|
803
|
+
for (const err of body.validation.errors) {
|
|
804
|
+
console.log(chalk.red(` • ${err}`));
|
|
805
|
+
}
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
throw new Error(body.error || `API error: ${res.status}`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const body = await res.json();
|
|
812
|
+
const submission = body.submission;
|
|
813
|
+
|
|
814
|
+
submitSpinner.succeed(chalk.green('Skill submitted for review!'));
|
|
815
|
+
|
|
816
|
+
console.log('');
|
|
817
|
+
console.log(` ${chalk.gray('Submission ID:')} ${chalk.white(submission.id)}`);
|
|
818
|
+
console.log(` ${chalk.gray('Status:')} ${chalk.yellow(submission.status)}`);
|
|
819
|
+
console.log('');
|
|
820
|
+
console.log(chalk.gray(' AI analysis will run automatically.'));
|
|
821
|
+
console.log(chalk.gray(' An admin will review and approve your skill.'));
|
|
822
|
+
console.log(chalk.gray(` Check status: ${chalk.white('myaidev-method addon status')}\n`));
|
|
836
823
|
} catch (err) {
|
|
837
|
-
|
|
824
|
+
submitSpinner.fail(chalk.red(`Submission failed: ${err.message}`));
|
|
838
825
|
process.exit(1);
|
|
839
|
-
} finally {
|
|
840
|
-
// Clean up temp directory
|
|
841
|
-
await fs.remove(tmpDir).catch(() => {});
|
|
842
826
|
}
|
|
843
827
|
}
|
|
844
828
|
|
|
845
829
|
// ── Status Command ─────────────────────────────────────────────────────
|
|
846
830
|
|
|
847
|
-
async function checkStatus(
|
|
831
|
+
async function checkStatus(nameOrId) {
|
|
848
832
|
if (!(await requireAuth())) return;
|
|
849
833
|
|
|
850
834
|
const spinner = ora('Fetching submission status...').start();
|
|
851
835
|
|
|
852
836
|
try {
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
837
|
+
// If it looks like a UUID, fetch by ID
|
|
838
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(nameOrId || '');
|
|
839
|
+
|
|
840
|
+
let submissions;
|
|
841
|
+
if (isUuid) {
|
|
842
|
+
const res = await authFetch(`${API_BASE}/submissions/${nameOrId}`);
|
|
843
|
+
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
844
|
+
const body = await res.json();
|
|
845
|
+
submissions = body.submission ? [body.submission] : [];
|
|
846
|
+
} else {
|
|
847
|
+
const params = nameOrId ? `?name=${encodeURIComponent(nameOrId)}` : '';
|
|
848
|
+
const res = await authFetch(`${API_BASE}/submissions${params}`);
|
|
849
|
+
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
850
|
+
const body = await res.json();
|
|
851
|
+
submissions = body.submissions || [];
|
|
858
852
|
}
|
|
859
853
|
|
|
860
|
-
const body = await res.json();
|
|
861
|
-
const submissions = body.submissions || [];
|
|
862
|
-
|
|
863
854
|
spinner.stop();
|
|
864
855
|
|
|
865
856
|
if (submissions.length === 0) {
|
|
@@ -873,16 +864,36 @@ async function checkStatus(name) {
|
|
|
873
864
|
for (const sub of submissions) {
|
|
874
865
|
const statusColor = {
|
|
875
866
|
pending_review: chalk.yellow,
|
|
867
|
+
ai_analyzing: chalk.magenta,
|
|
868
|
+
ready_for_review: chalk.blue,
|
|
876
869
|
approved: chalk.green,
|
|
877
|
-
merged: chalk.green.bold,
|
|
878
870
|
rejected: chalk.red,
|
|
879
871
|
changes_requested: chalk.yellow,
|
|
880
872
|
}[sub.status] || chalk.gray;
|
|
881
873
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
874
|
+
const statusLabel = {
|
|
875
|
+
pending_review: 'pending review',
|
|
876
|
+
ai_analyzing: 'AI analyzing...',
|
|
877
|
+
ready_for_review: 'ready for review',
|
|
878
|
+
approved: 'approved',
|
|
879
|
+
rejected: 'rejected',
|
|
880
|
+
changes_requested: 'changes requested',
|
|
881
|
+
}[sub.status] || sub.status;
|
|
882
|
+
|
|
883
|
+
console.log(` ${chalk.white.bold(sub.skillName)} ${statusColor(`[${statusLabel}]`)}`);
|
|
884
|
+
|
|
885
|
+
// Show AI score if available
|
|
886
|
+
if (sub.aiAnalysis?.overallScore != null) {
|
|
887
|
+
const score = sub.aiAnalysis.overallScore;
|
|
888
|
+
const scoreColor = score >= 75 ? chalk.green : score >= 50 ? chalk.yellow : chalk.red;
|
|
889
|
+
console.log(` ${chalk.gray('AI Score:')} ${scoreColor(`${score}/100`)} ${chalk.gray(`(${sub.aiAnalysis.recommendation})`)}`);
|
|
890
|
+
} else if (sub.aiAnalysisStatus === 'running') {
|
|
891
|
+
console.log(` ${chalk.gray('AI Score:')} ${chalk.magenta('analyzing...')}`);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (sub.reviewNotes) console.log(` ${chalk.gray('Review:')} ${sub.reviewNotes}`);
|
|
895
|
+
console.log(` ${chalk.gray('Submitted:')} ${new Date(sub.createdAt).toLocaleDateString()}`);
|
|
896
|
+
if (sub.reviewedAt) console.log(` ${chalk.gray('Reviewed:')} ${new Date(sub.reviewedAt).toLocaleDateString()}`);
|
|
886
897
|
console.log('');
|
|
887
898
|
}
|
|
888
899
|
} catch (err) {
|
package/src/cli/commands/auth.js
CHANGED
|
@@ -9,6 +9,14 @@ import { getAuthData, saveAuth, clearAuth, isAuthenticated } from '../../lib/aut
|
|
|
9
9
|
|
|
10
10
|
const API_BASE = 'https://dev.myai1.ai';
|
|
11
11
|
|
|
12
|
+
function ensureHttpsUrl(url) {
|
|
13
|
+
const value = String(url ?? '').trim();
|
|
14
|
+
if (!value) return value;
|
|
15
|
+
if (/^https:\/\//i.test(value)) return value;
|
|
16
|
+
if (/^http:\/\//i.test(value)) return value.replace(/^http:\/\//i, 'https://');
|
|
17
|
+
return `https://${value.replace(/^\/+/, '')}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export function registerAuthCommands(program) {
|
|
13
21
|
// ── login ───────────────────────────────────────────────────────────
|
|
14
22
|
program
|
|
@@ -31,7 +39,7 @@ export function registerAuthCommands(program) {
|
|
|
31
39
|
if (!initRes.ok) throw new Error(`Server returned ${initRes.status}`);
|
|
32
40
|
const initData = await initRes.json();
|
|
33
41
|
sessionId = initData.sessionId;
|
|
34
|
-
authUrl = initData.authUrl;
|
|
42
|
+
authUrl = ensureHttpsUrl(initData.authUrl);
|
|
35
43
|
} catch (err) {
|
|
36
44
|
console.log(chalk.red(' ✗ Failed to connect to MyAIDev marketplace.'));
|
|
37
45
|
console.log(chalk.gray(` ${err.message}\n`));
|
package/src/config/workflows.js
CHANGED
|
@@ -74,12 +74,17 @@ const WORKFLOWS = {
|
|
|
74
74
|
name: 'Development Workflow',
|
|
75
75
|
description: 'SPARC methodology for systematic software development',
|
|
76
76
|
skills: [
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
77
|
+
'myaidev-workflow',
|
|
78
|
+
'myaidev-architect',
|
|
79
|
+
'myaidev-coder',
|
|
80
|
+
'myaidev-tester',
|
|
81
|
+
'myaidev-reviewer',
|
|
82
|
+
'myaidev-documenter',
|
|
83
|
+
'myaidev-analyze',
|
|
84
|
+
'myaidev-debug',
|
|
85
|
+
'myaidev-refactor',
|
|
86
|
+
'myaidev-performance',
|
|
87
|
+
'myaidev-migrate'
|
|
83
88
|
],
|
|
84
89
|
scripts: [],
|
|
85
90
|
libs: [],
|
package/src/lib/ascii-banner.js
CHANGED
|
@@ -77,10 +77,10 @@ ${chalk.bold.hex('#808080')('─────────────────
|
|
|
77
77
|
*/
|
|
78
78
|
export function getInitSuccessMessage(cliType) {
|
|
79
79
|
const commands = cliType === 'claude'
|
|
80
|
-
? ' • View README: Press Ctrl+Shift+P → "Toggle Documentation"\n • Run workflow: /
|
|
80
|
+
? ' • View README: Press Ctrl+Shift+P → "Toggle Documentation"\n • Run workflow: /myaidev-workflow "Build your feature"'
|
|
81
81
|
: cliType === 'gemini'
|
|
82
|
-
? ' • View commands: ls .gemini/commands/\n • Run workflow: /
|
|
83
|
-
: ' • View commands: ls .opencode/commands/\n • Run workflow: /
|
|
82
|
+
? ' • View commands: ls .gemini/commands/\n • Run workflow: /myaidev-workflow "Build your feature"'
|
|
83
|
+
: ' • View commands: ls .opencode/commands/\n • Run workflow: /myaidev-workflow "Build your feature"';
|
|
84
84
|
|
|
85
85
|
return `
|
|
86
86
|
${chalk.green.bold('✨ Installation Complete!')}
|