qwen-superpowers 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.
package/cli/index.js ADDED
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * qwen-superpowers CLI
5
+ * Interface en ligne de commande pour gérer les skills et le workflow Qwen Superpowers.
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ const ROOT_DIR = path.join(__dirname, '..');
12
+ const SKILLS_DIR = path.join(ROOT_DIR, 'skills');
13
+ const CONFIG_FILE = path.join(ROOT_DIR, '.qwen-powers.json');
14
+
15
+ // ─── Couleurs CLI ────────────────────────────────────────────────
16
+ const colors = {
17
+ reset: '\x1b[0m',
18
+ bold: '\x1b[1m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ red: '\x1b[31m',
23
+ cyan: '\x1b[36m',
24
+ gray: '\x1b[90m',
25
+ };
26
+
27
+ function colorize(color, text) {
28
+ return `${colors[color]}${text}${colors.reset}`;
29
+ }
30
+
31
+ // ─── Commandes ───────────────────────────────────────────────────
32
+
33
+ const commands = {
34
+ help() {
35
+ const lines = [
36
+ '',
37
+ colorize('bold', colorize('cyan', '+---------------------------------------------------------+')),
38
+ colorize('bold', colorize('cyan', '|') + ' Qwen Superpowers CLI v1.0.0 ' + colorize('bold', colorize('cyan', '|'))),
39
+ colorize('bold', colorize('cyan', '+---------------------------------------------------------+')),
40
+ '',
41
+ colorize('bold', 'USAGE:'),
42
+ ' ' + colorize('green', 'qwen-powers') + ' <commande> [options]',
43
+ '',
44
+ colorize('bold', 'COMMANDES:'),
45
+ ' ' + colorize('yellow', 'list') + ' Liste tous les skills disponibles',
46
+ ' ' + colorize('yellow', 'show') + ' <skill> Affiche les details d\'un skill',
47
+ ' ' + colorize('yellow', 'init') + ' [target-dir] Initialise le plugin dans un projet cible',
48
+ ' ' + colorize('yellow', 'enable') + ' <skill> Active un skill dans la config locale',
49
+ ' ' + colorize('yellow', 'disable') + ' <skill> Desactive un skill',
50
+ ' ' + colorize('yellow', 'workflow') + ' Affiche le workflow en 7 etapes',
51
+ ' ' + colorize('yellow', 'hook') + ' <name> Execute un hook (pre-execute, post-execute)',
52
+ ' ' + colorize('yellow', 'update') + ' Met a jour les skills depuis le depot',
53
+ ' ' + colorize('yellow', 'create') + ' <nom> Cree un nouveau skill (squelette)',
54
+ ' ' + colorize('yellow', 'help') + ' Affiche cette aide',
55
+ '',
56
+ colorize('bold', 'EXEMPLES:'),
57
+ ' ' + colorize('gray', 'qwen-powers list'),
58
+ ' ' + colorize('gray', 'qwen-powers show test-driven-development'),
59
+ ' ' + colorize('gray', 'qwen-powers init .'),
60
+ ' ' + colorize('gray', 'qwen-powers enable tdd'),
61
+ '',
62
+ ];
63
+ console.log(lines.join('\n'));
64
+ },
65
+
66
+ list() {
67
+ const skills = fs.readdirSync(SKILLS_DIR).filter(d =>
68
+ fs.statSync(path.join(SKILLS_DIR, d)).isDirectory()
69
+ );
70
+
71
+ console.log(`\n${colorize('bold', colorize('cyan', '📚 Skills disponibles:'))}\n`);
72
+
73
+ skills.forEach((skill, i) => {
74
+ const skillFile = path.join(SKILLS_DIR, skill, 'SKILL.md');
75
+ let description = 'Pas de description.';
76
+ if (fs.existsSync(skillFile)) {
77
+ const content = fs.readFileSync(skillFile, 'utf-8');
78
+ const match = content.match(/^#\s*(.+)$/m);
79
+ const descMatch = content.match(/^[>-]\s+(.+)$/m);
80
+ description = descMatch ? descMatch[1] : (match ? match[1] : 'Pas de description.');
81
+ }
82
+ const enabled = isSkillEnabled(skill);
83
+ const status = enabled
84
+ ? colorize('green', '✓ activé')
85
+ : colorize('gray', '○ désactivé');
86
+ console.log(` ${colorize('bold', colorize('yellow', `${i + 1}. ${skill}`))} ${status}`);
87
+ console.log(` ${colorize('gray', description)}\n`);
88
+ });
89
+
90
+ console.log(` ${colorize('gray', `Total: ${skills.length} skills`)}\n`);
91
+ },
92
+
93
+ show(skillName) {
94
+ if (!skillName) {
95
+ console.error(colorize('red', '❌ Usage: qwen-powers show <nom-du-skill>'));
96
+ process.exit(1);
97
+ }
98
+ const skillDir = path.join(SKILLS_DIR, skillName);
99
+ const skillFile = path.join(skillDir, 'SKILL.md');
100
+
101
+ if (!fs.existsSync(skillFile)) {
102
+ console.error(colorize('red', `❌ Skill '${skillName}' introuvable.`));
103
+ console.log(` Répertoire: ${skillDir}`);
104
+ process.exit(1);
105
+ }
106
+
107
+ const content = fs.readFileSync(skillFile, 'utf-8');
108
+ console.log(`\n${colorize('bold', colorize('cyan', '━'.repeat(60)))}`);
109
+ console.log(content);
110
+ console.log(colorize('bold', colorize('cyan', '━'.repeat(60))) + '\n');
111
+ },
112
+
113
+ init(targetDir) {
114
+ const dest = path.resolve(targetDir || '.');
115
+ console.log(`\n${colorize('bold', colorize('green', `🚀 Initialisation de Qwen Superpowers dans: ${dest}`))}\n`);
116
+
117
+ // Copier le fichier de config racine
118
+ const configTemplate = path.join(ROOT_DIR, '.qwen-powers.example.json');
119
+ const configDest = path.join(dest, '.qwen-powers.json');
120
+
121
+ if (!fs.existsSync(configDest)) {
122
+ if (fs.existsSync(configTemplate)) {
123
+ fs.copyFileSync(configTemplate, configDest);
124
+ console.log(colorize('green', ' ✓ .qwen-powers.json créé'));
125
+ } else {
126
+ const defaultConfig = {
127
+ version: "1.0.0",
128
+ enabledSkills: [],
129
+ workflow: {
130
+ enforceStages: true,
131
+ stages: [
132
+ "brainstorming",
133
+ "git-worktrees",
134
+ "writing-plans",
135
+ "subagent-driven-development",
136
+ "test-driven-development",
137
+ "code-review",
138
+ "finishing-branch"
139
+ ]
140
+ },
141
+ hooks: {
142
+ preExecute: [],
143
+ postExecute: []
144
+ },
145
+ settings: {
146
+ tddEnforced: true,
147
+ codeReviewRequired: true,
148
+ autoRunTests: true,
149
+ autoLint: true
150
+ }
151
+ };
152
+ fs.writeFileSync(configDest, JSON.stringify(defaultConfig, null, 2));
153
+ console.log(colorize('green', ' ✓ .qwen-powers.json créé avec la config par défaut'));
154
+ }
155
+ } else {
156
+ console.log(colorize('yellow', ' ⚠ .qwen-powers.json existe déjà'));
157
+ }
158
+
159
+ // Créer le dossier .qwen-skills symlink ou copie
160
+ const skillsDest = path.join(dest, '.qwen-skills');
161
+ if (!fs.existsSync(skillsDest)) {
162
+ try {
163
+ // Sur Windows, on copie au lieu de symlink pour éviter les problèmes de permissions
164
+ copyDirRecursive(SKILLS_DIR, skillsDest);
165
+ console.log(colorize('green', ' ✓ Skills copiés dans .qwen-skills/'));
166
+ } catch (e) {
167
+ console.error(colorize('red', ` ❌ Erreur copie skills: ${e.message}`));
168
+ }
169
+ }
170
+
171
+ console.log(`\n${colorize('bold', colorize('green', '✅ Initialisation terminée !'))}`);
172
+ console.log(colorize('gray', ' Exécutez `qwen-powers list` pour voir les skills disponibles.'));
173
+ console.log(colorize('gray', ' Modifiez .qwen-powers.json pour activer les skills.\n'));
174
+ },
175
+
176
+ enable(skillName) {
177
+ if (!skillName) {
178
+ console.error(colorize('red', '❌ Usage: qwen-powers enable <nom-du-skill>'));
179
+ process.exit(1);
180
+ }
181
+ const config = loadConfig();
182
+ if (!config.enabledSkills.includes(skillName)) {
183
+ config.enabledSkills.push(skillName);
184
+ saveConfig(config);
185
+ console.log(colorize('green', `✓ Skill '${skillName}' activé.`));
186
+ } else {
187
+ console.log(colorize('yellow', `⚠ Skill '${skillName}' déjà activé.`));
188
+ }
189
+ },
190
+
191
+ disable(skillName) {
192
+ if (!skillName) {
193
+ console.error(colorize('red', '❌ Usage: qwen-powers disable <nom-du-skill>'));
194
+ process.exit(1);
195
+ }
196
+ const config = loadConfig();
197
+ config.enabledSkills = config.enabledSkills.filter(s => s !== skillName);
198
+ saveConfig(config);
199
+ console.log(colorize('green', `✓ Skill '${skillName}' désactivé.`));
200
+ },
201
+
202
+ workflow() {
203
+ const stages = [
204
+ { num: 1, name: 'Brainstorming', desc: 'Questionnement socratique pour affiner l\'idee -> sauvegarde du design doc' },
205
+ { num: 2, name: 'Git Worktrees', desc: 'Creation d\'un workspace isole -> setup -> verification tests propres' },
206
+ { num: 3, name: 'Writing Plans', desc: 'Decoupage en taches de 2-5 min avec chemins, snippets et etapes de verification' },
207
+ { num: 4, name: 'Subagent-Driven Dev', desc: 'Dispatch d\'un sous-agent par tache -> revue 2 etapes -> continue autonome' },
208
+ { num: 5, name: 'Test-Driven Dev', desc: 'Enforce RED -> GREEN -> REFACTOR -> supprime code ecrit avant tests -> commit si passe' },
209
+ { num: 6, name: 'Code Review', desc: 'Compare le resultat au plan -> bloque si issues critiques -> rapport par severite' },
210
+ { num: 7, name: 'Branch Finalization', desc: 'Verifie tous les tests -> presente options merge/PR/garder/supprimer -> cleanup' },
211
+ ];
212
+
213
+ console.log('');
214
+ console.log(colorize('bold', colorize('cyan', '+---------------------------------------------------------+')));
215
+ console.log(colorize('bold', colorize('cyan', '|') + ' Workflow en 7 Etapes ' + colorize('bold', colorize('cyan', '|'))));
216
+ console.log(colorize('bold', colorize('cyan', '+---------------------------------------------------------+')));
217
+ console.log('');
218
+
219
+ stages.forEach(s => {
220
+ console.log(' ' + colorize('bold', colorize('yellow', 'Etape ' + s.num + ':')) + ' ' + colorize('bold', s.name));
221
+ console.log(' ' + colorize('gray', s.desc));
222
+ console.log('');
223
+ });
224
+ },
225
+
226
+ hook(hookName) {
227
+ if (!hookName) {
228
+ console.error(colorize('red', '❌ Usage: qwen-powers hook <nom-du-hook>'));
229
+ process.exit(1);
230
+ }
231
+ const hookFile = path.join(ROOT_DIR, 'hooks', `${hookName}.js`);
232
+ if (!fs.existsSync(hookFile)) {
233
+ console.error(colorize('red', `❌ Hook '${hookName}' introuvable.`));
234
+ process.exit(1);
235
+ }
236
+ require(hookFile);
237
+ },
238
+
239
+ update() {
240
+ console.log(colorize('yellow', '⚠ La mise à jour automatique sera implémentée prochainement.'));
241
+ console.log(colorize('gray', ' Pour l\'instant, mettez à jour via: npm update qwen-superpowers'));
242
+ },
243
+
244
+ create(skillName) {
245
+ if (!skillName) {
246
+ console.error(colorize('red', '❌ Usage: qwen-powers create <nom-du-skill>'));
247
+ process.exit(1);
248
+ }
249
+ const slug = skillName.toLowerCase().replace(/\s+/g, '-');
250
+ const skillDir = path.join(SKILLS_DIR, slug);
251
+
252
+ if (fs.existsSync(skillDir)) {
253
+ console.error(colorize('red', `❌ Le skill '${slug}' existe déjà.`));
254
+ process.exit(1);
255
+ }
256
+
257
+ fs.mkdirSync(skillDir, { recursive: true });
258
+
259
+ const template = `# ${slug.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}
260
+
261
+ > Décrivez brièvement le but de ce skill.
262
+
263
+ ## Triggers
264
+ - Quand utiliser ce skill ?
265
+ - Quels motifs ou contextes déclenchent ce skill ?
266
+
267
+ ## Instructions
268
+ 1. Étape 1
269
+ 2. Étape 2
270
+ 3. Étape 3
271
+
272
+ ## Règles
273
+ - Règle 1
274
+ - Règle 2
275
+
276
+ ## Sortie attendue
277
+ - Que doit produire l'agent après exécution ?
278
+
279
+ ## Exemple d'utilisation
280
+ \`\`\`
281
+ Exemple concret d'invocation et de résultat attendu.
282
+ \`\`\`
283
+ `;
284
+
285
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), template);
286
+ console.log(colorize('green', `✓ Skill '${slug}' créé dans: ${skillDir}`));
287
+ },
288
+ };
289
+
290
+ // ─── Helpers ─────────────────────────────────────────────────────
291
+
292
+ function loadConfig() {
293
+ if (fs.existsSync(CONFIG_FILE)) {
294
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
295
+ }
296
+ return { enabledSkills: [], workflow: { enforceStages: true }, settings: {} };
297
+ }
298
+
299
+ function saveConfig(config) {
300
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
301
+ }
302
+
303
+ function isSkillEnabled(skillName) {
304
+ const config = loadConfig();
305
+ return config.enabledSkills && config.enabledSkills.includes(skillName);
306
+ }
307
+
308
+ function copyDirRecursive(src, dest) {
309
+ if (!fs.existsSync(dest)) {
310
+ fs.mkdirSync(dest, { recursive: true });
311
+ }
312
+ const entries = fs.readdirSync(src, { withFileTypes: true });
313
+ for (const entry of entries) {
314
+ const srcPath = path.join(src, entry.name);
315
+ const destPath = path.join(dest, entry.name);
316
+ if (entry.isDirectory()) {
317
+ copyDirRecursive(srcPath, destPath);
318
+ } else {
319
+ fs.copyFileSync(srcPath, destPath);
320
+ }
321
+ }
322
+ }
323
+
324
+ // ─── Point d'entrée ──────────────────────────────────────────────
325
+
326
+ const args = process.argv.slice(2);
327
+ const command = args[0] || 'help';
328
+
329
+ if (commands[command]) {
330
+ commands[command](...args.slice(1));
331
+ } else {
332
+ console.error(colorize('red', `❌ Commande '${command}' inconnue.`));
333
+ commands.help();
334
+ process.exit(1);
335
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Hook: post-execute
3
+ *
4
+ * Exécuté APRÈS qu'une tâche ou une commande est terminée.
5
+ * Utilisé pour vérifier le résultat, nettoyer l'environnement,
6
+ * et préparer l'étape suivante du workflow.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const ROOT = process.cwd();
13
+ const CONFIG_FILE = path.join(ROOT, '.qwen-powers.json');
14
+
15
+ function postExecute(context) {
16
+ const config = loadConfig();
17
+ const results = {
18
+ success: true,
19
+ nextSteps: [],
20
+ cleanup: [],
21
+ warnings: []
22
+ };
23
+
24
+ // Vérification post-TDD
25
+ if (config.settings && config.settings.tddEnforced && context.type === 'code') {
26
+ const testsPassed = context.testResults && context.testResults.passed;
27
+ if (!testsPassed) {
28
+ results.success = false;
29
+ results.warnings.push({
30
+ type: 'TDD_VIOLATION',
31
+ message: 'Les tests ne passent pas. Impossible de continuer ou de commit.'
32
+ });
33
+ }
34
+ }
35
+
36
+ // Vérification code review
37
+ if (config.settings && config.settings.codeReviewRequired) {
38
+ if (!context.reviewDone) {
39
+ results.nextSteps.push({
40
+ action: 'code-review',
41
+ message: 'Une revue de code est requise avant de continuer. Utilisez le skill `code-review`.'
42
+ });
43
+ }
44
+ }
45
+
46
+ // Auto-lint check
47
+ if (config.settings && config.settings.autoLint) {
48
+ results.nextSteps.push({
49
+ action: 'lint',
50
+ message: 'Vérifiez le linting: `npm run lint` (ou équivalent)'
51
+ });
52
+ }
53
+
54
+ // Prochaine étape du workflow
55
+ if (config.workflow && config.workflow.enforceStages) {
56
+ const currentStage = context.currentStage;
57
+ const stages = config.workflow.stages;
58
+ if (currentStage && stages) {
59
+ const currentIndex = stages.indexOf(currentStage);
60
+ if (currentIndex >= 0 && currentIndex < stages.length - 1) {
61
+ const nextStage = stages[currentIndex + 1];
62
+ results.nextSteps.push({
63
+ action: `workflow-next`,
64
+ message: `Prochaine étape du workflow: ${nextStage}`
65
+ });
66
+ } else if (currentIndex === stages.length - 1) {
67
+ results.nextSteps.push({
68
+ action: 'workflow-complete',
69
+ message: 'Workflow en 7 étapes terminé ! Prêt pour merge/PR.'
70
+ });
71
+ }
72
+ }
73
+ }
74
+
75
+ // Suggestions de cleanup
76
+ results.cleanup.push({
77
+ action: 'remove-debug',
78
+ message: 'Supprimer les logs/console.log de debug ajoutés pendant le dev'
79
+ });
80
+ results.cleanup.push({
81
+ action: 'format',
82
+ message: 'Formater le code: `npm run format` (ou équivalent)'
83
+ });
84
+
85
+ return results;
86
+ }
87
+
88
+ function loadConfig() {
89
+ if (fs.existsSync(CONFIG_FILE)) {
90
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
91
+ }
92
+ return { settings: {}, workflow: {} };
93
+ }
94
+
95
+ // Export pour utilisation par le système de hooks
96
+ module.exports = { postExecute };
97
+
98
+ // Si exécuté directement via CLI
99
+ if (require.main === module) {
100
+ const context = JSON.parse(process.argv[2] || '{}');
101
+ const result = postExecute(context);
102
+ console.log(JSON.stringify(result, null, 2));
103
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Hook: pre-execute
3
+ *
4
+ * Exécuté AVANT qu'une tâche ou une commande ne soit lancée.
5
+ * Utilisé pour valider l'environnement, sauvegarder l'état, ou blocker
6
+ * si des conditions ne sont pas remplies.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const ROOT = process.cwd();
13
+ const CONFIG_FILE = path.join(ROOT, '.qwen-powers.json');
14
+
15
+ function preExecute(context) {
16
+ const config = loadConfig();
17
+ const results = {
18
+ blocked: false,
19
+ warnings: [],
20
+ checks: []
21
+ };
22
+
23
+ // Check 1: TDD enforcement
24
+ if (config.settings && config.settings.tddEnforced && context.type === 'code') {
25
+ const hasTest = context.files && context.files.some(f => isTestFile(f));
26
+ if (!hasTest) {
27
+ results.blocked = true;
28
+ results.checks.push({
29
+ name: 'TDD Check',
30
+ status: '❌ FAIL',
31
+ message: 'Aucun fichier de test détecté. Le TDD est activé — écrivez le test d\'abord.'
32
+ });
33
+ } else {
34
+ results.checks.push({
35
+ name: 'TDD Check',
36
+ status: '✅ PASS',
37
+ message: 'Fichier de test détecté.'
38
+ });
39
+ }
40
+ }
41
+
42
+ // Check 2: Branch isolation
43
+ if (config.workflow && config.workflow.enforceStages) {
44
+ const currentBranch = getCurrentBranch();
45
+ if (currentBranch === 'main' || currentBranch === 'master') {
46
+ results.blocked = true;
47
+ results.checks.push({
48
+ name: 'Branch Check',
49
+ status: '❌ FAIL',
50
+ message: `Vous êtes sur la branche '${currentBranch}'. Créez une branche de feature ou utilisez un worktree.`
51
+ });
52
+ } else {
53
+ results.checks.push({
54
+ name: 'Branch Check',
55
+ status: '✅ PASS',
56
+ message: `Branche de travail: ${currentBranch}`
57
+ });
58
+ }
59
+ }
60
+
61
+ // Check 3: Tests passing baseline
62
+ if (config.settings && config.settings.autoRunTests) {
63
+ results.checks.push({
64
+ name: 'Baseline Check',
65
+ status: '⚠️ SKIP',
66
+ message: 'Vérification des tests existants — assurez-vous que les tests passent avant de commencer.'
67
+ });
68
+ }
69
+
70
+ return results;
71
+ }
72
+
73
+ function loadConfig() {
74
+ if (fs.existsSync(CONFIG_FILE)) {
75
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
76
+ }
77
+ return { settings: {}, workflow: {} };
78
+ }
79
+
80
+ function isTestFile(filePath) {
81
+ const testPatterns = ['.test.', '.spec.', '__tests__', '/test/', '_test.py', 'test_'];
82
+ return testPatterns.some(p => filePath.includes(p));
83
+ }
84
+
85
+ function getCurrentBranch() {
86
+ try {
87
+ const { execSync } = require('child_process');
88
+ return execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
89
+ } catch {
90
+ return 'unknown';
91
+ }
92
+ }
93
+
94
+ // Export pour utilisation par le système de hooks
95
+ module.exports = { preExecute };
96
+
97
+ // Si exécuté directement via CLI
98
+ if (require.main === module) {
99
+ const context = JSON.parse(process.argv[2] || '{}');
100
+ const result = preExecute(context);
101
+ console.log(JSON.stringify(result, null, 2));
102
+
103
+ if (result.blocked) {
104
+ process.exit(1);
105
+ }
106
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "qwen-superpowers",
3
+ "version": "1.0.0",
4
+ "description": "Plugin de super-pouvoirs pour Qwen Code — Transforme l'agent IA en ingenieur logiciel discipline avec workflow structure, TDD, revue de code et developpement par sous-agents.",
5
+ "main": "cli/index.js",
6
+ "bin": {
7
+ "qwen-powers": "./cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node cli/index.js help",
11
+ "list": "node cli/index.js list",
12
+ "install-plugin": "node scripts/install.js",
13
+ "verify": "node scripts/verify-skills.js",
14
+ "test": "npm run verify && npm run test:cli",
15
+ "test:cli": "node cli/index.js help && node cli/index.js list && node cli/index.js workflow",
16
+ "prepublishOnly": "npm test",
17
+ "preversion": "npm test",
18
+ "version": "node scripts/version.js && git add CHANGELOG.md",
19
+ "postversion": "echo \"Nouvelle version: $(node -p \"require('./package.json').version\")\""
20
+ },
21
+ "keywords": [
22
+ "qwen",
23
+ "qwen-code",
24
+ "cli",
25
+ "skills",
26
+ "plugin",
27
+ "tdd",
28
+ "code-review",
29
+ "workflow",
30
+ "ai-agent",
31
+ "superpowers",
32
+ "agent",
33
+ "coding-assistant"
34
+ ],
35
+ "author": "",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/stelio-fondation/qwen-superpowers"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/stelio-fondation/qwen-superpowers/issues"
43
+ },
44
+ "homepage": "https://github.com/stelio-fondation/qwen-superpowers#readme",
45
+ "files": [
46
+ "cli/",
47
+ "skills/",
48
+ "hooks/",
49
+ "scripts/",
50
+ "AGENTS.md",
51
+ "README.md",
52
+ "CHANGELOG.md",
53
+ "LICENSE",
54
+ "package.json"
55
+ ],
56
+ "engines": {
57
+ "node": ">=18.0.0"
58
+ },
59
+ "os": [
60
+ "win32",
61
+ "darwin",
62
+ "linux"
63
+ ],
64
+ "preferGlobal": true
65
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Script d'installation pour qwen-superpowers
3
+ * Copie les fichiers nécessaires dans le projet cible.
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const ROOT = path.join(__dirname, '..');
10
+
11
+ function install(targetDir) {
12
+ const dest = path.resolve(targetDir || process.cwd());
13
+ console.log(`\n🚀 Installation de Qwen Superpowers dans: ${dest}\n`);
14
+
15
+ // 1. Copier AGENTS.md
16
+ const agentsSrc = path.join(ROOT, 'AGENTS.md');
17
+ const agentsDest = path.join(dest, 'AGENTS.md');
18
+ if (!fs.existsSync(agentsDest)) {
19
+ fs.copyFileSync(agentsSrc, agentsDest);
20
+ console.log(' ✓ AGENTS.md copié');
21
+ } else {
22
+ console.log(' ⚠ AGENTS.md existe déjà');
23
+ }
24
+
25
+ // 2. Copier .qwen-powers.json
26
+ const configSrc = path.join(ROOT, '.qwen-powers.example.json');
27
+ const configDest = path.join(dest, '.qwen-powers.json');
28
+ if (!fs.existsSync(configDest)) {
29
+ fs.copyFileSync(configSrc, configDest);
30
+ console.log(' ✓ .qwen-powers.json créé');
31
+ } else {
32
+ console.log(' ⚠ .qwen-powers.json existe déjà');
33
+ }
34
+
35
+ // 3. Copier les skills
36
+ const skillsSrc = path.join(ROOT, 'skills');
37
+ const skillsDest = path.join(dest, '.qwen-skills');
38
+ if (!fs.existsSync(skillsDest)) {
39
+ copyDirRecursive(skillsSrc, skillsDest);
40
+ console.log(' ✓ Skills copiés dans .qwen-skills/');
41
+ } else {
42
+ console.log(' ⚠ .qwen-skills/ existe déjà');
43
+ }
44
+
45
+ console.log('\n✅ Installation terminée !');
46
+ console.log(' Modifiez .qwen-powers.json pour configurer les skills.\n');
47
+ }
48
+
49
+ function copyDirRecursive(src, dest) {
50
+ if (!fs.existsSync(dest)) {
51
+ fs.mkdirSync(dest, { recursive: true });
52
+ }
53
+ const entries = fs.readdirSync(src, { withFileTypes: true });
54
+ for (const entry of entries) {
55
+ const srcPath = path.join(src, entry.name);
56
+ const destPath = path.join(dest, entry.name);
57
+ if (entry.isDirectory()) {
58
+ copyDirRecursive(srcPath, destPath);
59
+ } else {
60
+ fs.copyFileSync(srcPath, destPath);
61
+ }
62
+ }
63
+ }
64
+
65
+ // Exécuter si appelé directement
66
+ if (require.main === module) {
67
+ const target = process.argv[2] || '.';
68
+ install(target);
69
+ }
70
+
71
+ module.exports = { install };