myaidev-method 0.3.3 → 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/bin/cli.js +8 -120
- package/package.json +1 -1
- package/skills/content-writer/agents/editor-agent.md +138 -0
- package/skills/content-writer/agents/planner-agent.md +121 -0
- package/skills/content-writer/agents/research-agent.md +83 -0
- package/skills/content-writer/agents/seo-agent.md +139 -0
- package/skills/content-writer/agents/visual-planner-agent.md +110 -0
- package/skills/content-writer/agents/writer-agent.md +85 -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/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/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/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/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/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/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/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/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/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/agents/analyzer-agent.md +317 -0
- package/skills/myaidev-workflow/agents/coordinator-agent.md +253 -0
- package/skills/skill-builder/SKILL.md +417 -0
- package/src/cli/commands/addon.js +86 -123
- package/src/lib/update-manager.js +120 -61
- package/src/templates/claude/CLAUDE.md +124 -0
|
@@ -17,7 +17,7 @@ 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';
|
|
@@ -215,11 +215,12 @@ async function installSkill(name, options) {
|
|
|
215
215
|
const { location } = await inquirer.prompt([{
|
|
216
216
|
type: 'list',
|
|
217
217
|
name: 'location',
|
|
218
|
-
message: '
|
|
218
|
+
message: 'Where do you want to install this skill?',
|
|
219
219
|
choices: [
|
|
220
|
-
{ name: `Project
|
|
221
|
-
{ 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' },
|
|
222
222
|
],
|
|
223
|
+
default: 'project',
|
|
223
224
|
}]);
|
|
224
225
|
targetDir = location === 'project' ? projectSkillsDir() : globalSkillsDir();
|
|
225
226
|
}
|
|
@@ -696,26 +697,10 @@ async function scaffoldSkill() {
|
|
|
696
697
|
return targetDir;
|
|
697
698
|
}
|
|
698
699
|
|
|
699
|
-
function ghAvailable() {
|
|
700
|
-
try {
|
|
701
|
-
execSync('gh auth status', { stdio: 'pipe' });
|
|
702
|
-
return true;
|
|
703
|
-
} catch {
|
|
704
|
-
return false;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
700
|
async function submitSkill(opts) {
|
|
709
|
-
// Step 1: Auth
|
|
701
|
+
// Step 1: Auth check
|
|
710
702
|
if (!(await requireAuth())) return;
|
|
711
703
|
|
|
712
|
-
if (!ghAvailable()) {
|
|
713
|
-
console.log(chalk.red('\n✗ GitHub CLI (gh) is required for submissions.'));
|
|
714
|
-
console.log(chalk.gray(' Install: https://cli.github.com'));
|
|
715
|
-
console.log(chalk.gray(' Then: gh auth login\n'));
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
704
|
// Step 2: Detect or scaffold SKILL.md
|
|
720
705
|
let targetPath = opts.dir ? path.resolve(opts.dir) : process.cwd();
|
|
721
706
|
let skillDir = targetPath;
|
|
@@ -747,14 +732,13 @@ async function submitSkill(opts) {
|
|
|
747
732
|
|
|
748
733
|
const scaffoldedDir = await scaffoldSkill();
|
|
749
734
|
if (!scaffoldedDir) return;
|
|
750
|
-
// User needs to edit the scaffold first
|
|
751
735
|
return;
|
|
752
736
|
}
|
|
753
737
|
|
|
754
|
-
// Step 3:
|
|
738
|
+
// Step 3: Local validation
|
|
755
739
|
const spinner = ora('Validating skill...').start();
|
|
756
740
|
const result = await validateSkill(skillDir, {
|
|
757
|
-
checkDuplicates:
|
|
741
|
+
checkDuplicates: false,
|
|
758
742
|
apiBase: API_BASE,
|
|
759
743
|
});
|
|
760
744
|
spinner.stop();
|
|
@@ -777,11 +761,11 @@ async function submitSkill(opts) {
|
|
|
777
761
|
}
|
|
778
762
|
|
|
779
763
|
// Step 4: Show summary and confirm
|
|
780
|
-
const slug = result.info.slug;
|
|
781
764
|
console.log(chalk.cyan('\n📤 Submit to MyAIDev Marketplace\n'));
|
|
782
765
|
console.log(` ${chalk.gray('Name:')} ${chalk.white.bold(result.info.name)}`);
|
|
783
|
-
console.log(` ${chalk.gray('Slug:')} ${chalk.white(slug)}`);
|
|
766
|
+
console.log(` ${chalk.gray('Slug:')} ${chalk.white(result.info.slug)}`);
|
|
784
767
|
console.log(` ${chalk.gray('Description:')} ${chalk.white(truncate(result.info.description, 60))}`);
|
|
768
|
+
console.log(` ${chalk.gray('Category:')} ${result.info.category || 'general'}`);
|
|
785
769
|
console.log(` ${chalk.gray('Sections:')} ${result.info.sectionCount}`);
|
|
786
770
|
console.log('');
|
|
787
771
|
|
|
@@ -798,116 +782,75 @@ async function submitSkill(opts) {
|
|
|
798
782
|
}
|
|
799
783
|
}
|
|
800
784
|
|
|
801
|
-
// Step 5:
|
|
802
|
-
const
|
|
803
|
-
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();
|
|
804
787
|
|
|
805
788
|
try {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const ghUser = execSync('gh api user --jq .login', { encoding: 'utf8' }).trim();
|
|
815
|
-
|
|
816
|
-
// Clone the user's fork
|
|
817
|
-
execSync(`gh repo clone ${ghUser}/myaidev-marketplace "${tmpDir}" -- --depth 1`, { stdio: 'pipe' });
|
|
818
|
-
|
|
819
|
-
// Create branch
|
|
820
|
-
const branch = `skill/${slug}`;
|
|
821
|
-
execSync(`git -C "${tmpDir}" checkout -b ${branch}`, { stdio: 'pipe' });
|
|
822
|
-
|
|
823
|
-
// Copy skill files
|
|
824
|
-
const destDir = path.join(tmpDir, 'skills', slug);
|
|
825
|
-
await fs.ensureDir(destDir);
|
|
826
|
-
await fs.copy(skillDir, destDir, {
|
|
827
|
-
filter: (src) => {
|
|
828
|
-
const ext = path.extname(src).toLowerCase();
|
|
829
|
-
const base = path.basename(src);
|
|
830
|
-
// Copy directories and allowed file types
|
|
831
|
-
if (fs.statSync(src).isDirectory()) return true;
|
|
832
|
-
return ['.md', '.json', '.yaml', '.yml', '.sh', '.js', '.ts', '.py', '.txt'].includes(ext) || base === '.gitkeep';
|
|
833
|
-
},
|
|
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
|
+
}),
|
|
834
797
|
});
|
|
835
798
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
prSpinner.succeed(chalk.green('Pull request created!'));
|
|
862
|
-
|
|
863
|
-
// Extract PR URL from output
|
|
864
|
-
const prUrl = prOutput.match(/https:\/\/github\.com\S+/)?.[0] || prOutput;
|
|
865
|
-
console.log(chalk.gray(` PR: ${prUrl}`));
|
|
866
|
-
|
|
867
|
-
// Step 6: Record submission
|
|
868
|
-
try {
|
|
869
|
-
await authFetch(`${API_BASE}/submissions`, {
|
|
870
|
-
method: 'POST',
|
|
871
|
-
body: JSON.stringify({
|
|
872
|
-
skillName: result.info.name,
|
|
873
|
-
slug,
|
|
874
|
-
description: result.info.description || '',
|
|
875
|
-
prUrl,
|
|
876
|
-
}),
|
|
877
|
-
});
|
|
878
|
-
} catch {
|
|
879
|
-
// Non-fatal: submission tracking is optional
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
console.log(chalk.cyan('\n Status: awaiting review'));
|
|
883
|
-
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`));
|
|
884
823
|
} catch (err) {
|
|
885
|
-
|
|
824
|
+
submitSpinner.fail(chalk.red(`Submission failed: ${err.message}`));
|
|
886
825
|
process.exit(1);
|
|
887
|
-
} finally {
|
|
888
|
-
// Clean up temp directory
|
|
889
|
-
await fs.remove(tmpDir).catch(() => {});
|
|
890
826
|
}
|
|
891
827
|
}
|
|
892
828
|
|
|
893
829
|
// ── Status Command ─────────────────────────────────────────────────────
|
|
894
830
|
|
|
895
|
-
async function checkStatus(
|
|
831
|
+
async function checkStatus(nameOrId) {
|
|
896
832
|
if (!(await requireAuth())) return;
|
|
897
833
|
|
|
898
834
|
const spinner = ora('Fetching submission status...').start();
|
|
899
835
|
|
|
900
836
|
try {
|
|
901
|
-
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
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 || [];
|
|
906
852
|
}
|
|
907
853
|
|
|
908
|
-
const body = await res.json();
|
|
909
|
-
const submissions = body.submissions || [];
|
|
910
|
-
|
|
911
854
|
spinner.stop();
|
|
912
855
|
|
|
913
856
|
if (submissions.length === 0) {
|
|
@@ -921,16 +864,36 @@ async function checkStatus(name) {
|
|
|
921
864
|
for (const sub of submissions) {
|
|
922
865
|
const statusColor = {
|
|
923
866
|
pending_review: chalk.yellow,
|
|
867
|
+
ai_analyzing: chalk.magenta,
|
|
868
|
+
ready_for_review: chalk.blue,
|
|
924
869
|
approved: chalk.green,
|
|
925
|
-
merged: chalk.green.bold,
|
|
926
870
|
rejected: chalk.red,
|
|
927
871
|
changes_requested: chalk.yellow,
|
|
928
872
|
}[sub.status] || chalk.gray;
|
|
929
873
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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()}`);
|
|
934
897
|
console.log('');
|
|
935
898
|
}
|
|
936
899
|
} catch (err) {
|
|
@@ -9,6 +9,11 @@ import { createHash } from 'crypto';
|
|
|
9
9
|
* Handles intelligent updates with conflict resolution
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
/** Pluralize helper: returns "1 file" or "N files" */
|
|
13
|
+
function pf(count) {
|
|
14
|
+
return `${count} file${count === 1 ? '' : 's'}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* Display ASCII art branding banner
|
|
14
19
|
*/
|
|
@@ -234,7 +239,11 @@ export async function updateComponent(componentType, projectDir, cliType, source
|
|
|
234
239
|
break;
|
|
235
240
|
case 'mcp':
|
|
236
241
|
targetDir = path.join(projectDir, `.${cliType}`, 'mcp');
|
|
237
|
-
sourceSubDir = path.join(sourceDir, '
|
|
242
|
+
sourceSubDir = path.join(sourceDir, 'src', 'mcp');
|
|
243
|
+
break;
|
|
244
|
+
case 'config':
|
|
245
|
+
targetDir = path.join(projectDir, `.${cliType}`);
|
|
246
|
+
sourceSubDir = path.join(sourceDir, 'src', 'templates', 'claude');
|
|
238
247
|
break;
|
|
239
248
|
case 'docs':
|
|
240
249
|
targetDir = projectDir;
|
|
@@ -417,6 +426,10 @@ async function getLineCount(filePath) {
|
|
|
417
426
|
async function getComponentFiles(componentType, dir) {
|
|
418
427
|
if (!await fs.pathExists(dir)) return [];
|
|
419
428
|
|
|
429
|
+
if (componentType === 'config') {
|
|
430
|
+
return ['CLAUDE.md'];
|
|
431
|
+
}
|
|
432
|
+
|
|
420
433
|
if (componentType === 'docs') {
|
|
421
434
|
return ['USER_GUIDE.md', 'PUBLISHING_GUIDE.md', 'COOLIFY_DEPLOYMENT.md', 'DEV_WORKFLOW_GUIDE.md',
|
|
422
435
|
'MCP_INTEGRATION.md', 'WORDPRESS_ADMIN_SCRIPTS.md', 'TECHNICAL_ARCHITECTURE.md',
|
|
@@ -488,7 +501,11 @@ export async function generateChangeSummary(components, projectDir, cliType, sou
|
|
|
488
501
|
break;
|
|
489
502
|
case 'mcp':
|
|
490
503
|
targetDir = path.join(projectDir, `.${cliType}`, 'mcp');
|
|
491
|
-
sourceSubDir = path.join(sourceDir, '
|
|
504
|
+
sourceSubDir = path.join(sourceDir, 'src', 'mcp');
|
|
505
|
+
break;
|
|
506
|
+
case 'config':
|
|
507
|
+
targetDir = path.join(projectDir, `.${cliType}`);
|
|
508
|
+
sourceSubDir = path.join(sourceDir, 'src', 'templates', 'claude');
|
|
492
509
|
break;
|
|
493
510
|
case 'docs':
|
|
494
511
|
targetDir = projectDir;
|
|
@@ -592,16 +609,41 @@ export function displayChangeSummary(summary, currentVersion, newVersion) {
|
|
|
592
609
|
// Change summary
|
|
593
610
|
console.log(chalk.cyan('║') + chalk.white.bold(' 📊 CHANGE SUMMARY'.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
594
611
|
|
|
595
|
-
const unchangedLine = ` ├─ ✅ Unchanged: ${summary.unchanged.length}
|
|
596
|
-
const newLine = ` ├─ 🆕 New
|
|
597
|
-
const modifiedLine = ` ├─ ⚠️ Modified: ${summary.modified.length}
|
|
598
|
-
const deletedLine = ` └─ 🗑️ Removed: ${summary.deleted.length}
|
|
612
|
+
const unchangedLine = ` ├─ ✅ Unchanged: ${pf(summary.unchanged.length)} (no action needed)`;
|
|
613
|
+
const newLine = ` ├─ 🆕 New: ${pf(summary.new.length)} (will be added)`;
|
|
614
|
+
const modifiedLine = ` ├─ ⚠️ Modified: ${pf(summary.modified.length)} (need decision)`;
|
|
615
|
+
const deletedLine = ` └─ 🗑️ Removed: ${pf(summary.deleted.length)} (optional cleanup)`;
|
|
599
616
|
|
|
600
617
|
console.log(chalk.cyan('║') + chalk.gray(unchangedLine.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
601
618
|
console.log(chalk.cyan('║') + chalk.green(newLine.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
602
619
|
console.log(chalk.cyan('║') + chalk.yellow(modifiedLine.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
603
620
|
console.log(chalk.cyan('║') + chalk.red(deletedLine.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
604
621
|
|
|
622
|
+
// Show new skills if any (users care about what's new)
|
|
623
|
+
const newSkills = summary.new
|
|
624
|
+
.filter(f => f.componentType === 'skills' && f.file.endsWith('SKILL.md'))
|
|
625
|
+
.map(f => f.file.split('/')[0]);
|
|
626
|
+
if (newSkills.length > 0) {
|
|
627
|
+
console.log(chalk.cyan('║') + ' '.repeat(boxWidth) + chalk.cyan('║'));
|
|
628
|
+
const newSkillsLabel = ` 🎯 New skills available:`;
|
|
629
|
+
console.log(chalk.cyan('║') + chalk.white.bold(newSkillsLabel.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
630
|
+
// Wrap skill names into lines that fit the box (max ~63 chars per line)
|
|
631
|
+
const maxLineLen = boxWidth - 6; // " " prefix + 1 padding
|
|
632
|
+
let currentLine = '';
|
|
633
|
+
for (let i = 0; i < newSkills.length; i++) {
|
|
634
|
+
const separator = currentLine ? ', ' : '';
|
|
635
|
+
if ((currentLine + separator + newSkills[i]).length > maxLineLen) {
|
|
636
|
+
console.log(chalk.cyan('║') + chalk.green(` ${currentLine}`.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
637
|
+
currentLine = newSkills[i];
|
|
638
|
+
} else {
|
|
639
|
+
currentLine += separator + newSkills[i];
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (currentLine) {
|
|
643
|
+
console.log(chalk.cyan('║') + chalk.green(` ${currentLine}`.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
605
647
|
// Show modified files by category if any
|
|
606
648
|
if (summary.modified.length > 0) {
|
|
607
649
|
console.log(chalk.cyan('║') + ' '.repeat(boxWidth) + chalk.cyan('║'));
|
|
@@ -612,7 +654,7 @@ export function displayChangeSummary(summary, currentVersion, newVersion) {
|
|
|
612
654
|
if (data.modified.length === 0) continue;
|
|
613
655
|
|
|
614
656
|
const catName = category.toUpperCase();
|
|
615
|
-
const catLine = ` │ ${catName} (${data.modified.length}
|
|
657
|
+
const catLine = ` │ ${catName} (${pf(data.modified.length)})`;
|
|
616
658
|
console.log(chalk.cyan('║') + chalk.white.bold(catLine.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
617
659
|
|
|
618
660
|
for (let i = 0; i < data.modified.length && i < 3; i++) {
|
|
@@ -708,7 +750,7 @@ export async function promptCategoryDecision(category, files) {
|
|
|
708
750
|
const botBorder = '╚' + '═'.repeat(boxWidth) + '╝';
|
|
709
751
|
|
|
710
752
|
console.log(chalk.cyan(topBorder));
|
|
711
|
-
console.log(chalk.cyan('║') + chalk.white.bold(` ${category.toUpperCase()} (${files.length} modified
|
|
753
|
+
console.log(chalk.cyan('║') + chalk.white.bold(` ${category.toUpperCase()} (${pf(files.length)} modified)`.padEnd(boxWidth)) + chalk.cyan('║'));
|
|
712
754
|
console.log(chalk.cyan(midBorder));
|
|
713
755
|
|
|
714
756
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -730,9 +772,9 @@ export async function promptCategoryDecision(category, files) {
|
|
|
730
772
|
name: 'decision',
|
|
731
773
|
message: `What would you like to do with ${category.toUpperCase()}?`,
|
|
732
774
|
choices: [
|
|
733
|
-
{ name: `Keep all my customizations (skip ${files.length}
|
|
734
|
-
{ name: `Update all (replace ${files.length}
|
|
735
|
-
{ name: `Review these ${files.length} files individually`, value: 'individual' }
|
|
775
|
+
{ name: `Keep all my customizations (skip ${pf(files.length)})`, value: 'keep' },
|
|
776
|
+
{ name: `Update all (replace ${pf(files.length)}, backup originals)`, value: 'update' },
|
|
777
|
+
{ name: `Review ${files.length === 1 ? 'this file' : `these ${files.length} files`} individually`, value: 'individual' }
|
|
736
778
|
]
|
|
737
779
|
}
|
|
738
780
|
]);
|
|
@@ -755,32 +797,24 @@ export async function executeSmartUpdate(summary, backupDir, options = {}) {
|
|
|
755
797
|
backedUp: []
|
|
756
798
|
};
|
|
757
799
|
|
|
758
|
-
// Add all new files
|
|
800
|
+
// Add all new files — always show progress
|
|
759
801
|
for (const f of summary.new) {
|
|
760
802
|
await fs.ensureDir(path.dirname(f.targetPath));
|
|
761
803
|
await fs.copy(f.sourcePath, f.targetPath);
|
|
762
804
|
results.added.push(f.file);
|
|
763
|
-
|
|
764
|
-
console.log(chalk.green(` ➕ ${f.file} (new)`));
|
|
765
|
-
}
|
|
805
|
+
console.log(chalk.green(` ➕ ${f.file}`));
|
|
766
806
|
}
|
|
767
807
|
|
|
768
|
-
// Keep all modified files (smart = preserve customizations)
|
|
808
|
+
// Keep all modified files (smart = preserve customizations) — always show
|
|
769
809
|
for (const f of summary.modified) {
|
|
770
|
-
// Create backup
|
|
771
810
|
const backupPath = path.join(backupDir, f.componentType, f.file);
|
|
772
811
|
await fs.ensureDir(path.dirname(backupPath));
|
|
773
812
|
await fs.copy(f.targetPath, backupPath);
|
|
774
813
|
results.backedUp.push(f.file);
|
|
775
814
|
results.kept.push(f.file);
|
|
776
|
-
|
|
777
|
-
console.log(chalk.gray(` 🛡️ ${f.file} (kept, backup created)`));
|
|
778
|
-
}
|
|
815
|
+
console.log(chalk.gray(` 🛡️ ${f.file} (kept, backup created)`));
|
|
779
816
|
}
|
|
780
817
|
|
|
781
|
-
// Update unchanged files (they're identical so no-op, but count them)
|
|
782
|
-
results.updated = summary.unchanged.map(f => f.file);
|
|
783
|
-
|
|
784
818
|
return results;
|
|
785
819
|
}
|
|
786
820
|
|
|
@@ -799,30 +833,24 @@ export async function executeAcceptAll(summary, backupDir, options = {}) {
|
|
|
799
833
|
backedUp: []
|
|
800
834
|
};
|
|
801
835
|
|
|
802
|
-
// Add all new files
|
|
836
|
+
// Add all new files — always show progress
|
|
803
837
|
for (const f of summary.new) {
|
|
804
838
|
await fs.ensureDir(path.dirname(f.targetPath));
|
|
805
839
|
await fs.copy(f.sourcePath, f.targetPath);
|
|
806
840
|
results.added.push(f.file);
|
|
807
|
-
|
|
808
|
-
console.log(chalk.green(` ➕ ${f.file} (new)`));
|
|
809
|
-
}
|
|
841
|
+
console.log(chalk.green(` ➕ ${f.file}`));
|
|
810
842
|
}
|
|
811
843
|
|
|
812
|
-
// Replace all modified files with backups
|
|
844
|
+
// Replace all modified files with backups — always show progress
|
|
813
845
|
for (const f of summary.modified) {
|
|
814
|
-
// Create backup
|
|
815
846
|
const backupPath = path.join(backupDir, f.componentType, f.file);
|
|
816
847
|
await fs.ensureDir(path.dirname(backupPath));
|
|
817
848
|
await fs.copy(f.targetPath, backupPath);
|
|
818
849
|
results.backedUp.push(f.file);
|
|
819
850
|
|
|
820
|
-
// Replace with new version
|
|
821
851
|
await fs.copy(f.sourcePath, f.targetPath);
|
|
822
852
|
results.updated.push(f.file);
|
|
823
|
-
|
|
824
|
-
console.log(chalk.blue(` ✅ ${f.file} (updated, backup created)`));
|
|
825
|
-
}
|
|
853
|
+
console.log(chalk.blue(` ✅ ${f.file} (updated)`));
|
|
826
854
|
}
|
|
827
855
|
|
|
828
856
|
return results;
|
|
@@ -842,22 +870,18 @@ export async function executeKeepAll(summary, options = {}) {
|
|
|
842
870
|
backedUp: []
|
|
843
871
|
};
|
|
844
872
|
|
|
845
|
-
// Add all new files
|
|
873
|
+
// Add all new files — always show progress
|
|
846
874
|
for (const f of summary.new) {
|
|
847
875
|
await fs.ensureDir(path.dirname(f.targetPath));
|
|
848
876
|
await fs.copy(f.sourcePath, f.targetPath);
|
|
849
877
|
results.added.push(f.file);
|
|
850
|
-
|
|
851
|
-
console.log(chalk.green(` ➕ ${f.file} (new)`));
|
|
852
|
-
}
|
|
878
|
+
console.log(chalk.green(` ➕ ${f.file}`));
|
|
853
879
|
}
|
|
854
880
|
|
|
855
|
-
// Keep all modified files
|
|
881
|
+
// Keep all modified files — always show
|
|
856
882
|
results.kept = summary.modified.map(f => f.file);
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
console.log(chalk.gray(` 🛡️ ${f.file} (kept)`));
|
|
860
|
-
}
|
|
883
|
+
for (const f of summary.modified) {
|
|
884
|
+
console.log(chalk.gray(` 🛡️ ${f.file} (kept)`));
|
|
861
885
|
}
|
|
862
886
|
|
|
863
887
|
return results;
|
|
@@ -958,34 +982,69 @@ export function displayUpdateResults(results, backupDir, newVersion) {
|
|
|
958
982
|
console.log(chalk.green('║') + chalk.white.bold(' ✅ Update Complete! '.padEnd(boxWidth)) + chalk.green('║'));
|
|
959
983
|
console.log(chalk.green(midBorder));
|
|
960
984
|
|
|
961
|
-
|
|
985
|
+
const totalChanges = results.updated.length + results.added.length + results.kept.length;
|
|
962
986
|
|
|
963
|
-
if (
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
987
|
+
if (totalChanges === 0) {
|
|
988
|
+
console.log(chalk.green('║') + chalk.white(' ✨ Everything is already current — no changes needed.'.padEnd(boxWidth)) + chalk.green('║'));
|
|
989
|
+
} else {
|
|
990
|
+
console.log(chalk.green('║') + chalk.white.bold(' 📊 Results:'.padEnd(boxWidth)) + chalk.green('║'));
|
|
967
991
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
992
|
+
const lines = [];
|
|
993
|
+
if (results.updated.length > 0) {
|
|
994
|
+
lines.push({ color: 'blue', text: `Updated: ${pf(results.updated.length)}` });
|
|
995
|
+
}
|
|
996
|
+
if (results.kept.length > 0) {
|
|
997
|
+
lines.push({ color: 'gray', text: `Kept: ${pf(results.kept.length)} (your customizations preserved)` });
|
|
998
|
+
}
|
|
999
|
+
if (results.added.length > 0) {
|
|
1000
|
+
lines.push({ color: 'green', text: `Added: ${pf(results.added.length)}` });
|
|
1001
|
+
}
|
|
1002
|
+
if (results.backedUp.length > 0) {
|
|
1003
|
+
const relativePath = path.relative(process.cwd(), backupDir);
|
|
1004
|
+
lines.push({ color: 'yellow', text: `Backed up: ${pf(results.backedUp.length)} → ${relativePath}/` });
|
|
1005
|
+
}
|
|
972
1006
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1007
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1008
|
+
const prefix = i === lines.length - 1 ? '└─' : '├─';
|
|
1009
|
+
const line = ` ${prefix} ${lines[i].text}`;
|
|
1010
|
+
console.log(chalk.green('║') + chalk[lines[i].color](line.padEnd(boxWidth)) + chalk.green('║'));
|
|
1011
|
+
}
|
|
977
1012
|
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
1013
|
+
// Show new skills that were added
|
|
1014
|
+
const newSkills = results.added
|
|
1015
|
+
.filter(f => f.endsWith('SKILL.md'))
|
|
1016
|
+
.map(f => f.split('/')[0]);
|
|
1017
|
+
if (newSkills.length > 0) {
|
|
1018
|
+
console.log(chalk.green('║') + ' '.repeat(boxWidth) + chalk.green('║'));
|
|
1019
|
+
const label = ` 🎯 New skills installed:`;
|
|
1020
|
+
console.log(chalk.green('║') + chalk.white.bold(label.padEnd(boxWidth)) + chalk.green('║'));
|
|
1021
|
+
const maxLineLen = boxWidth - 6;
|
|
1022
|
+
let currentLine = '';
|
|
1023
|
+
for (let i = 0; i < newSkills.length; i++) {
|
|
1024
|
+
const separator = currentLine ? ', ' : '';
|
|
1025
|
+
if ((currentLine + separator + newSkills[i]).length > maxLineLen) {
|
|
1026
|
+
console.log(chalk.green('║') + chalk.cyan(` ${currentLine}`.padEnd(boxWidth)) + chalk.green('║'));
|
|
1027
|
+
currentLine = newSkills[i];
|
|
1028
|
+
} else {
|
|
1029
|
+
currentLine += separator + newSkills[i];
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (currentLine) {
|
|
1033
|
+
console.log(chalk.green('║') + chalk.cyan(` ${currentLine}`.padEnd(boxWidth)) + chalk.green('║'));
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
982
1036
|
}
|
|
983
1037
|
|
|
984
1038
|
console.log(chalk.green('║') + ' '.repeat(boxWidth) + chalk.green('║'));
|
|
985
1039
|
|
|
986
|
-
//
|
|
1040
|
+
// Version and next steps
|
|
987
1041
|
console.log(chalk.green('║') + chalk.white.bold(` 🆕 Updated to v${newVersion}`.padEnd(boxWidth)) + chalk.green('║'));
|
|
988
|
-
|
|
1042
|
+
|
|
1043
|
+
// Only show restart reminder if skills were actually changed
|
|
1044
|
+
const skillsChanged = results.added.some(f => f.includes('/')) || results.updated.some(f => f.includes('/'));
|
|
1045
|
+
if (skillsChanged) {
|
|
1046
|
+
console.log(chalk.green('║') + chalk.yellow(' ⚠️ Restart your AI assistant to load updated skills'.padEnd(boxWidth)) + chalk.green('║'));
|
|
1047
|
+
}
|
|
989
1048
|
|
|
990
1049
|
console.log(chalk.green(botBorder));
|
|
991
1050
|
console.log('');
|