ac-framework 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -3
- package/src/cli.js +7 -1
- package/src/commands/init.js +138 -126
- package/src/ui/animations.js +281 -34
- package/src/ui/banner.js +78 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ac-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Agentic Coding Framework - Multi-assistant configuration system with OpenSpec workflows",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
"commander": "^12.1.0",
|
|
26
26
|
"gradient-string": "^3.0.0",
|
|
27
27
|
"inquirer": "^12.3.2",
|
|
28
|
-
"nanospinner": "^1.2.2"
|
|
29
|
-
"figlet": "^1.8.0"
|
|
28
|
+
"nanospinner": "^1.2.2"
|
|
30
29
|
},
|
|
31
30
|
"type": "module",
|
|
32
31
|
"engines": {
|
package/src/cli.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { dirname, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
4
7
|
import { initCommand } from './commands/init.js';
|
|
5
8
|
import { showBanner } from './ui/banner.js';
|
|
6
9
|
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const pkg = JSON.parse(await readFile(resolve(__dirname, '../package.json'), 'utf-8'));
|
|
12
|
+
|
|
7
13
|
const program = new Command();
|
|
8
14
|
|
|
9
15
|
program
|
|
10
16
|
.name('acfm')
|
|
11
17
|
.description('AC Framework - Agentic Coding Framework CLI')
|
|
12
|
-
.version(
|
|
18
|
+
.version(pkg.version);
|
|
13
19
|
|
|
14
20
|
program
|
|
15
21
|
.command('init')
|
package/src/commands/init.js
CHANGED
|
@@ -1,73 +1,94 @@
|
|
|
1
|
-
import { readdir, cp, access } from 'node:fs/promises';
|
|
1
|
+
import { readdir, cp, access, rm } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import gradient from 'gradient-string';
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
|
-
import { createSpinner } from 'nanospinner';
|
|
8
7
|
import { DESCRIPTIONS, formatFolderName, sleep } from '../utils/helpers.js';
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
matrixRain,
|
|
10
|
+
scanAnimation,
|
|
11
|
+
animatedSeparator,
|
|
12
|
+
revealList,
|
|
13
|
+
progressBar,
|
|
14
|
+
installWithAnimation,
|
|
15
|
+
celebrateSuccess,
|
|
16
|
+
showFailureSummary,
|
|
17
|
+
stepHeader,
|
|
18
|
+
} from '../ui/animations.js';
|
|
10
19
|
|
|
11
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
21
|
const __dirname = dirname(__filename);
|
|
13
22
|
|
|
14
23
|
const acGradient = gradient(['#6C5CE7', '#00CEC9', '#0984E3']);
|
|
15
|
-
|
|
24
|
+
|
|
25
|
+
const ALWAYS_INSTALL = ['openspec'];
|
|
16
26
|
|
|
17
27
|
async function getFrameworkFolders() {
|
|
18
28
|
const frameworkPath = resolve(__dirname, '../../framework');
|
|
19
29
|
const entries = await readdir(frameworkPath, { withFileTypes: true });
|
|
20
30
|
|
|
21
31
|
return entries
|
|
22
|
-
.filter((entry) => entry.isDirectory())
|
|
32
|
+
.filter((entry) => entry.isDirectory() && !ALWAYS_INSTALL.includes(entry.name))
|
|
23
33
|
.map((entry) => entry.name)
|
|
24
|
-
.sort((a, b) =>
|
|
25
|
-
// Hidden folders first, then normal
|
|
26
|
-
const aHidden = a.startsWith('.');
|
|
27
|
-
const bHidden = b.startsWith('.');
|
|
28
|
-
if (aHidden && !bHidden) return -1;
|
|
29
|
-
if (!aHidden && bHidden) return 1;
|
|
30
|
-
return a.localeCompare(b);
|
|
31
|
-
});
|
|
34
|
+
.sort((a, b) => a.localeCompare(b));
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
function buildChoices(folders) {
|
|
35
38
|
const choices = [];
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const folder of hidden) {
|
|
43
|
-
const desc = DESCRIPTIONS[folder] || '';
|
|
44
|
-
const displayName = formatFolderName(folder);
|
|
45
|
-
const label = `${chalk.hex('#DFE6E9').bold(displayName)}${desc ? chalk.hex('#636E72')(` — ${desc}`) : ''}`;
|
|
46
|
-
choices.push({
|
|
47
|
-
name: label,
|
|
48
|
-
value: folder,
|
|
49
|
-
short: displayName,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
40
|
+
choices.push(new inquirer.Separator(
|
|
41
|
+
chalk.hex('#636E72')(' ── ') +
|
|
42
|
+
chalk.hex('#6C5CE7').bold('AI Assistants') +
|
|
43
|
+
chalk.hex('#636E72')(' ─────────────────────────────')
|
|
44
|
+
));
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
46
|
+
for (const folder of folders) {
|
|
47
|
+
const desc = DESCRIPTIONS[folder] || '';
|
|
48
|
+
const displayName = formatFolderName(folder);
|
|
49
|
+
const icon = getAssistantIcon(folder);
|
|
50
|
+
const label =
|
|
51
|
+
`${chalk.hex('#636E72')(icon)} ${chalk.hex('#DFE6E9').bold(displayName)}` +
|
|
52
|
+
(desc ? chalk.hex('#636E72')(` · ${desc}`) : '');
|
|
53
|
+
choices.push({
|
|
54
|
+
name: label,
|
|
55
|
+
value: folder,
|
|
56
|
+
short: displayName,
|
|
57
|
+
});
|
|
66
58
|
}
|
|
67
59
|
|
|
68
60
|
return choices;
|
|
69
61
|
}
|
|
70
62
|
|
|
63
|
+
function getAssistantIcon(folder) {
|
|
64
|
+
const icons = {
|
|
65
|
+
'.agent': '⊡',
|
|
66
|
+
'.amazonq': '◈',
|
|
67
|
+
'.augment': '◇',
|
|
68
|
+
'.claude': '◉',
|
|
69
|
+
'.cline': '◎',
|
|
70
|
+
'.clinerules': '◎',
|
|
71
|
+
'.codebuddy': '◈',
|
|
72
|
+
'.codex': '⊞',
|
|
73
|
+
'.continue': '▹',
|
|
74
|
+
'.cospec': '⊙',
|
|
75
|
+
'.crush': '◆',
|
|
76
|
+
'.cursor': '▸',
|
|
77
|
+
'.factory': '⊟',
|
|
78
|
+
'.gemini': '◇',
|
|
79
|
+
'.github': '◈',
|
|
80
|
+
'.iflow': '▹',
|
|
81
|
+
'.kilocode': '◎',
|
|
82
|
+
'.opencode': '⊡',
|
|
83
|
+
'.qoder': '◇',
|
|
84
|
+
'.qwen': '◈',
|
|
85
|
+
'.roo': '◆',
|
|
86
|
+
'.trae': '▸',
|
|
87
|
+
'.windsurf': '◇',
|
|
88
|
+
};
|
|
89
|
+
return icons[folder] || '◦';
|
|
90
|
+
}
|
|
91
|
+
|
|
71
92
|
async function checkExisting(targetDir, folder) {
|
|
72
93
|
try {
|
|
73
94
|
await access(join(targetDir, folder));
|
|
@@ -80,41 +101,47 @@ async function checkExisting(targetDir, folder) {
|
|
|
80
101
|
export async function initCommand() {
|
|
81
102
|
const targetDir = process.cwd();
|
|
82
103
|
|
|
83
|
-
//
|
|
84
|
-
await
|
|
104
|
+
// ── Step 1: Scan ───────────────────────────────────────────────
|
|
105
|
+
await stepHeader(1, 3, 'Scanning framework modules');
|
|
106
|
+
await scanAnimation('Indexing available modules', 1000);
|
|
85
107
|
console.log();
|
|
86
108
|
|
|
87
|
-
// Matrix rain
|
|
88
|
-
|
|
89
|
-
await matrixRain(1500);
|
|
109
|
+
// Matrix rain transition
|
|
110
|
+
await matrixRain(1800);
|
|
90
111
|
|
|
91
112
|
let folders;
|
|
92
113
|
try {
|
|
93
114
|
folders = await getFrameworkFolders();
|
|
94
115
|
} catch {
|
|
95
|
-
console.log(chalk.
|
|
116
|
+
console.log(chalk.hex('#D63031')(' ✗ Error: Could not read framework directory.'));
|
|
96
117
|
console.log(chalk.hex('#636E72')(' Make sure ac-framework is installed correctly.'));
|
|
97
118
|
process.exit(1);
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
if (folders.length === 0) {
|
|
101
|
-
console.log(chalk.
|
|
122
|
+
console.log(chalk.hex('#FDCB6E')(' No modules found in framework directory.'));
|
|
102
123
|
process.exit(0);
|
|
103
124
|
}
|
|
104
125
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
);
|
|
126
|
+
// Module count display
|
|
127
|
+
const countBadge = chalk.hex('#2D3436').bgHex('#00CEC9').bold(` ${folders.length} `);
|
|
128
|
+
const autoBadge = chalk.hex('#2D3436').bgHex('#6C5CE7').bold(' +openspec ');
|
|
129
|
+
console.log(` ${countBadge} ${chalk.hex('#B2BEC3')('assistant modules found')} ${autoBadge} ${chalk.hex('#636E72')('auto-included')}`);
|
|
130
|
+
console.log();
|
|
131
|
+
await animatedSeparator(60);
|
|
132
|
+
console.log();
|
|
133
|
+
|
|
134
|
+
// ── Step 2: Select ─────────────────────────────────────────────
|
|
135
|
+
await stepHeader(2, 3, 'Select your assistants');
|
|
108
136
|
|
|
137
|
+
// Controls hint with styled keys
|
|
138
|
+
const key = (k) => chalk.hex('#2D3436').bgHex('#636E72')(` ${k} `);
|
|
109
139
|
console.log(
|
|
110
|
-
chalk.hex('#636E72')('
|
|
111
|
-
chalk.hex('#
|
|
112
|
-
chalk.hex('#636E72')('
|
|
113
|
-
chalk.hex('#00CEC9')('Space') +
|
|
114
|
-
chalk.hex('#636E72')(' to select, ') +
|
|
115
|
-
chalk.hex('#00CEC9')('Enter') +
|
|
116
|
-
chalk.hex('#636E72')(' to confirm\n')
|
|
140
|
+
` ${key('↑↓')} ${chalk.hex('#636E72')('navigate')} ` +
|
|
141
|
+
`${key('Space')} ${chalk.hex('#636E72')('toggle')} ` +
|
|
142
|
+
`${key('Enter')} ${chalk.hex('#636E72')('confirm')}`
|
|
117
143
|
);
|
|
144
|
+
console.log();
|
|
118
145
|
|
|
119
146
|
const choices = buildChoices(folders);
|
|
120
147
|
|
|
@@ -122,13 +149,13 @@ export async function initCommand() {
|
|
|
122
149
|
{
|
|
123
150
|
type: 'checkbox',
|
|
124
151
|
name: 'selected',
|
|
125
|
-
message: acGradient('
|
|
152
|
+
message: acGradient('Choose modules to install:'),
|
|
126
153
|
choices,
|
|
127
154
|
pageSize: 15,
|
|
128
155
|
loop: false,
|
|
129
156
|
validate(answer) {
|
|
130
157
|
if (answer.length === 0) {
|
|
131
|
-
return chalk.hex('#D63031')('
|
|
158
|
+
return chalk.hex('#D63031')('Select at least one module. Use Space to toggle.');
|
|
132
159
|
}
|
|
133
160
|
return true;
|
|
134
161
|
},
|
|
@@ -137,9 +164,10 @@ export async function initCommand() {
|
|
|
137
164
|
|
|
138
165
|
console.log();
|
|
139
166
|
|
|
140
|
-
// Check
|
|
167
|
+
// ── Check conflicts ────────────────────────────────────────────
|
|
168
|
+
const allForCheck = [...selected, ...ALWAYS_INSTALL];
|
|
141
169
|
const existing = [];
|
|
142
|
-
for (const folder of
|
|
170
|
+
for (const folder of allForCheck) {
|
|
143
171
|
if (await checkExisting(targetDir, folder)) {
|
|
144
172
|
existing.push(folder);
|
|
145
173
|
}
|
|
@@ -147,10 +175,14 @@ export async function initCommand() {
|
|
|
147
175
|
|
|
148
176
|
if (existing.length > 0) {
|
|
149
177
|
console.log(
|
|
150
|
-
chalk.hex('#FDCB6E')(' ⚠
|
|
178
|
+
chalk.hex('#FDCB6E')(' ⚠ These modules already exist in your project:\n')
|
|
151
179
|
);
|
|
152
180
|
for (const folder of existing) {
|
|
153
|
-
console.log(
|
|
181
|
+
console.log(
|
|
182
|
+
chalk.hex('#FDCB6E')(' ▸ ') +
|
|
183
|
+
chalk.hex('#DFE6E9')(formatFolderName(folder)) +
|
|
184
|
+
chalk.hex('#636E72')(` (${folder})`)
|
|
185
|
+
);
|
|
154
186
|
}
|
|
155
187
|
console.log();
|
|
156
188
|
|
|
@@ -165,28 +197,35 @@ export async function initCommand() {
|
|
|
165
197
|
|
|
166
198
|
if (!overwrite) {
|
|
167
199
|
const filtered = selected.filter((f) => !existing.includes(f));
|
|
168
|
-
|
|
169
|
-
|
|
200
|
+
const autoFiltered = ALWAYS_INSTALL.filter((f) => !existing.includes(f));
|
|
201
|
+
if (filtered.length === 0 && autoFiltered.length === 0) {
|
|
202
|
+
console.log(chalk.hex('#636E72')('\n Nothing new to install. Exiting.\n'));
|
|
170
203
|
process.exit(0);
|
|
171
204
|
}
|
|
172
205
|
selected.length = 0;
|
|
173
206
|
selected.push(...filtered);
|
|
174
207
|
console.log(
|
|
175
|
-
chalk.hex('#B2BEC3')(`\n
|
|
208
|
+
chalk.hex('#B2BEC3')(`\n Continuing with ${chalk.hex('#00CEC9').bold(filtered.length + autoFiltered.length)} new module(s)...\n`)
|
|
176
209
|
);
|
|
177
210
|
}
|
|
178
211
|
}
|
|
179
212
|
|
|
180
|
-
// Confirm selection
|
|
181
|
-
console.log(chalk.hex('#B2BEC3')('
|
|
182
|
-
|
|
213
|
+
// ── Confirm selection with animated reveal ─────────────────────
|
|
214
|
+
console.log(chalk.hex('#B2BEC3')(' Selected modules:\n'));
|
|
215
|
+
|
|
216
|
+
const selectedItems = selected.map((folder) => {
|
|
183
217
|
const desc = DESCRIPTIONS[folder] || '';
|
|
184
|
-
|
|
185
|
-
chalk.hex('#
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
189
|
-
|
|
218
|
+
return chalk.hex('#DFE6E9').bold(formatFolderName(folder)) +
|
|
219
|
+
(desc ? chalk.hex('#636E72')(` · ${desc}`) : '');
|
|
220
|
+
});
|
|
221
|
+
selectedItems.push(
|
|
222
|
+
chalk.hex('#DFE6E9').bold('Openspec') +
|
|
223
|
+
chalk.hex('#636E72')(` · ${DESCRIPTIONS['openspec']}`) +
|
|
224
|
+
chalk.hex('#6C5CE7').italic(' (auto)')
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
await revealList(selectedItems, { prefix: '◆', color: '#00CEC9', delay: 40 });
|
|
228
|
+
|
|
190
229
|
console.log();
|
|
191
230
|
|
|
192
231
|
const { confirm } = await inquirer.prompt([
|
|
@@ -203,73 +242,46 @@ export async function initCommand() {
|
|
|
203
242
|
process.exit(0);
|
|
204
243
|
}
|
|
205
244
|
|
|
245
|
+
// ── Step 3: Install ────────────────────────────────────────────
|
|
246
|
+
console.log();
|
|
247
|
+
await animatedSeparator(60);
|
|
206
248
|
console.log();
|
|
249
|
+
await stepHeader(3, 3, 'Installing modules');
|
|
207
250
|
|
|
208
|
-
// Install modules with progress
|
|
209
251
|
const frameworkPath = resolve(__dirname, '../../framework');
|
|
252
|
+
const allToInstall = [...selected, ...ALWAYS_INSTALL];
|
|
210
253
|
let installed = 0;
|
|
211
254
|
const errors = [];
|
|
212
255
|
|
|
213
|
-
for (const folder of
|
|
214
|
-
const
|
|
215
|
-
chalk.hex('#B2BEC3')(`Installing ${chalk.hex('#DFE6E9').bold(formatFolderName(folder))}...`)
|
|
216
|
-
).start();
|
|
256
|
+
for (const folder of allToInstall) {
|
|
257
|
+
const displayName = formatFolderName(folder);
|
|
217
258
|
|
|
218
259
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
await cp(src, dest, { recursive: true, force: true });
|
|
223
|
-
|
|
224
|
-
// Skip node_modules if copied
|
|
225
|
-
const nmPath = join(dest, 'node_modules');
|
|
226
|
-
try {
|
|
227
|
-
const { rm } = await import('node:fs/promises');
|
|
228
|
-
await rm(nmPath, { recursive: true, force: true });
|
|
229
|
-
} catch {
|
|
230
|
-
// node_modules didn't exist, that's fine
|
|
231
|
-
}
|
|
260
|
+
await installWithAnimation(displayName, async () => {
|
|
261
|
+
const src = join(frameworkPath, folder);
|
|
262
|
+
const dest = join(targetDir, folder);
|
|
232
263
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
264
|
+
await cp(src, dest, { recursive: true, force: true });
|
|
265
|
+
|
|
266
|
+
// Remove node_modules if present (e.g. .opencode)
|
|
267
|
+
try {
|
|
268
|
+
await rm(join(dest, 'node_modules'), { recursive: true, force: true });
|
|
269
|
+
} catch {
|
|
270
|
+
// Fine if it doesn't exist
|
|
271
|
+
}
|
|
238
272
|
});
|
|
273
|
+
installed++;
|
|
239
274
|
} catch (err) {
|
|
240
275
|
errors.push({ folder, error: err.message });
|
|
241
|
-
spinner.error({
|
|
242
|
-
text: chalk.hex('#D63031')(` Failed: ${formatFolderName(folder)} — ${err.message}`),
|
|
243
|
-
});
|
|
244
276
|
}
|
|
245
277
|
|
|
246
|
-
await sleep(
|
|
278
|
+
await sleep(80);
|
|
247
279
|
}
|
|
248
280
|
|
|
249
|
-
// Final
|
|
250
|
-
console.log();
|
|
251
|
-
console.log(
|
|
252
|
-
gradient(['#636E72', '#B2BEC3'])(' ─────────────────────────────────────────────────────────────────')
|
|
253
|
-
);
|
|
254
|
-
console.log();
|
|
255
|
-
|
|
281
|
+
// ── Final result ───────────────────────────────────────────────
|
|
256
282
|
if (errors.length === 0) {
|
|
257
|
-
await
|
|
258
|
-
console.log();
|
|
259
|
-
console.log(successGradient(' ✓ Installation complete!'));
|
|
260
|
-
console.log();
|
|
261
|
-
console.log(
|
|
262
|
-
chalk.hex('#B2BEC3')(` ${chalk.hex('#00CEC9').bold(installed)} module(s) installed successfully in ${chalk.hex('#DFE6E9')(targetDir)}`)
|
|
263
|
-
);
|
|
283
|
+
await celebrateSuccess(installed, targetDir);
|
|
264
284
|
} else {
|
|
265
|
-
|
|
266
|
-
chalk.hex('#FDCB6E')(` ${installed} installed, ${errors.length} failed.`)
|
|
267
|
-
);
|
|
285
|
+
await showFailureSummary(installed, errors);
|
|
268
286
|
}
|
|
269
|
-
|
|
270
|
-
console.log();
|
|
271
|
-
console.log(
|
|
272
|
-
chalk.hex('#636E72')(' Your project is ready. Happy coding!')
|
|
273
|
-
);
|
|
274
|
-
console.log();
|
|
275
287
|
}
|
package/src/ui/animations.js
CHANGED
|
@@ -1,44 +1,57 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import gradient from 'gradient-string';
|
|
2
3
|
import { createSpinner } from 'nanospinner';
|
|
3
4
|
import { sleep } from '../utils/helpers.js';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const acGradient = gradient(['#6C5CE7', '#00CEC9', '#0984E3']);
|
|
7
|
+
const successGradient = gradient(['#00CEC9', '#00B894', '#55EFC4']);
|
|
8
|
+
const warmGradient = gradient(['#FDCB6E', '#E17055', '#D63031']);
|
|
9
|
+
const glowGradient = gradient(['#0984E3', '#00CEC9', '#55EFC4', '#00CEC9', '#0984E3']);
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
// ── Matrix Rain ──────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export async function matrixRain(durationMs = 1800) {
|
|
8
14
|
const cols = Math.min(process.stdout.columns || 80, 80);
|
|
15
|
+
const rows = 8;
|
|
9
16
|
const drops = Array.from({ length: cols }, () => ({
|
|
10
|
-
y: Math.floor(Math.random() *
|
|
11
|
-
speed: 1 + Math.floor(Math.random() *
|
|
12
|
-
|
|
17
|
+
y: Math.floor(Math.random() * rows),
|
|
18
|
+
speed: 1 + Math.floor(Math.random() * 2),
|
|
19
|
+
trail: 2 + Math.floor(Math.random() * 3),
|
|
13
20
|
}));
|
|
14
|
-
const chars = '01
|
|
15
|
-
const colors = ['#00CEC9', '#0984E3', '#6C5CE7', '#00FF41'];
|
|
16
|
-
const
|
|
17
|
-
const frameTime = 60;
|
|
21
|
+
const chars = '01アイウエオカキクケコサシスセソ>>=</>{}[]ACFM';
|
|
22
|
+
const colors = ['#00CEC9', '#0984E3', '#6C5CE7', '#00FF41', '#55EFC4'];
|
|
23
|
+
const frameTime = 50;
|
|
18
24
|
const totalFrames = Math.floor(durationMs / frameTime);
|
|
19
25
|
|
|
26
|
+
// Print initial empty rows
|
|
27
|
+
for (let i = 0; i < rows; i++) console.log();
|
|
28
|
+
|
|
20
29
|
for (let frame = 0; frame < totalFrames; frame++) {
|
|
21
|
-
let output = '';
|
|
22
30
|
const grid = Array.from({ length: rows }, () => Array(cols).fill(' '));
|
|
23
31
|
|
|
24
32
|
for (let c = 0; c < cols; c++) {
|
|
25
33
|
const drop = drops[c];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
// Draw trail
|
|
35
|
+
for (let t = 0; t < drop.trail; t++) {
|
|
36
|
+
const ty = drop.y - t;
|
|
37
|
+
if (ty >= 0 && ty < rows) {
|
|
38
|
+
const ch = chars[Math.floor(Math.random() * chars.length)];
|
|
39
|
+
const brightness = t === 0 ? '#FFFFFF' : colors[Math.floor(Math.random() * colors.length)];
|
|
40
|
+
const opacity = t === 0 ? 1 : Math.max(0.2, 1 - t * 0.3);
|
|
41
|
+
grid[ty][c] = t === 0
|
|
42
|
+
? chalk.hex(brightness).bold(ch)
|
|
43
|
+
: chalk.hex(brightness)(ch);
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
drop.y += drop.speed;
|
|
36
|
-
if (drop.y > rows
|
|
37
|
-
drop.y = -Math.floor(Math.random() *
|
|
38
|
-
drop.speed = 1 + Math.floor(Math.random() *
|
|
47
|
+
if (drop.y - drop.trail > rows) {
|
|
48
|
+
drop.y = -Math.floor(Math.random() * 6);
|
|
49
|
+
drop.speed = 1 + Math.floor(Math.random() * 2);
|
|
50
|
+
drop.trail = 2 + Math.floor(Math.random() * 3);
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
53
|
|
|
54
|
+
let output = '';
|
|
42
55
|
for (const row of grid) {
|
|
43
56
|
output += ' ' + row.join('') + '\n';
|
|
44
57
|
}
|
|
@@ -48,7 +61,25 @@ export async function matrixRain(durationMs = 1500) {
|
|
|
48
61
|
await sleep(frameTime);
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
//
|
|
64
|
+
// Fade out effect
|
|
65
|
+
for (let fade = 0; fade < 4; fade++) {
|
|
66
|
+
process.stdout.write(`\x1B[${rows}A`);
|
|
67
|
+
for (let r = 0; r < rows; r++) {
|
|
68
|
+
let line = ' ';
|
|
69
|
+
for (let c = 0; c < cols; c++) {
|
|
70
|
+
if (Math.random() < 0.3 - fade * 0.07) {
|
|
71
|
+
const ch = chars[Math.floor(Math.random() * chars.length)];
|
|
72
|
+
line += chalk.hex('#2D3436')(ch);
|
|
73
|
+
} else {
|
|
74
|
+
line += ' ';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
console.log(line);
|
|
78
|
+
}
|
|
79
|
+
await sleep(60);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Clear the area
|
|
52
83
|
process.stdout.write(`\x1B[${rows}A`);
|
|
53
84
|
for (let i = 0; i < rows; i++) {
|
|
54
85
|
process.stdout.write('\x1B[2K\n');
|
|
@@ -56,27 +87,243 @@ export async function matrixRain(durationMs = 1500) {
|
|
|
56
87
|
process.stdout.write(`\x1B[${rows}A`);
|
|
57
88
|
}
|
|
58
89
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
// ── Scanning / Loading ───────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export async function scanAnimation(text, durationMs = 1000) {
|
|
93
|
+
const frames = ['◜', '◠', '◝', '◞', '◡', '◟'];
|
|
94
|
+
const totalFrames = Math.floor(durationMs / 80);
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
97
|
+
const frame = frames[i % frames.length];
|
|
98
|
+
const dots = '.'.repeat((i % 3) + 1).padEnd(3);
|
|
99
|
+
process.stdout.write(
|
|
100
|
+
`\r ${chalk.hex('#00CEC9')(frame)} ${chalk.hex('#B2BEC3')(text)}${chalk.hex('#636E72')(dots)}`
|
|
101
|
+
);
|
|
102
|
+
await sleep(80);
|
|
103
|
+
}
|
|
104
|
+
process.stdout.write(
|
|
105
|
+
`\r ${chalk.hex('#00CEC9')('◉')} ${chalk.hex('#00CEC9')(text)} \n`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Animated Separator ───────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
export async function animatedSeparator(width = 60) {
|
|
112
|
+
const ch = '─';
|
|
113
|
+
for (let i = 0; i <= width; i++) {
|
|
114
|
+
const before = ch.repeat(i);
|
|
115
|
+
const dot = '●';
|
|
116
|
+
const after = ch.repeat(Math.max(0, width - i));
|
|
117
|
+
process.stdout.write(
|
|
118
|
+
`\r ${glowGradient(before)}${chalk.hex('#00CEC9')(dot)}${chalk.hex('#2D3436')(after)}`
|
|
119
|
+
);
|
|
120
|
+
await sleep(4);
|
|
121
|
+
}
|
|
122
|
+
process.stdout.write(`\r ${glowGradient(ch.repeat(width))} \n`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Staggered List Reveal ────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
export async function revealList(items, { prefix = '◆', color = '#00CEC9', delay = 60 } = {}) {
|
|
128
|
+
for (const item of items) {
|
|
129
|
+
// Slide in from left
|
|
130
|
+
const maxSlide = 6;
|
|
131
|
+
for (let s = maxSlide; s >= 0; s--) {
|
|
132
|
+
const pad = ' '.repeat(s);
|
|
133
|
+
process.stdout.write(
|
|
134
|
+
`\r ${pad}${chalk.hex(color)(prefix)} ${item}`
|
|
135
|
+
);
|
|
136
|
+
await sleep(15);
|
|
137
|
+
}
|
|
138
|
+
console.log();
|
|
139
|
+
await sleep(delay);
|
|
140
|
+
}
|
|
63
141
|
}
|
|
64
142
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const barWidth =
|
|
143
|
+
// ── Progress Bar (enhanced) ──────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
export async function progressBar(label, steps = 30, durationMs = 1000) {
|
|
146
|
+
const barWidth = 35;
|
|
147
|
+
const blocks = ['░', '▒', '▓', '█'];
|
|
69
148
|
|
|
70
149
|
for (let i = 0; i <= steps; i++) {
|
|
71
|
-
const progress =
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
150
|
+
const progress = (i / steps) * barWidth;
|
|
151
|
+
const full = Math.floor(progress);
|
|
152
|
+
const partial = progress - full;
|
|
153
|
+
|
|
154
|
+
let bar = '';
|
|
155
|
+
for (let b = 0; b < barWidth; b++) {
|
|
156
|
+
if (b < full) {
|
|
157
|
+
bar += chalk.hex('#6C5CE7')('█');
|
|
158
|
+
} else if (b === full) {
|
|
159
|
+
const blockIdx = Math.floor(partial * blocks.length);
|
|
160
|
+
bar += chalk.hex('#A29BFE')(blocks[Math.min(blockIdx, blocks.length - 1)]);
|
|
161
|
+
} else {
|
|
162
|
+
bar += chalk.hex('#2D3436')('░');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
75
166
|
const pct = Math.round((i / steps) * 100);
|
|
167
|
+
const pctStr = `${pct}%`.padStart(4);
|
|
168
|
+
|
|
169
|
+
// Spinner character
|
|
170
|
+
const spinChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
171
|
+
const spin = i < steps
|
|
172
|
+
? chalk.hex('#00CEC9')(spinChars[i % spinChars.length])
|
|
173
|
+
: chalk.hex('#00CEC9')('✓');
|
|
174
|
+
|
|
76
175
|
process.stdout.write(
|
|
77
|
-
`\r ${chalk.hex('#B2BEC3')(label)} ${bar} ${chalk.hex('#00CEC9')(
|
|
176
|
+
`\r ${spin} ${chalk.hex('#B2BEC3')(label)} ${bar} ${chalk.hex('#00CEC9')(pctStr)}`
|
|
78
177
|
);
|
|
79
178
|
await sleep(durationMs / steps);
|
|
80
179
|
}
|
|
81
180
|
process.stdout.write('\n');
|
|
82
181
|
}
|
|
182
|
+
|
|
183
|
+
// ── Installation Spinner (enhanced) ──────────────────────────────
|
|
184
|
+
|
|
185
|
+
export async function installWithAnimation(name, task) {
|
|
186
|
+
const frames = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
187
|
+
let frameIdx = 0;
|
|
188
|
+
let running = true;
|
|
189
|
+
|
|
190
|
+
const animate = async () => {
|
|
191
|
+
while (running) {
|
|
192
|
+
const frame = chalk.hex('#6C5CE7')(frames[frameIdx % frames.length]);
|
|
193
|
+
process.stdout.write(
|
|
194
|
+
`\r ${frame} ${chalk.hex('#B2BEC3')('Installing')} ${chalk.hex('#DFE6E9').bold(name)}${chalk.hex('#636E72')('...')}`
|
|
195
|
+
);
|
|
196
|
+
frameIdx++;
|
|
197
|
+
await sleep(60);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const animPromise = animate();
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
await task();
|
|
205
|
+
running = false;
|
|
206
|
+
await sleep(70);
|
|
207
|
+
process.stdout.write(
|
|
208
|
+
`\r ${chalk.hex('#00CEC9')('✓')} ${chalk.hex('#00CEC9')(name)}${chalk.hex('#636E72')(' installed successfully')} \n`
|
|
209
|
+
);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
running = false;
|
|
212
|
+
await sleep(70);
|
|
213
|
+
process.stdout.write(
|
|
214
|
+
`\r ${chalk.hex('#D63031')('✗')} ${chalk.hex('#D63031')(name)}${chalk.hex('#636E72')(` — ${err.message}`)} \n`
|
|
215
|
+
);
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Success Celebration ──────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
export async function celebrateSuccess(moduleCount, targetDir) {
|
|
223
|
+
console.log();
|
|
224
|
+
await animatedSeparator(60);
|
|
225
|
+
console.log();
|
|
226
|
+
|
|
227
|
+
// Big checkmark animation
|
|
228
|
+
const check = [
|
|
229
|
+
' ██╗',
|
|
230
|
+
' ██╔╝',
|
|
231
|
+
' ██╔╝ ',
|
|
232
|
+
' ██╗ ██╔╝ ',
|
|
233
|
+
' ╚████╔╝ ',
|
|
234
|
+
' ╚═══╝ ',
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
for (const line of check) {
|
|
238
|
+
console.log(successGradient(line));
|
|
239
|
+
await sleep(50);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log();
|
|
243
|
+
await progressBar('Finalizing', 30, 800);
|
|
244
|
+
console.log();
|
|
245
|
+
|
|
246
|
+
// Sparkle animation on the success message
|
|
247
|
+
const msg = ' Installation complete!';
|
|
248
|
+
const sparkles = ['✦', '✧', '⊹', '✶', '⋆'];
|
|
249
|
+
for (let i = 0; i < 3; i++) {
|
|
250
|
+
const s1 = sparkles[Math.floor(Math.random() * sparkles.length)];
|
|
251
|
+
const s2 = sparkles[Math.floor(Math.random() * sparkles.length)];
|
|
252
|
+
const s3 = sparkles[Math.floor(Math.random() * sparkles.length)];
|
|
253
|
+
process.stdout.write(
|
|
254
|
+
`\r ${chalk.hex('#FDCB6E')(s1)} ${successGradient(msg.trim())} ${chalk.hex('#FDCB6E')(s2)} ${chalk.hex('#00CEC9')(s3)}`
|
|
255
|
+
);
|
|
256
|
+
await sleep(200);
|
|
257
|
+
}
|
|
258
|
+
console.log();
|
|
259
|
+
console.log();
|
|
260
|
+
|
|
261
|
+
// Module count badge
|
|
262
|
+
const countBadge = chalk.hex('#2D3436').bgHex('#00CEC9').bold(` ${moduleCount} modules `);
|
|
263
|
+
const pathBadge = chalk.hex('#636E72')(targetDir);
|
|
264
|
+
console.log(` ${countBadge} ${chalk.hex('#636E72')('installed in')} ${pathBadge}`);
|
|
265
|
+
|
|
266
|
+
console.log();
|
|
267
|
+
|
|
268
|
+
// Tips box
|
|
269
|
+
const boxW = 52;
|
|
270
|
+
const topBorder = chalk.hex('#636E72')(' ┌' + '─'.repeat(boxW) + '┐');
|
|
271
|
+
const botBorder = chalk.hex('#636E72')(' └' + '─'.repeat(boxW) + '┘');
|
|
272
|
+
const line = (content, raw) => {
|
|
273
|
+
const pad = boxW - raw.length;
|
|
274
|
+
return chalk.hex('#636E72')(' │') + content + ' '.repeat(Math.max(0, pad)) + chalk.hex('#636E72')('│');
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
console.log(topBorder);
|
|
278
|
+
console.log(line(
|
|
279
|
+
chalk.hex('#FDCB6E')(' ⚡ Quick Start'),
|
|
280
|
+
' ⚡ Quick Start'
|
|
281
|
+
));
|
|
282
|
+
console.log(line(
|
|
283
|
+
chalk.hex('#636E72')(' '),
|
|
284
|
+
' '
|
|
285
|
+
));
|
|
286
|
+
console.log(line(
|
|
287
|
+
chalk.hex('#B2BEC3')(' Your AI assistants are ready to use.'),
|
|
288
|
+
' Your AI assistants are ready to use.'
|
|
289
|
+
));
|
|
290
|
+
console.log(line(
|
|
291
|
+
chalk.hex('#B2BEC3')(' Open your project in your preferred IDE.'),
|
|
292
|
+
' Open your project in your preferred IDE.'
|
|
293
|
+
));
|
|
294
|
+
console.log(botBorder);
|
|
295
|
+
|
|
296
|
+
console.log();
|
|
297
|
+
console.log(chalk.hex('#636E72')(' Happy coding! ') + chalk.hex('#00CEC9')('→') + chalk.hex('#636E72')(' ac-framework'));
|
|
298
|
+
console.log();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Failure Summary ──────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
export async function showFailureSummary(installed, errors) {
|
|
304
|
+
console.log();
|
|
305
|
+
await animatedSeparator(60);
|
|
306
|
+
console.log();
|
|
307
|
+
|
|
308
|
+
console.log(
|
|
309
|
+
warmGradient(` ⚠ ${installed} installed, ${errors.length} failed`)
|
|
310
|
+
);
|
|
311
|
+
console.log();
|
|
312
|
+
|
|
313
|
+
for (const { folder, error } of errors) {
|
|
314
|
+
console.log(
|
|
315
|
+
chalk.hex('#D63031')(' ✗ ') +
|
|
316
|
+
chalk.hex('#DFE6E9')(folder) +
|
|
317
|
+
chalk.hex('#636E72')(` — ${error}`)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
console.log();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ── Step Header ──────────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
export async function stepHeader(stepNum, totalSteps, label) {
|
|
326
|
+
const stepBadge = chalk.hex('#2D3436').bgHex('#6C5CE7').bold(` ${stepNum}/${totalSteps} `);
|
|
327
|
+
console.log(` ${stepBadge} ${chalk.hex('#DFE6E9')(label)}`);
|
|
328
|
+
console.log();
|
|
329
|
+
}
|
package/src/ui/banner.js
CHANGED
|
@@ -1,44 +1,101 @@
|
|
|
1
|
-
import figlet from 'figlet';
|
|
2
1
|
import gradient from 'gradient-string';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import { sleep } from '../utils/helpers.js';
|
|
5
4
|
|
|
6
|
-
const acGradient = gradient(['#6C5CE7', '#00CEC9', '#0984E3', '#6C5CE7']);
|
|
7
|
-
const
|
|
5
|
+
const acGradient = gradient(['#6C5CE7', '#A29BFE', '#00CEC9', '#0984E3', '#6C5CE7']);
|
|
6
|
+
const dimGradient = gradient(['#2D3436', '#636E72', '#2D3436']);
|
|
7
|
+
const glowGradient = gradient(['#0984E3', '#00CEC9', '#55EFC4', '#00CEC9', '#0984E3']);
|
|
8
|
+
|
|
9
|
+
const LOGO = [
|
|
10
|
+
' ██████╗ ██████╗ ███████╗██████╗ █████╗ ███╗ ███╗███████╗',
|
|
11
|
+
' ██╔══██╗██╔════╝ ██╔════╝██╔══██╗██╔══██╗████╗ ████║██╔════╝',
|
|
12
|
+
' ███████║██║ █████╗ ██████╔╝███████║██╔████╔██║█████╗ ',
|
|
13
|
+
' ██╔══██║██║ ██╔══╝ ██╔══██╗██╔══██║██║╚██╔╝██║██╔══╝ ',
|
|
14
|
+
' ██║ ██║╚██████╗ ██║ ██║ ██║██║ ██║██║ ╚═╝ ██║███████╗',
|
|
15
|
+
' ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝',
|
|
16
|
+
];
|
|
8
17
|
|
|
9
18
|
export async function showBanner() {
|
|
10
19
|
console.clear();
|
|
11
20
|
console.log();
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
// Phase 1: Glitch-in effect — show random noise then resolve to the logo
|
|
23
|
+
const maxWidth = Math.max(...LOGO.map((l) => l.length));
|
|
24
|
+
const glitchChars = '█▓▒░╗╔╝╚═║╬╣╠╩╦';
|
|
25
|
+
const glitchSteps = 6;
|
|
26
|
+
|
|
27
|
+
for (let step = 0; step < glitchSteps; step++) {
|
|
28
|
+
const ratio = step / (glitchSteps - 1); // 0 → 1
|
|
29
|
+
const output = [];
|
|
30
|
+
for (const line of LOGO) {
|
|
31
|
+
let result = '';
|
|
32
|
+
for (let i = 0; i < line.length; i++) {
|
|
33
|
+
if (line[i] === ' ') {
|
|
34
|
+
result += ' ';
|
|
35
|
+
} else if (Math.random() < ratio) {
|
|
36
|
+
result += line[i];
|
|
37
|
+
} else {
|
|
38
|
+
result += glitchChars[Math.floor(Math.random() * glitchChars.length)];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
output.push(result);
|
|
42
|
+
}
|
|
17
43
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
44
|
+
if (step > 0) {
|
|
45
|
+
process.stdout.write(`\x1B[${LOGO.length}A`);
|
|
46
|
+
}
|
|
47
|
+
for (const line of output) {
|
|
48
|
+
const colored = step < glitchSteps - 1
|
|
49
|
+
? dimGradient(line)
|
|
50
|
+
: acGradient(line);
|
|
51
|
+
console.log(colored);
|
|
52
|
+
}
|
|
53
|
+
await sleep(step < glitchSteps - 1 ? 80 : 0);
|
|
23
54
|
}
|
|
24
55
|
|
|
25
56
|
console.log();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
57
|
+
|
|
58
|
+
// Phase 2: Animated separator with scanning effect
|
|
59
|
+
const sepWidth = 68;
|
|
60
|
+
const sepChars = '─';
|
|
61
|
+
for (let i = 0; i <= sepWidth; i++) {
|
|
62
|
+
const line =
|
|
63
|
+
chalk.hex('#2D3436')(' ') +
|
|
64
|
+
glowGradient(sepChars.repeat(i)) +
|
|
65
|
+
chalk.hex('#00CEC9')('●') +
|
|
66
|
+
chalk.hex('#2D3436')(sepChars.repeat(Math.max(0, sepWidth - i)));
|
|
67
|
+
process.stdout.write(`\r${line}`);
|
|
68
|
+
await sleep(6);
|
|
69
|
+
}
|
|
70
|
+
process.stdout.write(`\r ${glowGradient(sepChars.repeat(sepWidth))} \n`);
|
|
71
|
+
|
|
29
72
|
console.log();
|
|
30
73
|
|
|
31
|
-
// Typewriter
|
|
74
|
+
// Phase 3: Typewriter tagline with cursor blink
|
|
32
75
|
const tagline = ' Agentic Coding Framework — Multi-assistant configuration system';
|
|
76
|
+
const cursor = '▌';
|
|
33
77
|
for (let i = 0; i <= tagline.length; i++) {
|
|
34
|
-
|
|
35
|
-
|
|
78
|
+
const text = tagline.slice(0, i);
|
|
79
|
+
process.stdout.write(`\r${chalk.hex('#DFE6E9')(text)}${chalk.hex('#00CEC9')(cursor)}`);
|
|
80
|
+
await sleep(i % 4 === 0 ? 18 : 10);
|
|
81
|
+
}
|
|
82
|
+
// Blink cursor 3 times then remove
|
|
83
|
+
for (let b = 0; b < 3; b++) {
|
|
84
|
+
process.stdout.write(`\r${chalk.hex('#DFE6E9')(tagline)}${chalk.hex('#00CEC9')(cursor)}`);
|
|
85
|
+
await sleep(120);
|
|
86
|
+
process.stdout.write(`\r${chalk.hex('#DFE6E9')(tagline)} `);
|
|
87
|
+
await sleep(120);
|
|
36
88
|
}
|
|
37
89
|
process.stdout.write(`\r${chalk.hex('#DFE6E9')(tagline)} \n`);
|
|
38
90
|
|
|
91
|
+
// Phase 4: Info badges
|
|
92
|
+
console.log();
|
|
93
|
+
const version = chalk.hex('#2D3436').bgHex('#00CEC9').bold(' v1.x ');
|
|
94
|
+
const badge = chalk.hex('#2D3436').bgHex('#6C5CE7').bold(' CLI ');
|
|
95
|
+
const badge2 = chalk.hex('#2D3436').bgHex('#FDCB6E').bold(' 23 Assistants ');
|
|
96
|
+
console.log(` ${version} ${badge} ${badge2}`);
|
|
97
|
+
|
|
39
98
|
console.log();
|
|
40
|
-
|
|
41
|
-
subtleGradient(' ─────────────────────────────────────────────────────────────────')
|
|
42
|
-
);
|
|
99
|
+
process.stdout.write(` ${glowGradient(sepChars.repeat(sepWidth))} \n`);
|
|
43
100
|
console.log();
|
|
44
101
|
}
|