andrud 1.0.0 → 1.0.2

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 (122) hide show
  1. package/README.md +69 -83
  2. package/dist/__tests__/context.test.d.ts +5 -0
  3. package/dist/__tests__/context.test.d.ts.map +1 -0
  4. package/dist/__tests__/context.test.js +86 -0
  5. package/dist/__tests__/context.test.js.map +1 -0
  6. package/dist/__tests__/generator.test.d.ts +5 -0
  7. package/dist/__tests__/generator.test.d.ts.map +1 -0
  8. package/dist/__tests__/generator.test.js +83 -0
  9. package/dist/__tests__/generator.test.js.map +1 -0
  10. package/dist/__tests__/validation.test.d.ts +5 -0
  11. package/dist/__tests__/validation.test.d.ts.map +1 -0
  12. package/dist/__tests__/validation.test.js +81 -0
  13. package/dist/__tests__/validation.test.js.map +1 -0
  14. package/dist/cli/commands/create.d.ts +10 -0
  15. package/dist/cli/commands/create.d.ts.map +1 -0
  16. package/dist/cli/commands/create.js +203 -0
  17. package/dist/cli/commands/create.js.map +1 -0
  18. package/dist/cli/commands/info.d.ts +13 -0
  19. package/dist/cli/commands/info.d.ts.map +1 -0
  20. package/dist/cli/commands/info.js +153 -0
  21. package/dist/cli/commands/info.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +15 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +141 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/commands/list.d.ts +18 -0
  27. package/dist/cli/commands/list.d.ts.map +1 -0
  28. package/dist/cli/commands/list.js +122 -0
  29. package/dist/cli/commands/list.js.map +1 -0
  30. package/dist/cli/commands/new.d.ts +22 -0
  31. package/dist/cli/commands/new.d.ts.map +1 -0
  32. package/dist/cli/commands/new.js +245 -0
  33. package/dist/cli/commands/new.js.map +1 -0
  34. package/dist/cli/index.d.ts +5 -0
  35. package/dist/cli/index.d.ts.map +1 -0
  36. package/dist/cli/index.js +99 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/core/config.d.ts +89 -0
  39. package/dist/core/config.d.ts.map +1 -0
  40. package/dist/core/config.js +151 -0
  41. package/dist/core/config.js.map +1 -0
  42. package/dist/core/context.d.ts +47 -0
  43. package/dist/core/context.d.ts.map +1 -0
  44. package/dist/core/context.js +175 -0
  45. package/dist/core/context.js.map +1 -0
  46. package/dist/core/generator.d.ts +44 -0
  47. package/dist/core/generator.d.ts.map +1 -0
  48. package/{src/core/generator.ts → dist/core/generator.js} +394 -484
  49. package/dist/core/generator.js.map +1 -0
  50. package/dist/core/types.d.ts +125 -0
  51. package/dist/core/types.d.ts.map +1 -0
  52. package/dist/core/types.js +22 -0
  53. package/dist/core/types.js.map +1 -0
  54. package/dist/templates/index.d.ts +36 -0
  55. package/dist/templates/index.d.ts.map +1 -0
  56. package/dist/templates/index.js +141 -0
  57. package/dist/templates/index.js.map +1 -0
  58. package/dist/ui/colors.d.ts +40 -0
  59. package/dist/ui/colors.d.ts.map +1 -0
  60. package/dist/ui/colors.js +117 -0
  61. package/dist/ui/colors.js.map +1 -0
  62. package/dist/ui/output.d.ts +69 -0
  63. package/dist/ui/output.d.ts.map +1 -0
  64. package/dist/ui/output.js +199 -0
  65. package/dist/ui/output.js.map +1 -0
  66. package/dist/ui/prompts.d.ts +20 -0
  67. package/dist/ui/prompts.d.ts.map +1 -0
  68. package/dist/ui/prompts.js +118 -0
  69. package/dist/ui/prompts.js.map +1 -0
  70. package/dist/ui/spinners.d.ts +30 -0
  71. package/dist/ui/spinners.d.ts.map +1 -0
  72. package/dist/ui/spinners.js +74 -0
  73. package/dist/ui/spinners.js.map +1 -0
  74. package/dist/ui/types.d.ts +35 -0
  75. package/dist/ui/types.d.ts.map +1 -0
  76. package/dist/ui/types.js +5 -0
  77. package/dist/ui/types.js.map +1 -0
  78. package/dist/utils/filesystem.d.ts +38 -0
  79. package/dist/utils/filesystem.d.ts.map +1 -0
  80. package/dist/utils/filesystem.js +181 -0
  81. package/dist/utils/filesystem.js.map +1 -0
  82. package/dist/utils/logger.d.ts +27 -0
  83. package/dist/utils/logger.d.ts.map +1 -0
  84. package/dist/utils/logger.js +52 -0
  85. package/dist/utils/logger.js.map +1 -0
  86. package/dist/utils/object.d.ts +140 -0
  87. package/dist/utils/object.d.ts.map +1 -0
  88. package/dist/utils/object.js +385 -0
  89. package/dist/utils/object.js.map +1 -0
  90. package/dist/utils/validation.d.ts +35 -0
  91. package/dist/utils/validation.d.ts.map +1 -0
  92. package/dist/utils/validation.js +270 -0
  93. package/dist/utils/validation.js.map +1 -0
  94. package/package.json +8 -19
  95. package/CHANGELOG.md +0 -70
  96. package/CODE_REVIEW_ANALYSIS.md +0 -177
  97. package/CONTRIBUTING.md +0 -132
  98. package/FIXES_IMPLEMENTED.md +0 -546
  99. package/src/__tests__/context.test.ts +0 -133
  100. package/src/__tests__/generator.test.ts +0 -107
  101. package/src/__tests__/validation.test.ts +0 -105
  102. package/src/cli/commands/create.ts +0 -252
  103. package/src/cli/commands/info.ts +0 -178
  104. package/src/cli/commands/init.ts +0 -186
  105. package/src/cli/commands/list.ts +0 -156
  106. package/src/cli/commands/new.ts +0 -316
  107. package/src/cli/index.ts +0 -116
  108. package/src/core/config.ts +0 -172
  109. package/src/core/context.ts +0 -212
  110. package/src/core/types.ts +0 -184
  111. package/src/templates/index.ts +0 -162
  112. package/src/types/gradient-string.d.ts +0 -25
  113. package/src/ui/colors.ts +0 -139
  114. package/src/ui/output.ts +0 -230
  115. package/src/ui/prompts.ts +0 -170
  116. package/src/ui/spinners.ts +0 -95
  117. package/src/ui/types.ts +0 -41
  118. package/src/utils/filesystem.ts +0 -222
  119. package/src/utils/logger.ts +0 -67
  120. package/src/utils/object.ts +0 -456
  121. package/src/utils/validation.ts +0 -345
  122. package/tsconfig.json +0 -25
@@ -1,178 +0,0 @@
1
- /**
2
- * Template info command - shows detailed information about templates
3
- */
4
-
5
- import { printSection, printKeyValue } from '../../ui/output.js';
6
- import { gradientTeen, bold, primary, muted } from '../../ui/colors.js';
7
- import { getTemplateMetadata, getAllTemplates, getTemplatePreview } from '../../templates/index.js';
8
- import { GRADLE_VERSIONS, TEMPLATE_CONFIGS } from '../../core/config.js';
9
- import pc from 'picocolors';
10
- import type { TemplateType, TemplateMetadata } from '../../core/types.js';
11
-
12
- interface InfoCommandOptions {
13
- template?: string;
14
- json: boolean;
15
- }
16
-
17
- /**
18
- * Show detailed info about a template
19
- */
20
- export async function createInfoCommand(
21
- template?: string,
22
- options: InfoCommandOptions = { json: false }
23
- ): Promise<void> {
24
- if (options.json) {
25
- if (template) {
26
- const meta = getTemplateMetadata(template as TemplateType);
27
- if (!meta) {
28
- console.log(JSON.stringify({ error: `Template "${template}" not found` }, null, 2));
29
- return;
30
- }
31
- printInfoJson(meta);
32
- } else {
33
- // Show info for all templates
34
- const templates = getAllTemplates();
35
- console.log(JSON.stringify(templates, null, 2));
36
- }
37
- return;
38
- }
39
-
40
- if (template) {
41
- await showTemplateInfo(template as TemplateType);
42
- } else {
43
- // Show brief info for all templates
44
- console.log('');
45
- console.log(gradientTeen('Available Templates'));
46
- console.log('');
47
-
48
- const templates = getAllTemplates();
49
- const maxNameLength = Math.max(...templates.map(t => t.name.length));
50
- const maxIdLength = Math.max(...templates.map(t => t.id.length));
51
-
52
- templates.forEach((t, index) => {
53
- const paddedName = t.name.padEnd(maxNameLength);
54
- const paddedId = t.id.padEnd(maxIdLength);
55
-
56
- console.log(` ${primary(`${index + 1}.`)} ${bold(paddedName)} ${muted(`(${paddedId})`)}`);
57
- console.log(` ${t.description}`);
58
- console.log('');
59
- });
60
-
61
- console.log('Usage: andrud info <template-name>');
62
- console.log('Example: andrud info kotlin-compose');
63
- }
64
- }
65
-
66
- /**
67
- * Show detailed info for a specific template
68
- */
69
- async function showTemplateInfo(templateId: TemplateType): Promise<void> {
70
- const meta = getTemplateMetadata(templateId);
71
- if (!meta) {
72
- console.log(pc.red(`Template "${templateId}" not found.`));
73
- console.log('Available templates:');
74
- getAllTemplates().forEach(t => console.log(` - ${t.id}`));
75
- return;
76
- }
77
-
78
- // Get template config for version info
79
- const config = TEMPLATE_CONFIGS[templateId];
80
- if (!config) {
81
- console.log(pc.red(`Template configuration not found for "${templateId}".`));
82
- return;
83
- }
84
- const isCompose = templateId === 'kotlin-compose';
85
-
86
- console.log('');
87
- console.log(gradientTeen(`═══ ${meta.name} ═══`));
88
- console.log('');
89
- console.log(bold(meta.description));
90
- console.log('');
91
-
92
- // Key information
93
- printSection('Configuration');
94
- printKeyValue([
95
- { key: 'Template ID', value: meta.id },
96
- { key: 'Language', value: config.language === 'kotlin' ? pc.green('Kotlin') : pc.yellow('Java') },
97
- { key: 'UI Framework', value: isCompose ? pc.cyan('Jetpack Compose') : pc.gray('XML Layouts') },
98
- { key: 'Min SDK', value: `API ${config.minSdk}` },
99
- { key: 'Target SDK', value: `API ${config.targetSdk}` },
100
- { key: 'Compile SDK', value: `API ${config.compileSdk}` }
101
- ]);
102
-
103
- // Version information
104
- printSection('Versions');
105
- printKeyValue([
106
- { key: 'Gradle', value: config.gradleVersion },
107
- { key: 'Android Gradle Plugin', value: config.agpVersion },
108
- { key: 'Kotlin', value: config.kotlinVersion || 'N/A' },
109
- ...(isCompose ? [
110
- { key: 'Compose BOM', value: GRADLE_VERSIONS.COMPOSE_BOM },
111
- { key: 'Compose Compiler', value: GRADLE_VERSIONS.COMPOSE_COMPILER }
112
- ] : []),
113
- ...(templateId === 'native-cpp' ? [
114
- { key: 'NDK', value: config.ndkVersion || GRADLE_VERSIONS.NDK }
115
- ] : [])
116
- ]);
117
-
118
- // Features
119
- printSection('Features');
120
- meta.features.forEach((feature, i) => {
121
- console.log(` ${pc.green('+')} ${feature}`);
122
- });
123
-
124
- // Keywords
125
- printSection('Keywords');
126
- console.log(` ${meta.keywords.map(k => pc.cyan(k)).join(', ')}`);
127
-
128
- // Code preview
129
- const preview = getTemplatePreview(templateId);
130
- if (preview) {
131
- printSection('Code Preview');
132
- console.log(pc.gray('─'.repeat(60)));
133
- console.log(pc.dim(preview.split('\n').slice(0, 15).join('\n')));
134
- if (preview.split('\n').length > 15) {
135
- console.log(pc.gray('...'));
136
- }
137
- }
138
-
139
- console.log('');
140
- }
141
-
142
- /**
143
- * Print template info as JSON
144
- */
145
- function printInfoJson(meta: TemplateMetadata): void {
146
- const config = TEMPLATE_CONFIGS[meta.id];
147
- if (!config) return;
148
-
149
- const isCompose = meta.id === 'kotlin-compose';
150
-
151
- const info = {
152
- id: meta.id,
153
- name: meta.name,
154
- description: meta.description,
155
- language: config.language,
156
- uiFramework: config.uiFramework === 'compose' ? 'Jetpack Compose' : 'XML Layouts',
157
- keywords: meta.keywords,
158
- features: meta.features,
159
- versions: {
160
- gradle: config.gradleVersion,
161
- agp: config.agpVersion,
162
- kotlin: config.kotlinVersion || null,
163
- compose: config.composeEnabled ? {
164
- bom: GRADLE_VERSIONS.COMPOSE_BOM,
165
- compiler: GRADLE_VERSIONS.COMPOSE_COMPILER
166
- } : null,
167
- ndk: config.ndkVersion || null
168
- },
169
- sdk: {
170
- min: config.minSdk,
171
- target: config.targetSdk,
172
- compile: config.compileSdk
173
- },
174
- codePreview: getTemplatePreview(meta.id)
175
- };
176
-
177
- console.log(JSON.stringify(info, null, 2));
178
- }
@@ -1,186 +0,0 @@
1
- /**
2
- * Init project command - initialize Android project in current directory
3
- */
4
-
5
- import {
6
- select,
7
- text,
8
- confirm,
9
- isCancel,
10
- cancel
11
- } from '@clack/prompts';
12
- import {
13
- printWelcome,
14
- printSuccess,
15
- printError,
16
- printSection,
17
- printKeyValue,
18
- printGoodbye
19
- } from '../../ui/output.js';
20
- import { validatePackageStructure } from '../../utils/validation.js';
21
- import { generateProject } from '../../core/generator.js';
22
- import { buildDefaultProjectContext, buildTemplateContext } from '../../core/context.js';
23
- import { getCurrentWorkingDirectory } from '../../utils/filesystem.js';
24
- import { getTemplateMetadata, getAllTemplates } from '../../templates/index.js';
25
- import pc from 'picocolors';
26
- import type { TemplateType } from '../../core/types.js';
27
-
28
- interface InitCommandOptions {
29
- force: boolean;
30
- template?: string;
31
- }
32
-
33
- /**
34
- * Initialize Android project in current directory
35
- */
36
- export async function createInitCommand(
37
- _options: InitCommandOptions,
38
- args?: { template?: string }
39
- ): Promise<void> {
40
- try {
41
- printWelcome();
42
-
43
- const cwd = getCurrentWorkingDirectory();
44
- const projectName = cwd.split(/[\\/]/).pop() ?? 'MyApp';
45
-
46
- printSection('Initialize Android Project');
47
- console.log(`Current directory: ${pc.cyan(cwd)}`);
48
- console.log('');
49
-
50
- // Get templates
51
- const templates = getAllTemplates();
52
-
53
- // Step 1: Select template
54
- let selectedTemplate: string;
55
- if (args?.template) {
56
- const meta = getTemplateMetadata(args.template as TemplateType);
57
- if (!meta) {
58
- printError(`Unknown template: ${args.template}`);
59
- return;
60
- }
61
- selectedTemplate = args.template;
62
- } else {
63
- const templateOptions = templates.map(t => ({
64
- label: t.name,
65
- value: t.id,
66
- hint: t.keywords.slice(0, 2).join(', ')
67
- }));
68
-
69
- const templateResult = await select({
70
- message: '? Select a project template',
71
- options: templateOptions
72
- });
73
-
74
- if (isCancel(templateResult)) {
75
- cancel();
76
- return;
77
- }
78
-
79
- selectedTemplate = templateResult as string;
80
- }
81
-
82
- // Step 2: Get package name
83
- const defaultPackage = `com.example.${projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
84
- const packageResult = await text({
85
- message: '? What is the package name?',
86
- placeholder: 'com.example.myapp',
87
- defaultValue: defaultPackage,
88
- validate: (value: string) => {
89
- const result = validatePackageStructure(value);
90
- if (!result.valid) {
91
- return result.errors[0];
92
- }
93
- return undefined;
94
- }
95
- });
96
-
97
- if (isCancel(packageResult)) {
98
- cancel();
99
- return;
100
- }
101
-
102
- const packageName = packageResult as string;
103
- const validation = validatePackageStructure(packageName);
104
- if (!validation.valid) {
105
- printError('Invalid package name', validation.errors.join(', '));
106
- return;
107
- }
108
-
109
- // Confirm initialization
110
- const confirmInit = await confirm({
111
- message: `? Initialize Android project in ${cwd}?`,
112
- initialValue: true
113
- });
114
-
115
- if (isCancel(confirmInit)) {
116
- cancel();
117
- return;
118
- }
119
-
120
- if (!confirmInit) {
121
- console.log('Initialization cancelled.');
122
- return;
123
- }
124
-
125
- // Build context
126
- const baseContext = buildDefaultProjectContext(
127
- projectName,
128
- packageName,
129
- cwd,
130
- selectedTemplate as TemplateType,
131
- {
132
- git: true,
133
- readme: true,
134
- androidX: true,
135
- kotlinDsl: true
136
- }
137
- );
138
-
139
- const context = buildTemplateContext({
140
- appName: baseContext.appName,
141
- packageName: baseContext.packageName,
142
- projectDirectory: baseContext.projectDirectory,
143
- template: baseContext.template,
144
- uiFramework: baseContext.uiFramework,
145
- language: baseContext.language,
146
- android: baseContext.android,
147
- gradle: baseContext.gradle,
148
- features: baseContext as unknown as Record<string, boolean>,
149
- nativeCpp: baseContext.nativeCpp
150
- });
151
-
152
- // Generate project
153
- printSection('Generating Project');
154
-
155
- const result = await generateProject(context, {
156
- overwrite: true,
157
- dryRun: false,
158
- skipInstall: false,
159
- verbose: false
160
- });
161
-
162
- if (result.success) {
163
- printSuccess('Project initialized successfully!');
164
- printKeyValue([
165
- { key: 'Package', value: packageName },
166
- { key: 'Template', value: selectedTemplate },
167
- { key: 'Location', value: cwd },
168
- { key: 'Files created', value: result.generatedFiles.length.toString() }
169
- ]);
170
-
171
- printGoodbye(true);
172
- } else {
173
- printError('Project initialization failed');
174
- if (result.errors.length > 0) {
175
- result.errors.forEach(err => {
176
- console.log(` ${pc.red('•')} ${err.file ? `${err.file}: ` : ''}${err.message}`);
177
- });
178
- }
179
- printGoodbye(false);
180
- }
181
- } catch (error) {
182
- printError('An unexpected error occurred', (error as Error).message);
183
- printGoodbye(false);
184
- process.exit(1);
185
- }
186
- }
@@ -1,156 +0,0 @@
1
- /**
2
- * List templates command - shows all available templates
3
- */
4
-
5
- import { gradientTeen, bold, primary, muted } from '../../ui/colors.js';
6
- import { printSection } from '../../ui/output.js';
7
- import { getAllTemplates, getTemplatePreview, searchTemplates } from '../../templates/index.js';
8
- import { TEMPLATE_CONFIGS } from '../../core/config.js';
9
- import pc from 'picocolors';
10
- import type { TemplateType, TemplateMetadata } from '../../core/types.js';
11
-
12
- interface ListCommandOptions {
13
- json: boolean;
14
- search?: string;
15
- }
16
-
17
- /**
18
- * List all available templates
19
- */
20
- export async function createListCommand(
21
- options: ListCommandOptions = { json: false }
22
- ): Promise<void> {
23
- // Validate search input
24
- if (options.search && typeof options.search !== 'string') {
25
- console.log(pc.red('Error: Search query must be a string'));
26
- return;
27
- }
28
-
29
- if (options.search && options.search.length > 100) {
30
- console.log(pc.red('Error: Search query is too long (maximum 100 characters)'));
31
- return;
32
- }
33
-
34
- // Filter templates based on search
35
- const templates = options.search?.trim()
36
- ? searchTemplates(options.search.trim())
37
- : getAllTemplates();
38
-
39
- if (options.json) {
40
- const templateList = templates.map(t => ({
41
- id: t.id,
42
- name: t.name,
43
- description: t.description,
44
- language: TEMPLATE_CONFIGS[t.id]?.language === 'kotlin' ? 'Kotlin' : 'Java',
45
- uiFramework: TEMPLATE_CONFIGS[t.id]?.uiFramework === 'compose' ? 'Compose' : 'XML'
46
- }));
47
- console.log(JSON.stringify(templateList, null, 2));
48
- return;
49
- }
50
-
51
- if (templates.length === 0) {
52
- console.log(pc.yellow('No templates found.'));
53
- return;
54
- }
55
-
56
- console.log('');
57
- console.log(gradientTeen('╔══════════════════════════════════════════════════════════════╗'));
58
- console.log(gradientTeen('║ Android Project Templates ║'));
59
- console.log(gradientTeen('╚══════════════════════════════════════════════════════════════╝'));
60
- console.log('');
61
-
62
- templates.forEach((template, index) => {
63
- printTemplateCard(template, index + 1);
64
- });
65
-
66
- // Summary
67
- printSection('Summary');
68
- console.log(` Total templates: ${bold(templates.length.toString())}`);
69
- console.log(` Kotlin templates: ${bold(
70
- templates.filter(t => TEMPLATE_CONFIGS[t.id]?.language === 'kotlin').length.toString()
71
- )}`);
72
- console.log(` Java templates: ${bold(
73
- templates.filter(t => TEMPLATE_CONFIGS[t.id]?.language === 'java').length.toString()
74
- )}`);
75
- console.log(` Compose templates: ${bold(
76
- templates.filter(t => TEMPLATE_CONFIGS[t.id]?.uiFramework === 'compose').length.toString()
77
- )}`);
78
- console.log('');
79
-
80
- console.log(muted(' Use ') + primary('andrud info <template>') + muted(' for more details'));
81
- console.log('');
82
- }
83
-
84
- /**
85
- * Print a template as a card
86
- */
87
- function printTemplateCard(template: TemplateMetadata, number: number): void {
88
- const config = TEMPLATE_CONFIGS[template.id];
89
- if (!config) return;
90
-
91
- const isKotlin = config.language === 'kotlin';
92
- const isCompose = config.uiFramework === 'compose';
93
-
94
- // Template header
95
- console.log(primary(` ${number}. `) + bold(template.name));
96
- console.log(` ${template.description}`);
97
- console.log('');
98
-
99
- // Tags
100
- const tags: string[] = [];
101
- tags.push(isKotlin ? pc.green('Kotlin') : pc.yellow('Java'));
102
- tags.push(isCompose ? pc.cyan('Compose') : pc.gray('XML'));
103
- if (template.id === 'native-cpp') {
104
- tags.push(pc.magenta('NDK'));
105
- }
106
- console.log(` ${tags.join(' ')}`);
107
-
108
- // Key features
109
- const features = template.features.slice(0, 3);
110
- console.log(` ${muted('Features:')} ${features.join(pc.gray(', '))}`);
111
-
112
- // Version info
113
- console.log(` ${muted('Gradle:')} ${config.gradleVersion} ${muted('AGP:')} ${config.agpVersion}`);
114
-
115
- // Command example
116
- console.log('');
117
- console.log(` ${muted('Run:')} ${primary(`andrud new MyApp -t ${template.id}`)}`);
118
-
119
- // Code preview snippet
120
- const preview = getTemplatePreview(template.id);
121
- if (preview) {
122
- const lines = preview.split('\n').slice(0, 4);
123
- console.log('');
124
- console.log(' ' + pc.gray('┌─ Code preview'));
125
- lines.forEach((line, i) => {
126
- const isLast = i === lines.length - 1;
127
- const prefix = isLast ? ' ' : ' ';
128
- const linePrefix = isLast ? '└─' : '│ ';
129
- console.log(prefix + pc.gray(linePrefix) + pc.dim(line.substring(0, 50) + (line.length > 50 ? '...' : '')));
130
- });
131
- }
132
-
133
- console.log('');
134
- console.log(pc.gray(' ' + '─'.repeat(60)));
135
- console.log('');
136
- }
137
-
138
- /**
139
- * Print templates as a compact list
140
- */
141
- export function printTemplateList(templates: TemplateMetadata[]): void {
142
- const maxIdLength = Math.max(...templates.map(t => t.id.length));
143
- const maxNameLength = Math.max(...templates.map(t => t.name.length));
144
-
145
- templates.forEach((t, i) => {
146
- const config = TEMPLATE_CONFIGS[t.id];
147
- if (!config) return;
148
-
149
- const id = t.id.padEnd(maxIdLength);
150
- const name = t.name.padEnd(maxNameLength);
151
- const lang = config.language === 'kotlin' ? pc.green('K') : pc.yellow('J');
152
- const ui = config.uiFramework === 'compose' ? pc.cyan('C') : pc.gray('X');
153
-
154
- console.log(` ${primary(String(i + 1).padStart(2))} ${bold(id)} ${name} ${lang}${ui} ${muted(t.description.substring(0, 40))}`);
155
- });
156
- }