@amrhas82/agentic-kit 1.9.0 → 1.10.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/README.md +13 -2
- package/installer/cli.js +295 -1808
- package/installer/installation-engine.js +38 -36
- package/installer/package-manager.js +4 -2
- package/package.json +1 -1
- package/postinstall.js +19 -28
- package/installer/state-manager.js +0 -426
- package/installer/telemetry.js +0 -241
package/installer/cli.js
CHANGED
|
@@ -34,14 +34,10 @@ class InteractiveInstaller {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
this.selections = {
|
|
37
|
-
variant: null,
|
|
38
37
|
tools: [],
|
|
39
38
|
paths: {}
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
// Command-line arguments
|
|
43
|
-
this.cliArgs = this.parseCommandLineArgs();
|
|
44
|
-
|
|
45
41
|
// Initialize PackageManager for accessing package information
|
|
46
42
|
const PackageManager = require('./package-manager');
|
|
47
43
|
this.packageManager = new PackageManager();
|
|
@@ -51,240 +47,52 @@ class InteractiveInstaller {
|
|
|
51
47
|
id: 'claude',
|
|
52
48
|
name: 'Claude Code',
|
|
53
49
|
path: '~/.claude',
|
|
54
|
-
description: 'AI-powered development assistant'
|
|
55
|
-
useCase: 'General software development with conversational AI',
|
|
56
|
-
targetUsers: 'All developers'
|
|
50
|
+
description: 'AI-powered development assistant'
|
|
57
51
|
},
|
|
58
52
|
{
|
|
59
53
|
id: 'opencode',
|
|
60
54
|
name: 'Opencode',
|
|
61
55
|
path: '~/.config/opencode',
|
|
62
|
-
description: 'CLI-optimized AI codegen tool'
|
|
63
|
-
useCase: 'Terminal-based development and automation',
|
|
64
|
-
targetUsers: 'CLI power users, DevOps teams'
|
|
56
|
+
description: 'CLI-optimized AI codegen tool'
|
|
65
57
|
},
|
|
66
58
|
{
|
|
67
59
|
id: 'ampcode',
|
|
68
60
|
name: 'Ampcode',
|
|
69
61
|
path: '~/.config/amp',
|
|
70
|
-
description: 'Amplified AI development accelerator'
|
|
71
|
-
useCase: 'Velocity-focused workflows and rapid prototyping',
|
|
72
|
-
targetUsers: 'Teams seeking development acceleration'
|
|
62
|
+
description: 'Amplified AI development accelerator'
|
|
73
63
|
},
|
|
74
64
|
{
|
|
75
65
|
id: 'droid',
|
|
76
66
|
name: 'Droid',
|
|
77
67
|
path: '~/.factory',
|
|
78
|
-
description: 'Android-focused AI development companion'
|
|
79
|
-
useCase: 'Mobile app development with Android Studio',
|
|
80
|
-
targetUsers: 'Android developers, mobile teams'
|
|
68
|
+
description: 'Android-focused AI development companion'
|
|
81
69
|
}
|
|
82
70
|
];
|
|
83
|
-
|
|
84
|
-
this.variants = [
|
|
85
|
-
{ id: 'lite', name: 'Lite', agents: 3, skills: 0, description: 'Minimal setup, CI/CD' },
|
|
86
|
-
{ id: 'standard', name: 'Standard', agents: 13, skills: 8, description: 'Most users, general dev' },
|
|
87
|
-
{ id: 'pro', name: 'Pro', agents: 13, skills: 14, description: 'Advanced users, full features' }
|
|
88
|
-
];
|
|
89
71
|
}
|
|
90
72
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* @returns {Object} - Parsed arguments object
|
|
96
|
-
*/
|
|
97
|
-
parseCommandLineArgs() {
|
|
98
|
-
const args = process.argv.slice(2);
|
|
99
|
-
const parsed = {
|
|
100
|
-
help: false,
|
|
101
|
-
uninstall: null,
|
|
102
|
-
upgrade: null,
|
|
103
|
-
upgradeVariant: null,
|
|
104
|
-
variant: null,
|
|
105
|
-
tools: [],
|
|
106
|
-
paths: {},
|
|
107
|
-
silent: false,
|
|
108
|
-
nonInteractive: false,
|
|
109
|
-
config: null,
|
|
110
|
-
noTelemetry: false
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
for (let i = 0; i < args.length; i++) {
|
|
114
|
-
const arg = args[i];
|
|
115
|
-
|
|
116
|
-
if (arg === '--help' || arg === '-h') {
|
|
117
|
-
parsed.help = true;
|
|
118
|
-
} else if (arg === '--uninstall') {
|
|
119
|
-
parsed.uninstall = args[++i];
|
|
120
|
-
} else if (arg === '--upgrade') {
|
|
121
|
-
parsed.upgrade = args[++i];
|
|
122
|
-
parsed.upgradeVariant = args[++i];
|
|
123
|
-
} else if (arg === '--variant') {
|
|
124
|
-
parsed.variant = args[++i];
|
|
125
|
-
} else if (arg === '--tools') {
|
|
126
|
-
parsed.tools = args[++i].split(',').map(t => t.trim());
|
|
127
|
-
} else if (arg === '--path') {
|
|
128
|
-
const pathArg = args[++i];
|
|
129
|
-
const [tool, pathValue] = pathArg.split('=');
|
|
130
|
-
parsed.paths[tool] = pathValue;
|
|
131
|
-
} else if (arg === '--silent' || arg === '--non-interactive' || arg === '--yes' || arg === '-y') {
|
|
132
|
-
parsed.silent = true;
|
|
133
|
-
parsed.nonInteractive = true;
|
|
134
|
-
} else if (arg === '--config') {
|
|
135
|
-
parsed.config = args[++i];
|
|
136
|
-
} else if (arg === '--no-telemetry') {
|
|
137
|
-
parsed.noTelemetry = true;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
73
|
+
async askInstallOrUninstall() {
|
|
74
|
+
console.log('\nWhat would you like to do?\n');
|
|
75
|
+
console.log(' 1. Install tools');
|
|
76
|
+
console.log(' 2. Uninstall tools\n');
|
|
140
77
|
|
|
141
|
-
|
|
78
|
+
const choice = await this.askQuestion('Enter choice (1 or 2): ', '1');
|
|
79
|
+
return choice === '2' ? 'uninstall' : 'install';
|
|
142
80
|
}
|
|
143
81
|
|
|
144
|
-
/**
|
|
145
|
-
* Display help information
|
|
146
|
-
* Shows usage, options, and examples
|
|
147
|
-
*/
|
|
148
|
-
showHelp() {
|
|
149
|
-
console.log(`
|
|
150
|
-
${colors.bright}${colors.cyan}Agentic Kit Installer${colors.reset}
|
|
151
|
-
|
|
152
|
-
${colors.bright}USAGE:${colors.reset}
|
|
153
|
-
node installer/cli.js [OPTIONS]
|
|
154
|
-
|
|
155
|
-
${colors.bright}OPTIONS:${colors.reset}
|
|
156
|
-
${colors.green}--help, -h${colors.reset}
|
|
157
|
-
Display this help message
|
|
158
|
-
|
|
159
|
-
${colors.green}--uninstall <tool>${colors.reset}
|
|
160
|
-
Uninstall a specific tool
|
|
161
|
-
Example: node installer/cli.js --uninstall claude
|
|
162
|
-
|
|
163
|
-
${colors.green}--upgrade <tool> <variant>${colors.reset}
|
|
164
|
-
Upgrade or downgrade a tool to a different variant
|
|
165
|
-
Example: node installer/cli.js --upgrade claude pro
|
|
166
|
-
Example: node installer/cli.js --upgrade claude lite
|
|
167
|
-
|
|
168
|
-
${colors.green}--variant <lite|standard|pro>${colors.reset}
|
|
169
|
-
Select package variant (non-interactive mode)
|
|
170
|
-
Example: node installer/cli.js --variant standard --tools claude
|
|
171
|
-
|
|
172
|
-
${colors.green}--tools <tool1,tool2,...>${colors.reset}
|
|
173
|
-
Select tools to install (comma-separated, non-interactive mode)
|
|
174
|
-
Example: node installer/cli.js --variant standard --tools claude,opencode
|
|
175
|
-
|
|
176
|
-
${colors.green}--path <tool>=<path>${colors.reset}
|
|
177
|
-
Specify custom installation path for a tool
|
|
178
|
-
Example: node installer/cli.js --variant standard --tools claude --path claude=/custom/path
|
|
179
|
-
|
|
180
|
-
${colors.green}--silent, --non-interactive, --yes, -y${colors.reset}
|
|
181
|
-
Run in silent mode (no prompts, auto-confirm all)
|
|
182
|
-
Example: node installer/cli.js --variant standard --tools claude --silent
|
|
183
|
-
|
|
184
|
-
${colors.green}--config <file>${colors.reset}
|
|
185
|
-
Load configuration from JSON file
|
|
186
|
-
Example: node installer/cli.js --config install-config.json
|
|
187
|
-
|
|
188
|
-
${colors.green}--no-telemetry${colors.reset}
|
|
189
|
-
Disable anonymous usage statistics collection
|
|
190
|
-
Example: node installer/cli.js --no-telemetry
|
|
191
|
-
See docs/PRIVACY.md for details
|
|
192
|
-
|
|
193
|
-
${colors.bright}TOOLS:${colors.reset}
|
|
194
|
-
${colors.cyan}claude${colors.reset} - Claude Code (AI-powered development assistant)
|
|
195
|
-
${colors.cyan}opencode${colors.reset} - Opencode (CLI-optimized AI codegen tool)
|
|
196
|
-
${colors.cyan}ampcode${colors.reset} - Ampcode (Amplified AI development accelerator)
|
|
197
|
-
${colors.cyan}droid${colors.reset} - Droid (Android-focused AI development companion)
|
|
198
|
-
|
|
199
|
-
${colors.bright}VARIANTS:${colors.reset}
|
|
200
|
-
${colors.cyan}lite${colors.reset} - Minimal setup (3 agents, 0 skills) - For CI/CD
|
|
201
|
-
${colors.cyan}standard${colors.reset} - Standard setup (13 agents, 8 skills) - For most users
|
|
202
|
-
${colors.cyan}pro${colors.reset} - Full setup (13 agents, 22 skills) - For advanced users
|
|
203
|
-
|
|
204
|
-
${colors.bright}EXAMPLES:${colors.reset}
|
|
205
|
-
# Interactive installation (default)
|
|
206
|
-
node installer/cli.js
|
|
207
|
-
|
|
208
|
-
# Install Claude with Standard variant (non-interactive)
|
|
209
|
-
node installer/cli.js --variant standard --tools claude
|
|
210
|
-
|
|
211
|
-
# Install multiple tools with custom paths
|
|
212
|
-
node installer/cli.js --variant pro --tools claude,opencode --path claude=~/.claude-custom
|
|
213
|
-
|
|
214
|
-
# Uninstall a tool
|
|
215
|
-
node installer/cli.js --uninstall claude
|
|
216
|
-
|
|
217
|
-
# Silent installation for CI/CD
|
|
218
|
-
node installer/cli.js --variant lite --tools claude --silent
|
|
219
|
-
|
|
220
|
-
${colors.bright}For more information, visit:${colors.reset}
|
|
221
|
-
https://github.com/amrhas82/agentic-kit
|
|
222
|
-
`);
|
|
223
|
-
}
|
|
224
82
|
|
|
225
83
|
async run() {
|
|
226
84
|
try {
|
|
227
|
-
// Handle --help flag
|
|
228
|
-
if (this.cliArgs.help) {
|
|
229
|
-
this.showHelp();
|
|
230
|
-
process.exit(0);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Handle --uninstall flag
|
|
234
|
-
if (this.cliArgs.uninstall) {
|
|
235
|
-
await this.runUninstall(this.cliArgs.uninstall);
|
|
236
|
-
process.exit(0);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Handle --upgrade flag
|
|
240
|
-
if (this.cliArgs.upgrade) {
|
|
241
|
-
await this.runUpgrade(this.cliArgs.upgrade, this.cliArgs.upgradeVariant);
|
|
242
|
-
process.exit(0);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Handle --config flag
|
|
246
|
-
if (this.cliArgs.config) {
|
|
247
|
-
await this.loadConfig(this.cliArgs.config);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Handle non-interactive mode
|
|
251
|
-
if (this.cliArgs.variant && this.cliArgs.tools.length > 0) {
|
|
252
|
-
await this.runNonInteractive();
|
|
253
|
-
process.exit(0);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Interactive mode
|
|
257
85
|
this.showWelcome();
|
|
258
86
|
|
|
259
|
-
|
|
260
|
-
this.selections.variant = 'pro';
|
|
261
|
-
|
|
262
|
-
// Check for interrupted installation
|
|
263
|
-
const InstallationEngine = require('./installation-engine');
|
|
264
|
-
const PathManager = require('./path-manager');
|
|
265
|
-
const pathManager = new PathManager();
|
|
266
|
-
const installationEngine = new InstallationEngine(pathManager, this.packageManager);
|
|
267
|
-
|
|
268
|
-
const hasInterrupted = await installationEngine.hasInterruptedInstallation();
|
|
87
|
+
const action = await this.askInstallOrUninstall();
|
|
269
88
|
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
} else {
|
|
277
|
-
// Clear state and start fresh
|
|
278
|
-
await installationEngine.getStateManager().clearState();
|
|
279
|
-
console.log(`${colors.yellow}Starting fresh installation...${colors.reset}\n`);
|
|
280
|
-
}
|
|
89
|
+
if (action === 'uninstall') {
|
|
90
|
+
await this.runUninstall();
|
|
91
|
+
} else {
|
|
92
|
+
await this.selectTools();
|
|
93
|
+
this.setDefaultPaths();
|
|
94
|
+
await this.install();
|
|
281
95
|
}
|
|
282
|
-
|
|
283
|
-
// Simplified installation flow
|
|
284
|
-
await this.selectTools();
|
|
285
|
-
await this.setDefaultPaths();
|
|
286
|
-
await this.showSummary();
|
|
287
|
-
await this.install();
|
|
288
96
|
} catch (error) {
|
|
289
97
|
await this.handleFatalError(error);
|
|
290
98
|
} finally {
|
|
@@ -292,417 +100,194 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
292
100
|
}
|
|
293
101
|
}
|
|
294
102
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
* Detects installation, prompts for confirmation, and executes uninstall
|
|
298
|
-
*
|
|
299
|
-
* @param {string} toolId - Tool to uninstall (claude, opencode, ampcode, droid)
|
|
300
|
-
*/
|
|
301
|
-
async runUninstall(toolId) {
|
|
302
|
-
console.log(`\n${colors.bright}${colors.cyan}Agentic Kit Uninstaller${colors.reset}\n`);
|
|
303
|
-
|
|
304
|
-
// Validate tool ID
|
|
305
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
306
|
-
if (!tool) {
|
|
307
|
-
console.log(`${colors.red}Error: Unknown tool '${toolId}'${colors.reset}`);
|
|
308
|
-
console.log(`${colors.yellow}Available tools: ${this.tools.map(t => t.id).join(', ')}${colors.reset}\n`);
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
103
|
+
async runUninstall() {
|
|
104
|
+
console.log('\nDetecting installed tools...\n');
|
|
311
105
|
|
|
312
|
-
// Detect installation at default path
|
|
313
106
|
const os = require('os');
|
|
314
|
-
const defaultPath = tool.path.startsWith('~')
|
|
315
|
-
? path.join(os.homedir(), tool.path.slice(1))
|
|
316
|
-
: path.resolve(tool.path);
|
|
317
|
-
|
|
318
|
-
// Check if manifest exists at default path
|
|
319
|
-
const manifestPath = path.join(defaultPath, 'manifest.json');
|
|
320
|
-
let targetPath = defaultPath;
|
|
321
|
-
|
|
322
|
-
if (!fs.existsSync(manifestPath)) {
|
|
323
|
-
console.log(`${colors.yellow}No installation found at default path: ${defaultPath}${colors.reset}\n`);
|
|
324
|
-
|
|
325
|
-
// If not in silent mode, ask for custom path
|
|
326
|
-
if (!this.cliArgs.silent) {
|
|
327
|
-
const customPath = await this.askQuestion(
|
|
328
|
-
`${colors.cyan}Enter installation path (or press Enter to cancel):${colors.reset} `,
|
|
329
|
-
''
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
if (!customPath) {
|
|
333
|
-
console.log(`${colors.yellow}Uninstall cancelled${colors.reset}\n`);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
107
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
108
|
+
// Detect installed tools by checking if paths exist
|
|
109
|
+
const installedTools = [];
|
|
110
|
+
for (const tool of this.tools) {
|
|
111
|
+
const targetPath = tool.path.startsWith('~')
|
|
112
|
+
? path.join(os.homedir(), tool.path.slice(1))
|
|
113
|
+
: path.resolve(tool.path);
|
|
114
|
+
|
|
115
|
+
if (fs.existsSync(targetPath)) {
|
|
116
|
+
// Check for backups
|
|
117
|
+
const backups = this.findBackups(targetPath);
|
|
118
|
+
installedTools.push({
|
|
119
|
+
...tool,
|
|
120
|
+
expandedPath: targetPath,
|
|
121
|
+
hasBackup: backups.length > 0,
|
|
122
|
+
latestBackup: backups.sort().reverse()[0]
|
|
123
|
+
});
|
|
349
124
|
}
|
|
350
125
|
}
|
|
351
126
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const PathManager = require('./path-manager');
|
|
357
|
-
const InstallationEngine = require('./installation-engine');
|
|
358
|
-
const pathManager = new PathManager();
|
|
359
|
-
const installationEngine = new InstallationEngine(pathManager, this.packageManager);
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
// Read manifest to get file count
|
|
363
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
364
|
-
const fileCount = manifest.installedFiles
|
|
365
|
-
? Object.values(manifest.installedFiles).reduce((sum, arr) => sum + arr.length, 0)
|
|
366
|
-
: 0;
|
|
367
|
-
|
|
368
|
-
// Confirmation prompt (unless in silent mode)
|
|
369
|
-
if (!this.cliArgs.silent) {
|
|
370
|
-
console.log(`${colors.yellow}This will remove ${fileCount} file(s) and create a backup.${colors.reset}\n`);
|
|
371
|
-
|
|
372
|
-
const confirm = await this.askQuestion(
|
|
373
|
-
`${colors.bright}Proceed with uninstallation? (y/N):${colors.reset} `,
|
|
374
|
-
'n'
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
if (confirm.toLowerCase() !== 'y') {
|
|
378
|
-
console.log(`${colors.yellow}Uninstall cancelled${colors.reset}\n`);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
127
|
+
if (installedTools.length === 0) {
|
|
128
|
+
console.log('No tools are currently installed.\n');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
382
131
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
null, // confirmCallback (already confirmed above)
|
|
390
|
-
(progress) => {
|
|
391
|
-
// Display progress
|
|
392
|
-
const percentage = Math.round((progress.filesRemoved / progress.totalFiles) * 100);
|
|
393
|
-
process.stdout.write(`\r${colors.cyan}Progress:${colors.reset} ${percentage}% (${progress.filesRemoved}/${progress.totalFiles} files removed)`);
|
|
394
|
-
}
|
|
395
|
-
);
|
|
132
|
+
// Show installed tools
|
|
133
|
+
console.log('Installed tools:\n');
|
|
134
|
+
installedTools.forEach((tool, index) => {
|
|
135
|
+
const backupStatus = tool.hasBackup ? '(has backup)' : '(no backup)';
|
|
136
|
+
console.log(` ${index + 1}. ${tool.name} - ${tool.path} ${backupStatus}`);
|
|
137
|
+
});
|
|
396
138
|
|
|
397
|
-
|
|
398
|
-
|
|
139
|
+
// Select tools to uninstall
|
|
140
|
+
console.log('\nSelect tools to uninstall:');
|
|
141
|
+
const selectedIndices = await this.selectToolsCheckbox(installedTools);
|
|
399
142
|
|
|
400
|
-
|
|
401
|
-
console.log(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
console.log(`${colors.cyan}Backup created:${colors.reset} ${result.backupPath}\n`);
|
|
143
|
+
if (selectedIndices.length === 0) {
|
|
144
|
+
console.log('No tools selected. Cancelled.\n');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
405
147
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
});
|
|
411
|
-
console.log('');
|
|
412
|
-
}
|
|
148
|
+
// Uninstall each selected tool
|
|
149
|
+
for (const index of selectedIndices) {
|
|
150
|
+
const tool = installedTools[index];
|
|
151
|
+
console.log(`\nUninstalling ${tool.name}...`);
|
|
413
152
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
153
|
+
try {
|
|
154
|
+
if (tool.hasBackup) {
|
|
155
|
+
// Backup current, then restore from previous backup
|
|
156
|
+
const currentBackup = `${tool.expandedPath}.uninstall-backup.${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
157
|
+
fs.renameSync(tool.expandedPath, currentBackup);
|
|
158
|
+
console.log(`Current installation backed up: ${tool.path}.uninstall-backup.${new Date().toISOString().replace(/[:.]/g, '-')}`);
|
|
159
|
+
|
|
160
|
+
// Restore from latest backup
|
|
161
|
+
fs.renameSync(tool.latestBackup, tool.expandedPath);
|
|
162
|
+
console.log(`✓ Restored previous installation from: ${path.basename(tool.latestBackup)}`);
|
|
163
|
+
} else {
|
|
164
|
+
// No backup, just delete
|
|
165
|
+
const rimraf = (dir) => {
|
|
166
|
+
if (fs.existsSync(dir)) {
|
|
167
|
+
fs.readdirSync(dir).forEach((file) => {
|
|
168
|
+
const curPath = path.join(dir, file);
|
|
169
|
+
if (fs.lstatSync(curPath).isDirectory()) {
|
|
170
|
+
rimraf(curPath);
|
|
171
|
+
} else {
|
|
172
|
+
fs.unlinkSync(curPath);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
fs.rmdirSync(dir);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
rimraf(tool.expandedPath);
|
|
179
|
+
console.log(`✓ Deleted installation from: ${tool.path}`);
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.log(`✗ Failed to uninstall ${tool.name}: ${error.message}`);
|
|
420
183
|
}
|
|
421
|
-
|
|
422
|
-
} catch (error) {
|
|
423
|
-
console.error(`\n${colors.red}✗ Uninstall failed: ${error.message}${colors.reset}\n`);
|
|
424
|
-
process.exit(1);
|
|
425
184
|
}
|
|
185
|
+
|
|
186
|
+
console.log('\nDone!');
|
|
426
187
|
}
|
|
427
188
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
* @param {string} toolId - Tool to upgrade (claude, opencode, ampcode, droid)
|
|
433
|
-
* @param {string} newVariant - Target variant (lite, standard, pro)
|
|
434
|
-
*/
|
|
435
|
-
async runUpgrade(toolId, newVariant) {
|
|
436
|
-
console.log(`\n${colors.bright}${colors.cyan}Agentic Kit Variant Upgrade${colors.reset}\n`);
|
|
437
|
-
|
|
438
|
-
// Validate tool ID
|
|
439
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
440
|
-
if (!tool) {
|
|
441
|
-
console.log(`${colors.red}Error: Unknown tool '${toolId}'${colors.reset}`);
|
|
442
|
-
console.log(`${colors.yellow}Available tools: ${this.tools.map(t => t.id).join(', ')}${colors.reset}\n`);
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
189
|
+
findBackups(targetPath) {
|
|
190
|
+
const dir = path.dirname(targetPath);
|
|
191
|
+
const base = path.basename(targetPath);
|
|
192
|
+
const pattern = `${base}.backup.`;
|
|
445
193
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
194
|
+
try {
|
|
195
|
+
return fs.readdirSync(dir)
|
|
196
|
+
.filter(f => f.startsWith(pattern))
|
|
197
|
+
.map(f => path.join(dir, f));
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return [];
|
|
452
200
|
}
|
|
201
|
+
}
|
|
453
202
|
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
? path.join(os.homedir(), tool.path.slice(1))
|
|
458
|
-
: path.resolve(tool.path);
|
|
459
|
-
|
|
460
|
-
// Check if manifest exists at default path
|
|
461
|
-
const manifestPath = path.join(defaultPath, 'manifest.json');
|
|
462
|
-
let targetPath = defaultPath;
|
|
463
|
-
|
|
464
|
-
if (!fs.existsSync(manifestPath)) {
|
|
465
|
-
console.log(`${colors.yellow}No installation found at default path: ${defaultPath}${colors.reset}\n`);
|
|
466
|
-
|
|
467
|
-
// If not in silent mode, ask for custom path
|
|
468
|
-
if (!this.cliArgs.silent) {
|
|
469
|
-
const customPath = await this.askQuestion(
|
|
470
|
-
`${colors.cyan}Enter installation path (or press Enter to cancel):${colors.reset} `,
|
|
471
|
-
''
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
if (!customPath) {
|
|
475
|
-
console.log(`${colors.yellow}Upgrade cancelled${colors.reset}\n`);
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
targetPath = customPath.startsWith('~')
|
|
480
|
-
? path.join(os.homedir(), customPath.slice(1))
|
|
481
|
-
: path.resolve(customPath);
|
|
203
|
+
async selectToolsCheckbox(tools) {
|
|
204
|
+
const selected = new Set();
|
|
205
|
+
let currentIndex = 0;
|
|
482
206
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
console.log(`${colors.red}Error: No installation found at default path${colors.reset}\n`);
|
|
490
|
-
process.exit(1);
|
|
207
|
+
const renderList = () => {
|
|
208
|
+
// Move cursor up and clear each line individually
|
|
209
|
+
for (let i = 0; i <= tools.length; i++) {
|
|
210
|
+
process.stdout.write('\x1b[1A'); // Move up one line
|
|
211
|
+
process.stdout.write('\r'); // Carriage return to start of line
|
|
212
|
+
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
491
213
|
}
|
|
492
|
-
}
|
|
493
214
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
215
|
+
tools.forEach((tool, index) => {
|
|
216
|
+
const isSelected = selected.has(index);
|
|
217
|
+
const isCurrent = index === currentIndex;
|
|
218
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
219
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
220
|
+
const paddedName = tool.name.padEnd(20);
|
|
497
221
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
222
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description || ''}`);
|
|
223
|
+
});
|
|
224
|
+
console.log(''); // Empty line at bottom
|
|
225
|
+
};
|
|
502
226
|
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
227
|
+
// Initial render
|
|
228
|
+
tools.forEach((tool, index) => {
|
|
229
|
+
const isSelected = selected.has(index);
|
|
230
|
+
const isCurrent = index === currentIndex;
|
|
231
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
232
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
233
|
+
const paddedName = tool.name.padEnd(20);
|
|
508
234
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const pathManager = new PathManager();
|
|
513
|
-
const installationEngine = new InstallationEngine(pathManager, this.packageManager);
|
|
235
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description || ''}`);
|
|
236
|
+
});
|
|
237
|
+
console.log('');
|
|
514
238
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
console.log(`${colors.yellow}${action} Summary:${colors.reset}`);
|
|
521
|
-
console.log(` ${colors.green}+${data.filesAdded} file(s) to add${colors.reset}`);
|
|
522
|
-
console.log(` ${colors.red}-${data.filesRemoved} file(s) to remove${colors.reset}\n`);
|
|
523
|
-
|
|
524
|
-
const confirm = await this.askQuestion(
|
|
525
|
-
`${colors.bright}Proceed with ${action.toLowerCase()}? (y/N):${colors.reset} `,
|
|
526
|
-
'n'
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
resolve(confirm.toLowerCase() === 'y');
|
|
530
|
-
});
|
|
531
|
-
};
|
|
239
|
+
return new Promise((resolve) => {
|
|
240
|
+
const stdin = process.stdin;
|
|
241
|
+
stdin.setRawMode(true);
|
|
242
|
+
stdin.resume();
|
|
243
|
+
stdin.setEncoding('utf8');
|
|
532
244
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
245
|
+
const onKeypress = (key) => {
|
|
246
|
+
if (key === '\u0003' || key === '\u001b') { // Ctrl+C or ESC
|
|
247
|
+
stdin.setRawMode(false);
|
|
248
|
+
stdin.pause();
|
|
249
|
+
process.exit(0);
|
|
250
|
+
} else if (key === '\r' || key === '\n') { // Enter
|
|
251
|
+
stdin.setRawMode(false);
|
|
252
|
+
stdin.removeListener('data', onKeypress);
|
|
253
|
+
resolve(Array.from(selected));
|
|
254
|
+
} else if (key === ' ') { // Space - toggle
|
|
255
|
+
if (selected.has(currentIndex)) {
|
|
256
|
+
selected.delete(currentIndex);
|
|
257
|
+
} else {
|
|
258
|
+
selected.add(currentIndex);
|
|
539
259
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
adding_files: `Adding ${progress.count || 0} file(s)...`,
|
|
548
|
-
updating_manifest: 'Updating manifest...',
|
|
549
|
-
verifying: 'Verifying installation...',
|
|
550
|
-
complete: 'Complete!'
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
const message = stageMessages[progress.stage] || progress.stage;
|
|
554
|
-
process.stdout.write(`${colors.cyan}${message}${colors.reset}`);
|
|
260
|
+
renderList();
|
|
261
|
+
} else if (key === '\u001b[A') { // Up arrow
|
|
262
|
+
currentIndex = currentIndex > 0 ? currentIndex - 1 : tools.length - 1;
|
|
263
|
+
renderList();
|
|
264
|
+
} else if (key === '\u001b[B') { // Down arrow
|
|
265
|
+
currentIndex = currentIndex < tools.length - 1 ? currentIndex + 1 : 0;
|
|
266
|
+
renderList();
|
|
555
267
|
}
|
|
556
268
|
};
|
|
557
269
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
// Execute upgrade
|
|
561
|
-
const result = await installationEngine.upgradeVariant(
|
|
562
|
-
toolId,
|
|
563
|
-
newVariant,
|
|
564
|
-
targetPath,
|
|
565
|
-
confirmCallback,
|
|
566
|
-
progressCallback
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
// Clear progress line
|
|
570
|
-
process.stdout.write('\n\n');
|
|
571
|
-
|
|
572
|
-
// Display results
|
|
573
|
-
if (!result.success) {
|
|
574
|
-
console.log(`${colors.red}✗ Upgrade failed: ${result.error}${colors.reset}\n`);
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const action = result.filesAdded > result.filesRemoved ? 'Upgrade' : 'Downgrade';
|
|
579
|
-
console.log(`${colors.green}✓ ${tool.name} ${action.toLowerCase()}d successfully${colors.reset}`);
|
|
580
|
-
console.log(`${colors.cyan}From variant:${colors.reset} ${result.fromVariant}`);
|
|
581
|
-
console.log(`${colors.cyan}To variant:${colors.reset} ${result.toVariant}`);
|
|
582
|
-
console.log(`${colors.cyan}Files added:${colors.reset} ${result.filesAdded}`);
|
|
583
|
-
console.log(`${colors.cyan}Files removed:${colors.reset} ${result.filesRemoved}`);
|
|
584
|
-
console.log(`${colors.cyan}Backup created:${colors.reset} ${result.backupPath}\n`);
|
|
585
|
-
|
|
586
|
-
if (result.verification && !result.verification.valid) {
|
|
587
|
-
console.log(`${colors.yellow}Warnings:${colors.reset}`);
|
|
588
|
-
result.verification.issues.forEach(issue => {
|
|
589
|
-
console.log(` ${colors.yellow}⚠${colors.reset} ${issue.message}`);
|
|
590
|
-
});
|
|
591
|
-
console.log('');
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
} catch (error) {
|
|
595
|
-
console.error(`\n${colors.red}✗ Upgrade failed: ${error.message}${colors.reset}\n`);
|
|
596
|
-
process.exit(1);
|
|
597
|
-
}
|
|
270
|
+
stdin.on('data', onKeypress);
|
|
271
|
+
});
|
|
598
272
|
}
|
|
599
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Run upgrade/downgrade command for a specific tool
|
|
276
|
+
* Changes the variant of an installed tool
|
|
277
|
+
*
|
|
278
|
+
* @param {string} toolId - Tool to upgrade (claude, opencode, ampcode, droid)
|
|
279
|
+
* @param {string} newVariant - Target variant (lite, standard, pro)
|
|
280
|
+
*/
|
|
600
281
|
/**
|
|
601
282
|
* Load configuration from JSON file
|
|
602
283
|
* Used with --config flag
|
|
603
284
|
*
|
|
604
285
|
* @param {string} configPath - Path to configuration file
|
|
605
286
|
*/
|
|
606
|
-
async loadConfig(configPath) {
|
|
607
|
-
try {
|
|
608
|
-
const configContent = await fs.promises.readFile(configPath, 'utf8');
|
|
609
|
-
const config = JSON.parse(configContent);
|
|
610
|
-
|
|
611
|
-
// Validate and apply configuration
|
|
612
|
-
if (config.variant) {
|
|
613
|
-
this.cliArgs.variant = config.variant;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (config.tools && Array.isArray(config.tools)) {
|
|
617
|
-
this.cliArgs.tools = config.tools;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
if (config.paths && typeof config.paths === 'object') {
|
|
621
|
-
this.cliArgs.paths = { ...this.cliArgs.paths, ...config.paths };
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
if (config.silent !== undefined) {
|
|
625
|
-
this.cliArgs.silent = config.silent;
|
|
626
|
-
this.cliArgs.nonInteractive = config.silent;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
console.log(`${colors.green}✓ Configuration loaded from ${configPath}${colors.reset}\n`);
|
|
630
|
-
|
|
631
|
-
} catch (error) {
|
|
632
|
-
console.error(`${colors.red}Error loading configuration: ${error.message}${colors.reset}\n`);
|
|
633
|
-
process.exit(1);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
287
|
/**
|
|
638
288
|
* Run non-interactive installation
|
|
639
289
|
* Uses command-line arguments instead of prompts
|
|
640
290
|
*/
|
|
641
|
-
async runNonInteractive() {
|
|
642
|
-
console.log(`\n${colors.bright}${colors.cyan}Agentic Kit Installer (Non-Interactive Mode)${colors.reset}\n`);
|
|
643
|
-
|
|
644
|
-
// Validate variant
|
|
645
|
-
const variant = this.cliArgs.variant;
|
|
646
|
-
if (!['lite', 'standard', 'pro'].includes(variant)) {
|
|
647
|
-
console.log(`${colors.red}Error: Invalid variant '${variant}'${colors.reset}`);
|
|
648
|
-
console.log(`${colors.yellow}Valid variants: lite, standard, pro${colors.reset}\n`);
|
|
649
|
-
process.exit(1);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Validate tools
|
|
653
|
-
const validTools = this.tools.map(t => t.id);
|
|
654
|
-
const invalidTools = this.cliArgs.tools.filter(t => !validTools.includes(t));
|
|
655
|
-
if (invalidTools.length > 0) {
|
|
656
|
-
console.log(`${colors.red}Error: Invalid tool(s): ${invalidTools.join(', ')}${colors.reset}`);
|
|
657
|
-
console.log(`${colors.yellow}Valid tools: ${validTools.join(', ')}${colors.reset}\n`);
|
|
658
|
-
process.exit(1);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
if (this.cliArgs.tools.length === 0) {
|
|
662
|
-
console.log(`${colors.red}Error: At least one tool must be specified${colors.reset}\n`);
|
|
663
|
-
process.exit(1);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Set selections
|
|
667
|
-
this.selections.variant = variant;
|
|
668
|
-
this.selections.tools = this.cliArgs.tools;
|
|
669
|
-
|
|
670
|
-
// Set paths (use custom paths or defaults)
|
|
671
|
-
const os = require('os');
|
|
672
|
-
for (const toolId of this.selections.tools) {
|
|
673
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
674
|
-
|
|
675
|
-
if (this.cliArgs.paths[toolId]) {
|
|
676
|
-
this.selections.paths[toolId] = this.cliArgs.paths[toolId];
|
|
677
|
-
} else {
|
|
678
|
-
this.selections.paths[toolId] = tool.path;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// Display summary
|
|
683
|
-
const variantInfo = this.variants.find(v => v.id === variant);
|
|
684
|
-
console.log(`${colors.bright}Installation Summary:${colors.reset}`);
|
|
685
|
-
console.log(`${colors.cyan}Variant:${colors.reset} ${variantInfo.name} (${variantInfo.agents} agents, ${variantInfo.skills} skills)`);
|
|
686
|
-
console.log(`${colors.cyan}Tools:${colors.reset} ${this.selections.tools.join(', ')}`);
|
|
687
|
-
console.log('');
|
|
688
|
-
|
|
689
|
-
for (const toolId of this.selections.tools) {
|
|
690
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
691
|
-
const isCustom = this.selections.paths[toolId] !== tool.path;
|
|
692
|
-
console.log(`${colors.bright}${tool.name}${colors.reset}`);
|
|
693
|
-
console.log(` Path: ${this.selections.paths[toolId]}${isCustom ? ' (custom)' : ''}`);
|
|
694
|
-
}
|
|
695
|
-
console.log('');
|
|
696
|
-
|
|
697
|
-
// Run installation
|
|
698
|
-
try {
|
|
699
|
-
await this.install();
|
|
700
|
-
} catch (error) {
|
|
701
|
-
console.error(`${colors.red}✗ Installation failed: ${error.message}${colors.reset}\n`);
|
|
702
|
-
process.exit(1);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
291
|
/**
|
|
707
292
|
* Handle fatal errors with detailed error messages and actionable advice
|
|
708
293
|
* Categorizes errors and provides specific guidance for each type
|
|
@@ -715,26 +300,15 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
715
300
|
// Categorize the error and provide appropriate guidance
|
|
716
301
|
const errorInfo = this.categorizeError(error);
|
|
717
302
|
|
|
718
|
-
console.log(`${colors.red}${
|
|
719
|
-
console.log(`${colors.red}${'─'.repeat(60)}${colors.reset}\n`);
|
|
720
|
-
|
|
721
|
-
console.log(`${colors.bright}Error Type:${colors.reset} ${errorInfo.type}`);
|
|
722
|
-
console.log(`${colors.bright}Message:${colors.reset} ${error.message}\n`);
|
|
303
|
+
console.log(`${colors.red}Error: ${error.message}${colors.reset}`);
|
|
723
304
|
|
|
724
|
-
if (errorInfo.advice.length > 0) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
console.log(` ${index + 1}. ${advice}`);
|
|
305
|
+
if (errorInfo.advice.length > 0 && errorInfo.advice.length <= 3) {
|
|
306
|
+
errorInfo.advice.forEach(advice => {
|
|
307
|
+
console.log(` - ${advice}`);
|
|
728
308
|
});
|
|
729
309
|
console.log('');
|
|
730
310
|
}
|
|
731
311
|
|
|
732
|
-
// Display additional technical details if available
|
|
733
|
-
if (errorInfo.technicalDetails) {
|
|
734
|
-
console.log(`${colors.cyan}Technical Details:${colors.reset}`);
|
|
735
|
-
console.log(` ${errorInfo.technicalDetails}\n`);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
312
|
// Exit with error code
|
|
739
313
|
process.exit(1);
|
|
740
314
|
}
|
|
@@ -754,9 +328,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
754
328
|
return {
|
|
755
329
|
type: 'Permission Error',
|
|
756
330
|
advice: [
|
|
757
|
-
'Try
|
|
758
|
-
'Or choose a different installation directory
|
|
759
|
-
'Check directory permissions: ls -la on the parent directory'
|
|
331
|
+
'Try: sudo node installer/cli.js',
|
|
332
|
+
'Or choose a different installation directory'
|
|
760
333
|
],
|
|
761
334
|
technicalDetails: `Error code: ${code || 'EACCES'}`
|
|
762
335
|
};
|
|
@@ -767,10 +340,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
767
340
|
return {
|
|
768
341
|
type: 'Disk Space Error',
|
|
769
342
|
advice: [
|
|
770
|
-
'Free up disk space
|
|
771
|
-
'Check
|
|
772
|
-
'Consider installing to a different location with more space',
|
|
773
|
-
'The installer requires at least 50MB of free space'
|
|
343
|
+
'Free up disk space',
|
|
344
|
+
'Check: df -h'
|
|
774
345
|
],
|
|
775
346
|
technicalDetails: 'Installation requires approximately 10MB per tool'
|
|
776
347
|
};
|
|
@@ -781,10 +352,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
781
352
|
return {
|
|
782
353
|
type: 'Network Error',
|
|
783
354
|
advice: [
|
|
784
|
-
'Check
|
|
785
|
-
'
|
|
786
|
-
'Try again in a few moments',
|
|
787
|
-
'Use offline installation mode if available'
|
|
355
|
+
'Check internet connection',
|
|
356
|
+
'Try again later'
|
|
788
357
|
],
|
|
789
358
|
technicalDetails: `Network error code: ${code || 'UNKNOWN'}`
|
|
790
359
|
};
|
|
@@ -795,10 +364,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
795
364
|
return {
|
|
796
365
|
type: 'Missing Package Error',
|
|
797
366
|
advice: [
|
|
798
|
-
'
|
|
799
|
-
'
|
|
800
|
-
'Try reinstalling agentic-kit: npm uninstall -g @amrhas82/agentic-kit && npm install -g @amrhas82/agentic-kit',
|
|
801
|
-
'Check that you are running the installer from the correct directory'
|
|
367
|
+
'Run: npm install -g @amrhas82/agentic-kit',
|
|
368
|
+
'Check: packages directory exists'
|
|
802
369
|
],
|
|
803
370
|
technicalDetails: `Missing file or package validation failed`
|
|
804
371
|
};
|
|
@@ -809,10 +376,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
809
376
|
return {
|
|
810
377
|
type: 'Path Validation Error',
|
|
811
378
|
advice: [
|
|
812
|
-
'
|
|
813
|
-
'
|
|
814
|
-
'Check that you have write permissions for the specified path',
|
|
815
|
-
'Use default paths if custom paths are causing issues'
|
|
379
|
+
'Path must be absolute (starts with / or ~)',
|
|
380
|
+
'Check parent directory exists'
|
|
816
381
|
],
|
|
817
382
|
technicalDetails: 'Paths must be absolute and writable'
|
|
818
383
|
};
|
|
@@ -823,10 +388,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
823
388
|
return {
|
|
824
389
|
type: 'Invalid Input Error',
|
|
825
390
|
advice: [
|
|
826
|
-
'
|
|
827
|
-
'
|
|
828
|
-
'Paths must be absolute (start with / or ~)',
|
|
829
|
-
'Restart the installer and try again'
|
|
391
|
+
'Check your selections',
|
|
392
|
+
'Restart installer and try again'
|
|
830
393
|
],
|
|
831
394
|
technicalDetails: error.message
|
|
832
395
|
};
|
|
@@ -837,11 +400,9 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
837
400
|
return {
|
|
838
401
|
type: 'Installation Error',
|
|
839
402
|
advice: [
|
|
840
|
-
'Check
|
|
841
|
-
'Verify write permissions
|
|
842
|
-
'
|
|
843
|
-
'Try installing to a different location',
|
|
844
|
-
'Review the installation log at ~/.agentic-kit-install.log for details'
|
|
403
|
+
'Check disk space: df -h',
|
|
404
|
+
'Verify write permissions',
|
|
405
|
+
'Try different location'
|
|
845
406
|
],
|
|
846
407
|
technicalDetails: 'Installation process encountered an error during file operations'
|
|
847
408
|
};
|
|
@@ -851,10 +412,8 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
851
412
|
return {
|
|
852
413
|
type: 'Unknown Error',
|
|
853
414
|
advice: [
|
|
854
|
-
'Try running
|
|
855
|
-
'
|
|
856
|
-
'Report this issue at: https://github.com/amrhas82/agentic-kit/issues',
|
|
857
|
-
'Include the error message and your system information (OS, Node version)'
|
|
415
|
+
'Try running installer again',
|
|
416
|
+
'Report at: https://github.com/amrhas82/agentic-kit/issues'
|
|
858
417
|
],
|
|
859
418
|
technicalDetails: error.stack ? error.stack.split('\n')[1] : 'No additional details'
|
|
860
419
|
};
|
|
@@ -869,50 +428,6 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
869
428
|
* @param {number} totalTools - Total number of tools
|
|
870
429
|
* @returns {Promise<boolean>} True to continue, false to cancel
|
|
871
430
|
*/
|
|
872
|
-
async offerRecoveryOptions(failedTool, currentIndex, totalTools) {
|
|
873
|
-
const remainingTools = totalTools - currentIndex;
|
|
874
|
-
|
|
875
|
-
console.log(`${colors.yellow}┌─────────────────────────────────────────────────────────────┐${colors.reset}`);
|
|
876
|
-
console.log(`${colors.yellow}│ Installation Failure - Recovery Options │${colors.reset}`);
|
|
877
|
-
console.log(`${colors.yellow}└─────────────────────────────────────────────────────────────┘${colors.reset}\n`);
|
|
878
|
-
|
|
879
|
-
console.log(`${colors.bright}Status:${colors.reset} ${failedTool} installation failed`);
|
|
880
|
-
console.log(`${colors.bright}Remaining:${colors.reset} ${remainingTools} tool${remainingTools > 1 ? 's' : ''} to install\n`);
|
|
881
|
-
|
|
882
|
-
// In silent mode, auto-continue with remaining tools
|
|
883
|
-
if (this.cliArgs.silent) {
|
|
884
|
-
console.log(`${colors.yellow}Silent mode: auto-continuing with remaining tools${colors.reset}\n`);
|
|
885
|
-
console.log(`${colors.yellow}Note:${colors.reset} The failed installation has been rolled back automatically.`);
|
|
886
|
-
console.log(`${colors.yellow}No partial files remain for ${failedTool}.${colors.reset}\n`);
|
|
887
|
-
return true;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
console.log(`${colors.cyan}Options:${colors.reset}`);
|
|
891
|
-
console.log(` ${colors.green}C${colors.reset} - Continue with remaining tools (recommended)`);
|
|
892
|
-
console.log(` ${colors.red}Q${colors.reset} - Quit installation (successful installations will be kept)\n`);
|
|
893
|
-
|
|
894
|
-
console.log(`${colors.yellow}Note:${colors.reset} The failed installation has been rolled back automatically.`);
|
|
895
|
-
console.log(`${colors.yellow}No partial files remain for ${failedTool}.${colors.reset}\n`);
|
|
896
|
-
|
|
897
|
-
const answer = await this.askQuestion(
|
|
898
|
-
`${colors.bright}Choose an option (C/Q):${colors.reset} `,
|
|
899
|
-
'C'
|
|
900
|
-
);
|
|
901
|
-
|
|
902
|
-
const choice = answer.toUpperCase();
|
|
903
|
-
|
|
904
|
-
if (choice === 'C' || choice === '') {
|
|
905
|
-
console.log(`${colors.green}Continuing with remaining tools...${colors.reset}\n`);
|
|
906
|
-
return true;
|
|
907
|
-
} else if (choice === 'Q') {
|
|
908
|
-
return false;
|
|
909
|
-
} else {
|
|
910
|
-
// Invalid choice, ask again
|
|
911
|
-
console.log(`${colors.red}Invalid choice. Please enter C or Q.${colors.reset}\n`);
|
|
912
|
-
return this.offerRecoveryOptions(failedTool, currentIndex, totalTools);
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
|
|
916
431
|
/**
|
|
917
432
|
* Prompt user to resume interrupted installation
|
|
918
433
|
* Shows summary of previous installation progress
|
|
@@ -920,213 +435,50 @@ ${colors.bright}For more information, visit:${colors.reset}
|
|
|
920
435
|
* @param {InstallationEngine} installationEngine - Installation engine instance
|
|
921
436
|
* @returns {Promise<boolean>} - True if user wants to resume, false otherwise
|
|
922
437
|
*/
|
|
923
|
-
async promptResume(installationEngine) {
|
|
924
|
-
const summary = await installationEngine.getResumeSummary();
|
|
925
|
-
|
|
926
|
-
if (!summary) {
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
console.clear();
|
|
931
|
-
console.log(`\n${colors.yellow}${colors.bright}Previous Installation Detected${colors.reset}`);
|
|
932
|
-
console.log(`${colors.yellow}${'─'.repeat(60)}${colors.reset}\n`);
|
|
933
|
-
|
|
934
|
-
// Format timestamp
|
|
935
|
-
const startedAt = new Date(summary.startedAt);
|
|
936
|
-
const lastUpdated = new Date(summary.lastUpdated);
|
|
937
|
-
const timeDiff = Math.floor((lastUpdated - startedAt) / 1000 / 60);
|
|
938
|
-
|
|
939
|
-
console.log(`${colors.cyan}Session ID:${colors.reset} ${summary.sessionId}`);
|
|
940
|
-
console.log(`${colors.cyan}Started:${colors.reset} ${startedAt.toLocaleString()}`);
|
|
941
|
-
console.log(`${colors.cyan}Last Updated:${colors.reset} ${lastUpdated.toLocaleString()} (${timeDiff} min ago)`);
|
|
942
|
-
console.log(`${colors.cyan}Variant:${colors.reset} ${summary.variant}`);
|
|
943
|
-
console.log('');
|
|
944
|
-
|
|
945
|
-
// Show progress
|
|
946
|
-
console.log(`${colors.bright}Progress:${colors.reset}`);
|
|
947
|
-
console.log(` Tools: ${summary.completedTools}/${summary.totalTools} completed`);
|
|
948
|
-
|
|
949
|
-
if (summary.completedToolsList.length > 0) {
|
|
950
|
-
console.log(`\n${colors.green}Completed:${colors.reset}`);
|
|
951
|
-
summary.completedToolsList.forEach(toolId => {
|
|
952
|
-
console.log(` ${colors.green}✓${colors.reset} ${toolId}`);
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
if (summary.failedToolsList.length > 0) {
|
|
957
|
-
console.log(`\n${colors.red}Failed:${colors.reset}`);
|
|
958
|
-
summary.failedToolsList.forEach(toolId => {
|
|
959
|
-
console.log(` ${colors.red}✗${colors.reset} ${toolId}`);
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
if (summary.currentTool) {
|
|
964
|
-
console.log(`\n${colors.yellow}Current Tool:${colors.reset} ${summary.currentTool}`);
|
|
965
|
-
const progress = summary.currentToolProgress;
|
|
966
|
-
if (progress.totalFiles > 0) {
|
|
967
|
-
console.log(` Progress: ${progress.filesCompleted}/${progress.totalFiles} files (${progress.percentComplete}%)`);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
console.log('');
|
|
972
|
-
console.log(`${colors.bright}Would you like to resume this installation?${colors.reset}`);
|
|
973
|
-
console.log(` ${colors.green}Y${colors.reset} - Resume from where it left off`);
|
|
974
|
-
console.log(` ${colors.yellow}N${colors.reset} - Start fresh (previous state will be cleared)\n`);
|
|
975
|
-
|
|
976
|
-
const answer = await this.askQuestion(
|
|
977
|
-
`${colors.bright}Resume installation? (Y/n):${colors.reset} `,
|
|
978
|
-
'Y'
|
|
979
|
-
);
|
|
980
|
-
|
|
981
|
-
return answer.toLowerCase() !== 'n';
|
|
982
|
-
}
|
|
983
|
-
|
|
984
438
|
/**
|
|
985
439
|
* Resume interrupted installation
|
|
986
440
|
* Uses saved state to continue from where it left off
|
|
987
441
|
*
|
|
988
442
|
* @param {InstallationEngine} installationEngine - Installation engine instance with loaded state
|
|
989
443
|
*/
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
444
|
+
showWelcome() {
|
|
445
|
+
console.clear();
|
|
446
|
+
console.log(`
|
|
447
|
+
${colors.bright}${colors.cyan} █████╗ ██████╗ ███████╗███╗ ██╗████████╗██╗ ██████╗ ██╗ ██╗██╗████████╗${colors.reset}
|
|
448
|
+
${colors.bright}${colors.cyan}██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██║██╔════╝ ██║ ██╔╝██║╚══██╔══╝${colors.reset}
|
|
449
|
+
${colors.bright}${colors.cyan}███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║██║ █████╔╝ ██║ ██║${colors.reset}
|
|
450
|
+
${colors.bright}${colors.cyan}██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██║ ██║${colors.reset}
|
|
451
|
+
${colors.bright}${colors.cyan}██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ██║╚██████╗ ██║ ██╗██║ ██║${colors.reset}
|
|
452
|
+
${colors.bright}${colors.cyan}╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝${colors.reset}
|
|
453
|
+
|
|
454
|
+
${colors.bright}v1.10.0 | 14 agents + 20 commands per tool${colors.reset}
|
|
455
|
+
`);
|
|
456
|
+
}
|
|
999
457
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
this.selections.paths = state.paths;
|
|
1004
|
-
|
|
1005
|
-
// Use installMultipleTools with resume flag
|
|
1006
|
-
try {
|
|
1007
|
-
const results = await installationEngine.installMultipleTools(
|
|
1008
|
-
state.variant,
|
|
1009
|
-
state.tools,
|
|
1010
|
-
state.paths,
|
|
1011
|
-
(progress) => {
|
|
1012
|
-
// Progress callback (same as normal installation)
|
|
1013
|
-
this.drawProgressBar(
|
|
1014
|
-
progress.filesCompleted,
|
|
1015
|
-
progress.totalFiles,
|
|
1016
|
-
progress.percentage,
|
|
1017
|
-
progress.currentFile,
|
|
1018
|
-
this.formatBytes(progress.bytesTransferred),
|
|
1019
|
-
this.formatBytes(progress.totalBytes),
|
|
1020
|
-
this.formatBytes(progress.bytesTransferred / ((Date.now() - Date.now()) / 1000 || 1)) + '/s',
|
|
1021
|
-
'0:00',
|
|
1022
|
-
'0:00'
|
|
1023
|
-
);
|
|
1024
|
-
},
|
|
1025
|
-
true // resume = true
|
|
1026
|
-
);
|
|
1027
|
-
|
|
1028
|
-
// Display results
|
|
1029
|
-
console.log(`\n${colors.bright}Installation Complete${colors.reset}\n`);
|
|
1030
|
-
|
|
1031
|
-
if (results.successful.length > 0) {
|
|
1032
|
-
console.log(`${colors.green}Successfully installed:${colors.reset}`);
|
|
1033
|
-
results.successful.forEach(toolId => {
|
|
1034
|
-
console.log(` ${colors.green}✓${colors.reset} ${toolId}`);
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
if (results.skipped.length > 0) {
|
|
1039
|
-
console.log(`\n${colors.yellow}Skipped (already completed):${colors.reset}`);
|
|
1040
|
-
results.skipped.forEach(toolId => {
|
|
1041
|
-
console.log(` ${colors.yellow}○${colors.reset} ${toolId}`);
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
if (results.failed.length > 0) {
|
|
1046
|
-
console.log(`\n${colors.red}Failed:${colors.reset}`);
|
|
1047
|
-
results.failed.forEach(failure => {
|
|
1048
|
-
console.log(` ${colors.red}✗${colors.reset} ${failure.toolId}: ${failure.error}`);
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
} catch (error) {
|
|
1053
|
-
console.error(`${colors.red}Resume failed: ${error.message}${colors.reset}`);
|
|
1054
|
-
throw error;
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
showWelcome() {
|
|
1059
|
-
console.clear();
|
|
1060
|
-
console.log(`
|
|
1061
|
-
${colors.bright}${colors.cyan}┌─────────────────────────────────────────────────────────────┐${colors.reset}
|
|
1062
|
-
${colors.bright}${colors.cyan}│ Agentic Kit Installer v1.2.0 │${colors.reset}
|
|
1063
|
-
${colors.bright}${colors.cyan}│ 14 Agents + 20 Commands Per Tool │${colors.reset}
|
|
1064
|
-
${colors.bright}${colors.cyan}└─────────────────────────────────────────────────────────────┘${colors.reset}
|
|
1065
|
-
|
|
1066
|
-
${colors.bright}Available tools:${colors.reset}
|
|
1067
|
-
• ${colors.cyan}claude${colors.reset} - Claude Code (AI development assistant)
|
|
1068
|
-
• ${colors.cyan}opencode${colors.reset} - OpenCode (CLI-optimized AI tool)
|
|
1069
|
-
• ${colors.cyan}ampcode${colors.reset} - Ampcode (Development accelerator)
|
|
1070
|
-
• ${colors.cyan}droid${colors.reset} - Droid (Android-focused assistant)
|
|
1071
|
-
|
|
1072
|
-
${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
1073
|
-
`);
|
|
1074
|
-
|
|
1075
|
-
return new Promise(resolve => {
|
|
1076
|
-
this.rl.question('', resolve);
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
async selectVariant() {
|
|
1081
|
-
console.log(`\n${colors.bright}Step 1/4 — Choose Package Variant${colors.reset}\n`);
|
|
1082
|
-
|
|
1083
|
-
console.log('┌─────────────┬─────────┬─────────┬─────────────────────────────┐');
|
|
1084
|
-
console.log('│ Variant │ Agents │ Skills │ Description │');
|
|
1085
|
-
console.log('├─────────────┼─────────┼─────────┼─────────────────────────────┤');
|
|
1086
|
-
|
|
1087
|
-
this.variants.forEach((variant, index) => {
|
|
1088
|
-
const selected = index === 1 ? '●' : '○';
|
|
1089
|
-
const color = index === 1 ? colors.blue : colors.reset;
|
|
1090
|
-
console.log(`│ ${selected} ${color}${variant.name.padEnd(10)}${colors.reset} │ ${variant.agents.toString().padEnd(7)} │ ${variant.skills.toString().padEnd(7)} │ ${variant.description.padEnd(27)} │`);
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
console.log('└─────────────┴─────────┴─────────┴─────────────────────────────┘');
|
|
1094
|
-
console.log('\nUse arrow keys to navigate, Enter to select');
|
|
1095
|
-
|
|
1096
|
-
// Default to Standard
|
|
1097
|
-
this.selections.variant = 'standard';
|
|
1098
|
-
|
|
1099
|
-
return new Promise(resolve => {
|
|
1100
|
-
this.rl.question('\nPress Enter to continue (default: Standard)', (answer) => {
|
|
1101
|
-
if (answer && ['lite', 'standard', 'pro'].includes(answer.toLowerCase())) {
|
|
1102
|
-
this.selections.variant = answer.toLowerCase();
|
|
1103
|
-
}
|
|
1104
|
-
resolve();
|
|
1105
|
-
});
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
async selectTools() {
|
|
1110
|
-
console.log(`\n${colors.bright}Select tools to install${colors.reset}\n`);
|
|
1111
|
-
console.log(`${colors.cyan}(↑↓ navigate, space=toggle, a=all, enter=confirm)${colors.reset}\n`);
|
|
458
|
+
async selectTools() {
|
|
459
|
+
console.log(`\n${colors.bright}Select tools to install${colors.reset}\n`);
|
|
460
|
+
console.log(`${colors.cyan}(↑↓ navigate, space=toggle, a=all, enter=confirm)${colors.reset}\n`);
|
|
1112
461
|
|
|
1113
462
|
// Interactive checkbox selection
|
|
1114
463
|
const selected = new Set(['claude']); // Default to claude
|
|
1115
464
|
let currentIndex = 0;
|
|
1116
465
|
|
|
1117
466
|
const renderList = () => {
|
|
1118
|
-
//
|
|
1119
|
-
|
|
1120
|
-
|
|
467
|
+
// Move cursor up and clear each line individually
|
|
468
|
+
for (let i = 0; i <= this.tools.length; i++) {
|
|
469
|
+
process.stdout.write('\x1b[1A'); // Move up one line
|
|
470
|
+
process.stdout.write('\r'); // Carriage return to start of line
|
|
471
|
+
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
472
|
+
}
|
|
1121
473
|
|
|
1122
474
|
this.tools.forEach((tool, index) => {
|
|
1123
475
|
const isSelected = selected.has(tool.id);
|
|
1124
476
|
const isCurrent = index === currentIndex;
|
|
1125
|
-
const checkbox = isSelected ? '
|
|
477
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
1126
478
|
const pointer = isCurrent ? '»' : ' ';
|
|
1127
|
-
const
|
|
479
|
+
const paddedName = tool.name.padEnd(20);
|
|
1128
480
|
|
|
1129
|
-
console.log(`${pointer} ${
|
|
481
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description}`);
|
|
1130
482
|
});
|
|
1131
483
|
console.log(''); // Empty line at bottom
|
|
1132
484
|
};
|
|
@@ -1135,11 +487,11 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
1135
487
|
this.tools.forEach((tool, index) => {
|
|
1136
488
|
const isSelected = selected.has(tool.id);
|
|
1137
489
|
const isCurrent = index === currentIndex;
|
|
1138
|
-
const checkbox = isSelected ? '
|
|
490
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
1139
491
|
const pointer = isCurrent ? '»' : ' ';
|
|
1140
|
-
const
|
|
492
|
+
const paddedName = tool.name.padEnd(20);
|
|
1141
493
|
|
|
1142
|
-
console.log(`${pointer} ${
|
|
494
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description}`);
|
|
1143
495
|
});
|
|
1144
496
|
console.log('');
|
|
1145
497
|
|
|
@@ -1161,12 +513,7 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
1161
513
|
this.selections.tools = Array.from(selected);
|
|
1162
514
|
|
|
1163
515
|
// Show selection summary
|
|
1164
|
-
console.log(`${colors.green}Installing ${this.selections.tools.length} tool(s)
|
|
1165
|
-
this.selections.tools.forEach(id => {
|
|
1166
|
-
const tool = this.tools.find(t => t.id === id);
|
|
1167
|
-
console.log(` ${colors.green}✓${colors.reset} ${tool.name} ${colors.cyan}(14 agents + 20 commands)${colors.reset}`);
|
|
1168
|
-
});
|
|
1169
|
-
console.log('');
|
|
516
|
+
console.log(`${colors.green}Installing ${this.selections.tools.length} tool(s)${colors.reset}\n`);
|
|
1170
517
|
|
|
1171
518
|
resolve();
|
|
1172
519
|
} else if (key === ' ') { // Space - toggle
|
|
@@ -1201,271 +548,6 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
1201
548
|
}
|
|
1202
549
|
}
|
|
1203
550
|
|
|
1204
|
-
async configurePaths() {
|
|
1205
|
-
console.log(`\n${colors.bright}Step 3/4 — Path Configuration${colors.reset}\n`);
|
|
1206
|
-
|
|
1207
|
-
console.log('Default installation paths for selected tools:\n');
|
|
1208
|
-
|
|
1209
|
-
for (const toolId of this.selections.tools) {
|
|
1210
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
1211
|
-
console.log(`${colors.bright}${tool.name}${colors.reset}`);
|
|
1212
|
-
console.log(`${colors.cyan}Default path:${colors.reset} ${tool.path}`);
|
|
1213
|
-
|
|
1214
|
-
const customPath = await this.askQuestion(
|
|
1215
|
-
`Enter path (or press Enter for default): `,
|
|
1216
|
-
tool.path
|
|
1217
|
-
);
|
|
1218
|
-
|
|
1219
|
-
// Detect custom path
|
|
1220
|
-
if (customPath !== tool.path) {
|
|
1221
|
-
// Show custom path confirmation dialog
|
|
1222
|
-
const confirmed = await this.showCustomPathConfirmation(tool, customPath);
|
|
1223
|
-
|
|
1224
|
-
if (confirmed) {
|
|
1225
|
-
this.selections.paths[toolId] = customPath;
|
|
1226
|
-
console.log(`${colors.green}✓ Using custom path: ${customPath}${colors.reset}\n`);
|
|
1227
|
-
} else {
|
|
1228
|
-
this.selections.paths[toolId] = tool.path;
|
|
1229
|
-
console.log(`${colors.blue}Using default path: ${tool.path}${colors.reset}\n`);
|
|
1230
|
-
}
|
|
1231
|
-
} else {
|
|
1232
|
-
this.selections.paths[toolId] = tool.path;
|
|
1233
|
-
console.log(`${colors.green}✓ Using default path${colors.reset}\n`);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
async showCustomPathConfirmation(tool, customPath) {
|
|
1239
|
-
console.log(`\n${colors.yellow}┌─────────────────────────────────────────────────────────────┐${colors.reset}`);
|
|
1240
|
-
console.log(`${colors.yellow}│ Custom Path Confirmation │${colors.reset}`);
|
|
1241
|
-
console.log(`${colors.yellow}└─────────────────────────────────────────────────────────────┘${colors.reset}\n`);
|
|
1242
|
-
|
|
1243
|
-
console.log(`${colors.bright}Tool:${colors.reset} ${tool.name}`);
|
|
1244
|
-
console.log(`${colors.bright}Proposed custom path:${colors.reset} ${customPath}`);
|
|
1245
|
-
console.log(`${colors.bright}Default path:${colors.reset} ${tool.path}\n`);
|
|
1246
|
-
|
|
1247
|
-
// Validate the custom path
|
|
1248
|
-
const validation = this.validatePath(customPath);
|
|
1249
|
-
|
|
1250
|
-
// Display validation results
|
|
1251
|
-
console.log(`${colors.cyan}Validation Results:${colors.reset}`);
|
|
1252
|
-
|
|
1253
|
-
if (validation.valid) {
|
|
1254
|
-
console.log(`${colors.green}✓ Path is valid${colors.reset}`);
|
|
1255
|
-
if (validation.parentExists) {
|
|
1256
|
-
console.log(`${colors.green}✓ Parent directory exists${colors.reset}`);
|
|
1257
|
-
}
|
|
1258
|
-
if (validation.hasPermission) {
|
|
1259
|
-
console.log(`${colors.green}✓ Write permission available${colors.reset}`);
|
|
1260
|
-
}
|
|
1261
|
-
if (validation.hasDiskSpace) {
|
|
1262
|
-
console.log(`${colors.green}✓ Sufficient disk space${colors.reset}`);
|
|
1263
|
-
}
|
|
1264
|
-
} else {
|
|
1265
|
-
// Show validation warnings/errors
|
|
1266
|
-
validation.issues.forEach(issue => {
|
|
1267
|
-
const icon = issue.severity === 'error' ? '✗' : '⚠';
|
|
1268
|
-
const color = issue.severity === 'error' ? colors.red : colors.yellow;
|
|
1269
|
-
console.log(`${color}${icon} ${issue.message}${colors.reset}`);
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
console.log('');
|
|
1274
|
-
|
|
1275
|
-
// Require explicit confirmation
|
|
1276
|
-
if (!validation.valid && validation.issues.some(i => i.severity === 'error')) {
|
|
1277
|
-
console.log(`${colors.red}Cannot use this path due to validation errors${colors.reset}`);
|
|
1278
|
-
console.log(`${colors.yellow}Press Enter to use default path instead${colors.reset}`);
|
|
1279
|
-
await this.askQuestion('');
|
|
1280
|
-
return false;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
// Ask for confirmation
|
|
1284
|
-
const answer = await this.askQuestion(
|
|
1285
|
-
`${colors.bright}Confirm custom path? (y/N):${colors.reset} `,
|
|
1286
|
-
'n'
|
|
1287
|
-
);
|
|
1288
|
-
|
|
1289
|
-
return answer.toLowerCase() === 'y';
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
validatePath(customPath) {
|
|
1293
|
-
const result = {
|
|
1294
|
-
valid: true,
|
|
1295
|
-
issues: [],
|
|
1296
|
-
parentExists: false,
|
|
1297
|
-
hasPermission: false,
|
|
1298
|
-
hasDiskSpace: false
|
|
1299
|
-
};
|
|
1300
|
-
|
|
1301
|
-
// Expand tilde to home directory
|
|
1302
|
-
const expandedPath = customPath.startsWith('~')
|
|
1303
|
-
? path.join(require('os').homedir(), customPath.slice(1))
|
|
1304
|
-
: path.resolve(customPath);
|
|
1305
|
-
|
|
1306
|
-
try {
|
|
1307
|
-
// Check if path is absolute or can be resolved
|
|
1308
|
-
if (!path.isAbsolute(expandedPath)) {
|
|
1309
|
-
result.issues.push({
|
|
1310
|
-
severity: 'error',
|
|
1311
|
-
message: 'Path must be absolute'
|
|
1312
|
-
});
|
|
1313
|
-
result.valid = false;
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// Check parent directory exists
|
|
1317
|
-
const parentDir = path.dirname(expandedPath);
|
|
1318
|
-
if (fs.existsSync(parentDir)) {
|
|
1319
|
-
result.parentExists = true;
|
|
1320
|
-
|
|
1321
|
-
// Check write permission on parent directory
|
|
1322
|
-
try {
|
|
1323
|
-
fs.accessSync(parentDir, fs.constants.W_OK);
|
|
1324
|
-
result.hasPermission = true;
|
|
1325
|
-
} catch (err) {
|
|
1326
|
-
result.issues.push({
|
|
1327
|
-
severity: 'error',
|
|
1328
|
-
message: 'No write permission for parent directory'
|
|
1329
|
-
});
|
|
1330
|
-
result.valid = false;
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
// Check disk space (require at least 50MB free)
|
|
1334
|
-
try {
|
|
1335
|
-
const stats = fs.statfsSync ? fs.statfsSync(parentDir) : null;
|
|
1336
|
-
if (stats) {
|
|
1337
|
-
const availableSpace = stats.bavail * stats.bsize;
|
|
1338
|
-
const requiredSpace = 50 * 1024 * 1024; // 50MB
|
|
1339
|
-
|
|
1340
|
-
if (availableSpace >= requiredSpace) {
|
|
1341
|
-
result.hasDiskSpace = true;
|
|
1342
|
-
} else {
|
|
1343
|
-
result.issues.push({
|
|
1344
|
-
severity: 'warning',
|
|
1345
|
-
message: `Low disk space (${Math.round(availableSpace / 1024 / 1024)}MB available, 50MB recommended)`
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
|
-
} else {
|
|
1349
|
-
// Cannot check disk space on this platform
|
|
1350
|
-
result.hasDiskSpace = true; // Assume OK
|
|
1351
|
-
}
|
|
1352
|
-
} catch (err) {
|
|
1353
|
-
// Disk space check failed, but don't block installation
|
|
1354
|
-
result.hasDiskSpace = true; // Assume OK
|
|
1355
|
-
}
|
|
1356
|
-
} else {
|
|
1357
|
-
result.issues.push({
|
|
1358
|
-
severity: 'warning',
|
|
1359
|
-
message: 'Parent directory does not exist (will be created)'
|
|
1360
|
-
});
|
|
1361
|
-
// Still allow installation if parent can be created
|
|
1362
|
-
result.parentExists = false;
|
|
1363
|
-
result.hasPermission = true; // Assume OK if we can check grandparent
|
|
1364
|
-
result.hasDiskSpace = true; // Assume OK
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// Check if path already exists
|
|
1368
|
-
if (fs.existsSync(expandedPath)) {
|
|
1369
|
-
result.issues.push({
|
|
1370
|
-
severity: 'warning',
|
|
1371
|
-
message: 'Path already exists (files may be overwritten)'
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
} catch (err) {
|
|
1376
|
-
result.issues.push({
|
|
1377
|
-
severity: 'error',
|
|
1378
|
-
message: `Path validation error: ${err.message}`
|
|
1379
|
-
});
|
|
1380
|
-
result.valid = false;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
return result;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
async showSummary() {
|
|
1387
|
-
console.log(`\n${colors.bright}Step 4/4 — Installation Summary${colors.reset}\n`);
|
|
1388
|
-
|
|
1389
|
-
const variant = this.variants.find(v => v.id === this.selections.variant);
|
|
1390
|
-
console.log(`Package: ${variant.name} (${variant.agents} agents, ${variant.skills} skills)`);
|
|
1391
|
-
console.log(`Tools: ${this.selections.tools.map(id =>
|
|
1392
|
-
this.tools.find(t => t.id === id).name
|
|
1393
|
-
).join(', ')}\n`);
|
|
1394
|
-
|
|
1395
|
-
console.log('Installation Details:');
|
|
1396
|
-
console.log('┌─────────────┬──────────────────┬──────────┬─────────────┐');
|
|
1397
|
-
console.log('│ Tool │ Path │ Files │ Size │');
|
|
1398
|
-
console.log('├─────────────┼──────────────────┼──────────┼─────────────┤');
|
|
1399
|
-
|
|
1400
|
-
// Collect actual file counts and sizes for each tool
|
|
1401
|
-
let totalFiles = 0;
|
|
1402
|
-
let totalBytes = 0;
|
|
1403
|
-
const toolData = [];
|
|
1404
|
-
|
|
1405
|
-
for (const toolId of this.selections.tools) {
|
|
1406
|
-
const tool = this.tools.find(t => t.id === toolId);
|
|
1407
|
-
const installPath = this.selections.paths[toolId];
|
|
1408
|
-
const isCustom = installPath !== tool.path;
|
|
1409
|
-
|
|
1410
|
-
try {
|
|
1411
|
-
// Get actual package contents and size from PackageManager
|
|
1412
|
-
const contents = await this.packageManager.getPackageContents(toolId, this.selections.variant);
|
|
1413
|
-
const sizeInfo = await this.packageManager.getPackageSize(toolId, this.selections.variant);
|
|
1414
|
-
|
|
1415
|
-
toolData.push({
|
|
1416
|
-
tool,
|
|
1417
|
-
path: installPath,
|
|
1418
|
-
isCustom,
|
|
1419
|
-
fileCount: contents.totalFiles,
|
|
1420
|
-
size: sizeInfo.formattedSize
|
|
1421
|
-
});
|
|
1422
|
-
|
|
1423
|
-
totalFiles += contents.totalFiles;
|
|
1424
|
-
totalBytes += sizeInfo.size;
|
|
1425
|
-
} catch (error) {
|
|
1426
|
-
// If package data not available, show placeholder
|
|
1427
|
-
toolData.push({
|
|
1428
|
-
tool,
|
|
1429
|
-
path: installPath,
|
|
1430
|
-
isCustom,
|
|
1431
|
-
fileCount: 'N/A',
|
|
1432
|
-
size: 'N/A'
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
// Display tool rows with actual data
|
|
1438
|
-
for (const data of toolData) {
|
|
1439
|
-
const pathDisplay = data.isCustom ? `${data.path} *` : data.path;
|
|
1440
|
-
|
|
1441
|
-
// Format file count and size for display
|
|
1442
|
-
const fileCountStr = typeof data.fileCount === 'number' ? `${data.fileCount}` : data.fileCount;
|
|
1443
|
-
const sizeStr = data.size;
|
|
1444
|
-
|
|
1445
|
-
console.log(`│ ${data.tool.name.padEnd(11)} │ ${pathDisplay.padEnd(16)} │ ${fileCountStr.padEnd(8)} │ ${sizeStr.padEnd(11)} │`);
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
console.log('└─────────────┴──────────────────┴──────────┴─────────────┘');
|
|
1449
|
-
|
|
1450
|
-
// Show custom path footnote if applicable
|
|
1451
|
-
if (toolData.some(d => d.isCustom)) {
|
|
1452
|
-
console.log('\n* Custom path specified');
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
// Show totals if we have data
|
|
1456
|
-
if (totalFiles > 0) {
|
|
1457
|
-
// Format total size
|
|
1458
|
-
const totalSizeFormatted = this.formatBytes(totalBytes);
|
|
1459
|
-
console.log(`\n${colors.cyan}Total:${colors.reset} ${totalFiles} files, ${totalSizeFormatted}`);
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
console.log('\nPress Enter to install or Esc to cancel');
|
|
1463
|
-
|
|
1464
|
-
return new Promise(resolve => {
|
|
1465
|
-
this.rl.question('', resolve);
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
551
|
/**
|
|
1470
552
|
* Format bytes to human-readable size (helper method for summary display)
|
|
1471
553
|
* @param {number} bytes - Size in bytes
|
|
@@ -1495,492 +577,133 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
1495
577
|
*
|
|
1496
578
|
* @returns {Promise<object>} Object with success flag, errors array, and warnings array
|
|
1497
579
|
*/
|
|
1498
|
-
async performPreInstallationChecks() {
|
|
1499
|
-
const errors = [];
|
|
1500
|
-
const warnings = [];
|
|
1501
|
-
const os = require('os');
|
|
1502
|
-
|
|
1503
|
-
// Check Node.js version (require 14+)
|
|
1504
|
-
const nodeVersion = process.version;
|
|
1505
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
1506
|
-
if (majorVersion < 14) {
|
|
1507
|
-
errors.push(`Node.js version ${nodeVersion} is too old. Please upgrade to Node.js 14 or higher.`);
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
// Validate each selected tool's package
|
|
1511
|
-
for (const toolId of this.selections.tools) {
|
|
1512
|
-
try {
|
|
1513
|
-
const validation = await this.packageManager.validatePackage(toolId, this.selections.variant);
|
|
1514
|
-
if (!validation.valid) {
|
|
1515
|
-
errors.push(`Package validation failed for ${toolId}: ${validation.error}`);
|
|
1516
|
-
}
|
|
1517
|
-
} catch (error) {
|
|
1518
|
-
errors.push(`Cannot validate package for ${toolId}: ${error.message}`);
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
// Validate all installation paths
|
|
1523
|
-
for (const toolId of this.selections.tools) {
|
|
1524
|
-
const targetPath = this.selections.paths[toolId];
|
|
1525
|
-
const expandedPath = targetPath.startsWith('~')
|
|
1526
|
-
? path.join(os.homedir(), targetPath.slice(1))
|
|
1527
|
-
: path.resolve(targetPath);
|
|
1528
|
-
|
|
1529
|
-
// Check parent directory write permissions
|
|
1530
|
-
const parentDir = path.dirname(expandedPath);
|
|
1531
|
-
try {
|
|
1532
|
-
if (fs.existsSync(parentDir)) {
|
|
1533
|
-
fs.accessSync(parentDir, fs.constants.W_OK);
|
|
1534
|
-
} else {
|
|
1535
|
-
// Check if we can create parent directory
|
|
1536
|
-
const grandParentDir = path.dirname(parentDir);
|
|
1537
|
-
if (fs.existsSync(grandParentDir)) {
|
|
1538
|
-
fs.accessSync(grandParentDir, fs.constants.W_OK);
|
|
1539
|
-
} else {
|
|
1540
|
-
errors.push(`Cannot create installation path for ${toolId}: parent directories do not exist`);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
} catch (error) {
|
|
1544
|
-
errors.push(`No write permission for ${toolId} installation path: ${targetPath}`);
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
// Check if path already exists and has content
|
|
1548
|
-
if (fs.existsSync(expandedPath)) {
|
|
1549
|
-
try {
|
|
1550
|
-
const files = fs.readdirSync(expandedPath);
|
|
1551
|
-
if (files.length > 0) {
|
|
1552
|
-
warnings.push(`${toolId} installation path already exists and contains ${files.length} file(s). Existing installation will be backed up.`);
|
|
1553
|
-
}
|
|
1554
|
-
} catch (error) {
|
|
1555
|
-
warnings.push(`Cannot read existing installation directory for ${toolId}`);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
// Check available disk space
|
|
1561
|
-
try {
|
|
1562
|
-
// Calculate total required space for all selected tools
|
|
1563
|
-
let totalRequiredSpace = 0;
|
|
1564
|
-
for (const toolId of this.selections.tools) {
|
|
1565
|
-
try {
|
|
1566
|
-
const sizeInfo = await this.packageManager.getPackageSize(toolId, this.selections.variant);
|
|
1567
|
-
totalRequiredSpace += sizeInfo.size;
|
|
1568
|
-
} catch (error) {
|
|
1569
|
-
// Skip if we can't determine size
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
// Check disk space on home directory
|
|
1574
|
-
const homeDir = os.homedir();
|
|
1575
|
-
if (fs.statfsSync) {
|
|
1576
|
-
const stats = fs.statfsSync(homeDir);
|
|
1577
|
-
const availableSpace = stats.bavail * stats.bsize;
|
|
1578
|
-
const requiredSpace = totalRequiredSpace * 1.5; // 50% buffer for safety
|
|
1579
|
-
|
|
1580
|
-
if (availableSpace < requiredSpace) {
|
|
1581
|
-
const availableMB = Math.round(availableSpace / 1024 / 1024);
|
|
1582
|
-
const requiredMB = Math.round(requiredSpace / 1024 / 1024);
|
|
1583
|
-
errors.push(`Insufficient disk space: ${availableMB}MB available, ${requiredMB}MB required`);
|
|
1584
|
-
} else if (availableSpace < totalRequiredSpace * 2) {
|
|
1585
|
-
const availableMB = Math.round(availableSpace / 1024 / 1024);
|
|
1586
|
-
warnings.push(`Low disk space: ${availableMB}MB available. Consider freeing up space.`);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
} catch (error) {
|
|
1590
|
-
// Disk space check not available on this platform
|
|
1591
|
-
warnings.push('Could not check disk space (platform limitation)');
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
// Check for conflicting installations
|
|
1595
|
-
for (const toolId of this.selections.tools) {
|
|
1596
|
-
const targetPath = this.selections.paths[toolId];
|
|
1597
|
-
const expandedPath = targetPath.startsWith('~')
|
|
1598
|
-
? path.join(os.homedir(), targetPath.slice(1))
|
|
1599
|
-
: path.resolve(targetPath);
|
|
1600
|
-
|
|
1601
|
-
const manifestPath = path.join(expandedPath, 'manifest.json');
|
|
1602
|
-
if (fs.existsSync(manifestPath)) {
|
|
1603
|
-
try {
|
|
1604
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
1605
|
-
if (manifest.tool !== toolId) {
|
|
1606
|
-
warnings.push(`Path ${targetPath} contains a different tool (${manifest.tool}). This may cause conflicts.`);
|
|
1607
|
-
}
|
|
1608
|
-
} catch (error) {
|
|
1609
|
-
warnings.push(`Cannot read existing manifest at ${targetPath}`);
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
return {
|
|
1615
|
-
success: errors.length === 0,
|
|
1616
|
-
errors,
|
|
1617
|
-
warnings
|
|
1618
|
-
};
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
580
|
async install() {
|
|
1622
|
-
console.log(`\n${colors.bright}Installing ${this.selections.variant} package...${colors.reset}\n`);
|
|
1623
|
-
|
|
1624
|
-
// Perform pre-installation checks
|
|
1625
|
-
console.log(`${colors.cyan}Performing pre-installation checks...${colors.reset}`);
|
|
1626
|
-
const preCheckResult = await this.performPreInstallationChecks();
|
|
1627
|
-
|
|
1628
|
-
if (!preCheckResult.success) {
|
|
1629
|
-
console.log(`\n${colors.red}Pre-installation checks failed:${colors.reset}`);
|
|
1630
|
-
preCheckResult.errors.forEach(error => {
|
|
1631
|
-
console.log(` ${colors.red}✗${colors.reset} ${error}`);
|
|
1632
|
-
});
|
|
1633
|
-
|
|
1634
|
-
if (preCheckResult.warnings.length > 0) {
|
|
1635
|
-
console.log(`\n${colors.yellow}Warnings:${colors.reset}`);
|
|
1636
|
-
preCheckResult.warnings.forEach(warning => {
|
|
1637
|
-
console.log(` ${colors.yellow}⚠${colors.reset} ${warning}`);
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
throw new Error('Pre-installation checks failed. Please resolve the issues above and try again.');
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
if (preCheckResult.warnings.length > 0) {
|
|
1645
|
-
console.log(`${colors.yellow}Warnings detected:${colors.reset}`);
|
|
1646
|
-
preCheckResult.warnings.forEach(warning => {
|
|
1647
|
-
console.log(` ${colors.yellow}⚠${colors.reset} ${warning}`);
|
|
1648
|
-
});
|
|
1649
|
-
|
|
1650
|
-
// In silent mode, auto-proceed with warnings
|
|
1651
|
-
if (!this.cliArgs.silent) {
|
|
1652
|
-
const proceed = await this.askQuestion(
|
|
1653
|
-
`\n${colors.bright}Continue despite warnings? (Y/n):${colors.reset} `,
|
|
1654
|
-
'Y'
|
|
1655
|
-
);
|
|
1656
|
-
|
|
1657
|
-
if (proceed.toLowerCase() === 'n') {
|
|
1658
|
-
throw new Error('Installation cancelled by user due to warnings');
|
|
1659
|
-
}
|
|
1660
|
-
} else {
|
|
1661
|
-
console.log(`${colors.yellow}Silent mode: auto-proceeding despite warnings${colors.reset}`);
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
console.log(`${colors.green}✓ Pre-installation checks passed${colors.reset}\n`);
|
|
1666
|
-
|
|
1667
581
|
// Initialize InstallationEngine
|
|
1668
582
|
const PathManager = require('./path-manager');
|
|
1669
583
|
const InstallationEngine = require('./installation-engine');
|
|
584
|
+
const os = require('os');
|
|
1670
585
|
|
|
1671
586
|
const pathManager = new PathManager();
|
|
1672
587
|
const installationEngine = new InstallationEngine(pathManager, this.packageManager);
|
|
1673
588
|
|
|
1674
|
-
// Initialize state management for resume capability
|
|
1675
|
-
installationEngine.getStateManager().initializeState(
|
|
1676
|
-
this.selections.variant,
|
|
1677
|
-
this.selections.tools,
|
|
1678
|
-
this.selections.paths
|
|
1679
|
-
);
|
|
1680
|
-
await installationEngine.getStateManager().saveState({ stage: 'initializing' });
|
|
1681
|
-
|
|
1682
|
-
// Track overall progress
|
|
1683
|
-
let overallFilesCompleted = 0;
|
|
1684
|
-
let overallTotalFiles = 0;
|
|
1685
|
-
let successfulInstalls = [];
|
|
1686
|
-
let failedInstalls = [];
|
|
1687
|
-
let verificationResults = [];
|
|
1688
|
-
|
|
1689
|
-
// Calculate total files across all tools (only for valid tools)
|
|
1690
|
-
const toolFileCount = {};
|
|
1691
|
-
for (const toolId of this.selections.tools) {
|
|
1692
|
-
try {
|
|
1693
|
-
const contents = await this.packageManager.getPackageContents(toolId, this.selections.variant);
|
|
1694
|
-
toolFileCount[toolId] = contents.totalFiles;
|
|
1695
|
-
overallTotalFiles += contents.totalFiles;
|
|
1696
|
-
} catch (error) {
|
|
1697
|
-
// Skip tools that don't have valid packages
|
|
1698
|
-
toolFileCount[toolId] = 0;
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
const startTime = Date.now();
|
|
1703
|
-
|
|
1704
589
|
// Install each selected tool
|
|
1705
590
|
for (let i = 0; i < this.selections.tools.length; i++) {
|
|
1706
591
|
const toolId = this.selections.tools[i];
|
|
1707
592
|
const tool = this.tools.find(t => t.id === toolId);
|
|
1708
593
|
const targetPath = this.selections.paths[toolId];
|
|
1709
594
|
|
|
1710
|
-
console.log(`\n${colors.bright}
|
|
1711
|
-
console.log(`${colors.cyan}Target:${colors.reset} ${targetPath}\n`);
|
|
1712
|
-
|
|
1713
|
-
// Track files completed for this tool
|
|
1714
|
-
let toolFilesCompleted = 0;
|
|
1715
|
-
let toolStartTime = Date.now();
|
|
595
|
+
console.log(`\n${colors.bright}${colors.cyan}Installing ${tool.name}...${colors.reset}`);
|
|
1716
596
|
|
|
1717
597
|
try {
|
|
1718
|
-
|
|
598
|
+
const expandedPath = targetPath.startsWith('~')
|
|
599
|
+
? path.join(os.homedir(), targetPath.slice(1))
|
|
600
|
+
: path.resolve(targetPath);
|
|
601
|
+
|
|
602
|
+
// Start spinner
|
|
603
|
+
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
604
|
+
let spinnerIndex = 0;
|
|
605
|
+
const spinner = setInterval(() => {
|
|
606
|
+
process.stdout.write(`\r ${colors.cyan}${spinnerFrames[spinnerIndex]}${colors.reset} Installing...`);
|
|
607
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
608
|
+
}, 80);
|
|
609
|
+
|
|
610
|
+
// Install
|
|
1719
611
|
await installationEngine.installPackage(
|
|
1720
612
|
toolId,
|
|
1721
|
-
|
|
613
|
+
'pro', // Always pro variant
|
|
1722
614
|
targetPath,
|
|
1723
|
-
|
|
1724
|
-
// Update tool files completed (only count new files)
|
|
1725
|
-
if (progress.filesCompleted > toolFilesCompleted) {
|
|
1726
|
-
const newFiles = progress.filesCompleted - toolFilesCompleted;
|
|
1727
|
-
overallFilesCompleted += newFiles;
|
|
1728
|
-
toolFilesCompleted = progress.filesCompleted;
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
// Calculate elapsed time and speed
|
|
1732
|
-
const elapsedMs = Date.now() - toolStartTime;
|
|
1733
|
-
const elapsedSec = Math.floor(elapsedMs / 1000);
|
|
1734
|
-
const elapsedMin = Math.floor(elapsedSec / 60);
|
|
1735
|
-
const elapsedSecRemainder = elapsedSec % 60;
|
|
1736
|
-
const elapsedFormatted = `${elapsedMin}:${elapsedSecRemainder.toString().padStart(2, '0')}`;
|
|
1737
|
-
|
|
1738
|
-
// Calculate transfer speed (bytes per second)
|
|
1739
|
-
const speed = elapsedMs > 0 ? progress.bytesTransferred / (elapsedMs / 1000) : 0;
|
|
1740
|
-
const speedFormatted = this.formatBytes(speed);
|
|
1741
|
-
|
|
1742
|
-
// Calculate ETA
|
|
1743
|
-
const remainingBytes = progress.totalBytes - progress.bytesTransferred;
|
|
1744
|
-
const etaSec = speed > 0 ? Math.floor(remainingBytes / speed) : 0;
|
|
1745
|
-
const etaMin = Math.floor(etaSec / 60);
|
|
1746
|
-
const etaSecRemainder = etaSec % 60;
|
|
1747
|
-
const etaFormatted = `${etaMin}:${etaSecRemainder.toString().padStart(2, '0')}`;
|
|
1748
|
-
|
|
1749
|
-
// Draw progress bar for current tool
|
|
1750
|
-
this.drawProgressBar(
|
|
1751
|
-
progress.filesCompleted,
|
|
1752
|
-
progress.totalFiles,
|
|
1753
|
-
progress.percentage,
|
|
1754
|
-
progress.currentFile,
|
|
1755
|
-
this.formatBytes(progress.bytesTransferred),
|
|
1756
|
-
this.formatBytes(progress.totalBytes),
|
|
1757
|
-
speedFormatted,
|
|
1758
|
-
elapsedFormatted,
|
|
1759
|
-
etaFormatted
|
|
1760
|
-
);
|
|
1761
|
-
|
|
1762
|
-
// Draw overall progress
|
|
1763
|
-
// Cap overall percentage at 100% to handle file count mismatches
|
|
1764
|
-
const overallPercentage = overallTotalFiles > 0
|
|
1765
|
-
? Math.min(100, Math.round((overallFilesCompleted / overallTotalFiles) * 100))
|
|
1766
|
-
: 0;
|
|
1767
|
-
|
|
1768
|
-
this.drawOverallProgress(
|
|
1769
|
-
overallFilesCompleted,
|
|
1770
|
-
overallTotalFiles,
|
|
1771
|
-
overallPercentage,
|
|
1772
|
-
i + 1,
|
|
1773
|
-
this.selections.tools.length
|
|
1774
|
-
);
|
|
1775
|
-
}
|
|
615
|
+
null // No progress callback
|
|
1776
616
|
);
|
|
1777
617
|
|
|
1778
|
-
//
|
|
1779
|
-
|
|
1780
|
-
process.stdout.write('\
|
|
1781
|
-
|
|
1782
|
-
console.log(`${colors.green}✓ ${tool.name} installed successfully${colors.reset}`);
|
|
1783
|
-
console.log(` ${colors.cyan}Location:${colors.reset} ${targetPath}`);
|
|
1784
|
-
console.log(` ${colors.cyan}Files:${colors.reset} ${toolFilesCompleted} files`);
|
|
618
|
+
// Stop spinner
|
|
619
|
+
clearInterval(spinner);
|
|
620
|
+
process.stdout.write('\r\x1b[2K'); // Clear spinner line
|
|
1785
621
|
|
|
1786
|
-
//
|
|
1787
|
-
|
|
1788
|
-
|
|
622
|
+
// Get backup info from installation engine
|
|
623
|
+
const lastBackup = installationEngine.backupLog.length > 0
|
|
624
|
+
? installationEngine.backupLog[installationEngine.backupLog.length - 1]
|
|
625
|
+
: null;
|
|
1789
626
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
627
|
+
// Count actual installed components by checking directories
|
|
628
|
+
const countItems = (dir) => {
|
|
629
|
+
try {
|
|
630
|
+
if (fs.existsSync(dir)) {
|
|
631
|
+
return fs.readdirSync(dir).filter(f => !f.startsWith('.')).length;
|
|
632
|
+
}
|
|
633
|
+
} catch (e) {}
|
|
634
|
+
return 0;
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// Count only .md files in a directory
|
|
638
|
+
const countMdFiles = (dir) => {
|
|
639
|
+
try {
|
|
640
|
+
if (fs.existsSync(dir)) {
|
|
641
|
+
return fs.readdirSync(dir).filter(f => f.endsWith('.md')).length;
|
|
642
|
+
}
|
|
643
|
+
} catch (e) {}
|
|
644
|
+
return 0;
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// Check for agents (agents, agent, or droids)
|
|
648
|
+
let agentsCount = countItems(path.join(expandedPath, 'agents')) ||
|
|
649
|
+
countItems(path.join(expandedPath, 'agent')) ||
|
|
650
|
+
countItems(path.join(expandedPath, 'droids'));
|
|
651
|
+
let agentDir = 'agents';
|
|
652
|
+
if (fs.existsSync(path.join(expandedPath, 'agents'))) {
|
|
653
|
+
agentDir = 'agents';
|
|
654
|
+
} else if (fs.existsSync(path.join(expandedPath, 'agent'))) {
|
|
655
|
+
agentDir = 'agent';
|
|
656
|
+
} else if (fs.existsSync(path.join(expandedPath, 'droids'))) {
|
|
657
|
+
agentDir = 'droids';
|
|
1794
658
|
}
|
|
1795
659
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
name: tool.name,
|
|
1799
|
-
path: targetPath,
|
|
1800
|
-
fileCount: toolFilesCompleted
|
|
1801
|
-
});
|
|
1802
|
-
|
|
1803
|
-
verificationResults.push(verification);
|
|
660
|
+
// Check for skills (count directories)
|
|
661
|
+
const skillsCount = countItems(path.join(expandedPath, 'skills'));
|
|
1804
662
|
|
|
1805
|
-
//
|
|
1806
|
-
|
|
663
|
+
// Check for commands (count only .md files, both singular and plural)
|
|
664
|
+
const commandsCount = countMdFiles(path.join(expandedPath, 'commands')) ||
|
|
665
|
+
countMdFiles(path.join(expandedPath, 'command'));
|
|
666
|
+
const cmdDir = fs.existsSync(path.join(expandedPath, 'commands')) ? 'commands' : 'command';
|
|
1807
667
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
// Categorize the error for better user guidance
|
|
1814
|
-
const errorInfo = this.categorizeError(error);
|
|
1815
|
-
|
|
1816
|
-
console.error(`${colors.red}✗ Failed to install ${tool.name}${colors.reset}`);
|
|
1817
|
-
console.error(` ${colors.red}Error Type:${colors.reset} ${errorInfo.type}`);
|
|
1818
|
-
console.error(` ${colors.red}Message:${colors.reset} ${error.message}\n`);
|
|
1819
|
-
|
|
1820
|
-
// Display actionable advice
|
|
1821
|
-
if (errorInfo.advice.length > 0) {
|
|
1822
|
-
console.error(` ${colors.yellow}Suggested Actions:${colors.reset}`);
|
|
1823
|
-
errorInfo.advice.slice(0, 3).forEach((advice, index) => {
|
|
1824
|
-
console.error(` ${index + 1}. ${advice}`);
|
|
1825
|
-
});
|
|
1826
|
-
console.error('');
|
|
668
|
+
// Show backup info if it was created
|
|
669
|
+
if (lastBackup && lastBackup.original === expandedPath) {
|
|
670
|
+
const backupShortPath = lastBackup.backup.replace(os.homedir(), '~');
|
|
671
|
+
console.log(` ${colors.yellow}Backup:${colors.reset} ${backupShortPath}`);
|
|
1827
672
|
}
|
|
1828
673
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
await installationEngine.getStateManager().failCurrentTool(error);
|
|
1839
|
-
|
|
1840
|
-
// Offer recovery options if there are more tools to install
|
|
1841
|
-
if (i < this.selections.tools.length - 1) {
|
|
1842
|
-
const shouldContinue = await this.offerRecoveryOptions(tool.name, i + 1, this.selections.tools.length);
|
|
1843
|
-
|
|
1844
|
-
if (!shouldContinue) {
|
|
1845
|
-
console.log(`\n${colors.yellow}Installation cancelled by user${colors.reset}`);
|
|
1846
|
-
break; // Stop installing remaining tools
|
|
1847
|
-
}
|
|
674
|
+
// Show components with color
|
|
675
|
+
if (agentsCount > 0) {
|
|
676
|
+
console.log(` ${colors.green}✓${colors.reset} ${agentsCount} agents → ${targetPath}/${agentDir}`);
|
|
677
|
+
}
|
|
678
|
+
if (skillsCount > 0) {
|
|
679
|
+
console.log(` ${colors.green}✓${colors.reset} ${skillsCount} skills → ${targetPath}/skills`);
|
|
680
|
+
}
|
|
681
|
+
if (commandsCount > 0) {
|
|
682
|
+
console.log(` ${colors.green}✓${colors.reset} ${commandsCount} commands → ${targetPath}/${cmdDir}`);
|
|
1848
683
|
}
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
|
-
// Calculate total elapsed time
|
|
1853
|
-
const totalElapsedMs = Date.now() - startTime;
|
|
1854
|
-
const totalElapsedSec = Math.floor(totalElapsedMs / 1000);
|
|
1855
|
-
const totalElapsedMin = Math.floor(totalElapsedSec / 60);
|
|
1856
|
-
const totalElapsedSecRemainder = totalElapsedSec % 60;
|
|
1857
|
-
const totalElapsedFormatted = `${totalElapsedMin}:${totalElapsedSecRemainder.toString().padStart(2, '0')}`;
|
|
1858
|
-
|
|
1859
|
-
// Display final summary
|
|
1860
|
-
console.log(`\n${colors.bright}Installation Complete${colors.reset}`);
|
|
1861
|
-
console.log(`${colors.cyan}Total time:${colors.reset} ${totalElapsedFormatted}`);
|
|
1862
|
-
console.log(`${colors.cyan}Total files:${colors.reset} ${overallFilesCompleted} files\n`);
|
|
1863
|
-
|
|
1864
|
-
if (successfulInstalls.length > 0) {
|
|
1865
|
-
console.log(`${colors.green}Successfully installed:${colors.reset}`);
|
|
1866
|
-
for (const install of successfulInstalls) {
|
|
1867
|
-
console.log(` ${colors.green}✓${colors.reset} ${install.name} (${install.fileCount} files)`);
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
if (failedInstalls.length > 0) {
|
|
1872
|
-
console.log(`\n${colors.red}Failed installations:${colors.reset}`);
|
|
1873
|
-
for (const install of failedInstalls) {
|
|
1874
|
-
console.log(` ${colors.red}✗${colors.reset} ${install.name} (${install.errorType}): ${install.error}`);
|
|
1875
|
-
}
|
|
1876
|
-
console.log(`\n${colors.yellow}Note: Failed installations have been automatically rolled back${colors.reset}`);
|
|
1877
|
-
console.log(`${colors.yellow}No partial installations remain on your system${colors.reset}`);
|
|
1878
|
-
|
|
1879
|
-
// Offer retry options for failed installations
|
|
1880
|
-
if (failedInstalls.length > 0 && successfulInstalls.length > 0) {
|
|
1881
|
-
console.log(`\n${colors.cyan}You can retry failed installations by running the installer again${colors.reset}`);
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
684
|
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
for (let i = 0; i < successfulInstalls.length; i++) {
|
|
1889
|
-
const install = successfulInstalls[i];
|
|
1890
|
-
const verification = verificationResults[i];
|
|
1891
|
-
this.displayVerificationReport(verification, install.name);
|
|
685
|
+
} catch (error) {
|
|
686
|
+
// On ANY error, show it and exit immediately
|
|
687
|
+
throw error; // Will be caught by run() and handled by handleFatalError()
|
|
1892
688
|
}
|
|
1893
|
-
console.log(`${colors.bright}${'='.repeat(60)}${colors.reset}\n`);
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
// Generate and save installation report
|
|
1897
|
-
if (successfulInstalls.length > 0 || failedInstalls.length > 0) {
|
|
1898
|
-
await this.generateInstallationReport(
|
|
1899
|
-
successfulInstalls,
|
|
1900
|
-
failedInstalls,
|
|
1901
|
-
verificationResults,
|
|
1902
|
-
totalElapsedMs
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
// Clear installation state if all tools completed successfully
|
|
1907
|
-
if (failedInstalls.length === 0) {
|
|
1908
|
-
await installationEngine.getStateManager().clearState();
|
|
1909
|
-
console.log(`${colors.green}✓ Installation state cleared${colors.reset}`);
|
|
1910
|
-
} else {
|
|
1911
|
-
console.log(`\n${colors.yellow}Installation state preserved for resume capability${colors.reset}`);
|
|
1912
|
-
console.log(`${colors.yellow}Run the installer again to retry failed tools${colors.reset}`);
|
|
1913
689
|
}
|
|
1914
690
|
|
|
1915
|
-
|
|
1916
|
-
await this.collectInstallationTelemetry(
|
|
1917
|
-
failedInstalls.length === 0,
|
|
1918
|
-
this.selections.tools.length,
|
|
1919
|
-
failedInstalls.length,
|
|
1920
|
-
0, // warnings are tracked in verification
|
|
1921
|
-
totalElapsedMs
|
|
1922
|
-
);
|
|
691
|
+
console.log(`\n${colors.bright}${colors.green}Done!${colors.reset}`);
|
|
1923
692
|
}
|
|
1924
693
|
|
|
1925
694
|
/**
|
|
1926
695
|
* Draw progress bar for current tool installation
|
|
1927
696
|
* Updates in place without scrolling using ANSI escape codes
|
|
1928
697
|
*/
|
|
1929
|
-
drawProgressBar(filesCompleted, totalFiles, percentage, currentFile
|
|
1930
|
-
// Move cursor to beginning of line and clear it
|
|
1931
|
-
process.stdout.write('\x1b[2K\r');
|
|
1932
|
-
|
|
1933
|
-
// Calculate progress bar width (40 characters)
|
|
698
|
+
drawProgressBar(filesCompleted, totalFiles, percentage, currentFile) {
|
|
1934
699
|
const barWidth = 40;
|
|
1935
700
|
const filledWidth = Math.round((percentage / 100) * barWidth);
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1938
|
-
// Build progress bar
|
|
1939
|
-
const bar = '█'.repeat(filledWidth) + '░'.repeat(emptyWidth);
|
|
701
|
+
const bar = '#'.repeat(filledWidth) + '-'.repeat(barWidth - filledWidth);
|
|
1940
702
|
|
|
1941
|
-
//
|
|
1942
|
-
|
|
1943
|
-
let displayFileName = currentFile;
|
|
1944
|
-
if (displayFileName.length > maxFileNameLength) {
|
|
1945
|
-
displayFileName = '...' + displayFileName.slice(-(maxFileNameLength - 3));
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
// Display progress bar
|
|
1949
|
-
process.stdout.write(
|
|
1950
|
-
`[${colors.cyan}${bar}${colors.reset}] ${percentage}% (${filesCompleted}/${totalFiles} files)\n`
|
|
1951
|
-
);
|
|
1952
|
-
|
|
1953
|
-
// Display current file and stats
|
|
1954
|
-
process.stdout.write(
|
|
1955
|
-
`${colors.yellow}Copying:${colors.reset} ${displayFileName} | ` +
|
|
1956
|
-
`${bytesTransferred}/${totalBytes} | ` +
|
|
1957
|
-
`${speed}/s | ` +
|
|
1958
|
-
`Elapsed: ${elapsed} | ` +
|
|
1959
|
-
`ETA: ${eta}`
|
|
1960
|
-
);
|
|
1961
|
-
|
|
1962
|
-
// Move cursor up one line to overwrite on next update
|
|
1963
|
-
process.stdout.write('\x1b[1A');
|
|
703
|
+
// Single line, update in place
|
|
704
|
+
process.stdout.write(`\r[${bar}] ${percentage}% (${filesCompleted}/${totalFiles} files)`);
|
|
1964
705
|
}
|
|
1965
706
|
|
|
1966
|
-
/**
|
|
1967
|
-
* Draw overall progress across all tools
|
|
1968
|
-
*/
|
|
1969
|
-
drawOverallProgress(filesCompleted, totalFiles, percentage, currentTool, totalTools) {
|
|
1970
|
-
// This is called after the tool progress, so cursor is already up one line
|
|
1971
|
-
// Move down to write overall progress below the tool progress
|
|
1972
|
-
process.stdout.write('\x1b[2B'); // Move down 2 lines
|
|
1973
|
-
process.stdout.write('\x1b[2K\r'); // Clear line
|
|
1974
|
-
|
|
1975
|
-
// Display overall progress
|
|
1976
|
-
process.stdout.write(
|
|
1977
|
-
`${colors.bright}Overall:${colors.reset} Tool ${currentTool}/${totalTools} | ` +
|
|
1978
|
-
`${filesCompleted}/${totalFiles} files (${percentage}%)`
|
|
1979
|
-
);
|
|
1980
|
-
|
|
1981
|
-
// Move cursor back up
|
|
1982
|
-
process.stdout.write('\x1b[1A'); // Move up 1 line to be ready for next tool progress update
|
|
1983
|
-
}
|
|
1984
707
|
|
|
1985
708
|
askQuestion(prompt, defaultValue = '') {
|
|
1986
709
|
return new Promise(resolve => {
|
|
@@ -1997,79 +720,6 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
1997
720
|
* @param {object} verification - Verification result from InstallationEngine
|
|
1998
721
|
* @param {string} toolName - Display name of the tool
|
|
1999
722
|
*/
|
|
2000
|
-
displayVerificationReport(verification, toolName) {
|
|
2001
|
-
console.log(`\n${colors.bright}Verification Report: ${toolName}${colors.reset}`);
|
|
2002
|
-
console.log('─'.repeat(60));
|
|
2003
|
-
|
|
2004
|
-
if (verification.valid) {
|
|
2005
|
-
console.log(`${colors.green}✓ Installation verified successfully${colors.reset}`);
|
|
2006
|
-
} else {
|
|
2007
|
-
console.log(`${colors.red}✗ Verification failed${colors.reset}`);
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
// Display manifest location
|
|
2011
|
-
const manifestPath = path.join(verification.targetPath, 'manifest.json');
|
|
2012
|
-
console.log(`\n${colors.cyan}Manifest:${colors.reset} ${manifestPath}`);
|
|
2013
|
-
|
|
2014
|
-
// Display variant and version info
|
|
2015
|
-
if (verification.variant) {
|
|
2016
|
-
console.log(`${colors.cyan}Variant:${colors.reset} ${verification.variant}`);
|
|
2017
|
-
}
|
|
2018
|
-
if (verification.version) {
|
|
2019
|
-
console.log(`${colors.cyan}Version:${colors.reset} ${verification.version}`);
|
|
2020
|
-
}
|
|
2021
|
-
|
|
2022
|
-
// Display component counts
|
|
2023
|
-
console.log(`\n${colors.cyan}Components:${colors.reset}`);
|
|
2024
|
-
const components = verification.components;
|
|
2025
|
-
const agentCount = components.agents.found;
|
|
2026
|
-
const skillCount = components.skills.found;
|
|
2027
|
-
const resourceCount = components.resources.found;
|
|
2028
|
-
const hookCount = components.hooks.found;
|
|
2029
|
-
|
|
2030
|
-
const componentSummary = [];
|
|
2031
|
-
if (agentCount > 0) componentSummary.push(`${agentCount} agent${agentCount !== 1 ? 's' : ''}`);
|
|
2032
|
-
if (skillCount > 0) componentSummary.push(`${skillCount} skill${skillCount !== 1 ? 's' : ''}`);
|
|
2033
|
-
if (resourceCount > 0) componentSummary.push(`${resourceCount} resource${resourceCount !== 1 ? 's' : ''}`);
|
|
2034
|
-
if (hookCount > 0) componentSummary.push(`${hookCount} hook${hookCount !== 1 ? 's' : ''}`);
|
|
2035
|
-
|
|
2036
|
-
console.log(` ${componentSummary.join(', ')}`);
|
|
2037
|
-
|
|
2038
|
-
// Display issues if any
|
|
2039
|
-
if (verification.issues.length > 0) {
|
|
2040
|
-
console.log(`\n${colors.red}Issues:${colors.reset}`);
|
|
2041
|
-
for (const issue of verification.issues) {
|
|
2042
|
-
console.log(` ${colors.red}✗${colors.reset} ${issue.message}`);
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
// Display warnings if any
|
|
2047
|
-
if (verification.warnings.length > 0) {
|
|
2048
|
-
console.log(`\n${colors.yellow}Warnings:${colors.reset}`);
|
|
2049
|
-
for (const warning of verification.warnings) {
|
|
2050
|
-
console.log(` ${colors.yellow}⚠${colors.reset} ${warning.message}`);
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
|
|
2054
|
-
// Display next steps for successful installations
|
|
2055
|
-
if (verification.valid) {
|
|
2056
|
-
console.log(`\n${colors.bright}Next Steps:${colors.reset}`);
|
|
2057
|
-
console.log(` To use ${toolName}, navigate to: ${verification.targetPath}`);
|
|
2058
|
-
|
|
2059
|
-
// Provide tool-specific usage hints
|
|
2060
|
-
const toolId = verification.toolId;
|
|
2061
|
-
if (toolId === 'claude') {
|
|
2062
|
-
console.log(` ${colors.cyan}Quick start:${colors.reset} Run 'claude' to start using Claude Code`);
|
|
2063
|
-
} else if (toolId === 'opencode') {
|
|
2064
|
-
console.log(` ${colors.cyan}Quick start:${colors.reset} Run 'opencode' to start using Opencode`);
|
|
2065
|
-
} else if (toolId === 'ampcode') {
|
|
2066
|
-
console.log(` ${colors.cyan}Quick start:${colors.reset} Run 'ampcode' to start using Ampcode`);
|
|
2067
|
-
} else if (toolId === 'droid') {
|
|
2068
|
-
console.log(` ${colors.cyan}Quick start:${colors.reset} Run 'droid' to start using Droid`);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
|
|
2073
723
|
/**
|
|
2074
724
|
* Generate and save installation report to ~/.agentic-kit-install.log
|
|
2075
725
|
* Creates a detailed log of the installation session
|
|
@@ -2079,153 +729,12 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
2079
729
|
* @param {array} verificationResults - Array of verification result objects
|
|
2080
730
|
* @param {number} totalElapsedMs - Total elapsed time in milliseconds
|
|
2081
731
|
*/
|
|
2082
|
-
async generateInstallationReport(successfulInstalls, failedInstalls, verificationResults, totalElapsedMs) {
|
|
2083
|
-
const ReportTemplate = require('./report-template');
|
|
2084
|
-
const reportTemplate = new ReportTemplate();
|
|
2085
|
-
|
|
2086
|
-
// Prepare installation data for the report template
|
|
2087
|
-
const startTime = Date.now() - totalElapsedMs;
|
|
2088
|
-
const endTime = Date.now();
|
|
2089
|
-
|
|
2090
|
-
// Build tools array with complete information
|
|
2091
|
-
const tools = [];
|
|
2092
|
-
const allErrors = [];
|
|
2093
|
-
const allWarnings = [];
|
|
2094
|
-
|
|
2095
|
-
// Process successful installations
|
|
2096
|
-
for (let i = 0; i < successfulInstalls.length; i++) {
|
|
2097
|
-
const install = successfulInstalls[i];
|
|
2098
|
-
const verification = verificationResults[i];
|
|
2099
|
-
|
|
2100
|
-
// Get package size information
|
|
2101
|
-
let sizeBytes = 0;
|
|
2102
|
-
try {
|
|
2103
|
-
const sizeInfo = await this.packageManager.getPackageSize(install.toolId, this.selections.variant);
|
|
2104
|
-
sizeBytes = sizeInfo.bytes || 0;
|
|
2105
|
-
} catch (error) {
|
|
2106
|
-
// If we can't get size, estimate based on file count (rough estimate: 15KB per file)
|
|
2107
|
-
sizeBytes = install.fileCount * 15 * 1024;
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
// Get manifest path
|
|
2111
|
-
const manifestPath = path.join(install.path, 'manifest.json');
|
|
2112
|
-
|
|
2113
|
-
tools.push({
|
|
2114
|
-
toolId: install.toolId,
|
|
2115
|
-
path: install.path,
|
|
2116
|
-
filesInstalled: install.fileCount,
|
|
2117
|
-
sizeBytes: sizeBytes,
|
|
2118
|
-
components: verification && verification.components ? {
|
|
2119
|
-
agents: verification.components.agents.found,
|
|
2120
|
-
skills: verification.components.skills.found,
|
|
2121
|
-
resources: verification.components.resources.found,
|
|
2122
|
-
hooks: verification.components.hooks.found
|
|
2123
|
-
} : {},
|
|
2124
|
-
verified: verification ? verification.valid : false,
|
|
2125
|
-
verificationStatus: verification && verification.valid ? 'All components verified successfully' : 'Verification completed with issues',
|
|
2126
|
-
manifestPath: manifestPath
|
|
2127
|
-
});
|
|
2128
|
-
|
|
2129
|
-
// Collect warnings from verification
|
|
2130
|
-
if (verification && verification.warnings && verification.warnings.length > 0) {
|
|
2131
|
-
verification.warnings.forEach(warning => {
|
|
2132
|
-
allWarnings.push(`[${install.toolId}] ${warning.message}`);
|
|
2133
|
-
});
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
// Collect issues from verification as errors
|
|
2137
|
-
if (verification && verification.issues && verification.issues.length > 0) {
|
|
2138
|
-
verification.issues.forEach(issue => {
|
|
2139
|
-
allErrors.push(`[${install.toolId}] ${issue.message}`);
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
// Process failed installations
|
|
2145
|
-
for (const install of failedInstalls) {
|
|
2146
|
-
allErrors.push(`[${install.toolId}] Installation failed: ${install.error}`);
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
// Build installation data object
|
|
2150
|
-
const installationData = {
|
|
2151
|
-
variant: this.selections.variant,
|
|
2152
|
-
tools: tools,
|
|
2153
|
-
startTime: startTime,
|
|
2154
|
-
endTime: endTime,
|
|
2155
|
-
success: failedInstalls.length === 0,
|
|
2156
|
-
errors: allErrors,
|
|
2157
|
-
warnings: allWarnings
|
|
2158
|
-
};
|
|
2159
|
-
|
|
2160
|
-
// Generate and save report using ReportTemplate
|
|
2161
|
-
try {
|
|
2162
|
-
const reportPath = await reportTemplate.createAndSaveReport(installationData);
|
|
2163
|
-
console.log(`\n${colors.cyan}Installation report saved to:${colors.reset} ${reportPath}`);
|
|
2164
|
-
} catch (error) {
|
|
2165
|
-
console.warn(`${colors.yellow}Warning: Could not save installation report: ${error.message}${colors.reset}`);
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
|
|
2169
732
|
/**
|
|
2170
733
|
* Prompt user for telemetry consent
|
|
2171
734
|
* Only prompts if consent hasn't been set before and --no-telemetry flag not present
|
|
2172
735
|
*
|
|
2173
736
|
* @returns {Promise<void>}
|
|
2174
737
|
*/
|
|
2175
|
-
async promptTelemetryConsent() {
|
|
2176
|
-
// Skip if --no-telemetry flag is present
|
|
2177
|
-
if (this.cliArgs.noTelemetry) {
|
|
2178
|
-
return;
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
// Skip if in silent mode
|
|
2182
|
-
if (this.cliArgs.silent) {
|
|
2183
|
-
return;
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2186
|
-
const Telemetry = require('./telemetry');
|
|
2187
|
-
const telemetry = new Telemetry();
|
|
2188
|
-
|
|
2189
|
-
// Check if user has already made a decision
|
|
2190
|
-
const hasConsent = await telemetry.hasConsent();
|
|
2191
|
-
const hasOptedOut = await telemetry.hasOptedOut();
|
|
2192
|
-
|
|
2193
|
-
// Only prompt if user hasn't decided yet
|
|
2194
|
-
if (!hasConsent && !hasOptedOut) {
|
|
2195
|
-
console.log(`\n${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}`);
|
|
2196
|
-
console.log(`${colors.bright}Help Improve Agentic Kit${colors.reset}\n`);
|
|
2197
|
-
console.log('Would you like to share anonymous usage statistics to help improve agentic-kit?');
|
|
2198
|
-
console.log('\nData collected:');
|
|
2199
|
-
console.log(' • Package variant selected (lite/standard/pro)');
|
|
2200
|
-
console.log(' • Number of tools installed');
|
|
2201
|
-
console.log(' • Installation time and success status');
|
|
2202
|
-
console.log(' • Operating system type');
|
|
2203
|
-
console.log(' • Node.js version');
|
|
2204
|
-
console.log('\nData NOT collected:');
|
|
2205
|
-
console.log(' • File paths or directory locations');
|
|
2206
|
-
console.log(' • Personal information');
|
|
2207
|
-
console.log(' • Specific tool names');
|
|
2208
|
-
console.log(' • Any identifying information');
|
|
2209
|
-
console.log('\nYou can change this setting later or opt-out anytime.');
|
|
2210
|
-
console.log(`For details, see: ${colors.cyan}docs/PRIVACY.md${colors.reset}`);
|
|
2211
|
-
console.log(`${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}\n`);
|
|
2212
|
-
|
|
2213
|
-
const answer = await this.askQuestion(
|
|
2214
|
-
`${colors.bright}Share anonymous usage data? (y/N):${colors.reset} `,
|
|
2215
|
-
'N'
|
|
2216
|
-
);
|
|
2217
|
-
|
|
2218
|
-
const consent = answer.toLowerCase() === 'y';
|
|
2219
|
-
await telemetry.setConsent(consent);
|
|
2220
|
-
|
|
2221
|
-
if (consent) {
|
|
2222
|
-
console.log(`${colors.green}✓ Thank you! Anonymous usage statistics enabled${colors.reset}\n`);
|
|
2223
|
-
} else {
|
|
2224
|
-
console.log(`${colors.yellow}Usage statistics disabled${colors.reset}\n`);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
738
|
/**
|
|
2230
739
|
* Collect and send telemetry data for installation
|
|
2231
740
|
*
|
|
@@ -2236,28 +745,6 @@ ${colors.yellow}Press Enter to begin or Ctrl+C to exit${colors.reset}
|
|
|
2236
745
|
* @param {number} installationTime - Installation time in milliseconds
|
|
2237
746
|
* @returns {Promise<void>}
|
|
2238
747
|
*/
|
|
2239
|
-
async collectInstallationTelemetry(success, toolCount, errorCount, warningCount, installationTime) {
|
|
2240
|
-
// Skip if --no-telemetry flag is present
|
|
2241
|
-
if (this.cliArgs.noTelemetry) {
|
|
2242
|
-
return;
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
const Telemetry = require('./telemetry');
|
|
2246
|
-
const telemetry = new Telemetry();
|
|
2247
|
-
|
|
2248
|
-
try {
|
|
2249
|
-
await telemetry.collectInstallationStats({
|
|
2250
|
-
variant: this.selections.variant,
|
|
2251
|
-
toolCount: toolCount,
|
|
2252
|
-
installationTime: installationTime,
|
|
2253
|
-
success: success,
|
|
2254
|
-
errorCount: errorCount,
|
|
2255
|
-
warningCount: warningCount
|
|
2256
|
-
});
|
|
2257
|
-
} catch (error) {
|
|
2258
|
-
// Silently fail - don't interrupt user experience for telemetry issues
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
748
|
}
|
|
2262
749
|
|
|
2263
750
|
// Run installer if called directly
|