swift-code-reviewer-skill 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/CONTRIBUTING.md +86 -3
- package/README.md +315 -118
- package/bin/install.js +71 -90
- package/bin/lib/agents.js +134 -0
- package/bin/lib/prompt.js +58 -0
- package/core/swift-code-reviewer.core.md +281 -0
- package/examples/README.md +35 -0
- package/examples/claude-tca-review.md +140 -0
- package/examples/codex-async-algorithms-review.md +208 -0
- package/examples/gemini-isowords-review.md +207 -0
- package/package.json +6 -1
- package/templates/agents/claude/swift-code-reviewer.md +78 -0
- package/templates/agents/codex/swift-code-reviewer.md +211 -0
- package/templates/agents/gemini/swift-code-reviewer.md +211 -0
- package/templates/agents/kiro/swift-code-reviewer.md +218 -0
- package/templates/commands/claude/review.md +56 -0
- package/templates/commands/gemini/review.toml +15 -0
package/bin/install.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
2
3
|
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
@@ -6,7 +7,6 @@ const os = require('os');
|
|
|
6
7
|
|
|
7
8
|
const SKILL_NAME = 'swift-code-reviewer-skill';
|
|
8
9
|
|
|
9
|
-
// Colors for terminal output
|
|
10
10
|
const colors = {
|
|
11
11
|
reset: '\x1b[0m',
|
|
12
12
|
bright: '\x1b[1m',
|
|
@@ -14,7 +14,7 @@ const colors = {
|
|
|
14
14
|
yellow: '\x1b[33m',
|
|
15
15
|
blue: '\x1b[34m',
|
|
16
16
|
red: '\x1b[31m',
|
|
17
|
-
cyan: '\x1b[36m'
|
|
17
|
+
cyan: '\x1b[36m',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
function log(message, color = colors.reset) {
|
|
@@ -23,14 +23,9 @@ function log(message, color = colors.reset) {
|
|
|
23
23
|
|
|
24
24
|
function copyRecursive(src, dest) {
|
|
25
25
|
const stats = fs.statSync(src);
|
|
26
|
-
|
|
27
26
|
if (stats.isDirectory()) {
|
|
28
|
-
if (!fs.existsSync(dest)) {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const entries = fs.readdirSync(src);
|
|
33
|
-
for (const entry of entries) {
|
|
27
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
28
|
+
for (const entry of fs.readdirSync(src)) {
|
|
34
29
|
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
35
30
|
}
|
|
36
31
|
} else {
|
|
@@ -39,18 +34,20 @@ function copyRecursive(src, dest) {
|
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
function findPackageRoot() {
|
|
42
|
-
let
|
|
43
|
-
while (!fs.existsSync(path.join(
|
|
44
|
-
const parent = path.dirname(
|
|
45
|
-
if (parent ===
|
|
37
|
+
let dir = __dirname;
|
|
38
|
+
while (!fs.existsSync(path.join(dir, 'SKILL.md'))) {
|
|
39
|
+
const parent = path.dirname(dir);
|
|
40
|
+
if (parent === dir) {
|
|
46
41
|
log(' Error: Could not find SKILL.md in package', colors.red);
|
|
47
42
|
process.exit(1);
|
|
48
43
|
}
|
|
49
|
-
|
|
44
|
+
dir = parent;
|
|
50
45
|
}
|
|
51
|
-
return
|
|
46
|
+
return dir;
|
|
52
47
|
}
|
|
53
48
|
|
|
49
|
+
// ---------- global install (default command) ----------
|
|
50
|
+
|
|
54
51
|
function install() {
|
|
55
52
|
log('\n Swift Code Reviewer Skill Installer', colors.cyan + colors.bright);
|
|
56
53
|
log(' ====================================\n', colors.cyan);
|
|
@@ -76,113 +73,70 @@ function install() {
|
|
|
76
73
|
|
|
77
74
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
78
75
|
|
|
79
|
-
const filesToCopy = [
|
|
80
|
-
|
|
81
|
-
'README.md',
|
|
82
|
-
'LICENSE',
|
|
83
|
-
'CONTRIBUTING.md',
|
|
84
|
-
'CHANGELOG.md'
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
const dirsToCopy = ['references', 'skills', 'templates'];
|
|
76
|
+
const filesToCopy = ['SKILL.md', 'README.md', 'LICENSE', 'CONTRIBUTING.md', 'CHANGELOG.md'];
|
|
77
|
+
const dirsToCopy = ['core', 'references', 'skills', 'templates'];
|
|
88
78
|
|
|
89
79
|
for (const file of filesToCopy) {
|
|
90
80
|
const src = path.join(packageRoot, file);
|
|
91
|
-
const dest = path.join(targetDir, file);
|
|
92
|
-
|
|
93
81
|
if (fs.existsSync(src)) {
|
|
94
|
-
fs.copyFileSync(src,
|
|
82
|
+
fs.copyFileSync(src, path.join(targetDir, file));
|
|
95
83
|
log(` Copied: ${file}`, colors.green);
|
|
96
84
|
}
|
|
97
85
|
}
|
|
98
86
|
|
|
99
87
|
for (const dir of dirsToCopy) {
|
|
100
88
|
const src = path.join(packageRoot, dir);
|
|
101
|
-
const dest = path.join(targetDir, dir);
|
|
102
|
-
|
|
103
89
|
if (fs.existsSync(src)) {
|
|
104
|
-
copyRecursive(src,
|
|
90
|
+
copyRecursive(src, path.join(targetDir, dir));
|
|
105
91
|
log(` Copied: ${dir}/`, colors.green);
|
|
106
92
|
}
|
|
107
93
|
}
|
|
108
94
|
|
|
109
95
|
log('\n Installation complete!', colors.green + colors.bright);
|
|
110
96
|
log('\n The skill is now available in Claude Code.', colors.reset);
|
|
111
|
-
log(' To
|
|
97
|
+
log(' To scaffold the review agent into a project, run:', colors.reset);
|
|
112
98
|
log('\n npx swift-code-reviewer-skill init\n', colors.cyan);
|
|
113
|
-
log(' Or use it directly by asking Claude to:', colors.reset);
|
|
114
|
-
log('\n - "Review this PR"', colors.cyan);
|
|
115
|
-
log(' - "Review LoginView.swift"', colors.cyan);
|
|
116
|
-
log(' - "Review my uncommitted changes"', colors.cyan);
|
|
117
|
-
log(' - "Check if this follows our coding standards"\n', colors.cyan);
|
|
118
|
-
|
|
119
99
|
log(` Skill location: ${targetDir}`, colors.blue);
|
|
120
100
|
log(' Documentation: https://github.com/Viniciuscarvalho/swift-code-reviewer-skill\n', colors.blue);
|
|
121
101
|
}
|
|
122
102
|
|
|
123
|
-
|
|
103
|
+
// ---------- init (project scaffolding) ----------
|
|
104
|
+
|
|
105
|
+
async function init(flags) {
|
|
124
106
|
log('\n Swift Code Reviewer — Project Setup', colors.cyan + colors.bright);
|
|
125
107
|
log(' =====================================\n', colors.cyan);
|
|
126
108
|
|
|
127
109
|
const packageRoot = findPackageRoot();
|
|
128
110
|
const cwd = process.cwd();
|
|
129
111
|
|
|
130
|
-
// Verify we're in a git repo (likely a real project)
|
|
131
112
|
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
132
113
|
log(' Warning: Not a git repository. Running anyway.\n', colors.yellow);
|
|
133
114
|
}
|
|
134
115
|
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const commandsDir = path.join(claudeDir, 'commands');
|
|
116
|
+
const { selectAgents } = require('./lib/prompt');
|
|
117
|
+
const { AGENT_INSTALLERS } = require('./lib/agents');
|
|
138
118
|
|
|
139
|
-
const
|
|
140
|
-
|
|
119
|
+
const agents = await selectAgents({
|
|
120
|
+
allFlag: flags.all,
|
|
121
|
+
agentFlag: flags.agent,
|
|
122
|
+
isTTY: Boolean(process.stdout.isTTY),
|
|
123
|
+
});
|
|
141
124
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
log(' npx swift-code-reviewer-skill\n', colors.cyan);
|
|
146
|
-
process.exit(1);
|
|
125
|
+
if (agents.length === 0) {
|
|
126
|
+
log(' No agents selected. Nothing to do.\n', colors.yellow);
|
|
127
|
+
return;
|
|
147
128
|
}
|
|
148
129
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
let created = 0;
|
|
153
|
-
let skipped = 0;
|
|
154
|
-
|
|
155
|
-
// Copy agent
|
|
156
|
-
const agentDest = path.join(agentsDir, 'swift-code-reviewer.md');
|
|
157
|
-
if (fs.existsSync(agentDest)) {
|
|
158
|
-
log(' Skipped: .claude/agents/swift-code-reviewer.md (already exists)', colors.yellow);
|
|
159
|
-
skipped++;
|
|
160
|
-
} else {
|
|
161
|
-
fs.copyFileSync(templateAgentSrc, agentDest);
|
|
162
|
-
log(' Created: .claude/agents/swift-code-reviewer.md', colors.green);
|
|
163
|
-
created++;
|
|
130
|
+
const opts = { dryRun: flags.dryRun, force: flags.force };
|
|
131
|
+
for (const agent of agents) {
|
|
132
|
+
AGENT_INSTALLERS[agent](packageRoot, cwd, opts);
|
|
164
133
|
}
|
|
165
134
|
|
|
166
|
-
|
|
167
|
-
const commandDest = path.join(commandsDir, 'review.md');
|
|
168
|
-
if (fs.existsSync(commandDest)) {
|
|
169
|
-
log(' Skipped: .claude/commands/review.md (already exists)', colors.yellow);
|
|
170
|
-
skipped++;
|
|
171
|
-
} else {
|
|
172
|
-
fs.copyFileSync(templateCommandSrc, commandDest);
|
|
173
|
-
log(' Created: .claude/commands/review.md', colors.green);
|
|
174
|
-
created++;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
log(`\n Done! ${created} file(s) created, ${skipped} skipped.`, colors.green + colors.bright);
|
|
178
|
-
|
|
179
|
-
if (created > 0) {
|
|
180
|
-
log('\n Usage:', colors.reset);
|
|
181
|
-
log(' /review — run a full code review before pushing', colors.cyan);
|
|
182
|
-
log(' @swift-code-reviewer — invoke the agent directly\n', colors.cyan);
|
|
183
|
-
}
|
|
135
|
+
log('\n Done!\n', colors.green + colors.bright);
|
|
184
136
|
}
|
|
185
137
|
|
|
138
|
+
// ---------- uninstall ----------
|
|
139
|
+
|
|
186
140
|
function uninstall() {
|
|
187
141
|
log('\n Uninstalling Swift Code Reviewer Skill', colors.yellow + colors.bright);
|
|
188
142
|
log(' ======================================\n', colors.yellow);
|
|
@@ -199,29 +153,56 @@ function uninstall() {
|
|
|
199
153
|
}
|
|
200
154
|
}
|
|
201
155
|
|
|
156
|
+
// ---------- help ----------
|
|
157
|
+
|
|
202
158
|
function showHelp() {
|
|
203
159
|
log('\n Swift Code Reviewer Skill', colors.cyan + colors.bright);
|
|
204
160
|
log(' =========================\n', colors.cyan);
|
|
205
|
-
log(' Usage: npx swift-code-reviewer-skill [command]\n', colors.reset);
|
|
161
|
+
log(' Usage: npx swift-code-reviewer-skill [command] [options]\n', colors.reset);
|
|
206
162
|
log(' Commands:', colors.bright);
|
|
207
163
|
log(' (none) Install the skill to ~/.claude/skills/', colors.reset);
|
|
208
|
-
log(' init Scaffold
|
|
164
|
+
log(' init Scaffold review agent into the current project', colors.reset);
|
|
209
165
|
log(' uninstall Remove the skill from ~/.claude/skills/', colors.reset);
|
|
210
166
|
log(' help Show this help message\n', colors.reset);
|
|
167
|
+
log(' Options for init:', colors.bright);
|
|
168
|
+
log(' --agent <name[,name]> Target specific agent(s): claude, codex, gemini, kiro', colors.reset);
|
|
169
|
+
log(' --all Install for all supported agents', colors.reset);
|
|
170
|
+
log(' --force Overwrite existing files', colors.reset);
|
|
171
|
+
log(' --dry-run Preview writes without touching the filesystem\n', colors.reset);
|
|
211
172
|
log(' Examples:', colors.bright);
|
|
212
|
-
log(' npx swift-code-reviewer-skill # install skill globally', colors.cyan);
|
|
213
|
-
log(' npx swift-code-reviewer-skill init #
|
|
214
|
-
log(' npx swift-code-reviewer-skill
|
|
173
|
+
log(' npx swift-code-reviewer-skill # install skill globally (Claude)', colors.cyan);
|
|
174
|
+
log(' npx swift-code-reviewer-skill init # interactive agent picker', colors.cyan);
|
|
175
|
+
log(' npx swift-code-reviewer-skill init --all # all agents at once', colors.cyan);
|
|
176
|
+
log(' npx swift-code-reviewer-skill init --agent codex,gemini', colors.cyan);
|
|
177
|
+
log(' npx swift-code-reviewer-skill init --dry-run', colors.cyan);
|
|
178
|
+
log(' npx swift-code-reviewer-skill uninstall\n', colors.cyan);
|
|
215
179
|
}
|
|
216
180
|
|
|
217
|
-
//
|
|
181
|
+
// ---------- arg parsing ----------
|
|
182
|
+
|
|
183
|
+
function parseFlags(argv) {
|
|
184
|
+
const flags = { all: false, agent: null, dryRun: false, force: false };
|
|
185
|
+
for (let i = 0; i < argv.length; i++) {
|
|
186
|
+
const arg = argv[i];
|
|
187
|
+
if (arg === '--all') flags.all = true;
|
|
188
|
+
else if (arg === '--dry-run') flags.dryRun = true;
|
|
189
|
+
else if (arg === '--force') flags.force = true;
|
|
190
|
+
else if (arg === '--agent' && argv[i + 1]) { flags.agent = argv[++i]; }
|
|
191
|
+
else if (arg.startsWith('--agent=')) { flags.agent = arg.slice('--agent='.length); }
|
|
192
|
+
}
|
|
193
|
+
return flags;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------- entry point ----------
|
|
197
|
+
|
|
218
198
|
const args = process.argv.slice(2);
|
|
219
|
-
const command = args
|
|
199
|
+
const command = args.find((a) => !a.startsWith('-')) || '';
|
|
200
|
+
const flags = parseFlags(args.filter((a) => a.startsWith('-') || args.indexOf(a) > 0));
|
|
220
201
|
|
|
221
202
|
switch (command) {
|
|
222
203
|
case 'init':
|
|
223
204
|
case 'setup':
|
|
224
|
-
init();
|
|
205
|
+
init(flags).catch((err) => { console.error(err); process.exit(1); });
|
|
225
206
|
break;
|
|
226
207
|
case 'uninstall':
|
|
227
208
|
case 'remove':
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const colors = {
|
|
7
|
+
reset: '\x1b[0m',
|
|
8
|
+
bright: '\x1b[1m',
|
|
9
|
+
green: '\x1b[32m',
|
|
10
|
+
yellow: '\x1b[33m',
|
|
11
|
+
blue: '\x1b[34m',
|
|
12
|
+
cyan: '\x1b[36m',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function log(msg, color = colors.reset) {
|
|
16
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function writeFile(dest, src, label, dryRun) {
|
|
20
|
+
const exists = fs.existsSync(dest);
|
|
21
|
+
if (dryRun) {
|
|
22
|
+
log(` [dry-run] ${exists ? 'Update' : 'Create'}: ${label}`, colors.blue);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (exists) {
|
|
26
|
+
log(` Updated: ${label}`, colors.yellow);
|
|
27
|
+
} else {
|
|
28
|
+
log(` Created: ${label}`, colors.green);
|
|
29
|
+
}
|
|
30
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
31
|
+
fs.copyFileSync(src, dest);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function skipFile(label, dryRun) {
|
|
35
|
+
if (dryRun) {
|
|
36
|
+
log(` [dry-run] Skip (exists): ${label}`, colors.yellow);
|
|
37
|
+
} else {
|
|
38
|
+
log(` Skipped: ${label} (already exists)`, colors.yellow);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function installClaude(packageRoot, cwd, { dryRun = false, force = false } = {}) {
|
|
43
|
+
log('\n Installing for Claude Code…', colors.cyan);
|
|
44
|
+
|
|
45
|
+
const agentSrc = path.join(packageRoot, 'templates', 'agents', 'claude', 'swift-code-reviewer.md');
|
|
46
|
+
const cmdSrc = path.join(packageRoot, 'templates', 'commands', 'claude', 'review.md');
|
|
47
|
+
|
|
48
|
+
const agentDest = path.join(cwd, '.claude', 'agents', 'swift-code-reviewer.md');
|
|
49
|
+
const cmdDest = path.join(cwd, '.claude', 'commands', 'review.md');
|
|
50
|
+
|
|
51
|
+
if (!force && fs.existsSync(agentDest)) {
|
|
52
|
+
skipFile('.claude/agents/swift-code-reviewer.md', dryRun);
|
|
53
|
+
} else {
|
|
54
|
+
writeFile(agentDest, agentSrc, '.claude/agents/swift-code-reviewer.md', dryRun);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!force && fs.existsSync(cmdDest)) {
|
|
58
|
+
skipFile('.claude/commands/review.md', dryRun);
|
|
59
|
+
} else {
|
|
60
|
+
writeFile(cmdDest, cmdSrc, '.claude/commands/review.md', dryRun);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
log('\n Claude Code hints:', colors.reset);
|
|
64
|
+
log(' /review — run a full Swift code review', colors.cyan);
|
|
65
|
+
log(' @swift-code-reviewer — invoke the agent directly\n', colors.cyan);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function installCodex(packageRoot, cwd, { dryRun = false, force = false } = {}) {
|
|
69
|
+
log('\n Installing for OpenAI Codex CLI…', colors.cyan);
|
|
70
|
+
|
|
71
|
+
const agentSrc = path.join(packageRoot, 'templates', 'agents', 'codex', 'swift-code-reviewer.md');
|
|
72
|
+
const agentDest = path.join(cwd, 'swift-code-reviewer.md');
|
|
73
|
+
|
|
74
|
+
if (!force && fs.existsSync(agentDest)) {
|
|
75
|
+
skipFile('swift-code-reviewer.md', dryRun);
|
|
76
|
+
} else {
|
|
77
|
+
writeFile(agentDest, agentSrc, 'swift-code-reviewer.md', dryRun);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
log('\n Codex post-install — add this to your AGENTS.md:\n', colors.reset);
|
|
81
|
+
log(' ## Swift code review', colors.cyan);
|
|
82
|
+
log(' See swift-code-reviewer.md for the full review guide.\n', colors.cyan);
|
|
83
|
+
log(' Note: Codex concatenates AGENTS.md into the system prompt and does not', colors.yellow);
|
|
84
|
+
log(' auto-resolve @-path mentions. Paste the snippet above manually.\n', colors.yellow);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function installGemini(packageRoot, cwd, { dryRun = false, force = false } = {}) {
|
|
88
|
+
log('\n Installing for Google Gemini CLI…', colors.cyan);
|
|
89
|
+
|
|
90
|
+
const agentSrc = path.join(packageRoot, 'templates', 'agents', 'gemini', 'swift-code-reviewer.md');
|
|
91
|
+
const cmdSrc = path.join(packageRoot, 'templates', 'commands', 'gemini', 'review.toml');
|
|
92
|
+
|
|
93
|
+
const agentDest = path.join(cwd, 'swift-code-reviewer.md');
|
|
94
|
+
const cmdDest = path.join(cwd, '.gemini', 'commands', 'review.toml');
|
|
95
|
+
|
|
96
|
+
if (!force && fs.existsSync(agentDest)) {
|
|
97
|
+
skipFile('swift-code-reviewer.md', dryRun);
|
|
98
|
+
} else {
|
|
99
|
+
writeFile(agentDest, agentSrc, 'swift-code-reviewer.md', dryRun);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!force && fs.existsSync(cmdDest)) {
|
|
103
|
+
skipFile('.gemini/commands/review.toml', dryRun);
|
|
104
|
+
} else {
|
|
105
|
+
writeFile(cmdDest, cmdSrc, '.gemini/commands/review.toml', dryRun);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
log('\n Gemini CLI hints:', colors.reset);
|
|
109
|
+
log(' /review — run a full Swift code review (via review.toml)', colors.cyan);
|
|
110
|
+
log(' The command uses @./swift-code-reviewer.md to load the guide.\n', colors.cyan);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function installKiro(packageRoot, cwd, { dryRun = false, force = false } = {}) {
|
|
114
|
+
log('\n Installing for Kiro…', colors.cyan);
|
|
115
|
+
|
|
116
|
+
const agentSrc = path.join(packageRoot, 'templates', 'agents', 'kiro', 'swift-code-reviewer.md');
|
|
117
|
+
const agentDest = path.join(cwd, '.kiro', 'steering', 'swift-code-reviewer.md');
|
|
118
|
+
|
|
119
|
+
if (!force && fs.existsSync(agentDest)) {
|
|
120
|
+
skipFile('.kiro/steering/swift-code-reviewer.md', dryRun);
|
|
121
|
+
} else {
|
|
122
|
+
writeFile(agentDest, agentSrc, '.kiro/steering/swift-code-reviewer.md', dryRun);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
log('\n Kiro hints:', colors.reset);
|
|
126
|
+
log(' Steering auto-activates for any *.swift file (fileMatch inclusion).', colors.cyan);
|
|
127
|
+
log(' Note: workspace-scoped steering only — global steering has a known', colors.yellow);
|
|
128
|
+
log(' bug (https://github.com/kirodotdev/Kiro/issues/6171).\n', colors.yellow);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const AGENT_INSTALLERS = { claude: installClaude, codex: installCodex, gemini: installGemini, kiro: installKiro };
|
|
132
|
+
const VALID_AGENTS = Object.keys(AGENT_INSTALLERS);
|
|
133
|
+
|
|
134
|
+
module.exports = { installClaude, installCodex, installGemini, installKiro, AGENT_INSTALLERS, VALID_AGENTS };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { VALID_AGENTS } = require('./agents');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve which agents to install based on CLI flags and TTY state.
|
|
7
|
+
*
|
|
8
|
+
* Priority:
|
|
9
|
+
* 1. --all flag → all agents
|
|
10
|
+
* 2. --agent <name[,name]> flag → named agents
|
|
11
|
+
* 3. TTY available → interactive @inquirer/prompts checkbox
|
|
12
|
+
* 4. Non-TTY without flags → ['claude'] (CI-safe fallback)
|
|
13
|
+
*/
|
|
14
|
+
async function selectAgents({ allFlag, agentFlag, isTTY } = {}) {
|
|
15
|
+
if (allFlag) return [...VALID_AGENTS];
|
|
16
|
+
|
|
17
|
+
if (agentFlag) {
|
|
18
|
+
const requested = agentFlag.split(',').map((s) => s.trim().toLowerCase());
|
|
19
|
+
const invalid = requested.filter((a) => !VALID_AGENTS.includes(a));
|
|
20
|
+
if (invalid.length > 0) {
|
|
21
|
+
console.error(` Unknown agent(s): ${invalid.join(', ')}`);
|
|
22
|
+
console.error(` Valid options: ${VALID_AGENTS.join(', ')}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
return requested;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isTTY) {
|
|
29
|
+
return ['claude'];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { checkbox } = require('@inquirer/prompts');
|
|
33
|
+
const choices = VALID_AGENTS.map((name) => ({
|
|
34
|
+
name: agentLabel(name),
|
|
35
|
+
value: name,
|
|
36
|
+
checked: name === 'claude',
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
const selected = await checkbox({
|
|
40
|
+
message: 'Which agent(s) should the review guide be installed for?',
|
|
41
|
+
choices,
|
|
42
|
+
validate: (val) => val.length > 0 || 'Select at least one agent.',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return selected;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function agentLabel(name) {
|
|
49
|
+
const labels = {
|
|
50
|
+
claude: 'Claude Code (.claude/agents/ + .claude/commands/)',
|
|
51
|
+
codex: 'OpenAI Codex CLI (swift-code-reviewer.md at repo root)',
|
|
52
|
+
gemini: 'Google Gemini CLI (swift-code-reviewer.md + .gemini/commands/review.toml)',
|
|
53
|
+
kiro: 'Kiro (.kiro/steering/swift-code-reviewer.md, fileMatch: **/*.swift)',
|
|
54
|
+
};
|
|
55
|
+
return labels[name] || name;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { selectAgents };
|