push-guardian 1.0.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 (96) hide show
  1. package/.dockerignore +15 -0
  2. package/.pushguardian-plugins.json +10 -0
  3. package/Dockerfile +41 -0
  4. package/Dockerfile.dev +20 -0
  5. package/README.md +386 -0
  6. package/TECHNO.md +139 -0
  7. package/babel.config.js +1 -0
  8. package/developper_utils.md +119 -0
  9. package/docker-compose.yml +41 -0
  10. package/docs/PLUGINS.md +223 -0
  11. package/docs/technical/architecture.md +298 -0
  12. package/docs/technical/performance-guide.md +390 -0
  13. package/docs/technical/plugin-persistence.md +169 -0
  14. package/docs/technical/plugins-guide.md +409 -0
  15. package/jest.config.js +22 -0
  16. package/package.json +53 -0
  17. package/plugins/example-plugin/index.js +55 -0
  18. package/plugins/example-plugin/plugin.json +8 -0
  19. package/scripts/coverage-report.js +75 -0
  20. package/src/cli/command/config.js +33 -0
  21. package/src/cli/command/install.js +137 -0
  22. package/src/cli/command/mirror.js +90 -0
  23. package/src/cli/command/performance.js +160 -0
  24. package/src/cli/command/plugin.js +171 -0
  25. package/src/cli/command/security.js +152 -0
  26. package/src/cli/command/shell.js +238 -0
  27. package/src/cli/command/validate.js +54 -0
  28. package/src/cli/index.js +23 -0
  29. package/src/cli/install/codeQualityTools.js +156 -0
  30. package/src/cli/install/hooks.js +89 -0
  31. package/src/cli/install/mirroring.js +299 -0
  32. package/src/core/codeQualityTools/configAnalyzer.js +216 -0
  33. package/src/core/codeQualityTools/configGenerator.js +381 -0
  34. package/src/core/codeQualityTools/configManager.js +65 -0
  35. package/src/core/codeQualityTools/fileDetector.js +62 -0
  36. package/src/core/codeQualityTools/languageTools.js +104 -0
  37. package/src/core/codeQualityTools/toolInstaller.js +53 -0
  38. package/src/core/configManager.js +43 -0
  39. package/src/core/errorCMD.js +9 -0
  40. package/src/core/interactiveMenu/interactiveMenu.js +73 -0
  41. package/src/core/mirroring/branchSynchronizer.js +59 -0
  42. package/src/core/mirroring/generate.js +114 -0
  43. package/src/core/mirroring/repoManager.js +112 -0
  44. package/src/core/mirroring/syncManager.js +176 -0
  45. package/src/core/module/env-loader.js +109 -0
  46. package/src/core/performance/metricsCollector.js +217 -0
  47. package/src/core/performance/performanceAnalyzer.js +182 -0
  48. package/src/core/plugins/basePlugin.js +89 -0
  49. package/src/core/plugins/pluginManager.js +123 -0
  50. package/src/core/plugins/pluginRegistry.js +215 -0
  51. package/src/core/validator.js +53 -0
  52. package/src/hooks/constrains/constrains.js +174 -0
  53. package/src/hooks/constrains/constraintEngine.js +140 -0
  54. package/src/utils/chalk-wrapper.js +26 -0
  55. package/src/utils/exec-wrapper.js +6 -0
  56. package/tests/fixtures/mock-eslint-config-array.js +8 -0
  57. package/tests/fixtures/mock-eslint-config-single.js +6 -0
  58. package/tests/fixtures/mockLoadedPlugin.js +11 -0
  59. package/tests/setup.js +28 -0
  60. package/tests/unit/basePlugin.test.js +355 -0
  61. package/tests/unit/branchSynchronizer.test.js +308 -0
  62. package/tests/unit/cli-commands.test.js +144 -0
  63. package/tests/unit/codeQualityConfigManager.test.js +233 -0
  64. package/tests/unit/codeQualityTools.test.js +36 -0
  65. package/tests/unit/command-install.test.js +247 -0
  66. package/tests/unit/command-mirror.test.js +179 -0
  67. package/tests/unit/command-performance.test.js +169 -0
  68. package/tests/unit/command-plugin.test.js +288 -0
  69. package/tests/unit/command-security.test.js +277 -0
  70. package/tests/unit/command-shell.test.js +325 -0
  71. package/tests/unit/configAnalyzer.test.js +593 -0
  72. package/tests/unit/configGenerator.test.js +808 -0
  73. package/tests/unit/configManager.test.js +195 -0
  74. package/tests/unit/constrains.test.js +463 -0
  75. package/tests/unit/constraint.test.js +554 -0
  76. package/tests/unit/env-loader.test.js +279 -0
  77. package/tests/unit/fileDetector.test.js +171 -0
  78. package/tests/unit/install-codeQualityTools.test.js +343 -0
  79. package/tests/unit/install-hooks.test.js +280 -0
  80. package/tests/unit/install-mirroring.test.js +731 -0
  81. package/tests/unit/install-modules.test.js +81 -0
  82. package/tests/unit/interactiveMenu.test.js +426 -0
  83. package/tests/unit/languageTools.test.js +244 -0
  84. package/tests/unit/metricsCollector.test.js +354 -0
  85. package/tests/unit/mirroring-generate.test.js +96 -0
  86. package/tests/unit/modules-exist.test.js +96 -0
  87. package/tests/unit/performanceAnalyzer.test.js +473 -0
  88. package/tests/unit/pluginManager.test.js +427 -0
  89. package/tests/unit/pluginRegistry.test.js +592 -0
  90. package/tests/unit/repoManager.test.js +469 -0
  91. package/tests/unit/reviewAppManager.test.js +5 -0
  92. package/tests/unit/security-command.test.js +43 -0
  93. package/tests/unit/syncManager.test.js +494 -0
  94. package/tests/unit/toolInstaller.test.js +240 -0
  95. package/tests/unit/utils.test.js +144 -0
  96. package/tests/unit/validator.test.js +215 -0
@@ -0,0 +1,137 @@
1
+ const { installHooks } = require('../install/hooks');
2
+ const interactiveMenu = require('../../core/interactiveMenu/interactiveMenu');
3
+ const { installCodeQualityTools } = require('../install/codeQualityTools');
4
+ const { installMirroringTools } = require('../install/mirroring');
5
+ const { loadConfig } = require('../../core/configManager');
6
+ const { getChalk } = require('../../utils/chalk-wrapper');
7
+
8
+ module.exports = {
9
+ name: 'install',
10
+ description: 'Installer les hooks Git',
11
+ options: [
12
+ {
13
+ flags: '-f, --force',
14
+ description: "forcer l'installation même si déjà installé"
15
+ },
16
+ {
17
+ flags: '-a, --all',
18
+ description: 'installer tous les modules disponibles'
19
+ },
20
+ {
21
+ flags: '--hooks',
22
+ description: 'installer les hooks Git'
23
+ },
24
+ {
25
+ flags: '--code-quality',
26
+ description: 'installer les outils de qualité de code'
27
+ },
28
+ {
29
+ flags: '--skip-hooks',
30
+ description: 'ne pas installer les hooks Git'
31
+ },
32
+ {
33
+ flags: '--skip-code-quality',
34
+ description: 'ne pas installer les outils de qualité de code'
35
+ },
36
+ {
37
+ flags: '--mirroring',
38
+ description: 'installer le système de mirroring'
39
+ },
40
+ {
41
+ flags: '--skip-mirroring',
42
+ description: 'ne pas installer le système de mirroring'
43
+ },
44
+ {
45
+ flags: '--file <path>',
46
+ description: 'installer les modules qui se trouvent dans le fichier de configuration'
47
+ }
48
+ ],
49
+ action: async (options) => {
50
+ const chalk = getChalk();
51
+ try {
52
+ if (
53
+ (options.hooks && options.skipHooks) ||
54
+ (options.codeQuality && options.skipCodeQuality) ||
55
+ (options.mirroring && options.skipMirroring)
56
+ ) {
57
+ console.log(
58
+ chalk.red(
59
+ '❌ Options conflictuelles. Veuillez vérifier vos options.\n\t\tVous ne pouvez pas utiliser --hooks et --skip-hooks, --code-quality et --skip-code-quality, ou --mirroring et --skip-mirroring simultanément.'
60
+ )
61
+ );
62
+ return;
63
+ }
64
+
65
+ const config = options.file ? loadConfig(options.file) : null;
66
+ let selected = ['Hooks Git', 'Code Quality Tools', 'Mirroring'];
67
+ let preselectedHooks = false;
68
+ let preselectedCQT = [];
69
+ let preselectedMirroring = false;
70
+
71
+ if (options.file && config && config.install) {
72
+ if (config.install.hooks === true) {
73
+ preselectedHooks = true;
74
+ }
75
+ if (config.install.CQT && Array.isArray(config.install.CQT)) {
76
+ preselectedCQT = config.install.CQT;
77
+ }
78
+ if (config.install.mirroring === true) {
79
+ preselectedMirroring = true;
80
+ }
81
+ }
82
+
83
+ if (options.skipHooks) selected = selected.filter((item) => item !== 'Hooks Git');
84
+ if (options.skipCodeQuality) selected = selected.filter((item) => item !== 'Code Quality Tools');
85
+ if (options.skipMirroring) selected = selected.filter((item) => item !== 'Mirroring');
86
+ else if (
87
+ options.hooks ||
88
+ options.codeQuality ||
89
+ options.mirroring ||
90
+ options.skipHooks ||
91
+ options.skipCodeQuality ||
92
+ options.skipMirroring
93
+ ) {
94
+ selected = [];
95
+ if (options.hooks && !options.skipHooks) selected.push('Hooks Git');
96
+ if (options.codeQuality && !options.skipCodeQuality) selected.push('Code Quality Tools');
97
+ if (options.mirroring && !options.skipMirroring) selected.push('Mirroring');
98
+
99
+ /* istanbul ignore next */
100
+ if (
101
+ !options.hooks &&
102
+ !options.codeQuality &&
103
+ !options.mirroring &&
104
+ (options.skipHooks || options.skipCodeQuality || options.skipMirroring)
105
+ ) {
106
+ if (!options.skipHooks) selected.push('Hooks Git');
107
+ if (!options.skipCodeQuality) selected.push('Code Quality Tools');
108
+ if (!options.skipMirroring) selected.push('Mirroring');
109
+ }
110
+ } else {
111
+ if (options.all) {
112
+ selected = ['Hooks Git', 'Code Quality Tools', 'Mirroring'];
113
+ } else if (preselectedHooks || preselectedCQT.length > 0 || preselectedMirroring) {
114
+ selected = [];
115
+ if (preselectedHooks) selected.push('Hooks Git');
116
+ if (preselectedCQT.length > 0) selected.push('Code Quality Tools');
117
+ if (preselectedMirroring) selected.push('Mirroring');
118
+ } else {
119
+ selected = await interactiveMenu('Choisissez les modules à installer', selected);
120
+ }
121
+ }
122
+
123
+ const installers = {
124
+ 'Hooks Git': () => installHooks(['commit-msg', 'post-checkout', 'pre-push'], options.force),
125
+ 'Code Quality Tools': () => installCodeQualityTools(options.all, preselectedCQT),
126
+ Mirroring: () => installMirroringTools()
127
+ };
128
+
129
+ selected.forEach((item) => {
130
+ const installSelected = installers[item];
131
+ if (installSelected) installSelected();
132
+ });
133
+ } catch (error) {
134
+ console.error(chalk.red('❌ Une erreur est survenue :'), error.message);
135
+ }
136
+ }
137
+ };
@@ -0,0 +1,90 @@
1
+ const { loadConfig } = require('../../core/configManager');
2
+ const { SyncManager } = require('../../core/mirroring/syncManager');
3
+ const { generateWorkflow } = require('../../core/mirroring/generate');
4
+ const errorCMD = require('../../core/errorCMD');
5
+ const { getEnv, loadEnv } = require('../../core/module/env-loader');
6
+ const { getChalk } = require('../../utils/chalk-wrapper');
7
+
8
+ module.exports = {
9
+ name: 'mirror',
10
+ description: 'Référentiels miroirs sur plusieurs plateformes (GitHub, GitLab, BitBucket, Azure DevOps)',
11
+ options: [
12
+ { flags: '-s --source <platform>', description: 'Plateforme source (github, gitlab, bitbucket, azure)' },
13
+ { flags: '-t --target <platform>', description: 'Plateforme cible (github, gitlab, bitbucket, azure)' },
14
+ {
15
+ flags: '-r --repo <name>',
16
+ description: 'Nom du référentiel source (et cible si --target-repo non spécifié)'
17
+ },
18
+ { flags: '--source-repo <name>', description: 'Nom du référentiel source' },
19
+ { flags: '--target-repo <name>', description: 'Nom du référentiel cible' },
20
+ { flags: '--source-owner <owner>', description: 'Propriétaire du référentiel source (requis pour GitHub)' },
21
+ { flags: '--target-owner <owner>', description: 'Propriétaire du référentiel cible (requis pour GitHub)' },
22
+ { flags: '-S --sync-branches', description: 'Activer la synchronisation des branches' },
23
+ { flags: '-p --public-repo', description: 'Visibilité du mirroir en public' },
24
+ { flags: '-g --generate', description: 'Génére un workflow' }
25
+ ],
26
+ action: async (options) => {
27
+ const chalk = getChalk();
28
+ const normalizePlatform = (platform) => (platform ? String(platform).trim().toLowerCase() : platform);
29
+ try {
30
+ loadEnv();
31
+ const config = loadConfig();
32
+
33
+ if (options.generate) {
34
+ generateWorkflow();
35
+ return;
36
+ }
37
+
38
+ if (!config.mirroring) {
39
+ console.log(chalk.red('❌ Configuration de mise en miroir manquante dans push-guardian.config.json'));
40
+ process.exit(1);
41
+ }
42
+
43
+ const sourcePlatform = normalizePlatform(options.source || getEnv('SOURCE_PLATFORM'));
44
+ const targetPlatform = normalizePlatform(options.target || getEnv('TARGET_PLATFORM'));
45
+ const sourceRepo = options.sourceRepo || getEnv('SOURCE_REPO');
46
+ const targetRepo = options.targetRepo || options.repo || getEnv('TARGET_REPO');
47
+ const sourceOwner = options.sourceOwner || getEnv('SOURCE_OWNER');
48
+ const targetOwner = options.targetOwner || getEnv('TARGET_OWNER');
49
+
50
+ console.log(
51
+ `SP: ${sourcePlatform}\tTP: ${targetPlatform}\tSR: ${sourceRepo}\tTR: ${targetRepo}\tSO: ${sourceOwner}\tTO: ${targetOwner}`
52
+ );
53
+
54
+ if (!sourcePlatform || !targetPlatform || !sourceRepo || !targetRepo || !sourceOwner || !targetOwner) {
55
+ console.log(
56
+ chalk.red(
57
+ "❌ Plateformes, repos et propriétaires source/cible requis. Spécifiez-les via options ou configurez des valeurs par défaut lors de l'installation."
58
+ )
59
+ );
60
+ process.exit(1);
61
+ }
62
+
63
+ const platformsConfig = {
64
+ ...(config.mirroring.platforms || {})
65
+ };
66
+
67
+ [sourcePlatform, targetPlatform].filter(Boolean).forEach((platform) => {
68
+ platformsConfig[platform] = {
69
+ ...(platformsConfig[platform] || {}),
70
+ enabled: true
71
+ };
72
+ });
73
+
74
+ const syncManager = new SyncManager(platformsConfig);
75
+ await syncManager.mirror(
76
+ sourcePlatform,
77
+ targetPlatform,
78
+ sourceRepo,
79
+ targetRepo,
80
+ sourceOwner,
81
+ targetOwner,
82
+ options.syncBranches,
83
+ options.publicRepo
84
+ );
85
+ console.log(chalk.green('✅ Mise en miroir terminée avec succès'));
86
+ } catch (error) {
87
+ errorCMD(error);
88
+ }
89
+ }
90
+ };
@@ -0,0 +1,160 @@
1
+ const performanceAnalyzer = require('../../core/performance/performanceAnalyzer');
2
+ const metricsCollector = require('../../core/performance/metricsCollector');
3
+ const { getChalk } = require('../../utils/chalk-wrapper');
4
+ const chalk = getChalk();
5
+
6
+ module.exports = {
7
+ name: 'performance',
8
+ description: 'Analyser les performances de push-guardian',
9
+ options: [
10
+ {
11
+ flags: '-a, --analyze',
12
+ description: 'Analyser les performances actuelles'
13
+ },
14
+ {
15
+ flags: '-c, --compare <path>',
16
+ description: 'Comparer avec un rapport précédent'
17
+ },
18
+ {
19
+ flags: '-r, --report',
20
+ description: 'Afficher le dernier rapport'
21
+ },
22
+ {
23
+ flags: '--reset',
24
+ description: 'Réinitialiser les métriques'
25
+ },
26
+ {
27
+ flags: '-d, --detailed',
28
+ description: 'Afficher les détails complets'
29
+ },
30
+ {
31
+ flags: '-j, --json',
32
+ description: 'Sortie au format JSON'
33
+ }
34
+ ],
35
+ action: async (options) => {
36
+ try {
37
+ // Vérifier les options conflictuelles
38
+ const actions = [options.analyze, options.compare, options.report, options.reset].filter(Boolean);
39
+ if (actions.length > 1) {
40
+ console.log(
41
+ chalk.red(
42
+ '❌ Options conflictuelles. Veuillez choisir une seule action: --analyze, --compare, --report ou --reset'
43
+ )
44
+ );
45
+ return;
46
+ }
47
+
48
+ // Si aucune option n'est fournie, afficher l'aide
49
+ if (!options.analyze && !options.compare && !options.report && !options.reset) {
50
+ console.log(chalk.yellow('Actions disponibles:'));
51
+ console.log(chalk.cyan(' -a, --analyze') + ' - Analyser les performances actuelles');
52
+ console.log(chalk.cyan(' -c, --compare <path>') + ' - Comparer avec un rapport précédent');
53
+ console.log(chalk.cyan(' -r, --report') + ' - Afficher le dernier rapport');
54
+ console.log(chalk.cyan(' --reset') + ' - Réinitialiser les métriques');
55
+ console.log(chalk.gray('\nOptions supplémentaires: -d, --detailed, -j, --json'));
56
+ console.log(chalk.gray('Exemple: npx push-guardian performance --analyze --detailed'));
57
+ return;
58
+ }
59
+
60
+ // Exécuter l'action demandée
61
+ if (options.analyze) {
62
+ console.log(chalk.blue('📊 Analyse des performances en cours...'));
63
+ await performanceAnalyzer.analyze(options);
64
+ if (options.detailed) {
65
+ displayDetailedMetrics();
66
+ }
67
+ } else if (options.compare) {
68
+ console.log(chalk.blue('📊 Comparaison des rapports...'));
69
+ await performanceAnalyzer.compare(options.compare, options);
70
+ } else if (options.report) {
71
+ console.log(chalk.blue('📊 Rapport de performance actuel:'));
72
+ displayCurrentMetrics(options);
73
+ } else if (options.reset) {
74
+ metricsCollector.reset();
75
+ console.log(chalk.green('✅ Métriques réinitialisées'));
76
+ }
77
+ } catch (error) {
78
+ console.log(chalk.red('❌ Erreur:'), error.message);
79
+ process.exit(1);
80
+ }
81
+ }
82
+ };
83
+
84
+ /**
85
+ * Affiche les métriques actuelles
86
+ */
87
+ function displayCurrentMetrics(options) {
88
+ const metrics = metricsCollector.getMetrics();
89
+
90
+ if (options.json) {
91
+ console.log(JSON.stringify(metrics, null, 2));
92
+ return;
93
+ }
94
+
95
+ console.log(chalk.bold('\n📈 Métriques de performance:\n'));
96
+
97
+ // Métriques système
98
+ if (metrics.system) {
99
+ console.log(chalk.cyan('Système:'));
100
+ console.log(` RAM min: ${chalk.yellow(metrics.system.memory.min.toFixed(2))} MB`);
101
+ console.log(` RAM max: ${chalk.yellow(metrics.system.memory.max.toFixed(2))} MB`);
102
+ console.log(` RAM moyenne: ${chalk.yellow(metrics.system.memory.avg.toFixed(2))} MB`);
103
+ console.log(` Threads actifs: ${chalk.yellow(metrics.system.threads.active)}`);
104
+ console.log(` Ressources actives: ${chalk.yellow(metrics.system.threads.resources)}`);
105
+ }
106
+
107
+ if (metrics.validation) {
108
+ console.log(chalk.cyan('\nValidation:'));
109
+ console.log(` Durée: ${chalk.yellow(metrics.validation.duration || 0)}ms`);
110
+ console.log(` Fichiers analysés: ${chalk.yellow(metrics.validation.filesAnalyzed || 0)}`);
111
+ }
112
+
113
+ if (metrics.plugins) {
114
+ console.log(chalk.cyan('\nPlugins:'));
115
+ console.log(` Chargés: ${chalk.yellow(metrics.plugins.loaded || 0)}`);
116
+ console.log(` Temps de chargement: ${chalk.yellow(metrics.plugins.loadTime || 0)}ms`);
117
+ }
118
+
119
+ if (metrics.git) {
120
+ console.log(chalk.cyan('\nGit:'));
121
+ console.log(` Opérations: ${chalk.yellow(metrics.git.operations || 0)}`);
122
+ console.log(` Durée totale: ${chalk.yellow(metrics.git.duration || 0)}ms`);
123
+ }
124
+
125
+ // Afficher les statistiques calculées
126
+ if (metrics.stats) {
127
+ console.log(chalk.cyan('\nStatistiques:'));
128
+
129
+ if (metrics.stats.validation && metrics.stats.validation.count > 0) {
130
+ console.log(
131
+ ` Validations: ${chalk.yellow(metrics.stats.validation.count)} (${chalk.yellow(metrics.stats.validation.successRate.toFixed(1))}% succès)`
132
+ );
133
+ }
134
+
135
+ if (metrics.stats.hooks && metrics.stats.hooks.count > 0) {
136
+ console.log(
137
+ ` Hooks: ${chalk.yellow(metrics.stats.hooks.count)} (${chalk.yellow(metrics.stats.hooks.avgDuration.toFixed(2))}ms moy.)`
138
+ );
139
+ }
140
+
141
+ if (metrics.stats.linting && metrics.stats.linting.count > 0) {
142
+ console.log(
143
+ ` Linting: ${chalk.yellow(metrics.stats.linting.count)} (${chalk.yellow(metrics.stats.linting.avgDuration.toFixed(2))}ms moy.)`
144
+ );
145
+ }
146
+ }
147
+
148
+ console.log();
149
+ }
150
+
151
+ /**
152
+ * Affiche les métriques détaillées
153
+ */
154
+ function displayDetailedMetrics() {
155
+ const metrics = metricsCollector.getMetrics();
156
+
157
+ console.log(chalk.bold('\n🔍 Détails des métriques:\n'));
158
+ console.log(JSON.stringify(metrics, null, 2));
159
+ console.log();
160
+ }
@@ -0,0 +1,171 @@
1
+ const pluginRegistry = require('../../core/plugins/pluginRegistry');
2
+ const pluginManager = require('../../core/plugins/pluginManager');
3
+ const { getChalk } = require('../../utils/chalk-wrapper');
4
+ const chalk = getChalk();
5
+
6
+ module.exports = {
7
+ name: 'plugin',
8
+ description: 'Gestion des plugins push-guardian (list, commands, run, enable, disable, help)',
9
+ arguments: [
10
+ {
11
+ name: '<action>',
12
+ description: 'Action a executer (list, commands, run, enable, disable)'
13
+ },
14
+ { name: '[args...]', description: "Arguments de l'action" }
15
+ ],
16
+ action: async (action, args, options) => {
17
+ await pluginCommand([action, ...args], options);
18
+ }
19
+ };
20
+
21
+ /**
22
+ * Commande de gestion des plugins
23
+ * @param {Array} args - Arguments de la commande
24
+ * @param {Object} options - Options de la commande
25
+ */
26
+ async function pluginCommand(args, options) {
27
+ const action = args[0];
28
+
29
+ if (!action) {
30
+ displayHelp();
31
+ return;
32
+ }
33
+
34
+ switch (action) {
35
+ case 'enable':
36
+ await handleEnable(args[1]);
37
+ break;
38
+
39
+ case 'disable':
40
+ await handleDisable(args[1]);
41
+ break;
42
+
43
+ case 'list':
44
+ handleList();
45
+ break;
46
+
47
+ case 'commands':
48
+ handleCommands();
49
+ break;
50
+
51
+ case 'run':
52
+ await handleRun(args.slice(1), options);
53
+ break;
54
+
55
+ default:
56
+ console.log(chalk.red(`Action inconnue: ${action}`));
57
+ displayHelp();
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Active un plugin
63
+ */
64
+ async function handleEnable(pluginName) {
65
+ if (!pluginName) {
66
+ console.log(chalk.red('Nom du plugin requis'));
67
+ console.log(chalk.yellow('Usage: push-guardian plugin enable <nom>'));
68
+ return;
69
+ }
70
+
71
+ pluginRegistry.enablePlugin(pluginName);
72
+ }
73
+
74
+ /**
75
+ * Désactive un plugin
76
+ */
77
+ async function handleDisable(pluginName) {
78
+ if (!pluginName) {
79
+ console.log(chalk.red('Nom du plugin requis'));
80
+ console.log(chalk.yellow('Usage: push-guardian plugin disable <nom>'));
81
+ return;
82
+ }
83
+
84
+ pluginRegistry.disablePlugin(pluginName);
85
+ }
86
+
87
+ /**
88
+ * Liste les plugins installés
89
+ */
90
+ function handleList() {
91
+ pluginManager.listPlugins();
92
+ }
93
+
94
+ /**
95
+ * Liste les commandes disponibles
96
+ */
97
+ function handleCommands() {
98
+ pluginManager.displayCommands();
99
+ }
100
+
101
+ /**
102
+ * Exécute une commande d'un plugin
103
+ */
104
+ async function handleRun(runArgs, options) {
105
+ if (!runArgs || runArgs.length === 0) {
106
+ console.log(chalk.red('Nom de commande requis'));
107
+ console.log(chalk.yellow('Usage: push-guardian plugin run <commande> [args...]'));
108
+ console.log(chalk.yellow(' ou: push-guardian plugin run <plugin> <commande> [args...]'));
109
+ return;
110
+ }
111
+
112
+ let pluginName;
113
+ let commandName;
114
+ let args = [];
115
+
116
+ const [firstArg, secondArg] = runArgs;
117
+ const pluginInfo = pluginRegistry.getPluginInfo(firstArg);
118
+ const isExplicitPluginSyntax = Boolean(secondArg) && Boolean(pluginInfo);
119
+
120
+ if (isExplicitPluginSyntax) {
121
+ pluginName = firstArg;
122
+ commandName = secondArg;
123
+ args = runArgs.slice(2);
124
+ } else {
125
+ commandName = firstArg;
126
+ args = runArgs.slice(1);
127
+
128
+ const matches = pluginManager.listCommands().filter((cmd) => cmd.command === commandName);
129
+
130
+ if (matches.length === 0) {
131
+ console.log(chalk.red(`Commande plugin introuvable: ${commandName}`));
132
+ console.log(chalk.yellow('Utilisez: push-guardian plugin commands'));
133
+ return;
134
+ }
135
+
136
+ if (matches.length > 1) {
137
+ const plugins = [...new Set(matches.map((m) => m.plugin))].join(', ');
138
+ console.log(chalk.red(`Commande ambiguë: ${commandName}`));
139
+ console.log(chalk.yellow(`Plugins correspondants: ${plugins}`));
140
+ console.log(chalk.yellow(`Utilisez: push-guardian plugin run <plugin> ${commandName} [args...]`));
141
+ return;
142
+ }
143
+
144
+ pluginName = matches[0].plugin;
145
+ }
146
+
147
+ try {
148
+ const result = await pluginManager.executeCommand(pluginName, commandName, args, options);
149
+ if (result !== undefined) {
150
+ console.log(result);
151
+ }
152
+ } catch (error) {
153
+ console.log(chalk.red('Erreur:'), error.message);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Affiche l'aide
159
+ */
160
+ function displayHelp() {
161
+ console.log(chalk.bold('\nGestion des plugins push-guardian\n'));
162
+ console.log(chalk.cyan('Usage:') + ' push-guardian plugin <action> [options]\n');
163
+ console.log(chalk.bold('Actions disponibles:\n'));
164
+ console.log(' ' + chalk.green('enable <nom>') + ' - Active un plugin');
165
+ console.log(' ' + chalk.green('disable <nom>') + ' - Desactive un plugin');
166
+ console.log(' ' + chalk.green('list') + ' - Liste les plugins installes');
167
+ console.log(' ' + chalk.green('commands') + ' - Liste les commandes disponibles');
168
+ console.log(' ' + chalk.green('run <cmd> [args...]') + ' - Execute une commande unique');
169
+ console.log(' ' + chalk.green('run <plugin> <cmd>') + ' - Execute une commande de plugin');
170
+ console.log();
171
+ }