ac-framework 1.0.1 → 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/commands/init.js +119 -95
- 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.0
|
|
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/commands/init.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
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
|
-
const successGradient = gradient(['#00CEC9', '#00B894', '#55EFC4']);
|
|
16
24
|
|
|
17
25
|
const ALWAYS_INSTALL = ['openspec'];
|
|
18
26
|
|
|
@@ -29,11 +37,19 @@ async function getFrameworkFolders() {
|
|
|
29
37
|
function buildChoices(folders) {
|
|
30
38
|
const choices = [];
|
|
31
39
|
|
|
32
|
-
choices.push(new inquirer.Separator(
|
|
40
|
+
choices.push(new inquirer.Separator(
|
|
41
|
+
chalk.hex('#636E72')(' ── ') +
|
|
42
|
+
chalk.hex('#6C5CE7').bold('AI Assistants') +
|
|
43
|
+
chalk.hex('#636E72')(' ─────────────────────────────')
|
|
44
|
+
));
|
|
45
|
+
|
|
33
46
|
for (const folder of folders) {
|
|
34
47
|
const desc = DESCRIPTIONS[folder] || '';
|
|
35
48
|
const displayName = formatFolderName(folder);
|
|
36
|
-
const
|
|
49
|
+
const icon = getAssistantIcon(folder);
|
|
50
|
+
const label =
|
|
51
|
+
`${chalk.hex('#636E72')(icon)} ${chalk.hex('#DFE6E9').bold(displayName)}` +
|
|
52
|
+
(desc ? chalk.hex('#636E72')(` · ${desc}`) : '');
|
|
37
53
|
choices.push({
|
|
38
54
|
name: label,
|
|
39
55
|
value: folder,
|
|
@@ -44,6 +60,35 @@ function buildChoices(folders) {
|
|
|
44
60
|
return choices;
|
|
45
61
|
}
|
|
46
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
|
+
|
|
47
92
|
async function checkExisting(targetDir, folder) {
|
|
48
93
|
try {
|
|
49
94
|
await access(join(targetDir, folder));
|
|
@@ -56,44 +101,47 @@ async function checkExisting(targetDir, folder) {
|
|
|
56
101
|
export async function initCommand() {
|
|
57
102
|
const targetDir = process.cwd();
|
|
58
103
|
|
|
59
|
-
//
|
|
60
|
-
await
|
|
104
|
+
// ── Step 1: Scan ───────────────────────────────────────────────
|
|
105
|
+
await stepHeader(1, 3, 'Scanning framework modules');
|
|
106
|
+
await scanAnimation('Indexing available modules', 1000);
|
|
61
107
|
console.log();
|
|
62
108
|
|
|
63
|
-
// Matrix rain
|
|
64
|
-
|
|
65
|
-
await matrixRain(1500);
|
|
109
|
+
// Matrix rain transition
|
|
110
|
+
await matrixRain(1800);
|
|
66
111
|
|
|
67
112
|
let folders;
|
|
68
113
|
try {
|
|
69
114
|
folders = await getFrameworkFolders();
|
|
70
115
|
} catch {
|
|
71
|
-
console.log(chalk.
|
|
116
|
+
console.log(chalk.hex('#D63031')(' ✗ Error: Could not read framework directory.'));
|
|
72
117
|
console.log(chalk.hex('#636E72')(' Make sure ac-framework is installed correctly.'));
|
|
73
118
|
process.exit(1);
|
|
74
119
|
}
|
|
75
120
|
|
|
76
121
|
if (folders.length === 0) {
|
|
77
|
-
console.log(chalk.
|
|
122
|
+
console.log(chalk.hex('#FDCB6E')(' No modules found in framework directory.'));
|
|
78
123
|
process.exit(0);
|
|
79
124
|
}
|
|
80
125
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
84
|
-
console.log(
|
|
85
|
-
|
|
86
|
-
);
|
|
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();
|
|
87
133
|
|
|
134
|
+
// ── Step 2: Select ─────────────────────────────────────────────
|
|
135
|
+
await stepHeader(2, 3, 'Select your assistants');
|
|
136
|
+
|
|
137
|
+
// Controls hint with styled keys
|
|
138
|
+
const key = (k) => chalk.hex('#2D3436').bgHex('#636E72')(` ${k} `);
|
|
88
139
|
console.log(
|
|
89
|
-
chalk.hex('#636E72')('
|
|
90
|
-
chalk.hex('#
|
|
91
|
-
chalk.hex('#636E72')('
|
|
92
|
-
chalk.hex('#00CEC9')('Space') +
|
|
93
|
-
chalk.hex('#636E72')(' to select, ') +
|
|
94
|
-
chalk.hex('#00CEC9')('Enter') +
|
|
95
|
-
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')}`
|
|
96
143
|
);
|
|
144
|
+
console.log();
|
|
97
145
|
|
|
98
146
|
const choices = buildChoices(folders);
|
|
99
147
|
|
|
@@ -101,13 +149,13 @@ export async function initCommand() {
|
|
|
101
149
|
{
|
|
102
150
|
type: 'checkbox',
|
|
103
151
|
name: 'selected',
|
|
104
|
-
message: acGradient('
|
|
152
|
+
message: acGradient('Choose modules to install:'),
|
|
105
153
|
choices,
|
|
106
154
|
pageSize: 15,
|
|
107
155
|
loop: false,
|
|
108
156
|
validate(answer) {
|
|
109
157
|
if (answer.length === 0) {
|
|
110
|
-
return chalk.hex('#D63031')('
|
|
158
|
+
return chalk.hex('#D63031')('Select at least one module. Use Space to toggle.');
|
|
111
159
|
}
|
|
112
160
|
return true;
|
|
113
161
|
},
|
|
@@ -116,7 +164,7 @@ export async function initCommand() {
|
|
|
116
164
|
|
|
117
165
|
console.log();
|
|
118
166
|
|
|
119
|
-
// Check
|
|
167
|
+
// ── Check conflicts ────────────────────────────────────────────
|
|
120
168
|
const allForCheck = [...selected, ...ALWAYS_INSTALL];
|
|
121
169
|
const existing = [];
|
|
122
170
|
for (const folder of allForCheck) {
|
|
@@ -127,10 +175,14 @@ export async function initCommand() {
|
|
|
127
175
|
|
|
128
176
|
if (existing.length > 0) {
|
|
129
177
|
console.log(
|
|
130
|
-
chalk.hex('#FDCB6E')(' ⚠
|
|
178
|
+
chalk.hex('#FDCB6E')(' ⚠ These modules already exist in your project:\n')
|
|
131
179
|
);
|
|
132
180
|
for (const folder of existing) {
|
|
133
|
-
console.log(
|
|
181
|
+
console.log(
|
|
182
|
+
chalk.hex('#FDCB6E')(' ▸ ') +
|
|
183
|
+
chalk.hex('#DFE6E9')(formatFolderName(folder)) +
|
|
184
|
+
chalk.hex('#636E72')(` (${folder})`)
|
|
185
|
+
);
|
|
134
186
|
}
|
|
135
187
|
console.log();
|
|
136
188
|
|
|
@@ -147,33 +199,33 @@ export async function initCommand() {
|
|
|
147
199
|
const filtered = selected.filter((f) => !existing.includes(f));
|
|
148
200
|
const autoFiltered = ALWAYS_INSTALL.filter((f) => !existing.includes(f));
|
|
149
201
|
if (filtered.length === 0 && autoFiltered.length === 0) {
|
|
150
|
-
console.log(chalk.hex('#636E72')('\n
|
|
202
|
+
console.log(chalk.hex('#636E72')('\n Nothing new to install. Exiting.\n'));
|
|
151
203
|
process.exit(0);
|
|
152
204
|
}
|
|
153
205
|
selected.length = 0;
|
|
154
206
|
selected.push(...filtered);
|
|
155
207
|
console.log(
|
|
156
|
-
chalk.hex('#B2BEC3')(`\n
|
|
208
|
+
chalk.hex('#B2BEC3')(`\n Continuing with ${chalk.hex('#00CEC9').bold(filtered.length + autoFiltered.length)} new module(s)...\n`)
|
|
157
209
|
);
|
|
158
210
|
}
|
|
159
211
|
}
|
|
160
212
|
|
|
161
|
-
// Confirm selection
|
|
162
|
-
console.log(chalk.hex('#B2BEC3')('
|
|
163
|
-
|
|
213
|
+
// ── Confirm selection with animated reveal ─────────────────────
|
|
214
|
+
console.log(chalk.hex('#B2BEC3')(' Selected modules:\n'));
|
|
215
|
+
|
|
216
|
+
const selectedItems = selected.map((folder) => {
|
|
164
217
|
const desc = DESCRIPTIONS[folder] || '';
|
|
165
|
-
|
|
166
|
-
chalk.hex('#
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
console.log(
|
|
172
|
-
chalk.hex('#6C5CE7')(' ◆ ') +
|
|
218
|
+
return chalk.hex('#DFE6E9').bold(formatFolderName(folder)) +
|
|
219
|
+
(desc ? chalk.hex('#636E72')(` · ${desc}`) : '');
|
|
220
|
+
});
|
|
221
|
+
selectedItems.push(
|
|
173
222
|
chalk.hex('#DFE6E9').bold('Openspec') +
|
|
174
|
-
chalk.hex('#636E72')(`
|
|
175
|
-
chalk.hex('#
|
|
223
|
+
chalk.hex('#636E72')(` · ${DESCRIPTIONS['openspec']}`) +
|
|
224
|
+
chalk.hex('#6C5CE7').italic(' (auto)')
|
|
176
225
|
);
|
|
226
|
+
|
|
227
|
+
await revealList(selectedItems, { prefix: '◆', color: '#00CEC9', delay: 40 });
|
|
228
|
+
|
|
177
229
|
console.log();
|
|
178
230
|
|
|
179
231
|
const { confirm } = await inquirer.prompt([
|
|
@@ -190,74 +242,46 @@ export async function initCommand() {
|
|
|
190
242
|
process.exit(0);
|
|
191
243
|
}
|
|
192
244
|
|
|
245
|
+
// ── Step 3: Install ────────────────────────────────────────────
|
|
193
246
|
console.log();
|
|
247
|
+
await animatedSeparator(60);
|
|
248
|
+
console.log();
|
|
249
|
+
await stepHeader(3, 3, 'Installing modules');
|
|
194
250
|
|
|
195
|
-
// Install modules with progress — always include openspec
|
|
196
251
|
const frameworkPath = resolve(__dirname, '../../framework');
|
|
197
252
|
const allToInstall = [...selected, ...ALWAYS_INSTALL];
|
|
198
253
|
let installed = 0;
|
|
199
254
|
const errors = [];
|
|
200
255
|
|
|
201
256
|
for (const folder of allToInstall) {
|
|
202
|
-
const
|
|
203
|
-
chalk.hex('#B2BEC3')(`Installing ${chalk.hex('#DFE6E9').bold(formatFolderName(folder))}...`)
|
|
204
|
-
).start();
|
|
257
|
+
const displayName = formatFolderName(folder);
|
|
205
258
|
|
|
206
259
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
await cp(src, dest, { recursive: true, force: true });
|
|
211
|
-
|
|
212
|
-
// Skip node_modules if copied
|
|
213
|
-
const nmPath = join(dest, 'node_modules');
|
|
214
|
-
try {
|
|
215
|
-
const { rm } = await import('node:fs/promises');
|
|
216
|
-
await rm(nmPath, { recursive: true, force: true });
|
|
217
|
-
} catch {
|
|
218
|
-
// node_modules didn't exist, that's fine
|
|
219
|
-
}
|
|
260
|
+
await installWithAnimation(displayName, async () => {
|
|
261
|
+
const src = join(frameworkPath, folder);
|
|
262
|
+
const dest = join(targetDir, folder);
|
|
220
263
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
+
}
|
|
226
272
|
});
|
|
273
|
+
installed++;
|
|
227
274
|
} catch (err) {
|
|
228
275
|
errors.push({ folder, error: err.message });
|
|
229
|
-
spinner.error({
|
|
230
|
-
text: chalk.hex('#D63031')(` Failed: ${formatFolderName(folder)} — ${err.message}`),
|
|
231
|
-
});
|
|
232
276
|
}
|
|
233
277
|
|
|
234
|
-
await sleep(
|
|
278
|
+
await sleep(80);
|
|
235
279
|
}
|
|
236
280
|
|
|
237
|
-
// Final
|
|
238
|
-
console.log();
|
|
239
|
-
console.log(
|
|
240
|
-
gradient(['#636E72', '#B2BEC3'])(' ─────────────────────────────────────────────────────────────────')
|
|
241
|
-
);
|
|
242
|
-
console.log();
|
|
243
|
-
|
|
281
|
+
// ── Final result ───────────────────────────────────────────────
|
|
244
282
|
if (errors.length === 0) {
|
|
245
|
-
await
|
|
246
|
-
console.log();
|
|
247
|
-
console.log(successGradient(' ✓ Installation complete!'));
|
|
248
|
-
console.log();
|
|
249
|
-
console.log(
|
|
250
|
-
chalk.hex('#B2BEC3')(` ${chalk.hex('#00CEC9').bold(installed)} module(s) installed successfully in ${chalk.hex('#DFE6E9')(targetDir)}`)
|
|
251
|
-
);
|
|
283
|
+
await celebrateSuccess(installed, targetDir);
|
|
252
284
|
} else {
|
|
253
|
-
|
|
254
|
-
chalk.hex('#FDCB6E')(` ${installed} installed, ${errors.length} failed.`)
|
|
255
|
-
);
|
|
285
|
+
await showFailureSummary(installed, errors);
|
|
256
286
|
}
|
|
257
|
-
|
|
258
|
-
console.log();
|
|
259
|
-
console.log(
|
|
260
|
-
chalk.hex('#636E72')(' Your project is ready. Happy coding!')
|
|
261
|
-
);
|
|
262
|
-
console.log();
|
|
263
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
|
}
|