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.
Files changed (215) hide show
  1. package/CHANGELOG.md +441 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/cli.js +230 -0
  5. package/docs/.gitkeep +1 -0
  6. package/docs/CONTRIBUTING.md +739 -0
  7. package/docs/DUAL_PUBLISH_SUMMARY.md +177 -0
  8. package/docs/ERROR_HANDLING_IMPLEMENTATION.md +327 -0
  9. package/docs/GITHUB_PACKAGES.md +181 -0
  10. package/docs/GITHUB_SETUP.md +158 -0
  11. package/docs/INSTALLATION_DEMO.md +691 -0
  12. package/docs/INSTALLATION_LOCATIONS.md +299 -0
  13. package/docs/INSTALLER_GUIDE.md +1586 -0
  14. package/docs/INTEGRATION_ISSUES_9.1.md +341 -0
  15. package/docs/KNOWLEDGE_BASE.md +727 -0
  16. package/docs/MIGRATION.md +384 -0
  17. package/docs/PACKAGE_BASELINE.md +557 -0
  18. package/docs/PACKAGE_VALIDATION_REPORT.md +427 -0
  19. package/docs/PASS_INTEGRATION.md +307 -0
  20. package/docs/PASS_QUICK_START.md +150 -0
  21. package/docs/PRIVACY.md +203 -0
  22. package/docs/PUBLISHING.md +494 -0
  23. package/docs/QUICK-START.md +318 -0
  24. package/docs/RELEASE_NOTES_1.2.0.md +323 -0
  25. package/docs/SECURITY.md +317 -0
  26. package/docs/SILENT_MODE_GUIDE.md +526 -0
  27. package/docs/SKILLS_CONVERSION.md +154 -0
  28. package/docs/TESTING.md +582 -0
  29. package/docs/TEST_COVERAGE.md +347 -0
  30. package/docs/TROUBLESHOOTING.md +788 -0
  31. package/docs/UPDATED_VARIANT_CONFIGURATION.md +274 -0
  32. package/docs/VARIANT_CONFIGURATION.md +440 -0
  33. package/installer/cli.js +761 -0
  34. package/installer/installation-engine.js +1536 -0
  35. package/installer/package-manager.js +640 -0
  36. package/installer/path-manager.js +427 -0
  37. package/installer/report-template.js +298 -0
  38. package/installer/verification-system.js +274 -0
  39. package/package.json +83 -0
  40. package/packages/ampcode/AGENT.md +58 -0
  41. package/packages/ampcode/README.md +17 -0
  42. package/packages/ampcode/agents/1-create-prd.md +175 -0
  43. package/packages/ampcode/agents/2-generate-tasks.md +190 -0
  44. package/packages/ampcode/agents/3-process-task-list.md +225 -0
  45. package/packages/ampcode/agents/code-developer.md +198 -0
  46. package/packages/ampcode/agents/context-builder.md +142 -0
  47. package/packages/ampcode/agents/feature-planner.md +199 -0
  48. package/packages/ampcode/agents/market-researcher.md +89 -0
  49. package/packages/ampcode/agents/orchestrator.md +116 -0
  50. package/packages/ampcode/agents/quality-assurance.md +115 -0
  51. package/packages/ampcode/agents/system-architect.md +135 -0
  52. package/packages/ampcode/agents/ui-designer.md +184 -0
  53. package/packages/ampcode/commands/brainstorming.md +56 -0
  54. package/packages/ampcode/commands/code-review.md +107 -0
  55. package/packages/ampcode/commands/condition-based-waiting/example.ts +158 -0
  56. package/packages/ampcode/commands/condition-based-waiting.md +122 -0
  57. package/packages/ampcode/commands/debug.md +20 -0
  58. package/packages/ampcode/commands/docs-builder/templates.md +572 -0
  59. package/packages/ampcode/commands/docs-builder.md +106 -0
  60. package/packages/ampcode/commands/explain.md +18 -0
  61. package/packages/ampcode/commands/git-commit.md +14 -0
  62. package/packages/ampcode/commands/optimize.md +20 -0
  63. package/packages/ampcode/commands/refactor.md +21 -0
  64. package/packages/ampcode/commands/review.md +18 -0
  65. package/packages/ampcode/commands/root-cause-tracing/find-polluter.sh +63 -0
  66. package/packages/ampcode/commands/root-cause-tracing.md +176 -0
  67. package/packages/ampcode/commands/security.md +21 -0
  68. package/packages/ampcode/commands/ship.md +18 -0
  69. package/packages/ampcode/commands/skill-creator/scripts/init_skill.py +303 -0
  70. package/packages/ampcode/commands/skill-creator/scripts/package_skill.py +110 -0
  71. package/packages/ampcode/commands/skill-creator/scripts/quick_validate.py +65 -0
  72. package/packages/ampcode/commands/skill-creator.md +211 -0
  73. package/packages/ampcode/commands/stash.md +45 -0
  74. package/packages/ampcode/commands/systematic-debugging.md +297 -0
  75. package/packages/ampcode/commands/test-driven-development.md +390 -0
  76. package/packages/ampcode/commands/test-generate.md +18 -0
  77. package/packages/ampcode/commands/testing-anti-patterns.md +304 -0
  78. package/packages/ampcode/commands/verification-before-completion.md +152 -0
  79. package/packages/ampcode/settings.json +13 -0
  80. package/packages/ampcode/variants.json +8 -0
  81. package/packages/claude/CLAUDE.md +58 -0
  82. package/packages/claude/README.md +23 -0
  83. package/packages/claude/agents/1-create-prd.md +175 -0
  84. package/packages/claude/agents/2-generate-tasks.md +190 -0
  85. package/packages/claude/agents/3-process-task-list.md +225 -0
  86. package/packages/claude/agents/code-developer.md +198 -0
  87. package/packages/claude/agents/context-builder.md +142 -0
  88. package/packages/claude/agents/feature-planner.md +199 -0
  89. package/packages/claude/agents/market-researcher.md +89 -0
  90. package/packages/claude/agents/orchestrator.md +117 -0
  91. package/packages/claude/agents/quality-assurance.md +115 -0
  92. package/packages/claude/agents/system-architect.md +135 -0
  93. package/packages/claude/agents/ui-designer.md +184 -0
  94. package/packages/claude/commands/debug.md +20 -0
  95. package/packages/claude/commands/explain.md +18 -0
  96. package/packages/claude/commands/git-commit.md +14 -0
  97. package/packages/claude/commands/optimize.md +20 -0
  98. package/packages/claude/commands/refactor.md +21 -0
  99. package/packages/claude/commands/review.md +18 -0
  100. package/packages/claude/commands/security.md +21 -0
  101. package/packages/claude/commands/ship.md +18 -0
  102. package/packages/claude/commands/stash.md +45 -0
  103. package/packages/claude/commands/test-generate.md +18 -0
  104. package/packages/claude/skills/brainstorming/SKILL.md +56 -0
  105. package/packages/claude/skills/code-review/SKILL.md +107 -0
  106. package/packages/claude/skills/code-review/code-reviewer.md +146 -0
  107. package/packages/claude/skills/condition-based-waiting/SKILL.md +122 -0
  108. package/packages/claude/skills/condition-based-waiting/example.ts +158 -0
  109. package/packages/claude/skills/docs-builder/SKILL.md +106 -0
  110. package/packages/claude/skills/docs-builder/references/templates.md +572 -0
  111. package/packages/claude/skills/root-cause-tracing/SKILL.md +176 -0
  112. package/packages/claude/skills/root-cause-tracing/find-polluter.sh +63 -0
  113. package/packages/claude/skills/skill-creator/LICENSE.txt +202 -0
  114. package/packages/claude/skills/skill-creator/SKILL.md +211 -0
  115. package/packages/claude/skills/skill-creator/scripts/init_skill.py +303 -0
  116. package/packages/claude/skills/skill-creator/scripts/package_skill.py +110 -0
  117. package/packages/claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  118. package/packages/claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
  119. package/packages/claude/skills/systematic-debugging/SKILL.md +296 -0
  120. package/packages/claude/skills/systematic-debugging/test-academic.md +14 -0
  121. package/packages/claude/skills/systematic-debugging/test-pressure-1.md +58 -0
  122. package/packages/claude/skills/systematic-debugging/test-pressure-2.md +68 -0
  123. package/packages/claude/skills/systematic-debugging/test-pressure-3.md +69 -0
  124. package/packages/claude/skills/test-driven-development/SKILL.md +392 -0
  125. package/packages/claude/skills/testing-anti-patterns/SKILL.md +304 -0
  126. package/packages/claude/skills/verification-before-completion/SKILL.md +152 -0
  127. package/packages/claude/variants.json +9 -0
  128. package/packages/droid/AGENTS.md +52 -0
  129. package/packages/droid/README.md +17 -0
  130. package/packages/droid/change_settings.json +61 -0
  131. package/packages/droid/commands/brainstorming.md +56 -0
  132. package/packages/droid/commands/code-review.md +107 -0
  133. package/packages/droid/commands/condition-based-waiting/example.ts +158 -0
  134. package/packages/droid/commands/condition-based-waiting.md +122 -0
  135. package/packages/droid/commands/debug.md +20 -0
  136. package/packages/droid/commands/docs-builder/templates.md +572 -0
  137. package/packages/droid/commands/docs-builder.md +106 -0
  138. package/packages/droid/commands/explain.md +18 -0
  139. package/packages/droid/commands/git-commit.md +14 -0
  140. package/packages/droid/commands/optimize.md +20 -0
  141. package/packages/droid/commands/refactor.md +21 -0
  142. package/packages/droid/commands/review.md +18 -0
  143. package/packages/droid/commands/root-cause-tracing/find-polluter.sh +63 -0
  144. package/packages/droid/commands/root-cause-tracing.md +176 -0
  145. package/packages/droid/commands/security.md +21 -0
  146. package/packages/droid/commands/ship.md +18 -0
  147. package/packages/droid/commands/skill-creator/scripts/init_skill.py +303 -0
  148. package/packages/droid/commands/skill-creator/scripts/package_skill.py +110 -0
  149. package/packages/droid/commands/skill-creator/scripts/quick_validate.py +65 -0
  150. package/packages/droid/commands/skill-creator.md +211 -0
  151. package/packages/droid/commands/stash.md +45 -0
  152. package/packages/droid/commands/systematic-debugging.md +297 -0
  153. package/packages/droid/commands/test-driven-development.md +390 -0
  154. package/packages/droid/commands/test-generate.md +18 -0
  155. package/packages/droid/commands/testing-anti-patterns.md +304 -0
  156. package/packages/droid/commands/verification-before-completion.md +152 -0
  157. package/packages/droid/droids/1-create-prd.md +170 -0
  158. package/packages/droid/droids/2-generate-tasks.md +190 -0
  159. package/packages/droid/droids/3-process-task-list.md +225 -0
  160. package/packages/droid/droids/code-developer.md +198 -0
  161. package/packages/droid/droids/context-builder.md +142 -0
  162. package/packages/droid/droids/feature-planner.md +199 -0
  163. package/packages/droid/droids/market-researcher.md +89 -0
  164. package/packages/droid/droids/orchestrator.md +116 -0
  165. package/packages/droid/droids/quality-assurance.md +115 -0
  166. package/packages/droid/droids/system-architect.md +135 -0
  167. package/packages/droid/droids/ui-designer.md +184 -0
  168. package/packages/droid/variants.json +8 -0
  169. package/packages/opencode/AGENTS.md +52 -0
  170. package/packages/opencode/README.md +17 -0
  171. package/packages/opencode/agent/1-create-prd.md +179 -0
  172. package/packages/opencode/agent/2-generate-tasks.md +194 -0
  173. package/packages/opencode/agent/3-process-task-list.md +229 -0
  174. package/packages/opencode/agent/code-developer.md +202 -0
  175. package/packages/opencode/agent/context-builder.md +146 -0
  176. package/packages/opencode/agent/feature-planner.md +203 -0
  177. package/packages/opencode/agent/market-researcher.md +93 -0
  178. package/packages/opencode/agent/orchestrator.md +120 -0
  179. package/packages/opencode/agent/quality-assurance.md +119 -0
  180. package/packages/opencode/agent/system-architect.md +139 -0
  181. package/packages/opencode/agent/ui-designer.md +188 -0
  182. package/packages/opencode/command/brainstorming.md +56 -0
  183. package/packages/opencode/command/code-review.md +107 -0
  184. package/packages/opencode/command/condition-based-waiting/example.ts +158 -0
  185. package/packages/opencode/command/condition-based-waiting.md +122 -0
  186. package/packages/opencode/command/debug.md +20 -0
  187. package/packages/opencode/command/docs-builder/templates.md +572 -0
  188. package/packages/opencode/command/docs-builder.md +106 -0
  189. package/packages/opencode/command/explain.md +18 -0
  190. package/packages/opencode/command/git-commit.md +14 -0
  191. package/packages/opencode/command/optimize.md +20 -0
  192. package/packages/opencode/command/refactor.md +21 -0
  193. package/packages/opencode/command/review.md +18 -0
  194. package/packages/opencode/command/root-cause-tracing/find-polluter.sh +63 -0
  195. package/packages/opencode/command/root-cause-tracing.md +176 -0
  196. package/packages/opencode/command/security.md +21 -0
  197. package/packages/opencode/command/ship.md +18 -0
  198. package/packages/opencode/command/skill-creator/scripts/init_skill.py +303 -0
  199. package/packages/opencode/command/skill-creator/scripts/package_skill.py +110 -0
  200. package/packages/opencode/command/skill-creator/scripts/quick_validate.py +65 -0
  201. package/packages/opencode/command/skill-creator.md +211 -0
  202. package/packages/opencode/command/stash.md +45 -0
  203. package/packages/opencode/command/systematic-debugging.md +297 -0
  204. package/packages/opencode/command/test-driven-development.md +390 -0
  205. package/packages/opencode/command/test-generate.md +18 -0
  206. package/packages/opencode/command/testing-anti-patterns.md +304 -0
  207. package/packages/opencode/command/verification-before-completion.md +152 -0
  208. package/packages/opencode/opencode.jsonc +201 -0
  209. package/packages/opencode/variants.json +8 -0
  210. package/packages/subagentic-manual.md +349 -0
  211. package/postinstall.js +21 -0
  212. package/tools/ampcode/manifest-template.json +14 -0
  213. package/tools/claude/manifest-template.json +14 -0
  214. package/tools/droid/manifest-template.json +14 -0
  215. package/tools/opencode/manifest-template.json +14 -0
@@ -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;