@hobui/viui-cli 0.0.6 → 0.0.7

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 (240) hide show
  1. package/README.md +138 -139
  2. package/dist/adapters/adapter-registry.d.ts +12 -0
  3. package/dist/adapters/adapter-registry.d.ts.map +1 -0
  4. package/dist/adapters/adapter-registry.js +49 -0
  5. package/dist/adapters/adapter-types.d.ts +20 -0
  6. package/dist/adapters/adapter-types.d.ts.map +1 -0
  7. package/dist/adapters/adapter-types.js +1 -0
  8. package/dist/adapters/aider-adapter.d.ts +3 -0
  9. package/dist/adapters/aider-adapter.d.ts.map +1 -0
  10. package/dist/adapters/aider-adapter.js +8 -0
  11. package/dist/adapters/claude-adapter.d.ts +3 -0
  12. package/dist/adapters/claude-adapter.d.ts.map +1 -0
  13. package/dist/adapters/claude-adapter.js +15 -0
  14. package/dist/adapters/cline-adapter.d.ts +3 -0
  15. package/dist/adapters/cline-adapter.d.ts.map +1 -0
  16. package/dist/adapters/cline-adapter.js +8 -0
  17. package/dist/adapters/copilot-adapter.d.ts +5 -0
  18. package/dist/adapters/copilot-adapter.d.ts.map +1 -0
  19. package/dist/adapters/copilot-adapter.js +20 -0
  20. package/dist/adapters/cursor-adapter.d.ts +3 -0
  21. package/dist/adapters/cursor-adapter.d.ts.map +1 -0
  22. package/dist/adapters/cursor-adapter.js +18 -0
  23. package/dist/adapters/external/bolt-adapter.d.ts +3 -0
  24. package/dist/adapters/external/bolt-adapter.d.ts.map +1 -0
  25. package/dist/adapters/external/bolt-adapter.js +15 -0
  26. package/dist/adapters/external/chatgpt-adapter.d.ts +3 -0
  27. package/dist/adapters/external/chatgpt-adapter.d.ts.map +1 -0
  28. package/dist/adapters/external/chatgpt-adapter.js +14 -0
  29. package/dist/adapters/external/external-adapter-base.d.ts +15 -0
  30. package/dist/adapters/external/external-adapter-base.d.ts.map +1 -0
  31. package/dist/adapters/external/external-adapter-base.js +92 -0
  32. package/dist/adapters/external/gemini-adapter.d.ts +3 -0
  33. package/dist/adapters/external/gemini-adapter.d.ts.map +1 -0
  34. package/dist/adapters/external/gemini-adapter.js +14 -0
  35. package/dist/adapters/external/lovable-adapter.d.ts +3 -0
  36. package/dist/adapters/external/lovable-adapter.d.ts.map +1 -0
  37. package/dist/adapters/external/lovable-adapter.js +14 -0
  38. package/dist/adapters/external/v0-adapter.d.ts +3 -0
  39. package/dist/adapters/external/v0-adapter.d.ts.map +1 -0
  40. package/dist/adapters/external/v0-adapter.js +15 -0
  41. package/dist/adapters/windsurf-adapter.d.ts +3 -0
  42. package/dist/adapters/windsurf-adapter.d.ts.map +1 -0
  43. package/dist/adapters/windsurf-adapter.js +23 -0
  44. package/dist/assets/plugins/viui-conf/apply-theme-body.ts +23 -4
  45. package/dist/assets/plugins/viui-conf/defaults/README.md +2 -0
  46. package/dist/assets/plugins/viui-conf/defaults/app-bar.ts +1 -1
  47. package/dist/assets/plugins/viui-conf/defaults/buttons.ts +1 -1
  48. package/dist/assets/plugins/viui-conf/defaults/by-theme/minimalist-2.ts +1 -1
  49. package/dist/assets/plugins/viui-conf/defaults/cards.ts +1 -1
  50. package/dist/assets/plugins/viui-conf/defaults/expansion-panels.ts +16 -0
  51. package/dist/assets/plugins/viui-conf/defaults/index.ts +3 -0
  52. package/dist/assets/plugins/viui-conf/defaults/inputs.ts +11 -1
  53. package/dist/assets/plugins/viui-conf/design-tokens.ts +135 -0
  54. package/dist/assets/plugins/viui-conf/theme-base.ts +1 -1
  55. package/dist/assets/plugins/viui-conf/v-dark.ts +3 -5
  56. package/dist/assets/plugins/viui-conf/v-light.ts +3 -5
  57. package/dist/assets/plugins/vuetify.ts +36 -0
  58. package/dist/assets/prompt-data/components.json +106 -0
  59. package/dist/assets/prompt-data/tokens.json +83 -0
  60. package/dist/assets/themes/_bento-grid.scss +8 -0
  61. package/dist/assets/themes/_glassmorphism.scss +8 -0
  62. package/dist/assets/themes/_material.scss +8 -0
  63. package/dist/assets/themes/_minimalist-2.scss +375 -0
  64. package/dist/assets/themes/_minimalist.scss +9 -0
  65. package/dist/assets/themes/_neo-brutalism.scss +199 -0
  66. package/dist/assets/themes/bento-grid.scss +4 -0
  67. package/dist/assets/themes/glassmorphism.scss +4 -0
  68. package/dist/assets/themes/index.scss +11 -0
  69. package/dist/assets/themes/material.scss +4 -0
  70. package/dist/assets/themes/minimalist-2.scss +5 -0
  71. package/dist/assets/themes/minimalist.scss +4 -0
  72. package/dist/assets/themes/neo-brutalism.scss +5 -0
  73. package/dist/assets/viui-themes/_neo-brutalism.scss +70 -152
  74. package/dist/cli-paths.d.ts +7 -0
  75. package/dist/cli-paths.d.ts.map +1 -0
  76. package/dist/cli-paths.js +19 -0
  77. package/dist/cli.js +28 -578
  78. package/dist/cli.legacy.d.ts +3 -0
  79. package/dist/cli.legacy.d.ts.map +1 -0
  80. package/dist/cli.legacy.js +597 -0
  81. package/dist/commands/audit.d.ts +3 -0
  82. package/dist/commands/audit.d.ts.map +1 -0
  83. package/dist/commands/audit.js +152 -0
  84. package/dist/commands/config/config-export.d.ts +6 -0
  85. package/dist/commands/config/config-export.d.ts.map +1 -0
  86. package/dist/commands/config/config-export.js +23 -0
  87. package/dist/commands/config/config-health.d.ts +6 -0
  88. package/dist/commands/config/config-health.d.ts.map +1 -0
  89. package/dist/commands/config/config-health.js +42 -0
  90. package/dist/commands/config/config-import.d.ts +6 -0
  91. package/dist/commands/config/config-import.d.ts.map +1 -0
  92. package/dist/commands/config/config-import.js +63 -0
  93. package/dist/commands/config/config-rollback.d.ts +6 -0
  94. package/dist/commands/config/config-rollback.d.ts.map +1 -0
  95. package/dist/commands/config/config-rollback.js +47 -0
  96. package/dist/commands/config/config-setup.d.ts +6 -0
  97. package/dist/commands/config/config-setup.d.ts.map +1 -0
  98. package/dist/commands/config/config-setup.js +103 -0
  99. package/dist/commands/config/config-status.d.ts +6 -0
  100. package/dist/commands/config/config-status.d.ts.map +1 -0
  101. package/dist/commands/config/config-status.js +42 -0
  102. package/dist/commands/config/config-uninstall.d.ts +6 -0
  103. package/dist/commands/config/config-uninstall.d.ts.map +1 -0
  104. package/dist/commands/config/config-uninstall.js +74 -0
  105. package/dist/commands/config.d.ts +6 -0
  106. package/dist/commands/config.d.ts.map +1 -0
  107. package/dist/commands/config.js +19 -0
  108. package/dist/commands/docs.d.ts +3 -0
  109. package/dist/commands/docs.d.ts.map +1 -0
  110. package/dist/commands/docs.js +17 -0
  111. package/dist/commands/doctor.d.ts +3 -0
  112. package/dist/commands/doctor.d.ts.map +1 -0
  113. package/dist/commands/doctor.js +93 -0
  114. package/dist/commands/init.d.ts +3 -0
  115. package/dist/commands/init.d.ts.map +1 -0
  116. package/dist/commands/init.js +183 -0
  117. package/dist/commands/sync.d.ts +3 -0
  118. package/dist/commands/sync.d.ts.map +1 -0
  119. package/dist/commands/sync.js +73 -0
  120. package/dist/commands/theme.d.ts +3 -0
  121. package/dist/commands/theme.d.ts.map +1 -0
  122. package/dist/commands/theme.js +86 -0
  123. package/dist/commands/update.d.ts +3 -0
  124. package/dist/commands/update.d.ts.map +1 -0
  125. package/dist/commands/update.js +97 -0
  126. package/dist/prompts/prompt-builder.d.ts +4 -0
  127. package/dist/prompts/prompt-builder.d.ts.map +1 -0
  128. package/dist/prompts/prompt-builder.js +18 -0
  129. package/dist/prompts/prompt-data-loader.d.ts +11 -0
  130. package/dist/prompts/prompt-data-loader.d.ts.map +1 -0
  131. package/dist/prompts/prompt-data-loader.js +15 -0
  132. package/dist/prompts/prompt-sections/section-code-examples.d.ts +2 -0
  133. package/dist/prompts/prompt-sections/section-code-examples.d.ts.map +1 -0
  134. package/dist/prompts/prompt-sections/section-code-examples.js +36 -0
  135. package/dist/prompts/prompt-sections/section-color-tokens.d.ts +2 -0
  136. package/dist/prompts/prompt-sections/section-color-tokens.d.ts.map +1 -0
  137. package/dist/prompts/prompt-sections/section-color-tokens.js +19 -0
  138. package/dist/prompts/prompt-sections/section-component-map.d.ts +3 -0
  139. package/dist/prompts/prompt-sections/section-component-map.d.ts.map +1 -0
  140. package/dist/prompts/prompt-sections/section-component-map.js +12 -0
  141. package/dist/prompts/prompt-sections/section-typography-spacing.d.ts +2 -0
  142. package/dist/prompts/prompt-sections/section-typography-spacing.d.ts.map +1 -0
  143. package/dist/prompts/prompt-sections/section-typography-spacing.js +29 -0
  144. package/dist/services/backup-service.d.ts +7 -0
  145. package/dist/services/backup-service.d.ts.map +1 -0
  146. package/dist/services/backup-service.js +54 -0
  147. package/dist/services/config-service.d.ts +17 -0
  148. package/dist/services/config-service.d.ts.map +1 -0
  149. package/dist/services/config-service.js +64 -0
  150. package/dist/services/diff-engine.d.ts +13 -0
  151. package/dist/services/diff-engine.d.ts.map +1 -0
  152. package/dist/services/diff-engine.js +59 -0
  153. package/dist/services/ide-detector.d.ts +9 -0
  154. package/dist/services/ide-detector.d.ts.map +1 -0
  155. package/dist/services/ide-detector.js +113 -0
  156. package/dist/services/ide-detector.spec.d.ts +2 -0
  157. package/dist/services/ide-detector.spec.d.ts.map +1 -0
  158. package/dist/services/ide-detector.spec.js +108 -0
  159. package/dist/services/lock-file-service.d.ts +15 -0
  160. package/dist/services/lock-file-service.d.ts.map +1 -0
  161. package/dist/services/lock-file-service.js +74 -0
  162. package/dist/services/mcp-config-reader.d.ts +11 -0
  163. package/dist/services/mcp-config-reader.d.ts.map +1 -0
  164. package/dist/services/mcp-config-reader.js +40 -0
  165. package/dist/services/mcp-config-reader.spec.d.ts +2 -0
  166. package/dist/services/mcp-config-reader.spec.d.ts.map +1 -0
  167. package/dist/services/mcp-config-reader.spec.js +125 -0
  168. package/dist/services/mcp-config-writer.d.ts +11 -0
  169. package/dist/services/mcp-config-writer.d.ts.map +1 -0
  170. package/dist/services/mcp-config-writer.js +98 -0
  171. package/dist/services/mcp-config-writer.spec.d.ts +2 -0
  172. package/dist/services/mcp-config-writer.spec.d.ts.map +1 -0
  173. package/dist/services/mcp-config-writer.spec.js +162 -0
  174. package/dist/services/merge-engine.d.ts +12 -0
  175. package/dist/services/merge-engine.d.ts.map +1 -0
  176. package/dist/services/merge-engine.js +54 -0
  177. package/dist/services/vuetify-scaffold-service.d.ts +5 -0
  178. package/dist/services/vuetify-scaffold-service.d.ts.map +1 -0
  179. package/dist/services/vuetify-scaffold-service.js +67 -0
  180. package/dist/templates/vuetify-plugin.d.ts +90 -0
  181. package/dist/templates/vuetify-plugin.d.ts.map +1 -0
  182. package/dist/templates/vuetify-plugin.js +33 -0
  183. package/dist/types/command-types.d.ts +15 -0
  184. package/dist/types/command-types.d.ts.map +1 -0
  185. package/dist/types/command-types.js +2 -0
  186. package/dist/types/config-types.d.ts +29 -0
  187. package/dist/types/config-types.d.ts.map +1 -0
  188. package/dist/types/config-types.js +10 -0
  189. package/dist/types/ide-types.d.ts +29 -0
  190. package/dist/types/ide-types.d.ts.map +1 -0
  191. package/dist/types/ide-types.js +4 -0
  192. package/dist/types/lock-file-types.d.ts +27 -0
  193. package/dist/types/lock-file-types.d.ts.map +1 -0
  194. package/dist/types/lock-file-types.js +2 -0
  195. package/dist/utils/diff-display.d.ts +18 -0
  196. package/dist/utils/diff-display.d.ts.map +1 -0
  197. package/dist/utils/diff-display.js +61 -0
  198. package/dist/utils/fs-safe.d.ts +9 -0
  199. package/dist/utils/fs-safe.d.ts.map +1 -0
  200. package/dist/utils/fs-safe.js +44 -0
  201. package/dist/utils/logger.d.ts +14 -0
  202. package/dist/utils/logger.d.ts.map +1 -0
  203. package/dist/utils/logger.js +28 -0
  204. package/dist/utils/open-browser.d.ts +3 -0
  205. package/dist/utils/open-browser.d.ts.map +1 -0
  206. package/dist/utils/open-browser.js +13 -0
  207. package/package.json +11 -6
  208. package/dist/assets/cursor/.design-system-version +0 -1
  209. package/dist/assets/cursor/commands/audit-accessibility.md +0 -25
  210. package/dist/assets/cursor/commands/audit-ui.md +0 -35
  211. package/dist/assets/cursor/commands/component.md +0 -18
  212. package/dist/assets/cursor/commands/fix-storybook.md +0 -24
  213. package/dist/assets/cursor/commands/generate-component-from-figma.md +0 -26
  214. package/dist/assets/cursor/commands/generate-page-from-figma.md +0 -26
  215. package/dist/assets/cursor/plans/DESIGN_SYSTEM_PLAN.md +0 -177
  216. package/dist/assets/cursor/plans/PLANS_INDEX.md +0 -35
  217. package/dist/assets/cursor/rules/accessibility-contrast.mdc +0 -38
  218. package/dist/assets/cursor/rules/bem-class-style.mdc +0 -107
  219. package/dist/assets/cursor/rules/component-naming.mdc +0 -57
  220. package/dist/assets/cursor/rules/design-system-component-library.mdc +0 -59
  221. package/dist/assets/cursor/rules/design-system-workflow.mdc +0 -48
  222. package/dist/assets/cursor/rules/figma-mapping.mdc +0 -37
  223. package/dist/assets/cursor/rules/icons.mdc +0 -42
  224. package/dist/assets/cursor/rules/project-structure.mdc +0 -137
  225. package/dist/assets/cursor/rules/storybook-component-template.mdc +0 -103
  226. package/dist/assets/cursor/rules/storybook.mdc +0 -68
  227. package/dist/assets/cursor/rules/tokens.mdc +0 -32
  228. package/dist/assets/cursor/rules/viui-themes.mdc +0 -53
  229. package/dist/assets/cursor/rules/vuetify-layout.mdc +0 -52
  230. package/dist/assets/cursor/skills/accessibility.md +0 -75
  231. package/dist/assets/cursor/skills/design-system-thinking.md +0 -40
  232. package/dist/assets/cursor/skills/figma-interpretation.md +0 -38
  233. package/dist/assets/cursor/skills/vue-vuetify-design-system-architect.md +0 -60
  234. package/dist/assets/cursor/sync-manifest.json +0 -6
  235. package/dist/assets/viui-themes/bento-grid-global.scss +0 -5
  236. package/dist/assets/viui-themes/glassmorphism-global.scss +0 -5
  237. package/dist/assets/viui-themes/material-global.scss +0 -5
  238. package/dist/assets/viui-themes/minimalist-2-global.scss +0 -5
  239. package/dist/assets/viui-themes/minimalist-global.scss +0 -5
  240. package/dist/assets/viui-themes/neo-brutalism-global.scss +0 -5
@@ -0,0 +1,152 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { logger } from '../utils/logger.js';
5
+ import { readConfig } from '../services/config-service.js';
6
+ /** Scan files recursively matching extensions */
7
+ function scanFiles(dir, extensions) {
8
+ if (!fs.existsSync(dir))
9
+ return [];
10
+ const results = [];
11
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
12
+ const full = path.join(dir, entry.name);
13
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'tokens')
14
+ continue;
15
+ if (entry.isDirectory()) {
16
+ results.push(...scanFiles(full, extensions));
17
+ }
18
+ else if (extensions.some((ext) => entry.name.endsWith(ext))) {
19
+ results.push(full);
20
+ }
21
+ }
22
+ return results;
23
+ }
24
+ /** Count occurrences of pattern in file contents */
25
+ function countPattern(files, pattern) {
26
+ let count = 0;
27
+ for (const f of files) {
28
+ const content = fs.readFileSync(f, 'utf8');
29
+ const matches = content.match(pattern);
30
+ if (matches)
31
+ count += matches.length;
32
+ }
33
+ return count;
34
+ }
35
+ /** Exclude token definition files from token coverage scan */
36
+ const TOKEN_DEF_PATTERNS = ['inet-tokens.scss', 'design-tokens.ts', 'inet-design-tokens.ts'];
37
+ /** Token coverage: ratio of var(--*) usage vs hardcoded color/spacing values */
38
+ function auditTokenCoverage(srcDir) {
39
+ const files = scanFiles(srcDir, ['.vue', '.ts', '.scss', '.css'])
40
+ .filter((f) => !TOKEN_DEF_PATTERNS.some((p) => f.endsWith(p)));
41
+ const tokenUsage = countPattern(files, /var\(--[\w-]+\)/g);
42
+ const hardcodedColors = countPattern(files, /#(?:[0-9a-fA-F]{3,8})\b/g);
43
+ const hardcodedSpacing = countPattern(files, /\b\d+(?:\.\d+)?px\b/g);
44
+ const hardcoded = hardcodedColors + hardcodedSpacing;
45
+ const total = tokenUsage + hardcoded;
46
+ const score = total === 0 ? 100 : Math.round((tokenUsage / total) * 100);
47
+ return {
48
+ category: 'Token Coverage',
49
+ score,
50
+ details: `${tokenUsage} token vars, ${hardcoded} hardcoded values`,
51
+ };
52
+ }
53
+ /** Component usage: ratio of Vi* vs raw v-* components */
54
+ function auditComponentUsage(srcDir) {
55
+ const files = scanFiles(srcDir, ['.vue']);
56
+ const viUsage = countPattern(files, /<Vi[A-Z]\w+/g);
57
+ const vuetifyDirect = countPattern(files, /<v-(btn|text-field|select|checkbox|card|alert|dialog|data-table|pagination)\b/g);
58
+ const total = viUsage + vuetifyDirect;
59
+ const score = total === 0 ? 100 : Math.round((viUsage / total) * 100);
60
+ return {
61
+ category: 'Component Usage',
62
+ score,
63
+ details: `${viUsage} Vi* components, ${vuetifyDirect} direct Vuetify`,
64
+ };
65
+ }
66
+ /** A11y score: check aria-label on icon buttons */
67
+ function auditA11y(srcDir) {
68
+ const files = scanFiles(srcDir, ['.vue']);
69
+ let iconButtons = 0;
70
+ let withAria = 0;
71
+ for (const f of files) {
72
+ const content = fs.readFileSync(f, 'utf8');
73
+ const iconMatches = content.match(/<(?:v-btn|ViButton)[^>]*icon[^>]*>/gi) ?? [];
74
+ iconButtons += iconMatches.length;
75
+ for (const m of iconMatches) {
76
+ if (/aria-label/i.test(m))
77
+ withAria++;
78
+ }
79
+ }
80
+ const score = iconButtons === 0 ? 100 : Math.round((withAria / iconButtons) * 100);
81
+ return {
82
+ category: 'Accessibility',
83
+ score,
84
+ details: `${withAria}/${iconButtons} icon buttons with aria-label`,
85
+ };
86
+ }
87
+ /** Theme consistency: verify Vuetify theme config references tokens */
88
+ function auditThemeConsistency(cwd) {
89
+ const vuetifyPath = path.join(cwd, 'src', 'plugins', 'vuetify.ts');
90
+ if (!fs.existsSync(vuetifyPath)) {
91
+ return { category: 'Theme Consistency', score: 50, details: 'No vuetify.ts found' };
92
+ }
93
+ const content = fs.readFileSync(vuetifyPath, 'utf8');
94
+ let score = 0;
95
+ const checks = [];
96
+ if (content.includes('viui-conf')) {
97
+ score += 40;
98
+ checks.push('viui-conf imported');
99
+ }
100
+ if (content.includes('apply-theme-body')) {
101
+ score += 30;
102
+ checks.push('theme body applied');
103
+ }
104
+ if (content.includes('v-light') || content.includes('v-dark')) {
105
+ score += 30;
106
+ checks.push('light/dark themes');
107
+ }
108
+ return {
109
+ category: 'Theme Consistency',
110
+ score,
111
+ details: checks.length ? checks.join(', ') : 'No token integration found',
112
+ };
113
+ }
114
+ export function registerAuditCommand(program) {
115
+ program
116
+ .command('audit')
117
+ .description('Run compliance audit and show score /100')
118
+ .option('--json', 'Output as JSON for CI')
119
+ .action((opts) => {
120
+ const cwd = process.cwd();
121
+ const config = readConfig(cwd);
122
+ const srcDir = path.join(cwd, 'src');
123
+ if (!fs.existsSync(srcDir)) {
124
+ logger.error('src/ directory not found.');
125
+ process.exitCode = 1;
126
+ return;
127
+ }
128
+ const scores = [
129
+ auditTokenCoverage(srcDir),
130
+ auditComponentUsage(srcDir),
131
+ auditA11y(srcDir),
132
+ auditThemeConsistency(cwd),
133
+ ];
134
+ const overall = Math.round(scores.reduce((sum, s) => sum + s.score, 0) / scores.length);
135
+ if (opts.json) {
136
+ console.log(JSON.stringify({ overall, scores }, null, 2));
137
+ return;
138
+ }
139
+ logger.header('viui audit — Compliance Report\n');
140
+ for (const s of scores) {
141
+ const color = s.score >= 80 ? chalk.green : s.score >= 50 ? chalk.yellow : chalk.red;
142
+ console.log(` ${color(`${String(s.score).padStart(3)}/100`)} ${s.category}`);
143
+ console.log(chalk.dim(` ${s.details}`));
144
+ }
145
+ logger.br();
146
+ const overallColor = overall >= 80 ? chalk.green : overall >= 50 ? chalk.yellow : chalk.red;
147
+ console.log(` ${overallColor(`Overall: ${overall}/100`)}`);
148
+ logger.br();
149
+ if (overall < 50)
150
+ process.exitCode = 1;
151
+ });
152
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-export.ts — Xuất tất cả MCP config ra stdout dạng JSON
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerExportSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-export.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-export.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAIxC,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAuBjE"}
@@ -0,0 +1,23 @@
1
+ import { detectIdes } from '../../services/ide-detector.js';
2
+ import { readMcpConfig } from '../../services/mcp-config-reader.js';
3
+ export function registerExportSubcommand(configCmd) {
4
+ configCmd
5
+ .command('export')
6
+ .description('Xuất cấu hình MCP ra stdout (JSON)')
7
+ .action(() => {
8
+ const ides = detectIdes(process.cwd());
9
+ const result = {};
10
+ for (const ide of ides) {
11
+ const project = readMcpConfig(ide, false);
12
+ const global = ide.globalConfigPath ? readMcpConfig(ide, true) : null;
13
+ if (project.length > 0 || (global && global.length > 0)) {
14
+ result[ide.id] = {
15
+ name: ide.name,
16
+ project: project.length > 0 ? project : null,
17
+ global: global && global.length > 0 ? global : null,
18
+ };
19
+ }
20
+ }
21
+ console.log(JSON.stringify(result, null, 2));
22
+ });
23
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-health.ts — Kiểm tra MCP server có phản hồi không
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerHealthSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-health.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-health.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOxC,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAwCjE"}
@@ -0,0 +1,42 @@
1
+ import chalk from 'chalk';
2
+ import { logger } from '../../utils/logger.js';
3
+ const DEFAULT_REMOTE = 'https://viui.inet.vn/mcp';
4
+ const DEFAULT_TIMEOUT = 5000;
5
+ export function registerHealthSubcommand(configCmd) {
6
+ configCmd
7
+ .command('health')
8
+ .description('Kiểm tra MCP server có hoạt động không')
9
+ .option('--timeout <ms>', 'Timeout (ms)', String(DEFAULT_TIMEOUT))
10
+ .action(async (opts) => {
11
+ const timeout = parseInt(opts.timeout, 10) || DEFAULT_TIMEOUT;
12
+ const url = DEFAULT_REMOTE;
13
+ logger.info(`Kiểm tra: ${chalk.cyan(url)} (timeout: ${timeout}ms)`);
14
+ logger.br();
15
+ const start = Date.now();
16
+ try {
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), timeout);
19
+ const res = await fetch(url, {
20
+ method: 'GET',
21
+ signal: controller.signal,
22
+ });
23
+ clearTimeout(timer);
24
+ const elapsed = Date.now() - start;
25
+ if (res.ok || res.status === 405) {
26
+ // MCP servers often return 405 for GET (expects POST)
27
+ logger.success(`Server hoạt động — ${elapsed}ms (HTTP ${res.status})`);
28
+ }
29
+ else {
30
+ logger.warn(`Server phản hồi HTTP ${res.status} — ${elapsed}ms`);
31
+ }
32
+ }
33
+ catch (err) {
34
+ const elapsed = Date.now() - start;
35
+ const msg = err.name === 'AbortError'
36
+ ? `Timeout sau ${elapsed}ms`
37
+ : err.message;
38
+ logger.error(`Server không phản hồi — ${msg}`);
39
+ process.exitCode = 1;
40
+ }
41
+ });
42
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-import.ts — Nhập cấu hình MCP từ file JSON
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerImportSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-import.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-import.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAYxC,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA6DjE"}
@@ -0,0 +1,63 @@
1
+ import fs from 'node:fs';
2
+ import { checkbox } from '@inquirer/prompts';
3
+ import { logger } from '../../utils/logger.js';
4
+ import { detectIdes } from '../../services/ide-detector.js';
5
+ import { writeConfig } from '../../services/mcp-config-writer.js';
6
+ export function registerImportSubcommand(configCmd) {
7
+ configCmd
8
+ .command('import <file>')
9
+ .description('Nhập cấu hình MCP từ file JSON')
10
+ .option('--yes', 'Tự động áp dụng không hỏi')
11
+ .action(async (file, opts) => {
12
+ if (!fs.existsSync(file)) {
13
+ logger.error(`File không tồn tại: ${file}`);
14
+ process.exitCode = 1;
15
+ return;
16
+ }
17
+ let importData;
18
+ try {
19
+ importData = JSON.parse(fs.readFileSync(file, 'utf8'));
20
+ }
21
+ catch {
22
+ logger.error('File không phải JSON hợp lệ');
23
+ process.exitCode = 1;
24
+ return;
25
+ }
26
+ const ides = detectIdes(process.cwd());
27
+ const importableIdes = ides.filter(ide => importData[ide.id]);
28
+ if (importableIdes.length === 0) {
29
+ logger.warn('File import không chứa config cho IDE nào trên máy.');
30
+ return;
31
+ }
32
+ let selected = importableIdes;
33
+ if (!opts.yes) {
34
+ selected = await checkbox({
35
+ message: 'Chọn IDE để áp dụng config:',
36
+ choices: importableIdes.map(ide => ({
37
+ name: ide.name,
38
+ value: ide,
39
+ checked: true,
40
+ })),
41
+ });
42
+ }
43
+ for (const ide of selected) {
44
+ try {
45
+ const data = importData[ide.id];
46
+ const entries = (data?.project || data?.global);
47
+ if (entries && entries.length > 0) {
48
+ const serverUrl = entries[0].serverUrl;
49
+ const mcpConfig = {
50
+ serverName: ide.id === 'vscode' ? 'viui' : 'inet-viui',
51
+ serverUrl,
52
+ transport: 'http',
53
+ };
54
+ writeConfig(ide, mcpConfig);
55
+ logger.success(`${ide.name}: đã áp dụng config từ file`);
56
+ }
57
+ }
58
+ catch (err) {
59
+ logger.error(`${ide.name}: lỗi — ${err.message}`);
60
+ }
61
+ }
62
+ });
63
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-rollback.ts — Khôi phục config từ file backup (.bak)
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerRollbackSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-rollback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-rollback.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-rollback.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUxC,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA8CnE"}
@@ -0,0 +1,47 @@
1
+ import fs from 'node:fs';
2
+ import { select, confirm } from '@inquirer/prompts';
3
+ import { logger } from '../../utils/logger.js';
4
+ import { detectIdes } from '../../services/ide-detector.js';
5
+ export function registerRollbackSubcommand(configCmd) {
6
+ configCmd
7
+ .command('rollback')
8
+ .description('Khôi phục cấu hình từ backup (.bak)')
9
+ .option('--global', 'Khôi phục global config')
10
+ .action(async (opts) => {
11
+ const useGlobal = !!opts.global;
12
+ const ides = detectIdes(process.cwd());
13
+ const backups = ides
14
+ .map(ide => {
15
+ const configPath = useGlobal ? ide.globalConfigPath : ide.configPath;
16
+ const bakPath = configPath ? `${configPath}.bak` : null;
17
+ return { ide, bakPath, exists: bakPath ? fs.existsSync(bakPath) : false };
18
+ })
19
+ .filter(b => b.exists);
20
+ if (backups.length === 0) {
21
+ logger.info('Không tìm thấy file backup nào.');
22
+ return;
23
+ }
24
+ const chosen = await select({
25
+ message: 'Chọn backup để khôi phục:',
26
+ choices: backups.map(b => {
27
+ const stat = fs.statSync(b.bakPath);
28
+ return {
29
+ name: `${b.ide.name} — ${b.bakPath} (${stat.mtime.toLocaleString()})`,
30
+ value: b,
31
+ };
32
+ }),
33
+ });
34
+ const ok = await confirm({
35
+ message: `Khôi phục ${chosen.ide.name} từ backup?`,
36
+ default: true,
37
+ });
38
+ if (!ok)
39
+ return;
40
+ const configPath = useGlobal ? chosen.ide.globalConfigPath : chosen.ide.configPath;
41
+ if (fs.existsSync(configPath)) {
42
+ fs.copyFileSync(configPath, `${configPath}.pre-rollback.bak`);
43
+ }
44
+ fs.copyFileSync(chosen.bakPath, configPath);
45
+ logger.success(`${chosen.ide.name}: đã khôi phục từ backup`);
46
+ });
47
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-setup.ts — Subcommand `viui config [setup]`: cấu hình MCP cho IDE
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerSetupSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-setup.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-setup.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA+DxC,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA0DhE"}
@@ -0,0 +1,103 @@
1
+ import chalk from 'chalk';
2
+ import { checkbox, confirm } from '@inquirer/prompts';
3
+ import { logger } from '../../utils/logger.js';
4
+ import { detectIdes } from '../../services/ide-detector.js';
5
+ import { generateMcpConfig, buildConfigObject, writeConfig } from '../../services/mcp-config-writer.js';
6
+ import { hasViuiConfig } from '../../services/mcp-config-reader.js';
7
+ /** Post-config instructions per IDE */
8
+ const IDE_INSTRUCTIONS = {
9
+ vscode: 'Nhấn Ctrl+Shift+P → "Reload Window" để áp dụng',
10
+ cursor: 'Khởi động lại Cursor để áp dụng',
11
+ claude: 'MCP sẽ tự động nhận config ở lần chạy tiếp theo',
12
+ windsurf: 'Khởi động lại Windsurf để áp dụng',
13
+ trae: 'Khởi động lại Trae để áp dụng',
14
+ gemini: 'MCP sẽ tự động nhận config ở lần chạy tiếp theo',
15
+ };
16
+ /** Chọn IDE qua @inquirer/prompts checkbox */
17
+ async function promptSelectIdes(ides) {
18
+ const choices = ides
19
+ .filter(ide => ide.detected)
20
+ .map(ide => ({ name: ide.name, value: ide, checked: true }));
21
+ if (choices.length === 0) {
22
+ logger.warn('Không phát hiện IDE nào.');
23
+ return [];
24
+ }
25
+ return checkbox({ message: 'Chọn IDE để cấu hình MCP:', choices });
26
+ }
27
+ /** Ghi config cho 1 IDE, hỗ trợ dry-run */
28
+ function applyConfig(ide, useRemote, useGlobal, dryRun) {
29
+ try {
30
+ const mcpConfig = generateMcpConfig(ide, useRemote);
31
+ if (dryRun) {
32
+ const targetPath = useGlobal ? ide.globalConfigPath : ide.configPath;
33
+ logger.info(`${ide.name} → ${targetPath}`);
34
+ const preview = buildConfigObject(ide, mcpConfig);
35
+ console.log(chalk.dim(JSON.stringify(preview, null, 2)));
36
+ return;
37
+ }
38
+ writeConfig(ide, mcpConfig, useGlobal);
39
+ const targetPath = useGlobal ? ide.globalConfigPath : ide.configPath;
40
+ logger.success(`${ide.name}: đã ghi cấu hình → ${targetPath}`);
41
+ logger.dim(` ${IDE_INSTRUCTIONS[ide.id] || 'Khởi động lại IDE để áp dụng'}`);
42
+ }
43
+ catch (err) {
44
+ logger.error(`${ide.name}: lỗi — ${err.message}`);
45
+ }
46
+ }
47
+ export function registerSetupSubcommand(configCmd) {
48
+ configCmd
49
+ .command('setup', { isDefault: true })
50
+ .description('Cấu hình MCP server cho IDE (mặc định)')
51
+ .option('--remote', 'Dùng server từ xa (mặc định)')
52
+ .option('--local', 'Dùng server cục bộ')
53
+ .option('--yes', 'Tự động cấu hình tất cả IDE đã phát hiện')
54
+ .option('--dry-run', 'Xem trước cấu hình, không ghi file')
55
+ .option('--global', 'Cấu hình global thay vì project-level')
56
+ .action(async (opts) => {
57
+ const cwd = process.cwd();
58
+ const useRemote = !opts.local;
59
+ const useGlobal = !!opts.global;
60
+ logger.header('viui config — Cấu hình MCP Server\n');
61
+ if (opts.dryRun)
62
+ logger.warn('Chế độ DRY-RUN — không ghi file\n');
63
+ const serverLabel = useRemote
64
+ ? chalk.cyan('https://viui.inet.vn/mcp') + chalk.dim(' (từ xa)')
65
+ : chalk.yellow('http://localhost:3200/mcp') + chalk.dim(' (cục bộ)');
66
+ logger.info(`Server: ${serverLabel}`);
67
+ logger.info(`Scope: ${useGlobal ? 'Global (hệ thống)' : 'Project (thư mục hiện tại)'}`);
68
+ logger.br();
69
+ const ides = detectIdes(cwd);
70
+ const available = useGlobal
71
+ ? ides.filter(i => i.globalConfigPath !== null)
72
+ : ides.filter(i => i.configPath !== '');
73
+ let selected;
74
+ if (opts.yes) {
75
+ selected = available.filter(i => i.detected);
76
+ if (selected.length === 0) {
77
+ logger.warn('Không phát hiện IDE nào.');
78
+ return;
79
+ }
80
+ logger.info(`Tự động cấu hình ${selected.length} IDE...`);
81
+ }
82
+ else {
83
+ selected = await promptSelectIdes(available);
84
+ if (selected.length === 0)
85
+ return;
86
+ }
87
+ for (const ide of selected) {
88
+ if (hasViuiConfig(ide, useGlobal) && !opts.yes && !opts.dryRun) {
89
+ const overwrite = await confirm({
90
+ message: `${ide.name} đã có cấu hình MCP. Ghi đè?`,
91
+ default: true,
92
+ });
93
+ if (!overwrite)
94
+ continue;
95
+ }
96
+ applyConfig(ide, useRemote, useGlobal, !!opts.dryRun);
97
+ }
98
+ logger.br();
99
+ if (!opts.dryRun) {
100
+ logger.dim('Khởi động lại IDE để áp dụng cấu hình MCP.');
101
+ }
102
+ });
103
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-status.ts — Hiển thị trạng thái MCP config của tất cả IDE
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerStatusSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-status.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-status.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAWxC,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA4CjE"}
@@ -0,0 +1,42 @@
1
+ import chalk from 'chalk';
2
+ import { logger } from '../../utils/logger.js';
3
+ import { detectIdes } from '../../services/ide-detector.js';
4
+ import { readMcpConfig } from '../../services/mcp-config-reader.js';
5
+ export function registerStatusSubcommand(configCmd) {
6
+ configCmd
7
+ .command('status')
8
+ .description('Hiển thị trạng thái cấu hình MCP của tất cả IDE')
9
+ .option('--global', 'Hiển thị global config thay vì project-level')
10
+ .option('--json', 'Xuất kết quả dạng JSON')
11
+ .action((opts) => {
12
+ const ides = detectIdes(process.cwd());
13
+ if (opts.json) {
14
+ const result = ides.map(ide => ({
15
+ id: ide.id,
16
+ name: ide.name,
17
+ detected: ide.detected,
18
+ projectConfig: readMcpConfig(ide, false),
19
+ globalConfig: ide.globalConfigPath ? readMcpConfig(ide, true) : null,
20
+ }));
21
+ console.log(JSON.stringify(result, null, 2));
22
+ return;
23
+ }
24
+ logger.header('Trạng thái MCP Config\n');
25
+ for (const ide of ides) {
26
+ const projectEntries = readMcpConfig(ide, false);
27
+ const globalEntries = ide.globalConfigPath ? readMcpConfig(ide, true) : null;
28
+ const projectStatus = projectEntries.length > 0
29
+ ? chalk.green('✓') + ' ' + projectEntries[0].serverUrl
30
+ : ide.configPath ? chalk.dim('✗ chưa cấu hình') : chalk.dim('— không hỗ trợ');
31
+ const globalStatus = globalEntries === null
32
+ ? chalk.dim('— không hỗ trợ')
33
+ : globalEntries.length > 0
34
+ ? chalk.green('✓') + ' ' + globalEntries[0].serverUrl
35
+ : chalk.dim('✗ chưa cấu hình');
36
+ const detected = ide.detected ? chalk.green('●') : chalk.dim('○');
37
+ console.log(` ${detected} ${ide.name.padEnd(14)} Project: ${projectStatus}`);
38
+ console.log(` ${''.padEnd(17)} Global: ${globalStatus}`);
39
+ logger.br();
40
+ }
41
+ });
42
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config-uninstall.ts — Xóa cấu hình MCP viui khỏi IDE config files
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerUninstallSubcommand(configCmd: Command): void;
6
+ //# sourceMappingURL=config-uninstall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-uninstall.d.ts","sourceRoot":"","sources":["../../../src/commands/config/config-uninstall.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAuCxC,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA6CpE"}
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs';
2
+ import { confirm, checkbox } from '@inquirer/prompts';
3
+ import { logger } from '../../utils/logger.js';
4
+ import { detectIdes } from '../../services/ide-detector.js';
5
+ import { hasViuiConfig } from '../../services/mcp-config-reader.js';
6
+ /** Xóa viui/inet-viui entry khỏi config file */
7
+ function removeViuiEntry(ide, useGlobal) {
8
+ const configPath = useGlobal ? ide.globalConfigPath : ide.configPath;
9
+ if (!configPath || !fs.existsSync(configPath))
10
+ return;
11
+ fs.copyFileSync(configPath, `${configPath}.bak`);
12
+ let data;
13
+ try {
14
+ data = JSON.parse(fs.readFileSync(configPath, 'utf8'));
15
+ }
16
+ catch {
17
+ logger.error(`${ide.name}: config file corrupt, đã backup tại ${configPath}.bak`);
18
+ return;
19
+ }
20
+ const topKey = ide.configFormat === 'vscode' ? 'servers' : 'mcpServers';
21
+ const servers = data[topKey];
22
+ if (servers) {
23
+ delete servers['viui'];
24
+ delete servers['inet-viui'];
25
+ if (Object.keys(servers).length === 0)
26
+ delete data[topKey];
27
+ }
28
+ fs.writeFileSync(configPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
29
+ }
30
+ export function registerUninstallSubcommand(configCmd) {
31
+ configCmd
32
+ .command('uninstall')
33
+ .description('Xóa cấu hình MCP viui khỏi IDE')
34
+ .option('--global', 'Xóa global config thay vì project-level')
35
+ .option('--yes', 'Tự động xóa không hỏi')
36
+ .action(async (opts) => {
37
+ const useGlobal = !!opts.global;
38
+ const ides = detectIdes(process.cwd());
39
+ const configured = ides.filter(ide => hasViuiConfig(ide, useGlobal));
40
+ if (configured.length === 0) {
41
+ logger.info('Không tìm thấy cấu hình MCP viui nào.');
42
+ return;
43
+ }
44
+ let selected;
45
+ if (opts.yes) {
46
+ selected = configured;
47
+ }
48
+ else {
49
+ selected = await checkbox({
50
+ message: 'Chọn IDE để xóa cấu hình MCP:',
51
+ choices: configured.map(ide => ({ name: ide.name, value: ide, checked: true })),
52
+ });
53
+ }
54
+ if (selected.length === 0)
55
+ return;
56
+ if (!opts.yes) {
57
+ const ok = await confirm({
58
+ message: `Xóa cấu hình MCP viui khỏi ${selected.length} IDE?`,
59
+ default: false,
60
+ });
61
+ if (!ok)
62
+ return;
63
+ }
64
+ for (const ide of selected) {
65
+ try {
66
+ removeViuiEntry(ide, useGlobal);
67
+ logger.success(`${ide.name}: đã xóa cấu hình MCP viui`);
68
+ }
69
+ catch (err) {
70
+ logger.error(`${ide.name}: lỗi — ${err.message}`);
71
+ }
72
+ }
73
+ });
74
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * config.ts — Subcommand router cho `viui config`
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerConfigCommand(program: Command): void;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASxC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAY5D"}