liteagents 2.4.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/CHANGELOG.md +441 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/cli.js +230 -0
- package/docs/.gitkeep +1 -0
- package/docs/CONTRIBUTING.md +739 -0
- package/docs/DUAL_PUBLISH_SUMMARY.md +177 -0
- package/docs/ERROR_HANDLING_IMPLEMENTATION.md +327 -0
- package/docs/GITHUB_PACKAGES.md +181 -0
- package/docs/GITHUB_SETUP.md +158 -0
- package/docs/INSTALLATION_DEMO.md +691 -0
- package/docs/INSTALLATION_LOCATIONS.md +299 -0
- package/docs/INSTALLER_GUIDE.md +1586 -0
- package/docs/INTEGRATION_ISSUES_9.1.md +341 -0
- package/docs/KNOWLEDGE_BASE.md +727 -0
- package/docs/MIGRATION.md +384 -0
- package/docs/PACKAGE_BASELINE.md +557 -0
- package/docs/PACKAGE_VALIDATION_REPORT.md +427 -0
- package/docs/PASS_INTEGRATION.md +307 -0
- package/docs/PASS_QUICK_START.md +150 -0
- package/docs/PRIVACY.md +203 -0
- package/docs/PUBLISHING.md +494 -0
- package/docs/QUICK-START.md +318 -0
- package/docs/RELEASE_NOTES_1.2.0.md +323 -0
- package/docs/SECURITY.md +317 -0
- package/docs/SILENT_MODE_GUIDE.md +526 -0
- package/docs/SKILLS_CONVERSION.md +154 -0
- package/docs/TESTING.md +582 -0
- package/docs/TEST_COVERAGE.md +347 -0
- package/docs/TROUBLESHOOTING.md +788 -0
- package/docs/UPDATED_VARIANT_CONFIGURATION.md +274 -0
- package/docs/VARIANT_CONFIGURATION.md +440 -0
- package/installer/cli.js +761 -0
- package/installer/installation-engine.js +1536 -0
- package/installer/package-manager.js +640 -0
- package/installer/path-manager.js +427 -0
- package/installer/report-template.js +298 -0
- package/installer/verification-system.js +274 -0
- package/package.json +83 -0
- package/packages/ampcode/AGENT.md +58 -0
- package/packages/ampcode/README.md +17 -0
- package/packages/ampcode/agents/1-create-prd.md +175 -0
- package/packages/ampcode/agents/2-generate-tasks.md +190 -0
- package/packages/ampcode/agents/3-process-task-list.md +225 -0
- package/packages/ampcode/agents/code-developer.md +198 -0
- package/packages/ampcode/agents/context-builder.md +142 -0
- package/packages/ampcode/agents/feature-planner.md +199 -0
- package/packages/ampcode/agents/market-researcher.md +89 -0
- package/packages/ampcode/agents/orchestrator.md +116 -0
- package/packages/ampcode/agents/quality-assurance.md +115 -0
- package/packages/ampcode/agents/system-architect.md +135 -0
- package/packages/ampcode/agents/ui-designer.md +184 -0
- package/packages/ampcode/commands/brainstorming.md +56 -0
- package/packages/ampcode/commands/code-review.md +107 -0
- package/packages/ampcode/commands/condition-based-waiting/example.ts +158 -0
- package/packages/ampcode/commands/condition-based-waiting.md +122 -0
- package/packages/ampcode/commands/debug.md +20 -0
- package/packages/ampcode/commands/docs-builder/templates.md +572 -0
- package/packages/ampcode/commands/docs-builder.md +106 -0
- package/packages/ampcode/commands/explain.md +18 -0
- package/packages/ampcode/commands/git-commit.md +14 -0
- package/packages/ampcode/commands/optimize.md +20 -0
- package/packages/ampcode/commands/refactor.md +21 -0
- package/packages/ampcode/commands/review.md +18 -0
- package/packages/ampcode/commands/root-cause-tracing/find-polluter.sh +63 -0
- package/packages/ampcode/commands/root-cause-tracing.md +176 -0
- package/packages/ampcode/commands/security.md +21 -0
- package/packages/ampcode/commands/ship.md +18 -0
- package/packages/ampcode/commands/skill-creator/scripts/init_skill.py +303 -0
- package/packages/ampcode/commands/skill-creator/scripts/package_skill.py +110 -0
- package/packages/ampcode/commands/skill-creator/scripts/quick_validate.py +65 -0
- package/packages/ampcode/commands/skill-creator.md +211 -0
- package/packages/ampcode/commands/stash.md +45 -0
- package/packages/ampcode/commands/systematic-debugging.md +297 -0
- package/packages/ampcode/commands/test-driven-development.md +390 -0
- package/packages/ampcode/commands/test-generate.md +18 -0
- package/packages/ampcode/commands/testing-anti-patterns.md +304 -0
- package/packages/ampcode/commands/verification-before-completion.md +152 -0
- package/packages/ampcode/settings.json +13 -0
- package/packages/ampcode/variants.json +8 -0
- package/packages/claude/CLAUDE.md +58 -0
- package/packages/claude/README.md +23 -0
- package/packages/claude/agents/1-create-prd.md +175 -0
- package/packages/claude/agents/2-generate-tasks.md +190 -0
- package/packages/claude/agents/3-process-task-list.md +225 -0
- package/packages/claude/agents/code-developer.md +198 -0
- package/packages/claude/agents/context-builder.md +142 -0
- package/packages/claude/agents/feature-planner.md +199 -0
- package/packages/claude/agents/market-researcher.md +89 -0
- package/packages/claude/agents/orchestrator.md +117 -0
- package/packages/claude/agents/quality-assurance.md +115 -0
- package/packages/claude/agents/system-architect.md +135 -0
- package/packages/claude/agents/ui-designer.md +184 -0
- package/packages/claude/commands/debug.md +20 -0
- package/packages/claude/commands/explain.md +18 -0
- package/packages/claude/commands/git-commit.md +14 -0
- package/packages/claude/commands/optimize.md +20 -0
- package/packages/claude/commands/refactor.md +21 -0
- package/packages/claude/commands/review.md +18 -0
- package/packages/claude/commands/security.md +21 -0
- package/packages/claude/commands/ship.md +18 -0
- package/packages/claude/commands/stash.md +45 -0
- package/packages/claude/commands/test-generate.md +18 -0
- package/packages/claude/skills/brainstorming/SKILL.md +56 -0
- package/packages/claude/skills/code-review/SKILL.md +107 -0
- package/packages/claude/skills/code-review/code-reviewer.md +146 -0
- package/packages/claude/skills/condition-based-waiting/SKILL.md +122 -0
- package/packages/claude/skills/condition-based-waiting/example.ts +158 -0
- package/packages/claude/skills/docs-builder/SKILL.md +106 -0
- package/packages/claude/skills/docs-builder/references/templates.md +572 -0
- package/packages/claude/skills/root-cause-tracing/SKILL.md +176 -0
- package/packages/claude/skills/root-cause-tracing/find-polluter.sh +63 -0
- package/packages/claude/skills/skill-creator/LICENSE.txt +202 -0
- package/packages/claude/skills/skill-creator/SKILL.md +211 -0
- package/packages/claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/packages/claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/packages/claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/packages/claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/packages/claude/skills/systematic-debugging/SKILL.md +296 -0
- package/packages/claude/skills/systematic-debugging/test-academic.md +14 -0
- package/packages/claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/packages/claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/packages/claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/packages/claude/skills/test-driven-development/SKILL.md +392 -0
- package/packages/claude/skills/testing-anti-patterns/SKILL.md +304 -0
- package/packages/claude/skills/verification-before-completion/SKILL.md +152 -0
- package/packages/claude/variants.json +9 -0
- package/packages/droid/AGENTS.md +52 -0
- package/packages/droid/README.md +17 -0
- package/packages/droid/change_settings.json +61 -0
- package/packages/droid/commands/brainstorming.md +56 -0
- package/packages/droid/commands/code-review.md +107 -0
- package/packages/droid/commands/condition-based-waiting/example.ts +158 -0
- package/packages/droid/commands/condition-based-waiting.md +122 -0
- package/packages/droid/commands/debug.md +20 -0
- package/packages/droid/commands/docs-builder/templates.md +572 -0
- package/packages/droid/commands/docs-builder.md +106 -0
- package/packages/droid/commands/explain.md +18 -0
- package/packages/droid/commands/git-commit.md +14 -0
- package/packages/droid/commands/optimize.md +20 -0
- package/packages/droid/commands/refactor.md +21 -0
- package/packages/droid/commands/review.md +18 -0
- package/packages/droid/commands/root-cause-tracing/find-polluter.sh +63 -0
- package/packages/droid/commands/root-cause-tracing.md +176 -0
- package/packages/droid/commands/security.md +21 -0
- package/packages/droid/commands/ship.md +18 -0
- package/packages/droid/commands/skill-creator/scripts/init_skill.py +303 -0
- package/packages/droid/commands/skill-creator/scripts/package_skill.py +110 -0
- package/packages/droid/commands/skill-creator/scripts/quick_validate.py +65 -0
- package/packages/droid/commands/skill-creator.md +211 -0
- package/packages/droid/commands/stash.md +45 -0
- package/packages/droid/commands/systematic-debugging.md +297 -0
- package/packages/droid/commands/test-driven-development.md +390 -0
- package/packages/droid/commands/test-generate.md +18 -0
- package/packages/droid/commands/testing-anti-patterns.md +304 -0
- package/packages/droid/commands/verification-before-completion.md +152 -0
- package/packages/droid/droids/1-create-prd.md +170 -0
- package/packages/droid/droids/2-generate-tasks.md +190 -0
- package/packages/droid/droids/3-process-task-list.md +225 -0
- package/packages/droid/droids/code-developer.md +198 -0
- package/packages/droid/droids/context-builder.md +142 -0
- package/packages/droid/droids/feature-planner.md +199 -0
- package/packages/droid/droids/market-researcher.md +89 -0
- package/packages/droid/droids/orchestrator.md +116 -0
- package/packages/droid/droids/quality-assurance.md +115 -0
- package/packages/droid/droids/system-architect.md +135 -0
- package/packages/droid/droids/ui-designer.md +184 -0
- package/packages/droid/variants.json +8 -0
- package/packages/opencode/AGENTS.md +52 -0
- package/packages/opencode/README.md +17 -0
- package/packages/opencode/agent/1-create-prd.md +179 -0
- package/packages/opencode/agent/2-generate-tasks.md +194 -0
- package/packages/opencode/agent/3-process-task-list.md +229 -0
- package/packages/opencode/agent/code-developer.md +202 -0
- package/packages/opencode/agent/context-builder.md +146 -0
- package/packages/opencode/agent/feature-planner.md +203 -0
- package/packages/opencode/agent/market-researcher.md +93 -0
- package/packages/opencode/agent/orchestrator.md +120 -0
- package/packages/opencode/agent/quality-assurance.md +119 -0
- package/packages/opencode/agent/system-architect.md +139 -0
- package/packages/opencode/agent/ui-designer.md +188 -0
- package/packages/opencode/command/brainstorming.md +56 -0
- package/packages/opencode/command/code-review.md +107 -0
- package/packages/opencode/command/condition-based-waiting/example.ts +158 -0
- package/packages/opencode/command/condition-based-waiting.md +122 -0
- package/packages/opencode/command/debug.md +20 -0
- package/packages/opencode/command/docs-builder/templates.md +572 -0
- package/packages/opencode/command/docs-builder.md +106 -0
- package/packages/opencode/command/explain.md +18 -0
- package/packages/opencode/command/git-commit.md +14 -0
- package/packages/opencode/command/optimize.md +20 -0
- package/packages/opencode/command/refactor.md +21 -0
- package/packages/opencode/command/review.md +18 -0
- package/packages/opencode/command/root-cause-tracing/find-polluter.sh +63 -0
- package/packages/opencode/command/root-cause-tracing.md +176 -0
- package/packages/opencode/command/security.md +21 -0
- package/packages/opencode/command/ship.md +18 -0
- package/packages/opencode/command/skill-creator/scripts/init_skill.py +303 -0
- package/packages/opencode/command/skill-creator/scripts/package_skill.py +110 -0
- package/packages/opencode/command/skill-creator/scripts/quick_validate.py +65 -0
- package/packages/opencode/command/skill-creator.md +211 -0
- package/packages/opencode/command/stash.md +45 -0
- package/packages/opencode/command/systematic-debugging.md +297 -0
- package/packages/opencode/command/test-driven-development.md +390 -0
- package/packages/opencode/command/test-generate.md +18 -0
- package/packages/opencode/command/testing-anti-patterns.md +304 -0
- package/packages/opencode/command/verification-before-completion.md +152 -0
- package/packages/opencode/opencode.jsonc +201 -0
- package/packages/opencode/variants.json +8 -0
- package/packages/subagentic-manual.md +349 -0
- package/postinstall.js +21 -0
- package/tools/ampcode/manifest-template.json +14 -0
- package/tools/claude/manifest-template.json +14 -0
- package/tools/droid/manifest-template.json +14 -0
- package/tools/opencode/manifest-template.json +14 -0
package/installer/cli.js
ADDED
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interactive CLI Installer for Agentic Kit
|
|
5
|
+
*
|
|
6
|
+
* Provides 4-step installation process:
|
|
7
|
+
* 1. Package variant selection (Lite/Standard/Pro)
|
|
8
|
+
* 2. Tool selection (Claude/Opencode/Ampcode/Droid)
|
|
9
|
+
* 3. Path configuration with confirmation
|
|
10
|
+
* 4. Installation summary and execution
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const readline = require('readline');
|
|
16
|
+
|
|
17
|
+
// ANSI color codes
|
|
18
|
+
const colors = {
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
bright: '\x1b[1m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
blue: '\x1b[34m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
cyan: '\x1b[36m',
|
|
25
|
+
magenta: '\x1b[35m',
|
|
26
|
+
red: '\x1b[31m'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class InteractiveInstaller {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.rl = readline.createInterface({
|
|
32
|
+
input: process.stdin,
|
|
33
|
+
output: process.stdout
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.selections = {
|
|
37
|
+
tools: [],
|
|
38
|
+
paths: {}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Initialize PackageManager for accessing package information
|
|
42
|
+
const PackageManager = require('./package-manager');
|
|
43
|
+
this.packageManager = new PackageManager();
|
|
44
|
+
|
|
45
|
+
this.tools = [
|
|
46
|
+
{
|
|
47
|
+
id: 'claude',
|
|
48
|
+
name: 'Claude Code',
|
|
49
|
+
path: '~/.claude',
|
|
50
|
+
description: 'AI-powered development assistant'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'opencode',
|
|
54
|
+
name: 'Opencode',
|
|
55
|
+
path: '~/.config/opencode',
|
|
56
|
+
description: 'CLI-optimized AI codegen tool'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'ampcode',
|
|
60
|
+
name: 'Ampcode',
|
|
61
|
+
path: '~/.config/amp',
|
|
62
|
+
description: 'Amplified AI development accelerator'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'droid',
|
|
66
|
+
name: 'Droid',
|
|
67
|
+
path: '~/.factory',
|
|
68
|
+
description: 'Android-focused AI development companion'
|
|
69
|
+
}
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
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');
|
|
77
|
+
console.log(' 3. Exit\n');
|
|
78
|
+
|
|
79
|
+
const choice = await this.askQuestion('Enter choice (1-3): ', '1');
|
|
80
|
+
if (choice === '3') {
|
|
81
|
+
console.log('\nGoodbye!\n');
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
return choice === '2' ? 'uninstall' : 'install';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async run() {
|
|
89
|
+
try {
|
|
90
|
+
this.showWelcome();
|
|
91
|
+
|
|
92
|
+
const action = await this.askInstallOrUninstall();
|
|
93
|
+
|
|
94
|
+
if (action === 'uninstall') {
|
|
95
|
+
await this.runUninstall();
|
|
96
|
+
} else {
|
|
97
|
+
await this.selectTools();
|
|
98
|
+
this.setDefaultPaths();
|
|
99
|
+
await this.install();
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
await this.handleFatalError(error);
|
|
103
|
+
} finally {
|
|
104
|
+
this.rl.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async runUninstall() {
|
|
109
|
+
console.log('\nDetecting installed tools...\n');
|
|
110
|
+
|
|
111
|
+
const os = require('os');
|
|
112
|
+
|
|
113
|
+
// Detect installed tools by checking if paths exist
|
|
114
|
+
const installedTools = [];
|
|
115
|
+
for (const tool of this.tools) {
|
|
116
|
+
const targetPath = tool.path.startsWith('~')
|
|
117
|
+
? path.join(os.homedir(), tool.path.slice(1))
|
|
118
|
+
: path.resolve(tool.path);
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(targetPath)) {
|
|
121
|
+
// Check for backups
|
|
122
|
+
const backups = this.findBackups(targetPath);
|
|
123
|
+
installedTools.push({
|
|
124
|
+
...tool,
|
|
125
|
+
expandedPath: targetPath,
|
|
126
|
+
hasBackup: backups.length > 0,
|
|
127
|
+
latestBackup: backups.sort().reverse()[0]
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (installedTools.length === 0) {
|
|
133
|
+
console.log('No tools are currently installed.\n');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Show installed tools
|
|
138
|
+
console.log('Installed tools:\n');
|
|
139
|
+
installedTools.forEach((tool, index) => {
|
|
140
|
+
const backupStatus = tool.hasBackup ? '(has backup)' : '(no backup)';
|
|
141
|
+
console.log(` ${index + 1}. ${tool.name} - ${tool.path} ${backupStatus}`);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Select tools to uninstall
|
|
145
|
+
console.log('\nSelect tools to uninstall:');
|
|
146
|
+
const selectedIndices = await this.selectToolsCheckbox(installedTools);
|
|
147
|
+
|
|
148
|
+
if (selectedIndices.length === 0) {
|
|
149
|
+
console.log('No tools selected. Cancelled.\n');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Uninstall each selected tool
|
|
154
|
+
for (const index of selectedIndices) {
|
|
155
|
+
const tool = installedTools[index];
|
|
156
|
+
console.log(`\nUninstalling ${tool.name}...`);
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
if (tool.hasBackup) {
|
|
160
|
+
// Backup current, then restore from previous backup
|
|
161
|
+
const currentBackup = `${tool.expandedPath}.uninstall-backup.${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
162
|
+
fs.renameSync(tool.expandedPath, currentBackup);
|
|
163
|
+
console.log(`Current installation backed up: ${tool.path}.uninstall-backup.${new Date().toISOString().replace(/[:.]/g, '-')}`);
|
|
164
|
+
|
|
165
|
+
// Restore from latest backup
|
|
166
|
+
fs.renameSync(tool.latestBackup, tool.expandedPath);
|
|
167
|
+
console.log(`✓ Restored previous installation from: ${path.basename(tool.latestBackup)}`);
|
|
168
|
+
} else {
|
|
169
|
+
// No backup, just delete
|
|
170
|
+
const rimraf = (dir) => {
|
|
171
|
+
if (fs.existsSync(dir)) {
|
|
172
|
+
fs.readdirSync(dir).forEach((file) => {
|
|
173
|
+
const curPath = path.join(dir, file);
|
|
174
|
+
if (fs.lstatSync(curPath).isDirectory()) {
|
|
175
|
+
rimraf(curPath);
|
|
176
|
+
} else {
|
|
177
|
+
fs.unlinkSync(curPath);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
fs.rmdirSync(dir);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
rimraf(tool.expandedPath);
|
|
184
|
+
console.log(`✓ Deleted installation from: ${tool.path}`);
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.log(`✗ Failed to uninstall ${tool.name}: ${error.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log('\nDone!');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
findBackups(targetPath) {
|
|
195
|
+
const dir = path.dirname(targetPath);
|
|
196
|
+
const base = path.basename(targetPath);
|
|
197
|
+
const pattern = `${base}.backup.`;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
return fs.readdirSync(dir)
|
|
201
|
+
.filter(f => f.startsWith(pattern))
|
|
202
|
+
.map(f => path.join(dir, f));
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async selectToolsCheckbox(tools) {
|
|
209
|
+
const selected = new Set();
|
|
210
|
+
let currentIndex = 0;
|
|
211
|
+
|
|
212
|
+
const renderList = () => {
|
|
213
|
+
// Move cursor up and clear each line individually
|
|
214
|
+
for (let i = 0; i <= tools.length; i++) {
|
|
215
|
+
process.stdout.write('\x1b[1A'); // Move up one line
|
|
216
|
+
process.stdout.write('\r'); // Carriage return to start of line
|
|
217
|
+
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
tools.forEach((tool, index) => {
|
|
221
|
+
const isSelected = selected.has(index);
|
|
222
|
+
const isCurrent = index === currentIndex;
|
|
223
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
224
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
225
|
+
const paddedName = tool.name.padEnd(20);
|
|
226
|
+
|
|
227
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description || ''}`);
|
|
228
|
+
});
|
|
229
|
+
console.log(''); // Empty line at bottom
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Initial render
|
|
233
|
+
tools.forEach((tool, index) => {
|
|
234
|
+
const isSelected = selected.has(index);
|
|
235
|
+
const isCurrent = index === currentIndex;
|
|
236
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
237
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
238
|
+
const paddedName = tool.name.padEnd(20);
|
|
239
|
+
|
|
240
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description || ''}`);
|
|
241
|
+
});
|
|
242
|
+
console.log('');
|
|
243
|
+
|
|
244
|
+
return new Promise((resolve) => {
|
|
245
|
+
const stdin = process.stdin;
|
|
246
|
+
stdin.setRawMode(true);
|
|
247
|
+
stdin.resume();
|
|
248
|
+
stdin.setEncoding('utf8');
|
|
249
|
+
|
|
250
|
+
const onKeypress = (key) => {
|
|
251
|
+
if (key === '\u0003' || key === '\u001b') { // Ctrl+C or ESC
|
|
252
|
+
stdin.setRawMode(false);
|
|
253
|
+
stdin.pause();
|
|
254
|
+
process.exit(0);
|
|
255
|
+
} else if (key === '\r' || key === '\n') { // Enter
|
|
256
|
+
stdin.setRawMode(false);
|
|
257
|
+
stdin.removeListener('data', onKeypress);
|
|
258
|
+
resolve(Array.from(selected));
|
|
259
|
+
} else if (key === ' ') { // Space - toggle
|
|
260
|
+
if (selected.has(currentIndex)) {
|
|
261
|
+
selected.delete(currentIndex);
|
|
262
|
+
} else {
|
|
263
|
+
selected.add(currentIndex);
|
|
264
|
+
}
|
|
265
|
+
renderList();
|
|
266
|
+
} else if (key === '\u001b[A') { // Up arrow
|
|
267
|
+
currentIndex = currentIndex > 0 ? currentIndex - 1 : tools.length - 1;
|
|
268
|
+
renderList();
|
|
269
|
+
} else if (key === '\u001b[B') { // Down arrow
|
|
270
|
+
currentIndex = currentIndex < tools.length - 1 ? currentIndex + 1 : 0;
|
|
271
|
+
renderList();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
stdin.on('data', onKeypress);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Run upgrade/downgrade command for a specific tool
|
|
281
|
+
* Changes the variant of an installed tool
|
|
282
|
+
*
|
|
283
|
+
* @param {string} toolId - Tool to upgrade (claude, opencode, ampcode, droid)
|
|
284
|
+
* @param {string} newVariant - Target variant (lite, standard, pro)
|
|
285
|
+
*/
|
|
286
|
+
/**
|
|
287
|
+
* Load configuration from JSON file
|
|
288
|
+
* Used with --config flag
|
|
289
|
+
*
|
|
290
|
+
* @param {string} configPath - Path to configuration file
|
|
291
|
+
*/
|
|
292
|
+
/**
|
|
293
|
+
* Run non-interactive installation
|
|
294
|
+
* Uses command-line arguments instead of prompts
|
|
295
|
+
*/
|
|
296
|
+
/**
|
|
297
|
+
* Handle fatal errors with detailed error messages and actionable advice
|
|
298
|
+
* Categorizes errors and provides specific guidance for each type
|
|
299
|
+
*
|
|
300
|
+
* @param {Error} error - The error that occurred
|
|
301
|
+
*/
|
|
302
|
+
async handleFatalError(error) {
|
|
303
|
+
console.log(''); // Add spacing
|
|
304
|
+
|
|
305
|
+
// Categorize the error and provide appropriate guidance
|
|
306
|
+
const errorInfo = this.categorizeError(error);
|
|
307
|
+
|
|
308
|
+
console.log(`${colors.red}Error: ${error.message}${colors.reset}`);
|
|
309
|
+
|
|
310
|
+
if (errorInfo.advice.length > 0 && errorInfo.advice.length <= 3) {
|
|
311
|
+
errorInfo.advice.forEach(advice => {
|
|
312
|
+
console.log(` - ${advice}`);
|
|
313
|
+
});
|
|
314
|
+
console.log('');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Exit with error code
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Categorize errors and provide actionable advice
|
|
323
|
+
*
|
|
324
|
+
* @param {Error} error - The error to categorize
|
|
325
|
+
* @returns {object} Object with type, advice array, and technical details
|
|
326
|
+
*/
|
|
327
|
+
categorizeError(error) {
|
|
328
|
+
const message = error.message.toLowerCase();
|
|
329
|
+
const code = error.code;
|
|
330
|
+
|
|
331
|
+
// Permission errors
|
|
332
|
+
if (code === 'EACCES' || code === 'EPERM' || message.includes('permission denied')) {
|
|
333
|
+
return {
|
|
334
|
+
type: 'Permission Error',
|
|
335
|
+
advice: [
|
|
336
|
+
'Try: sudo node installer/cli.js',
|
|
337
|
+
'Or choose a different installation directory'
|
|
338
|
+
],
|
|
339
|
+
technicalDetails: `Error code: ${code || 'EACCES'}`
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Disk space errors
|
|
344
|
+
if (code === 'ENOSPC' || message.includes('no space') || message.includes('disk space')) {
|
|
345
|
+
return {
|
|
346
|
+
type: 'Disk Space Error',
|
|
347
|
+
advice: [
|
|
348
|
+
'Free up disk space',
|
|
349
|
+
'Check: df -h'
|
|
350
|
+
],
|
|
351
|
+
technicalDetails: 'Installation requires approximately 10MB per tool'
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Network errors (if applicable)
|
|
356
|
+
if (code === 'ENOTFOUND' || code === 'ETIMEDOUT' || message.includes('network') || message.includes('connection')) {
|
|
357
|
+
return {
|
|
358
|
+
type: 'Network Error',
|
|
359
|
+
advice: [
|
|
360
|
+
'Check internet connection',
|
|
361
|
+
'Try again later'
|
|
362
|
+
],
|
|
363
|
+
technicalDetails: `Network error code: ${code || 'UNKNOWN'}`
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// File not found / missing package errors
|
|
368
|
+
if (code === 'ENOENT' || message.includes('no such file') || message.includes('not found') || message.includes('invalid package')) {
|
|
369
|
+
return {
|
|
370
|
+
type: 'Missing Package Error',
|
|
371
|
+
advice: [
|
|
372
|
+
'Run: npm install -g agentflow',
|
|
373
|
+
'Check: packages directory exists'
|
|
374
|
+
],
|
|
375
|
+
technicalDetails: `Missing file or package validation failed`
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Path validation errors (check before invalid input errors)
|
|
380
|
+
if (message.includes('path') && (message.includes('invalid') || message.includes('absolute') || message.includes('must be absolute'))) {
|
|
381
|
+
return {
|
|
382
|
+
type: 'Path Validation Error',
|
|
383
|
+
advice: [
|
|
384
|
+
'Path must be absolute (starts with / or ~)',
|
|
385
|
+
'Check parent directory exists'
|
|
386
|
+
],
|
|
387
|
+
technicalDetails: 'Paths must be absolute and writable'
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Invalid input errors
|
|
392
|
+
if (message.includes('invalid') || message.includes('must be') || message.includes('required')) {
|
|
393
|
+
return {
|
|
394
|
+
type: 'Invalid Input Error',
|
|
395
|
+
advice: [
|
|
396
|
+
'Check your selections',
|
|
397
|
+
'Restart installer and try again'
|
|
398
|
+
],
|
|
399
|
+
technicalDetails: error.message
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Installation failures (general)
|
|
404
|
+
if (message.includes('install') || message.includes('copy') || message.includes('failed')) {
|
|
405
|
+
return {
|
|
406
|
+
type: 'Installation Error',
|
|
407
|
+
advice: [
|
|
408
|
+
'Check disk space: df -h',
|
|
409
|
+
'Verify write permissions',
|
|
410
|
+
'Try different location'
|
|
411
|
+
],
|
|
412
|
+
technicalDetails: 'Installation process encountered an error during file operations'
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Generic error
|
|
417
|
+
return {
|
|
418
|
+
type: 'Unknown Error',
|
|
419
|
+
advice: [
|
|
420
|
+
'Try running installer again',
|
|
421
|
+
'Report at: https://github.com/amrhas82/agentflow/issues'
|
|
422
|
+
],
|
|
423
|
+
technicalDetails: error.stack ? error.stack.split('\n')[1] : 'No additional details'
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Offer recovery options when an installation fails
|
|
429
|
+
* Allows user to continue with remaining tools or cancel
|
|
430
|
+
*
|
|
431
|
+
* @param {string} failedTool - Name of the tool that failed
|
|
432
|
+
* @param {number} currentIndex - Current tool index (1-based)
|
|
433
|
+
* @param {number} totalTools - Total number of tools
|
|
434
|
+
* @returns {Promise<boolean>} True to continue, false to cancel
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* Prompt user to resume interrupted installation
|
|
438
|
+
* Shows summary of previous installation progress
|
|
439
|
+
*
|
|
440
|
+
* @param {InstallationEngine} installationEngine - Installation engine instance
|
|
441
|
+
* @returns {Promise<boolean>} - True if user wants to resume, false otherwise
|
|
442
|
+
*/
|
|
443
|
+
/**
|
|
444
|
+
* Resume interrupted installation
|
|
445
|
+
* Uses saved state to continue from where it left off
|
|
446
|
+
*
|
|
447
|
+
* @param {InstallationEngine} installationEngine - Installation engine instance with loaded state
|
|
448
|
+
*/
|
|
449
|
+
showWelcome() {
|
|
450
|
+
console.clear();
|
|
451
|
+
console.log(`
|
|
452
|
+
${colors.bright}${colors.cyan} █████╗ ██████╗ ███████╗███╗ ██╗████████╗██╗ ██████╗ ██╗ ██╗██╗████████╗${colors.reset}
|
|
453
|
+
${colors.bright}${colors.cyan}██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██║██╔════╝ ██║ ██╔╝██║╚══██╔══╝${colors.reset}
|
|
454
|
+
${colors.bright}${colors.cyan}███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║██║ █████╔╝ ██║ ██║${colors.reset}
|
|
455
|
+
${colors.bright}${colors.cyan}██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██║ ██║${colors.reset}
|
|
456
|
+
${colors.bright}${colors.cyan}██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ██║╚██████╗ ██║ ██╗██║ ██║${colors.reset}
|
|
457
|
+
${colors.bright}${colors.cyan}╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝${colors.reset}
|
|
458
|
+
|
|
459
|
+
${colors.bright}v2.4.0 | 11 agents + 20 commands per tool${colors.reset}
|
|
460
|
+
`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async selectTools() {
|
|
464
|
+
console.log(`\n${colors.bright}Select tools to install${colors.reset}\n`);
|
|
465
|
+
console.log(`${colors.cyan}(↑↓ navigate, space=toggle, a=all, enter=confirm)${colors.reset}\n`);
|
|
466
|
+
|
|
467
|
+
// Interactive checkbox selection
|
|
468
|
+
const selected = new Set(['claude']); // Default to claude
|
|
469
|
+
let currentIndex = 0;
|
|
470
|
+
|
|
471
|
+
const renderList = () => {
|
|
472
|
+
// Move cursor up and clear each line individually
|
|
473
|
+
for (let i = 0; i <= this.tools.length; i++) {
|
|
474
|
+
process.stdout.write('\x1b[1A'); // Move up one line
|
|
475
|
+
process.stdout.write('\r'); // Carriage return to start of line
|
|
476
|
+
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
this.tools.forEach((tool, index) => {
|
|
480
|
+
const isSelected = selected.has(tool.id);
|
|
481
|
+
const isCurrent = index === currentIndex;
|
|
482
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
483
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
484
|
+
const paddedName = tool.name.padEnd(20);
|
|
485
|
+
|
|
486
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description}`);
|
|
487
|
+
});
|
|
488
|
+
console.log(''); // Empty line at bottom
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Initial render
|
|
492
|
+
this.tools.forEach((tool, index) => {
|
|
493
|
+
const isSelected = selected.has(tool.id);
|
|
494
|
+
const isCurrent = index === currentIndex;
|
|
495
|
+
const checkbox = isSelected ? '[x]' : '[ ]';
|
|
496
|
+
const pointer = isCurrent ? '»' : ' ';
|
|
497
|
+
const paddedName = tool.name.padEnd(20);
|
|
498
|
+
|
|
499
|
+
console.log(`${pointer} ${checkbox} ${paddedName} - ${tool.description}`);
|
|
500
|
+
});
|
|
501
|
+
console.log('');
|
|
502
|
+
|
|
503
|
+
return new Promise((resolve) => {
|
|
504
|
+
const stdin = process.stdin;
|
|
505
|
+
stdin.setRawMode(true);
|
|
506
|
+
stdin.resume();
|
|
507
|
+
stdin.setEncoding('utf8');
|
|
508
|
+
|
|
509
|
+
const onKeypress = (key) => {
|
|
510
|
+
if (key === '\u0003' || key === '\u001b') { // Ctrl+C or ESC
|
|
511
|
+
stdin.setRawMode(false);
|
|
512
|
+
stdin.pause();
|
|
513
|
+
process.exit(0);
|
|
514
|
+
} else if (key === '\r' || key === '\n') { // Enter
|
|
515
|
+
stdin.setRawMode(false);
|
|
516
|
+
stdin.removeListener('data', onKeypress);
|
|
517
|
+
|
|
518
|
+
this.selections.tools = Array.from(selected);
|
|
519
|
+
|
|
520
|
+
// Show selection summary
|
|
521
|
+
console.log(`${colors.green}Installing ${this.selections.tools.length} tool(s)${colors.reset}\n`);
|
|
522
|
+
|
|
523
|
+
resolve();
|
|
524
|
+
} else if (key === ' ') { // Space - toggle
|
|
525
|
+
const toolId = this.tools[currentIndex].id;
|
|
526
|
+
if (selected.has(toolId)) {
|
|
527
|
+
selected.delete(toolId);
|
|
528
|
+
} else {
|
|
529
|
+
selected.add(toolId);
|
|
530
|
+
}
|
|
531
|
+
renderList();
|
|
532
|
+
} else if (key === 'a' || key === 'A') { // Select all
|
|
533
|
+
this.tools.forEach(tool => selected.add(tool.id));
|
|
534
|
+
renderList();
|
|
535
|
+
} else if (key === '\u001b[A') { // Up arrow
|
|
536
|
+
currentIndex = currentIndex > 0 ? currentIndex - 1 : this.tools.length - 1;
|
|
537
|
+
renderList();
|
|
538
|
+
} else if (key === '\u001b[B') { // Down arrow
|
|
539
|
+
currentIndex = currentIndex < this.tools.length - 1 ? currentIndex + 1 : 0;
|
|
540
|
+
renderList();
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
stdin.on('data', onKeypress);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async setDefaultPaths() {
|
|
549
|
+
// Automatically use default paths for all selected tools
|
|
550
|
+
for (const toolId of this.selections.tools) {
|
|
551
|
+
const tool = this.tools.find(t => t.id === toolId);
|
|
552
|
+
this.selections.paths[toolId] = tool.path;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Format bytes to human-readable size (helper method for summary display)
|
|
558
|
+
* @param {number} bytes - Size in bytes
|
|
559
|
+
* @returns {string} Formatted size string (e.g., "8.39 MB")
|
|
560
|
+
*/
|
|
561
|
+
formatBytes(bytes) {
|
|
562
|
+
if (bytes === 0) return '0 Bytes';
|
|
563
|
+
|
|
564
|
+
const k = 1024;
|
|
565
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
566
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
567
|
+
|
|
568
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Get PackageManager instance (for testing and internal use)
|
|
573
|
+
* @returns {PackageManager} Package manager instance
|
|
574
|
+
*/
|
|
575
|
+
getPackageManager() {
|
|
576
|
+
return this.packageManager;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Perform pre-installation checks to catch potential issues early
|
|
581
|
+
* Validates packages, paths, permissions, and disk space
|
|
582
|
+
*
|
|
583
|
+
* @returns {Promise<object>} Object with success flag, errors array, and warnings array
|
|
584
|
+
*/
|
|
585
|
+
async install() {
|
|
586
|
+
// Initialize InstallationEngine
|
|
587
|
+
const PathManager = require('./path-manager');
|
|
588
|
+
const InstallationEngine = require('./installation-engine');
|
|
589
|
+
const os = require('os');
|
|
590
|
+
|
|
591
|
+
const pathManager = new PathManager();
|
|
592
|
+
const installationEngine = new InstallationEngine(pathManager, this.packageManager);
|
|
593
|
+
|
|
594
|
+
// Install each selected tool
|
|
595
|
+
for (let i = 0; i < this.selections.tools.length; i++) {
|
|
596
|
+
const toolId = this.selections.tools[i];
|
|
597
|
+
const tool = this.tools.find(t => t.id === toolId);
|
|
598
|
+
const targetPath = this.selections.paths[toolId];
|
|
599
|
+
|
|
600
|
+
console.log(`\n${colors.bright}${colors.cyan}Installing ${tool.name}...${colors.reset}`);
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
const expandedPath = targetPath.startsWith('~')
|
|
604
|
+
? path.join(os.homedir(), targetPath.slice(1))
|
|
605
|
+
: path.resolve(targetPath);
|
|
606
|
+
|
|
607
|
+
// Start spinner
|
|
608
|
+
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
609
|
+
let spinnerIndex = 0;
|
|
610
|
+
const spinner = setInterval(() => {
|
|
611
|
+
process.stdout.write(`\r ${colors.cyan}${spinnerFrames[spinnerIndex]}${colors.reset} Installing...`);
|
|
612
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
613
|
+
}, 80);
|
|
614
|
+
|
|
615
|
+
// Install
|
|
616
|
+
await installationEngine.installPackage(
|
|
617
|
+
toolId,
|
|
618
|
+
'pro', // Always pro variant
|
|
619
|
+
targetPath,
|
|
620
|
+
null // No progress callback
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
// Stop spinner
|
|
624
|
+
clearInterval(spinner);
|
|
625
|
+
process.stdout.write('\r\x1b[2K'); // Clear spinner line
|
|
626
|
+
|
|
627
|
+
// Get backup info from installation engine
|
|
628
|
+
const lastBackup = installationEngine.backupLog.length > 0
|
|
629
|
+
? installationEngine.backupLog[installationEngine.backupLog.length - 1]
|
|
630
|
+
: null;
|
|
631
|
+
|
|
632
|
+
// Count actual installed components by checking directories
|
|
633
|
+
const countItems = (dir) => {
|
|
634
|
+
try {
|
|
635
|
+
if (fs.existsSync(dir)) {
|
|
636
|
+
return fs.readdirSync(dir).filter(f => !f.startsWith('.')).length;
|
|
637
|
+
}
|
|
638
|
+
} catch (e) {}
|
|
639
|
+
return 0;
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// Count only .md files in a directory
|
|
643
|
+
const countMdFiles = (dir) => {
|
|
644
|
+
try {
|
|
645
|
+
if (fs.existsSync(dir)) {
|
|
646
|
+
return fs.readdirSync(dir).filter(f => f.endsWith('.md')).length;
|
|
647
|
+
}
|
|
648
|
+
} catch (e) {}
|
|
649
|
+
return 0;
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
// Check for agents (agents, agent, or droids)
|
|
653
|
+
let agentsCount = countItems(path.join(expandedPath, 'agents')) ||
|
|
654
|
+
countItems(path.join(expandedPath, 'agent')) ||
|
|
655
|
+
countItems(path.join(expandedPath, 'droids'));
|
|
656
|
+
let agentDir = 'agents';
|
|
657
|
+
if (fs.existsSync(path.join(expandedPath, 'agents'))) {
|
|
658
|
+
agentDir = 'agents';
|
|
659
|
+
} else if (fs.existsSync(path.join(expandedPath, 'agent'))) {
|
|
660
|
+
agentDir = 'agent';
|
|
661
|
+
} else if (fs.existsSync(path.join(expandedPath, 'droids'))) {
|
|
662
|
+
agentDir = 'droids';
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Check for skills (count directories)
|
|
666
|
+
const skillsCount = countItems(path.join(expandedPath, 'skills'));
|
|
667
|
+
|
|
668
|
+
// Check for commands (count only .md files, both singular and plural)
|
|
669
|
+
const commandsCount = countMdFiles(path.join(expandedPath, 'commands')) ||
|
|
670
|
+
countMdFiles(path.join(expandedPath, 'command'));
|
|
671
|
+
const cmdDir = fs.existsSync(path.join(expandedPath, 'commands')) ? 'commands' : 'command';
|
|
672
|
+
|
|
673
|
+
// Show backup info if it was created
|
|
674
|
+
if (lastBackup && lastBackup.original === expandedPath) {
|
|
675
|
+
const backupShortPath = lastBackup.backup.replace(os.homedir(), '~');
|
|
676
|
+
console.log(` ${colors.yellow}Backup:${colors.reset} ${backupShortPath}`);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Show components with color
|
|
680
|
+
if (agentsCount > 0) {
|
|
681
|
+
console.log(` ${colors.green}✓${colors.reset} ${agentsCount} agents → ${targetPath}/${agentDir}`);
|
|
682
|
+
}
|
|
683
|
+
if (skillsCount > 0) {
|
|
684
|
+
console.log(` ${colors.green}✓${colors.reset} ${skillsCount} skills → ${targetPath}/skills`);
|
|
685
|
+
}
|
|
686
|
+
if (commandsCount > 0) {
|
|
687
|
+
console.log(` ${colors.green}✓${colors.reset} ${commandsCount} commands → ${targetPath}/${cmdDir}`);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
} catch (error) {
|
|
691
|
+
// On ANY error, show it and exit immediately
|
|
692
|
+
throw error; // Will be caught by run() and handled by handleFatalError()
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
console.log(`\n${colors.bright}${colors.green}Done!${colors.reset}`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Draw progress bar for current tool installation
|
|
701
|
+
* Updates in place without scrolling using ANSI escape codes
|
|
702
|
+
*/
|
|
703
|
+
drawProgressBar(filesCompleted, totalFiles, percentage, currentFile) {
|
|
704
|
+
const barWidth = 40;
|
|
705
|
+
const filledWidth = Math.round((percentage / 100) * barWidth);
|
|
706
|
+
const bar = '#'.repeat(filledWidth) + '-'.repeat(barWidth - filledWidth);
|
|
707
|
+
|
|
708
|
+
// Single line, update in place
|
|
709
|
+
process.stdout.write(`\r[${bar}] ${percentage}% (${filesCompleted}/${totalFiles} files)`);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
askQuestion(prompt, defaultValue = '') {
|
|
714
|
+
return new Promise(resolve => {
|
|
715
|
+
this.rl.question(prompt, (answer) => {
|
|
716
|
+
resolve(answer.trim() || defaultValue);
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Display verification report for a single tool
|
|
723
|
+
* Shows verification status, component counts, and any issues/warnings
|
|
724
|
+
*
|
|
725
|
+
* @param {object} verification - Verification result from InstallationEngine
|
|
726
|
+
* @param {string} toolName - Display name of the tool
|
|
727
|
+
*/
|
|
728
|
+
/**
|
|
729
|
+
* Generate and save installation report to ~/.agentflow-install.log
|
|
730
|
+
* Creates a detailed log of the installation session
|
|
731
|
+
*
|
|
732
|
+
* @param {array} successfulInstalls - Array of successful installation objects
|
|
733
|
+
* @param {array} failedInstalls - Array of failed installation objects
|
|
734
|
+
* @param {array} verificationResults - Array of verification result objects
|
|
735
|
+
* @param {number} totalElapsedMs - Total elapsed time in milliseconds
|
|
736
|
+
*/
|
|
737
|
+
/**
|
|
738
|
+
* Prompt user for telemetry consent
|
|
739
|
+
* Only prompts if consent hasn't been set before and --no-telemetry flag not present
|
|
740
|
+
*
|
|
741
|
+
* @returns {Promise<void>}
|
|
742
|
+
*/
|
|
743
|
+
/**
|
|
744
|
+
* Collect and send telemetry data for installation
|
|
745
|
+
*
|
|
746
|
+
* @param {boolean} success - Installation success status
|
|
747
|
+
* @param {number} toolCount - Number of tools installed
|
|
748
|
+
* @param {number} errorCount - Number of errors encountered
|
|
749
|
+
* @param {number} warningCount - Number of warnings encountered
|
|
750
|
+
* @param {number} installationTime - Installation time in milliseconds
|
|
751
|
+
* @returns {Promise<void>}
|
|
752
|
+
*/
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Run installer if called directly
|
|
756
|
+
if (require.main === module) {
|
|
757
|
+
const installer = new InteractiveInstaller();
|
|
758
|
+
installer.run().catch(console.error);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
module.exports = InteractiveInstaller;
|