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.
Files changed (50) hide show
  1. package/bin/cli.js +8 -120
  2. package/package.json +1 -1
  3. package/skills/content-writer/agents/editor-agent.md +138 -0
  4. package/skills/content-writer/agents/planner-agent.md +121 -0
  5. package/skills/content-writer/agents/research-agent.md +83 -0
  6. package/skills/content-writer/agents/seo-agent.md +139 -0
  7. package/skills/content-writer/agents/visual-planner-agent.md +110 -0
  8. package/skills/content-writer/agents/writer-agent.md +85 -0
  9. package/skills/myaidev-analyze/agents/dependency-mapper-agent.md +236 -0
  10. package/skills/myaidev-analyze/agents/pattern-detector-agent.md +240 -0
  11. package/skills/myaidev-analyze/agents/structure-scanner-agent.md +171 -0
  12. package/skills/myaidev-analyze/agents/tech-profiler-agent.md +291 -0
  13. package/skills/myaidev-architect/agents/compliance-checker-agent.md +287 -0
  14. package/skills/myaidev-architect/agents/requirements-analyst-agent.md +194 -0
  15. package/skills/myaidev-architect/agents/system-designer-agent.md +315 -0
  16. package/skills/myaidev-coder/agents/implementer-agent.md +185 -0
  17. package/skills/myaidev-coder/agents/integration-agent.md +168 -0
  18. package/skills/myaidev-coder/agents/pattern-scanner-agent.md +161 -0
  19. package/skills/myaidev-coder/agents/self-reviewer-agent.md +168 -0
  20. package/skills/myaidev-debug/agents/fix-agent-debug.md +317 -0
  21. package/skills/myaidev-debug/agents/hypothesis-agent.md +226 -0
  22. package/skills/myaidev-debug/agents/investigator-agent.md +250 -0
  23. package/skills/myaidev-debug/agents/symptom-collector-agent.md +231 -0
  24. package/skills/myaidev-documenter/agents/code-reader-agent.md +172 -0
  25. package/skills/myaidev-documenter/agents/doc-validator-agent.md +174 -0
  26. package/skills/myaidev-documenter/agents/doc-writer-agent.md +379 -0
  27. package/skills/myaidev-migrate/agents/migration-planner-agent.md +237 -0
  28. package/skills/myaidev-migrate/agents/migration-writer-agent.md +248 -0
  29. package/skills/myaidev-migrate/agents/schema-analyzer-agent.md +190 -0
  30. package/skills/myaidev-performance/agents/benchmark-agent.md +281 -0
  31. package/skills/myaidev-performance/agents/optimizer-agent.md +277 -0
  32. package/skills/myaidev-performance/agents/profiler-agent.md +252 -0
  33. package/skills/myaidev-refactor/agents/refactor-executor-agent.md +221 -0
  34. package/skills/myaidev-refactor/agents/refactor-planner-agent.md +213 -0
  35. package/skills/myaidev-refactor/agents/regression-guard-agent.md +242 -0
  36. package/skills/myaidev-refactor/agents/smell-detector-agent.md +233 -0
  37. package/skills/myaidev-reviewer/agents/auto-fixer-agent.md +238 -0
  38. package/skills/myaidev-reviewer/agents/code-analyst-agent.md +220 -0
  39. package/skills/myaidev-reviewer/agents/security-scanner-agent.md +262 -0
  40. package/skills/myaidev-tester/agents/coverage-analyst-agent.md +163 -0
  41. package/skills/myaidev-tester/agents/tdd-driver-agent.md +242 -0
  42. package/skills/myaidev-tester/agents/test-runner-agent.md +176 -0
  43. package/skills/myaidev-tester/agents/test-strategist-agent.md +154 -0
  44. package/skills/myaidev-tester/agents/test-writer-agent.md +242 -0
  45. package/skills/myaidev-workflow/agents/analyzer-agent.md +317 -0
  46. package/skills/myaidev-workflow/agents/coordinator-agent.md +253 -0
  47. package/skills/skill-builder/SKILL.md +417 -0
  48. package/src/cli/commands/addon.js +86 -123
  49. package/src/lib/update-manager.js +120 -61
  50. 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
- import { execSync } from 'child_process';
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: 'Install location:',
218
+ message: 'Where do you want to install this skill?',
219
219
  choices: [
220
- { name: `Project ${chalk.gray('.claude/skills/')}`, value: 'project' },
221
- { name: `Global ${chalk.gray('~/.claude/skills/')}`, value: '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 checks
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: Validate
738
+ // Step 3: Local validation
755
739
  const spinner = ora('Validating skill...').start();
756
740
  const result = await validateSkill(skillDir, {
757
- checkDuplicates: true,
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: Fork, branch, commit, PR
802
- const prSpinner = ora('Creating pull request...').start();
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
- // Fork and clone
807
- try {
808
- execSync(`gh repo fork ${MARKETPLACE_REPO} --clone=false`, { stdio: 'pipe' });
809
- } catch {
810
- // Fork may already exist, that's fine
811
- }
812
-
813
- // Get the user's GitHub username
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
- // Commit
837
- execSync(`git -C "${tmpDir}" add skills/${slug}`, { stdio: 'pipe' });
838
- execSync(`git -C "${tmpDir}" commit -m "feat: add skill ${result.info.name}"`, { stdio: 'pipe' });
839
-
840
- // Push
841
- execSync(`git -C "${tmpDir}" push -u origin ${branch}`, { stdio: 'pipe' });
842
-
843
- // Create PR
844
- const prBody = [
845
- `## Skill Submission: ${result.info.name}`,
846
- '',
847
- `**Description**: ${result.info.description || 'N/A'}`,
848
- `**Category**: ${result.info.category || 'general'}`,
849
- '',
850
- '### Checklist',
851
- '- [x] SKILL.md has valid YAML frontmatter',
852
- '- [x] Passes local validation',
853
- '- [ ] Tested locally with Claude Code',
854
- ].join('\n');
855
-
856
- const prOutput = execSync(
857
- `gh pr create --repo ${MARKETPLACE_REPO} --head ${ghUser}:${branch} --title "feat: add skill ${result.info.name}" --body "${prBody.replace(/"/g, '\\"')}"`,
858
- { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
859
- ).trim();
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
- prSpinner.fail(chalk.red(`Submission failed: ${err.message}`));
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(name) {
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
- const params = name ? `?name=${encodeURIComponent(name)}` : '';
902
- const res = await authFetch(`${API_BASE}/submissions${params}`);
903
-
904
- if (!res.ok) {
905
- throw new Error(`API error: ${res.status}`);
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
- console.log(` ${chalk.white.bold(sub.skillName)} ${statusColor(`[${sub.status}]`)}`);
931
- if (sub.prUrl) console.log(chalk.gray(` PR: ${sub.prUrl}`));
932
- if (sub.reviewNotes) console.log(chalk.gray(` Notes: ${sub.reviewNotes}`));
933
- console.log(chalk.gray(` Submitted: ${new Date(sub.createdAt).toLocaleDateString()}`));
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, '.claude', 'mcp');
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, '.claude', 'mcp');
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} files (auto-skip)`;
596
- const newLine = ` ├─ 🆕 New files: ${summary.new.length} files (auto-add)`;
597
- const modifiedLine = ` ├─ ⚠️ Modified: ${summary.modified.length} files (need decision)`;
598
- const deletedLine = ` └─ 🗑️ Removed: ${summary.deleted.length} files (optional delete)`;
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} files)`;
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 files)`.padEnd(boxWidth)) + chalk.cyan('║'));
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} files)`, value: 'keep' },
734
- { name: `Update all (replace ${files.length} files, backup originals)`, value: 'update' },
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
- if (options.verbose) {
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
- if (options.verbose) {
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
- if (options.verbose) {
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
- if (options.verbose) {
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
- if (options.verbose) {
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
- if (options.verbose) {
858
- for (const f of summary.modified) {
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
- console.log(chalk.green('║') + chalk.white.bold(' 📊 Results:'.padEnd(boxWidth)) + chalk.green('║'));
985
+ const totalChanges = results.updated.length + results.added.length + results.kept.length;
962
986
 
963
- if (results.updated.length > 0) {
964
- const line = ` ├─ Updated: ${results.updated.length} files`;
965
- console.log(chalk.green('║') + chalk.blue(line.padEnd(boxWidth)) + chalk.green('║'));
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
- if (results.kept.length > 0) {
969
- const line = ` ├─ Kept: ${results.kept.length} files (your customizations preserved)`;
970
- console.log(chalk.green('') + chalk.gray(line.padEnd(boxWidth)) + chalk.green('║'));
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
- if (results.added.length > 0) {
974
- const line = ` ├─ Added: ${results.added.length} new files`;
975
- console.log(chalk.green('║') + chalk.green(line.padEnd(boxWidth)) + chalk.green('║'));
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
- if (results.backedUp.length > 0) {
979
- const relativePath = path.relative(process.cwd(), backupDir);
980
- const line = ` └─ Backed up: ${results.backedUp.length} files → ${relativePath}/`;
981
- console.log(chalk.green('║') + chalk.yellow(line.padEnd(boxWidth)) + chalk.green(''));
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
- // What's new section
1040
+ // Version and next steps
987
1041
  console.log(chalk.green('║') + chalk.white.bold(` 🆕 Updated to v${newVersion}`.padEnd(boxWidth)) + chalk.green('║'));
988
- console.log(chalk.green('║') + chalk.gray(' Run /myai-configure --status to see your configuration'.padEnd(boxWidth)) + chalk.green('║'));
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('');