stigmergy 1.2.12 → 1.3.1-beta

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 (84) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/builtin-skills.json +43 -0
  4. package/config/enhanced-cli-config.json +438 -0
  5. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  6. package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
  7. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  8. package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
  9. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  10. package/docs/LESSONS_LEARNED.md +252 -0
  11. package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
  12. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  13. package/docs/correct-skillsio-implementation.md +368 -0
  14. package/docs/development_guidelines.md +276 -0
  15. package/docs/independent-resume-implementation.md +198 -0
  16. package/docs/resumesession-final-implementation.md +195 -0
  17. package/docs/resumesession-usage.md +87 -0
  18. package/package.json +19 -9
  19. package/scripts/analyze-router.js +168 -0
  20. package/scripts/run-comprehensive-tests.js +230 -0
  21. package/scripts/run-quick-tests.js +90 -0
  22. package/scripts/test-runner.js +344 -0
  23. package/skills/resumesession/INDEPENDENT_SKILL.md +171 -0
  24. package/skills/resumesession/SKILL.md +127 -0
  25. package/skills/resumesession/__init__.py +33 -0
  26. package/skills/resumesession/implementations/simple-resume.js +13 -0
  27. package/src/adapters/claude/install_claude_integration.js +9 -1
  28. package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
  29. package/src/adapters/codex/install_codex_integration.js +15 -5
  30. package/src/adapters/gemini/install_gemini_integration.js +3 -1
  31. package/src/adapters/qwen/install_qwen_integration.js +3 -1
  32. package/src/cli/commands/autoinstall.js +65 -0
  33. package/src/cli/commands/errors.js +190 -0
  34. package/src/cli/commands/independent-resume.js +395 -0
  35. package/src/cli/commands/install.js +179 -0
  36. package/src/cli/commands/permissions.js +108 -0
  37. package/src/cli/commands/project.js +485 -0
  38. package/src/cli/commands/scan.js +97 -0
  39. package/src/cli/commands/simple-resume.js +377 -0
  40. package/src/cli/commands/skills.js +158 -0
  41. package/src/cli/commands/status.js +113 -0
  42. package/src/cli/commands/stigmergy-resume.js +775 -0
  43. package/src/cli/commands/system.js +301 -0
  44. package/src/cli/commands/universal-resume.js +394 -0
  45. package/src/cli/router-beta.js +471 -0
  46. package/src/cli/utils/environment.js +75 -0
  47. package/src/cli/utils/formatters.js +47 -0
  48. package/src/cli/utils/skills_cache.js +92 -0
  49. package/src/core/cache_cleaner.js +1 -0
  50. package/src/core/cli_adapters.js +345 -0
  51. package/src/core/cli_help_analyzer.js +582 -26
  52. package/src/core/cli_path_detector.js +702 -709
  53. package/src/core/cli_tools.js +515 -160
  54. package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
  55. package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
  56. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  57. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  58. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
  59. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
  60. package/src/core/coordination/nodejs/generators/index.js +12 -0
  61. package/src/core/enhanced_cli_installer.js +1208 -608
  62. package/src/core/enhanced_cli_parameter_handler.js +402 -0
  63. package/src/core/execution_mode_detector.js +222 -0
  64. package/src/core/installer.js +151 -106
  65. package/src/core/local_skill_scanner.js +732 -0
  66. package/src/core/multilingual/language-pattern-manager.js +1 -1
  67. package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
  68. package/src/core/skills/StigmergySkillManager.js +123 -16
  69. package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
  70. package/src/core/smart_router.js +291 -2
  71. package/src/index.js +10 -4
  72. package/src/utils.js +66 -7
  73. package/test/cli-integration.test.js +304 -0
  74. package/test/direct_smart_router_test.js +88 -0
  75. package/test/enhanced-cli-agent-skill-test.js +485 -0
  76. package/test/simple_test.js +82 -0
  77. package/test/smart_router_test_runner.js +123 -0
  78. package/test/smart_routing_edge_cases.test.js +284 -0
  79. package/test/smart_routing_simple_verification.js +139 -0
  80. package/test/smart_routing_verification.test.js +346 -0
  81. package/test/specific-cli-agent-skill-analysis.js +385 -0
  82. package/test/unit/smart_router.test.js +295 -0
  83. package/test/very_simple_test.js +54 -0
  84. package/src/cli/router.js +0 -1737
@@ -0,0 +1,471 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stigmergy CLI - Modular Router
5
+ * Multi-Agents Cross-AI CLI Tools Collaboration System
6
+ * Modular implementation with separated concerns
7
+ * Version: 2.0.0
8
+ */
9
+
10
+ // Core imports
11
+ const path = require('path');
12
+ const os = require('os');
13
+ const { Command } = require('commander');
14
+ const chalk = require('chalk');
15
+
16
+ // Import CLI tools configuration
17
+ const { CLI_TOOLS } = require('../core/cli_tools');
18
+
19
+ // Import modular components
20
+ const { formatBytes } = require('./utils/formatters');
21
+ const { getWorkingDirectoryForTool, getEnvironmentForTool } = require('./utils/environment');
22
+
23
+ // Import execution mode detection and CLI adapters
24
+ const ExecutionModeDetector = require('../core/execution_mode_detector');
25
+ const { CLIAdapterManager } = require('../core/cli_adapters');
26
+
27
+ // Create instances
28
+ const modeDetector = new ExecutionModeDetector();
29
+ const cliAdapterManager = new CLIAdapterManager();
30
+ const { handleInstallCommand } = require('./commands/install');
31
+ const { handleStatusCommand } = require('./commands/status');
32
+ const { handleScanCommand } = require('./commands/scan');
33
+ const { handlePermCheckCommand, handleFixPermsCommand } = require('./commands/permissions');
34
+ const { handleDiagnosticCommand, handleCleanCommand } = require('./commands/system');
35
+ const { handleSkillMainCommand, printSkillsHelp } = require('./commands/skills');
36
+ const { handleErrorsCommand } = require('./commands/errors');
37
+ const { handleAutoInstallCommand } = require('./commands/autoinstall');
38
+ const { handleResumeCommand, printResumeHelp } = require('./commands/stigmergy-resume');
39
+ const { getCLIPath } = require('../core/cli_tools');
40
+ const {
41
+ handleUpgradeCommand,
42
+ handleDeployCommand,
43
+ handleInitCommand,
44
+ handleSetupCommand,
45
+ handleCallCommand
46
+ } = require('./commands/project');
47
+ const SmartRouter = require('../core/smart_router');
48
+ const { errorHandler } = require('../core/error_handler');
49
+ const { executeCommand } = require('../utils');
50
+ const { setupGlobalErrorHandlers } = require('../core/error_handler');
51
+
52
+ // Set up global error handlers
53
+ setupGlobalErrorHandlers();
54
+
55
+ /**
56
+ * Add OAuth authentication arguments to command
57
+ * @param {Command} command - Commander command instance
58
+ * @returns {Command} Command with OAuth args added
59
+ */
60
+ function addOAuthAuthArgsCommand(command) {
61
+ return command
62
+ .option('--client-id <id>', 'OAuth client ID')
63
+ .option('--client-secret <secret>', 'OAuth client secret')
64
+ .option('--access-token <token>', 'OAuth access token')
65
+ .option('--auth-url <url>', 'OAuth authentication URL');
66
+ }
67
+
68
+ /**
69
+ * Add OAuth authentication arguments to command args for execution
70
+ * @param {string} toolName - Name of the CLI tool
71
+ * @param {Array} args - Current arguments array
72
+ * @returns {Array} Arguments with OAuth auth added
73
+ */
74
+ function addOAuthAuthArgs(toolName, args = []) {
75
+ const toolConfig = CLI_TOOLS[toolName];
76
+
77
+ if (toolConfig && toolConfig.oauth) {
78
+ const oauth = toolConfig.oauth;
79
+ if (oauth.authRequired) {
80
+ // Qwen-specific OAuth handling
81
+ if (toolName === 'qwen' && process.env.QWEN_ACCESS_TOKEN) {
82
+ return [...args, '--access-token', process.env.QWEN_ACCESS_TOKEN];
83
+ }
84
+ }
85
+ }
86
+
87
+ return args;
88
+ }
89
+
90
+ /**
91
+ * Main CLI router function
92
+ */
93
+ async function main() {
94
+ const program = new Command();
95
+ const packageJson = require('../../package.json');
96
+
97
+ // Program setup
98
+ program
99
+ .version(packageJson.version)
100
+ .description('Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System')
101
+ .name('stigmergy');
102
+
103
+ // Version command (override the built-in --version)
104
+ program
105
+ .command('version')
106
+ .description('Show version information')
107
+ .action(() => {
108
+ const packageJson = require('../../package.json');
109
+ console.log(`Stigmergy CLI v${packageJson.version}`);
110
+ });
111
+
112
+ // Error reporting command
113
+ program
114
+ .command('errors')
115
+ .description('Generate comprehensive error report')
116
+ .option('--save', 'Save report to file')
117
+ .option('-v, --verbose', 'Verbose output')
118
+ .action(async (options) => {
119
+ await handleErrorsCommand(options);
120
+ });
121
+
122
+ // Install command
123
+ program
124
+ .command('install')
125
+ .alias('inst')
126
+ .alias('a')
127
+ .description('Install CLI tools')
128
+ .option('-c, --cli <cli>', 'Install specific CLI tool')
129
+ .option('-v, --verbose', 'Verbose output')
130
+ .option('-f, --force', 'Force installation')
131
+ .option('--all', 'Install all CLI tools (ignore autoInstall filter)')
132
+ .action(async (options) => {
133
+ // 检测是否通过 'a' 别名调用,自动设置 --all 选项
134
+ const commandName = process.argv[2];
135
+ if (commandName === 'a') {
136
+ options.all = true;
137
+ }
138
+ await handleInstallCommand(options);
139
+ });
140
+
141
+ // Project management commands
142
+ program
143
+ .command('upgrade')
144
+ .description('Upgrade AI CLI tools to latest versions')
145
+ .option('--dry-run', 'Show what would be upgraded without actually upgrading')
146
+ .option('-f, --force', 'Force upgrade')
147
+ .option('-v, --verbose', 'Verbose output')
148
+ .action(async (options) => {
149
+ await handleUpgradeCommand(options);
150
+ });
151
+
152
+ program
153
+ .command('deploy')
154
+ .description('Deploy integration hooks to CLI tools')
155
+ .option('-v, --verbose', 'Verbose output')
156
+ .action(async (options) => {
157
+ await handleDeployCommand(options);
158
+ });
159
+
160
+ program
161
+ .command('init')
162
+ .description('Initialize Stigmergy project in current directory')
163
+ .option('-v, --verbose', 'Verbose output')
164
+ .action(async (options) => {
165
+ await handleInitCommand(options);
166
+ });
167
+
168
+ program
169
+ .command('setup')
170
+ .description('Complete Stigmergy setup (install + deploy + init)')
171
+ .option('-v, --verbose', 'Verbose output')
172
+ .action(async (options) => {
173
+ await handleSetupCommand(options);
174
+ });
175
+
176
+ program
177
+ .command('call')
178
+ .description('Smart AI tool routing based on prompt')
179
+ .argument('<prompt>', 'Prompt to process with smart routing')
180
+ .option('-i, --interactive', 'Run in interactive mode (continuous conversation)')
181
+ .option('-p, --print', 'Run in one-time mode (print and exit)')
182
+ .option('-v, --verbose', 'Verbose output')
183
+ .action(async (prompt, options) => {
184
+ await handleCallCommand(prompt, options);
185
+ });
186
+
187
+ // Status command
188
+ program
189
+ .command('status')
190
+ .description('Check CLI tools status')
191
+ .option('-c, --cli <cli>', 'Check status of specific CLI tool')
192
+ .option('--json', 'Output in JSON format')
193
+ .option('-v, --verbose', 'Verbose output')
194
+ .action(async (options) => {
195
+ await handleStatusCommand(options);
196
+ });
197
+
198
+ // Scan command
199
+ program
200
+ .command('scan')
201
+ .description('Scan for available CLI tools')
202
+ .option('-d, --deep', 'Deep scan for CLI tools')
203
+ .option('--json', 'Output in JSON format')
204
+ .option('-v, --verbose', 'Verbose output')
205
+ .action(async (options) => {
206
+ await handleScanCommand(options);
207
+ });
208
+
209
+ // Permission management commands
210
+ program
211
+ .command('fix-perms')
212
+ .description('Fix directory permissions automatically')
213
+ .option('-v, --verbose', 'Verbose output')
214
+ .action(async (options) => {
215
+ await handleFixPermsCommand(options);
216
+ });
217
+
218
+ program
219
+ .command('perm-check')
220
+ .description('Check directory permissions')
221
+ .option('-v, --verbose', 'Verbose output')
222
+ .action(async (options) => {
223
+ await handlePermCheckCommand(options);
224
+ });
225
+
226
+ // System commands
227
+ program
228
+ .command('clean')
229
+ .alias('c')
230
+ .description('Intelligent cache cleaning')
231
+ .option('--dry-run', 'Show what would be cleaned without actually cleaning')
232
+ .option('-q, --quiet', 'Suppress detailed output, show only summary')
233
+ .option('-v, --verbose', 'Verbose output (show permission errors)')
234
+ .action(async (options) => {
235
+ await handleCleanCommand(options);
236
+ });
237
+
238
+ program
239
+ .command('diagnostic')
240
+ .aliases(['diag', 'd'])
241
+ .description('System diagnostic')
242
+ .option('-v, --verbose', 'Verbose output')
243
+ .action(async (options) => {
244
+ await handleDiagnosticCommand(options);
245
+ });
246
+
247
+ // Skills management commands
248
+ program
249
+ .command('skill')
250
+ .description('Skills management system')
251
+ .argument('[subcommand]', 'Skill subcommand (install/list/read/validate/remove/sync)')
252
+ .argument('[args...]', 'Additional arguments')
253
+ .option('-v, --verbose', 'Verbose output')
254
+ .option('-f, --force', 'Force operation')
255
+ .option('--no-auto-sync', 'Disable auto-sync')
256
+ .action(async (subcommand, args, options) => {
257
+ if (!subcommand) {
258
+ printSkillsHelp();
259
+ return;
260
+ }
261
+ await handleSkillMainCommand(subcommand, args, options);
262
+ });
263
+
264
+ // Skill command aliases (shortcuts)
265
+ program
266
+ .command('skill-i')
267
+ .description('Install a skill (alias for: skill install)')
268
+ .argument('<source>', 'Skill source to install')
269
+ .option('-v, --verbose', 'Verbose output')
270
+ .action(async (source, options) => {
271
+ await handleSkillMainCommand('install', [source], options);
272
+ });
273
+
274
+ program
275
+ .command('skill-l')
276
+ .description('List installed skills (alias for: skill list)')
277
+ .option('-v, --verbose', 'Verbose output')
278
+ .action(async (options) => {
279
+ await handleSkillMainCommand('list', [], options);
280
+ });
281
+
282
+ program
283
+ .command('skill-r')
284
+ .description('Read a skill (alias for: skill read)')
285
+ .argument('<skill-name>', 'Name of skill to read')
286
+ .option('-v, --verbose', 'Verbose output')
287
+ .action(async (skillName, options) => {
288
+ await handleSkillMainCommand('read', [skillName], options);
289
+ });
290
+
291
+ program
292
+ .command('skill-v')
293
+ .description('Validate/read skill (alias for: skill validate/read)')
294
+ .argument('<path-or-name>', 'Path to validate or skill name to read')
295
+ .option('-v, --verbose', 'Verbose output')
296
+ .action(async (pathOrName, options) => {
297
+ await handleSkillMainCommand('validate', [pathOrName], options);
298
+ });
299
+
300
+ program
301
+ .command('skill-d')
302
+ .description('Remove a skill (alias for: skill remove)')
303
+ .argument('<skill-name>', 'Name of skill to remove')
304
+ .option('-v, --verbose', 'Verbose output')
305
+ .option('-f, --force', 'Force removal')
306
+ .action(async (skillName, options) => {
307
+ await handleSkillMainCommand('remove', [skillName], options);
308
+ });
309
+
310
+ program
311
+ .command('skill-m')
312
+ .description('Remove a skill (移除 alias for: skill remove)')
313
+ .argument('<skill-name>', 'Name of skill to remove')
314
+ .option('-v, --verbose', 'Verbose output')
315
+ .option('-f, --force', 'Force removal')
316
+ .action(async (skillName, options) => {
317
+ await handleSkillMainCommand('remove', [skillName], options);
318
+ });
319
+
320
+ // Auto-install command (for npm postinstall)
321
+ program
322
+ .command('auto-install')
323
+ .description('Automated installation for npm postinstall')
324
+ .option('-v, --verbose', 'Verbose output')
325
+ .option('-f, --force', 'Force installation')
326
+ .action(async (options) => {
327
+ await handleAutoInstallCommand(options);
328
+ });
329
+
330
+ // Resume session command
331
+ program
332
+ .command('resume')
333
+ .description('Resume session - Cross-CLI session recovery and history management')
334
+ .argument('[cli]', 'CLI tool to filter (claude, gemini, qwen, iflow, codebuddy, codex, qodercli)')
335
+ .argument('[limit]', 'Maximum number of sessions to show')
336
+ .option('-v, --verbose', 'Verbose output')
337
+ .action(async (cli, limit, options) => {
338
+ const args = [];
339
+ if (cli) args.push(cli);
340
+ if (limit) args.push(limit);
341
+ await handleResumeCommand(args, options);
342
+ });
343
+
344
+ // Route commands to CLI tools
345
+ for (const tool of ['claude', 'gemini', 'qwen', 'codebuddy', 'codex', 'iflow', 'qodercli', 'copilot']) {
346
+ program
347
+ .command(tool)
348
+ .description(`Use ${tool} CLI tool`)
349
+ .option('-i, --interactive', 'Run in interactive mode (continuous conversation)')
350
+ .option('-p, --print', 'Run in one-time mode (print and exit)')
351
+ .allowUnknownOption(true)
352
+ .action(async (options, command) => {
353
+ try {
354
+ // Get the tool path directly (we know the tool name)
355
+ const toolPath = await getCLIPath(tool);
356
+
357
+ if (toolPath) {
358
+ // Join args to form the prompt
359
+ const prompt = command.args.join(' ');
360
+
361
+ if (process.env.DEBUG === 'true') {
362
+ console.log(`[DEBUG] Tool path: ${toolPath}`);
363
+ console.log(`[DEBUG] Prompt: ${prompt}`);
364
+ }
365
+
366
+ // Detect execution mode
367
+ const mode = modeDetector.detect({
368
+ interactive: options.interactive,
369
+ print: options.print,
370
+ verbose: process.env.DEBUG === 'true'
371
+ });
372
+
373
+ const modeDescription = modeDetector.getModeDescription(mode);
374
+ if (process.env.DEBUG === 'true' || options.interactive || options.print) {
375
+ console.log(chalk.gray(`[MODE] ${modeDescription}`));
376
+ }
377
+
378
+ // Use CLI adapter to get appropriate arguments for the tool and mode
379
+ const adaptedArgs = cliAdapterManager.getArguments(tool, mode, prompt);
380
+
381
+ if (process.env.DEBUG === 'true') {
382
+ console.log(`[DEBUG] Adapted args for ${tool} (${mode}): ${adaptedArgs.join(' ')}`);
383
+ }
384
+
385
+ // Use enhanced parameter handling for intelligent argument generation
386
+ let toolArgs = adaptedArgs; // Start with adapted args
387
+
388
+ try {
389
+ if (process.env.DEBUG === 'true') {
390
+ console.log('[DEBUG] Initializing parameter handler...');
391
+ }
392
+ const EnhancedCLIParameterHandler = require('../core/enhanced_cli_parameter_handler');
393
+ const paramHandler = new EnhancedCLIParameterHandler();
394
+
395
+ // Generate optimized arguments with agent/skill support
396
+ // Skip for interactive mode to avoid double-processing
397
+ if (mode === 'one-time') {
398
+ const paramResult = await paramHandler.generateArgumentsWithRetry(
399
+ tool,
400
+ prompt,
401
+ {
402
+ maxRetries: 3,
403
+ enableAgentSkillOptimization: true
404
+ }
405
+ );
406
+
407
+ toolArgs = paramResult.arguments;
408
+
409
+ if (process.env.DEBUG === 'true') {
410
+ console.log(`[DEBUG] Generated args: ${toolArgs.join(' ')}`);
411
+ }
412
+ }
413
+ } catch (paramError) {
414
+ // Fallback to adapted args if parameter handler fails
415
+ if (process.env.DEBUG === 'true') {
416
+ console.log(chalk.yellow(`[WARN] Parameter handler failed, using adapted args: ${paramError.message}`));
417
+ }
418
+ }
419
+
420
+ // Add OAuth authentication if needed
421
+ toolArgs = addOAuthAuthArgs(tool, toolArgs);
422
+
423
+ console.log(chalk.gray(`[EXEC] ${tool}: ${prompt}`));
424
+
425
+ // Set up environment for the tool
426
+ const toolEnv = getEnvironmentForTool(tool);
427
+ const workingDir = getWorkingDirectoryForTool(tool);
428
+
429
+ const result = await executeCommand(toolPath, toolArgs, {
430
+ stdio: 'inherit',
431
+ shell: true,
432
+ cwd: workingDir,
433
+ env: toolEnv
434
+ });
435
+
436
+ if (!result.success) {
437
+ console.log(chalk.yellow(`[WARN] ${tool} exited with code ${result.code}`));
438
+ }
439
+ process.exit(result.code || 0);
440
+ } else {
441
+ console.log(chalk.red(`[ERROR] Could not find ${tool}`));
442
+ console.log(chalk.yellow('[INFO] Make sure the tool is installed: stigmergy install'));
443
+ process.exit(1);
444
+ }
445
+ } catch (error) {
446
+ if (process.env.DEBUG === 'true') {
447
+ console.log(chalk.red(`[ERROR] Exception: ${error.message}`));
448
+ console.log(chalk.red(error.stack));
449
+ }
450
+ const cliError = await errorHandler.handleCLIError(tool, error, command.args.join(' '));
451
+ console.log(chalk.red(`[ERROR] Failed to execute ${tool}:`));
452
+ console.log(chalk.red(cliError.message));
453
+ process.exit(1);
454
+ }
455
+ });
456
+ }
457
+
458
+ // Add OAuth arguments for OAuth-enabled tools
459
+ const oauthTools = ['gemini', 'claude']; // Tools that support OAuth
460
+ oauthTools.forEach(tool => {
461
+ const cmd = program.commands.find(c => c.name() === tool);
462
+ if (cmd) {
463
+ addOAuthAuthArgsCommand(cmd);
464
+ }
465
+ });
466
+
467
+ // Parse command line arguments
468
+ program.parse(process.argv);
469
+ }
470
+
471
+ module.exports = main;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * CLI Environment Module
3
+ * Contains environment and working directory management
4
+ */
5
+
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ /**
10
+ * Get appropriate working directory for CLI tools
11
+ * @param {string} toolName - Name of the CLI tool
12
+ * @returns {string} Working directory path
13
+ */
14
+ function getWorkingDirectoryForTool(toolName) {
15
+ switch (toolName) {
16
+ case 'claude':
17
+ return process.cwd(); // Current working directory for Claude
18
+ case 'gemini':
19
+ return process.cwd(); // Current working directory for Gemini
20
+ case 'qwen':
21
+ return path.join(os.homedir(), '.qwen'); // Qwen's home directory
22
+ case 'codebuddy':
23
+ return process.cwd(); // Current working directory for CodeBuddy
24
+ case 'copilot':
25
+ return process.cwd(); // Current working directory for Copilot
26
+ default:
27
+ return process.cwd(); // Default to current working directory
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Get environment variables for specific CLI tool
33
+ * @param {string} toolName - Name of the CLI tool
34
+ * @returns {Object} Environment variables object
35
+ */
36
+ function getEnvironmentForTool(toolName) {
37
+ const env = { ...process.env };
38
+
39
+ // Tool-specific environment setup
40
+ switch (toolName) {
41
+ case 'qwen':
42
+ // For Qwen CLI, clear Node.js environment variables to avoid import conflicts
43
+ delete env.NODE_PATH;
44
+ delete env.NODE_OPTIONS;
45
+ // Ensure clean environment
46
+ env.PWD = getWorkingDirectoryForTool(toolName);
47
+ break;
48
+ case 'claude':
49
+ // Claude-specific environment setup if needed
50
+ break;
51
+ case 'gemini':
52
+ // Gemini-specific environment setup if needed
53
+ break;
54
+ default:
55
+ // Use default environment
56
+ break;
57
+ }
58
+
59
+ return env;
60
+ }
61
+
62
+ /**
63
+ * Check if CLI tool needs special working directory
64
+ * @param {string} toolName - Name of the CLI tool
65
+ * @returns {boolean} True if special directory needed
66
+ */
67
+ function needsSpecialDirectory(toolName) {
68
+ return ['qwen'].includes(toolName);
69
+ }
70
+
71
+ module.exports = {
72
+ getWorkingDirectoryForTool,
73
+ getEnvironmentForTool,
74
+ needsSpecialDirectory
75
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * CLI Formatters Module
3
+ * Contains helper functions for formatting output
4
+ */
5
+
6
+ /**
7
+ * Format bytes into human readable string
8
+ * @param {number} bytes - Number of bytes
9
+ * @returns {string} Formatted string
10
+ */
11
+ function formatBytes(bytes) {
12
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
13
+ if (bytes === 0) return '0 Bytes';
14
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
15
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
16
+ }
17
+
18
+ /**
19
+ * Format duration in milliseconds to human readable string
20
+ * @param {number} ms - Duration in milliseconds
21
+ * @returns {string} Formatted duration
22
+ */
23
+ function formatDuration(ms) {
24
+ if (ms < 1000) return `${ms}ms`;
25
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
26
+ if (ms < 3600000) return `${(ms / 60000).toFixed(1)}m`;
27
+ return `${(ms / 3600000).toFixed(1)}h`;
28
+ }
29
+
30
+ /**
31
+ * Format CLI tool status for display
32
+ * @param {Object} status - Status object
33
+ * @returns {string} Formatted status
34
+ */
35
+ function formatToolStatus(status) {
36
+ if (status.installed) {
37
+ return `${status.tool}: ✅ Installed (v${status.version || 'unknown'})`;
38
+ } else {
39
+ return `${status.tool}: ❌ Not installed`;
40
+ }
41
+ }
42
+
43
+ module.exports = {
44
+ formatBytes,
45
+ formatDuration,
46
+ formatToolStatus
47
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Skills/Agents Cache Management Utilities
3
+ * Provides centralized cache initialization and management for skills and agents
4
+ */
5
+
6
+ const LocalSkillScanner = require('../../core/local_skill_scanner');
7
+
8
+ /**
9
+ * Initialize or update skills/agents cache
10
+ * This function checks if cache exists, and if not, generates it.
11
+ * If cache exists, it performs incremental update (only scans changed directories).
12
+ *
13
+ * Called during: scan, install, setup, init, upgrade commands
14
+ *
15
+ * @param {Object} options - Options
16
+ * @param {boolean} options.verbose - Show detailed output
17
+ * @param {boolean} options.force - Force full rescan
18
+ * @returns {Promise<Object>} Cache statistics
19
+ */
20
+ async function ensureSkillsCache(options = {}) {
21
+ const { verbose = false, force = false } = options;
22
+
23
+ try {
24
+ const scanner = new LocalSkillScanner();
25
+
26
+ // Check if cache exists
27
+ const hasCache = await scanner.hasCache();
28
+
29
+ if (!hasCache) {
30
+ // First time - generate cache
31
+ console.log('[CACHE] Generating skills/agents cache (first time)...');
32
+ await scanner.initialize();
33
+ const results = scanner.getScanResults();
34
+
35
+ if (results) {
36
+ const totalSkills = Object.values(results.skills || {}).flat().length;
37
+ const totalAgents = Object.values(results.agents || {}).flat().length;
38
+ console.log(`[CACHE] Cache generated: ${totalSkills} skills, ${totalAgents} agents`);
39
+
40
+ return {
41
+ success: true,
42
+ skills: totalSkills,
43
+ agents: totalAgents,
44
+ action: 'generated'
45
+ };
46
+ }
47
+ } else if (force) {
48
+ // Force refresh requested
49
+ console.log('[CACHE] Refreshing skills/agents cache...');
50
+ await scanner.initialize(true);
51
+ const results = scanner.getScanResults();
52
+
53
+ if (results) {
54
+ const totalSkills = Object.values(results.skills || {}).flat().length;
55
+ const totalAgents = Object.values(results.agents || {}).flat().length;
56
+ console.log(`[CACHE] Cache refreshed: ${totalSkills} skills, ${totalAgents} agents`);
57
+
58
+ return {
59
+ success: true,
60
+ skills: totalSkills,
61
+ agents: totalAgents,
62
+ action: 'refreshed'
63
+ };
64
+ }
65
+ } else {
66
+ // Incremental update - only scan changed directories
67
+ if (verbose || process.env.DEBUG === 'true') {
68
+ console.log('[CACHE] Updating skills/agents cache (incremental)...');
69
+ }
70
+ await scanner.scanIncremental();
71
+
72
+ return {
73
+ success: true,
74
+ action: 'updated'
75
+ };
76
+ }
77
+ } catch (error) {
78
+ // Cache initialization is not critical - don't fail the command
79
+ if (verbose || process.env.DEBUG === 'true') {
80
+ console.log(`[CACHE] Warning: ${error.message}`);
81
+ }
82
+
83
+ return {
84
+ success: false,
85
+ error: error.message
86
+ };
87
+ }
88
+ }
89
+
90
+ module.exports = {
91
+ ensureSkillsCache
92
+ };
@@ -254,6 +254,7 @@ class CacheCleaner {
254
254
  'codex',
255
255
  'copilot',
256
256
  'qwencode',
257
+ 'kode',
257
258
  ];
258
259
 
259
260
  let totalCleaned = 0;