oh-my-customcode 0.65.2 → 0.67.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/README.md +3 -3
- package/dist/cli/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/session-env-check.sh +20 -0
- package/templates/.claude/skills/de-lead-routing/SKILL.md +9 -2
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +9 -2
- package/templates/.claude/skills/gemini-exec/SKILL.md +215 -0
- package/templates/.claude/skills/gemini-exec/scripts/gemini-wrapper.cjs +485 -0
- package/templates/.claude/skills/intent-detection/patterns/agent-triggers.yaml +27 -2
- package/templates/.claude/skills/rtk-exec/SKILL.md +199 -0
- package/templates/.claude/skills/rtk-exec/scripts/rtk-wrapper.cjs +377 -0
- package/templates/CLAUDE.md +14 -1
- package/templates/manifest.json +2 -2
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* gemini-wrapper.cjs
|
|
5
|
+
*
|
|
6
|
+
* Node.js wrapper for Google Gemini CLI (non-interactive execution).
|
|
7
|
+
* Executes gemini in prompt mode with structured JSON output.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node gemini-wrapper.cjs --prompt "your prompt" [options]
|
|
11
|
+
*
|
|
12
|
+
* Options:
|
|
13
|
+
* --prompt <text> Required: prompt to execute
|
|
14
|
+
* --json Enable JSON output from gemini (-o json)
|
|
15
|
+
* --stream-json Enable stream-JSON output (-o stream-json)
|
|
16
|
+
* --output <path> Save response to file
|
|
17
|
+
* --model <name> Specify model (default: gemini CLI default)
|
|
18
|
+
* --timeout <ms> Execution timeout in milliseconds (default: 120000, max: 600000)
|
|
19
|
+
* --yolo Use yolo approval mode (auto-approve all actions)
|
|
20
|
+
* --sandbox Run in sandbox mode
|
|
21
|
+
* --plan Use plan approval mode
|
|
22
|
+
* --working-dir <dir> Set working directory for execution
|
|
23
|
+
*
|
|
24
|
+
* Output (JSON to stdout):
|
|
25
|
+
* Success: { "success": true, "output": "...", "duration_ms": 1234, ... }
|
|
26
|
+
* Failure: { "success": false, "error": "...", "stderr": "...", ... }
|
|
27
|
+
*
|
|
28
|
+
* Exit codes:
|
|
29
|
+
* 0 = success
|
|
30
|
+
* 1 = execution error
|
|
31
|
+
* 2 = validation error (missing binary/auth)
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const { spawn, execFileSync } = require('child_process');
|
|
35
|
+
const fs = require('fs');
|
|
36
|
+
const path = require('path');
|
|
37
|
+
const os = require('os');
|
|
38
|
+
|
|
39
|
+
// Configuration
|
|
40
|
+
const DEFAULT_TIMEOUT_MS = 120000; // 2 minutes
|
|
41
|
+
const MAX_TIMEOUT_MS = 600000; // 10 minutes
|
|
42
|
+
const KILL_GRACE_PERIOD_MS = 5000; // 5 seconds for graceful shutdown
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse command line arguments
|
|
46
|
+
* @returns {Object} Parsed arguments
|
|
47
|
+
*/
|
|
48
|
+
function parseArgs() {
|
|
49
|
+
const args = {
|
|
50
|
+
prompt: null,
|
|
51
|
+
json: false,
|
|
52
|
+
streamJson: false,
|
|
53
|
+
output: null,
|
|
54
|
+
model: null,
|
|
55
|
+
timeout: DEFAULT_TIMEOUT_MS,
|
|
56
|
+
yolo: false,
|
|
57
|
+
sandbox: false,
|
|
58
|
+
plan: false,
|
|
59
|
+
workingDir: null,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
63
|
+
const arg = process.argv[i];
|
|
64
|
+
|
|
65
|
+
switch (arg) {
|
|
66
|
+
case '--prompt':
|
|
67
|
+
if (i + 1 < process.argv.length) {
|
|
68
|
+
args.prompt = process.argv[++i];
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case '--json':
|
|
72
|
+
args.json = true;
|
|
73
|
+
break;
|
|
74
|
+
case '--stream-json':
|
|
75
|
+
args.streamJson = true;
|
|
76
|
+
break;
|
|
77
|
+
case '--output':
|
|
78
|
+
if (i + 1 < process.argv.length) {
|
|
79
|
+
args.output = process.argv[++i];
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case '--model':
|
|
83
|
+
if (i + 1 < process.argv.length) {
|
|
84
|
+
args.model = process.argv[++i];
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case '--timeout':
|
|
88
|
+
if (i + 1 < process.argv.length) {
|
|
89
|
+
const timeoutValue = parseInt(process.argv[++i], 10);
|
|
90
|
+
if (!isNaN(timeoutValue)) {
|
|
91
|
+
args.timeout = Math.min(timeoutValue, MAX_TIMEOUT_MS);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case '--yolo':
|
|
96
|
+
args.yolo = true;
|
|
97
|
+
break;
|
|
98
|
+
case '--sandbox':
|
|
99
|
+
args.sandbox = true;
|
|
100
|
+
break;
|
|
101
|
+
case '--plan':
|
|
102
|
+
args.plan = true;
|
|
103
|
+
break;
|
|
104
|
+
case '--working-dir':
|
|
105
|
+
if (i + 1 < process.argv.length) {
|
|
106
|
+
args.workingDir = process.argv[++i];
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return args;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validate environment for gemini execution
|
|
117
|
+
* @returns {Object} Validation result { valid: boolean, errors: string[] }
|
|
118
|
+
*/
|
|
119
|
+
function validateEnvironment() {
|
|
120
|
+
const errors = [];
|
|
121
|
+
|
|
122
|
+
// Check for gemini binary
|
|
123
|
+
try {
|
|
124
|
+
execFileSync('which', ['gemini'], { stdio: 'pipe' });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// Try common installation paths
|
|
127
|
+
const commonPaths = [
|
|
128
|
+
'/usr/local/bin/gemini',
|
|
129
|
+
path.join(os.homedir(), '.local', 'bin', 'gemini'),
|
|
130
|
+
path.join(os.homedir(), 'bin', 'gemini'),
|
|
131
|
+
path.join(os.homedir(), '.npm-global', 'bin', 'gemini'),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
const geminiExists = commonPaths.some(p => fs.existsSync(p));
|
|
135
|
+
if (!geminiExists) {
|
|
136
|
+
errors.push('gemini binary not found in PATH or common locations');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check authentication (multiple methods supported)
|
|
141
|
+
const hasGoogleApiKey = !!process.env.GOOGLE_API_KEY;
|
|
142
|
+
const hasGeminiApiKey = !!process.env.GEMINI_API_KEY;
|
|
143
|
+
|
|
144
|
+
if (!hasGoogleApiKey && !hasGeminiApiKey) {
|
|
145
|
+
console.error('[gemini-wrapper] Note: GOOGLE_API_KEY/GEMINI_API_KEY not set, relying on gcloud auth');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
valid: errors.length === 0,
|
|
150
|
+
errors,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Build gemini command array
|
|
156
|
+
* @param {Object} options - Command options
|
|
157
|
+
* @returns {Object} Command structure { binary: string, args: string[] }
|
|
158
|
+
*/
|
|
159
|
+
function buildCommand(options) {
|
|
160
|
+
const args = [];
|
|
161
|
+
|
|
162
|
+
// Prompt mode (non-interactive, ephemeral)
|
|
163
|
+
args.push('-p', options.prompt);
|
|
164
|
+
|
|
165
|
+
// Output format
|
|
166
|
+
if (options.streamJson) {
|
|
167
|
+
args.push('-o', 'stream-json');
|
|
168
|
+
} else if (options.json) {
|
|
169
|
+
args.push('-o', 'json');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Model selection
|
|
173
|
+
if (options.model) {
|
|
174
|
+
args.push('-m', options.model);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Approval mode
|
|
178
|
+
if (options.yolo) {
|
|
179
|
+
args.push('-y');
|
|
180
|
+
} else if (options.plan) {
|
|
181
|
+
args.push('--approval-mode', 'plan');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Sandbox mode
|
|
185
|
+
if (options.sandbox) {
|
|
186
|
+
args.push('-s');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
binary: 'gemini',
|
|
191
|
+
args,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Execute gemini command
|
|
197
|
+
* @param {string} binary - Binary to execute
|
|
198
|
+
* @param {string[]} args - Command arguments
|
|
199
|
+
* @param {number} timeout - Timeout in milliseconds
|
|
200
|
+
* @param {string|null} workingDir - Working directory
|
|
201
|
+
* @returns {Promise<Object>} Execution result
|
|
202
|
+
*/
|
|
203
|
+
function executeGemini(binary, args, timeout, workingDir = null) {
|
|
204
|
+
return new Promise((resolve) => {
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
let stdout = '';
|
|
207
|
+
let stderr = '';
|
|
208
|
+
let timedOut = false;
|
|
209
|
+
|
|
210
|
+
const spawnOptions = {
|
|
211
|
+
cwd: workingDir || process.cwd(),
|
|
212
|
+
env: process.env,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const child = spawn(binary, args, spawnOptions);
|
|
216
|
+
|
|
217
|
+
// Collect output
|
|
218
|
+
child.stdout.on('data', (data) => {
|
|
219
|
+
stdout += data.toString();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
child.stderr.on('data', (data) => {
|
|
223
|
+
stderr += data.toString();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Set timeout
|
|
227
|
+
const timeoutHandle = setTimeout(() => {
|
|
228
|
+
timedOut = true;
|
|
229
|
+
console.error('[gemini-wrapper] Timeout reached, terminating process...');
|
|
230
|
+
|
|
231
|
+
// Graceful termination attempt
|
|
232
|
+
child.kill('SIGTERM');
|
|
233
|
+
|
|
234
|
+
// Force kill after grace period
|
|
235
|
+
setTimeout(() => {
|
|
236
|
+
if (!child.killed) {
|
|
237
|
+
console.error('[gemini-wrapper] Force killing process...');
|
|
238
|
+
child.kill('SIGKILL');
|
|
239
|
+
}
|
|
240
|
+
}, KILL_GRACE_PERIOD_MS);
|
|
241
|
+
}, timeout);
|
|
242
|
+
|
|
243
|
+
// Handle process exit
|
|
244
|
+
child.on('close', (exitCode) => {
|
|
245
|
+
clearTimeout(timeoutHandle);
|
|
246
|
+
const durationMs = Date.now() - startTime;
|
|
247
|
+
|
|
248
|
+
resolve({
|
|
249
|
+
exitCode: exitCode !== null ? exitCode : 1,
|
|
250
|
+
stdout,
|
|
251
|
+
stderr,
|
|
252
|
+
timedOut,
|
|
253
|
+
durationMs,
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Handle spawn errors
|
|
258
|
+
child.on('error', (error) => {
|
|
259
|
+
clearTimeout(timeoutHandle);
|
|
260
|
+
const durationMs = Date.now() - startTime;
|
|
261
|
+
|
|
262
|
+
resolve({
|
|
263
|
+
exitCode: 1,
|
|
264
|
+
stdout,
|
|
265
|
+
stderr: stderr + '\nSpawn error: ' + error.message,
|
|
266
|
+
timedOut: false,
|
|
267
|
+
durationMs,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Parse JSON output from gemini (-o json)
|
|
275
|
+
* Gemini JSON output is a single JSON object: { session_id, response, stats }
|
|
276
|
+
* @param {string} output - Raw output string
|
|
277
|
+
* @returns {Object} Parsed result { response: string|null, stats: object|null, parseError: string|null }
|
|
278
|
+
*/
|
|
279
|
+
function parseJson(output) {
|
|
280
|
+
try {
|
|
281
|
+
const data = JSON.parse(output.trim());
|
|
282
|
+
return {
|
|
283
|
+
response: data.response || null,
|
|
284
|
+
stats: data.stats || null,
|
|
285
|
+
sessionId: data.session_id || null,
|
|
286
|
+
parseError: null,
|
|
287
|
+
};
|
|
288
|
+
} catch (error) {
|
|
289
|
+
return {
|
|
290
|
+
response: null,
|
|
291
|
+
stats: null,
|
|
292
|
+
sessionId: null,
|
|
293
|
+
parseError: `Failed to parse JSON: ${error.message}`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Parse stream-JSON output from gemini (-o stream-json)
|
|
300
|
+
* Stream format: newline-delimited JSON events
|
|
301
|
+
* { type: "init", ... }
|
|
302
|
+
* { type: "message", role: "user"|"assistant", content: "..." }
|
|
303
|
+
* { type: "result", stats: {...} }
|
|
304
|
+
* @param {string} output - Raw output string
|
|
305
|
+
* @returns {Object} Parsed result { events: object[], finalMessage: string|null, stats: object|null, parseErrors: string[] }
|
|
306
|
+
*/
|
|
307
|
+
function parseStreamJson(output) {
|
|
308
|
+
const lines = output.split('\n').filter(line => line.trim().length > 0);
|
|
309
|
+
const events = [];
|
|
310
|
+
const parseErrors = [];
|
|
311
|
+
let finalMessage = null;
|
|
312
|
+
let stats = null;
|
|
313
|
+
|
|
314
|
+
for (const line of lines) {
|
|
315
|
+
try {
|
|
316
|
+
const event = JSON.parse(line);
|
|
317
|
+
events.push(event);
|
|
318
|
+
|
|
319
|
+
// Extract final assistant message
|
|
320
|
+
if (event.type === 'message' && event.role === 'assistant') {
|
|
321
|
+
finalMessage = event.content || event.text || finalMessage;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Extract stats from result event
|
|
325
|
+
if (event.type === 'result') {
|
|
326
|
+
stats = event.stats || null;
|
|
327
|
+
if (event.response) {
|
|
328
|
+
finalMessage = event.response;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Fallback: look for common response patterns
|
|
333
|
+
if (!finalMessage && event.content && event.role === 'model') {
|
|
334
|
+
finalMessage = event.content;
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
parseErrors.push(`Failed to parse line: ${error.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
events,
|
|
343
|
+
finalMessage,
|
|
344
|
+
stats,
|
|
345
|
+
parseErrors,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Main execution function
|
|
351
|
+
*/
|
|
352
|
+
async function main() {
|
|
353
|
+
const args = parseArgs();
|
|
354
|
+
|
|
355
|
+
// Validate required arguments
|
|
356
|
+
if (!args.prompt) {
|
|
357
|
+
const result = {
|
|
358
|
+
success: false,
|
|
359
|
+
error: 'Missing required argument: --prompt',
|
|
360
|
+
exit_code: 2,
|
|
361
|
+
};
|
|
362
|
+
console.log(JSON.stringify(result, null, 2));
|
|
363
|
+
process.exit(2);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Validate environment
|
|
367
|
+
const validation = validateEnvironment();
|
|
368
|
+
if (!validation.valid) {
|
|
369
|
+
const result = {
|
|
370
|
+
success: false,
|
|
371
|
+
error: 'Environment validation failed',
|
|
372
|
+
validation_errors: validation.errors,
|
|
373
|
+
exit_code: 2,
|
|
374
|
+
};
|
|
375
|
+
console.log(JSON.stringify(result, null, 2));
|
|
376
|
+
process.exit(2);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.error(`[gemini-wrapper] Executing gemini with timeout: ${args.timeout}ms`);
|
|
380
|
+
if (args.workingDir) {
|
|
381
|
+
console.error(`[gemini-wrapper] Working directory: ${args.workingDir}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Build command
|
|
385
|
+
const command = buildCommand(args);
|
|
386
|
+
console.error(`[gemini-wrapper] Command: ${command.binary} ${command.args.join(' ')}`);
|
|
387
|
+
|
|
388
|
+
// Execute
|
|
389
|
+
const execResult = await executeGemini(
|
|
390
|
+
command.binary,
|
|
391
|
+
command.args,
|
|
392
|
+
args.timeout,
|
|
393
|
+
args.workingDir
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// Process result
|
|
397
|
+
let output = null;
|
|
398
|
+
let eventsCount = 0;
|
|
399
|
+
let stats = null;
|
|
400
|
+
|
|
401
|
+
if (args.streamJson && execResult.stdout) {
|
|
402
|
+
const parsed = parseStreamJson(execResult.stdout);
|
|
403
|
+
eventsCount = parsed.events.length;
|
|
404
|
+
output = parsed.finalMessage;
|
|
405
|
+
stats = parsed.stats;
|
|
406
|
+
|
|
407
|
+
if (parsed.parseErrors.length > 0) {
|
|
408
|
+
console.error('[gemini-wrapper] Stream-JSON parse errors:', parsed.parseErrors.join('; '));
|
|
409
|
+
}
|
|
410
|
+
} else if (args.json && execResult.stdout) {
|
|
411
|
+
const parsed = parseJson(execResult.stdout);
|
|
412
|
+
output = parsed.response;
|
|
413
|
+
stats = parsed.stats;
|
|
414
|
+
|
|
415
|
+
if (parsed.parseError) {
|
|
416
|
+
console.error('[gemini-wrapper] JSON parse error:', parsed.parseError);
|
|
417
|
+
// Fallback to raw output
|
|
418
|
+
output = execResult.stdout.trim();
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
output = execResult.stdout.trim();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Determine success
|
|
425
|
+
const success = execResult.exitCode === 0 && !execResult.timedOut;
|
|
426
|
+
|
|
427
|
+
// Build result object
|
|
428
|
+
const result = {
|
|
429
|
+
success,
|
|
430
|
+
duration_ms: execResult.durationMs,
|
|
431
|
+
exit_code: execResult.exitCode,
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (success) {
|
|
435
|
+
result.output = output || execResult.stdout;
|
|
436
|
+
result.model = args.model || '(default)';
|
|
437
|
+
if (args.streamJson) {
|
|
438
|
+
result.events_count = eventsCount;
|
|
439
|
+
}
|
|
440
|
+
if (stats) {
|
|
441
|
+
result.stats = stats;
|
|
442
|
+
}
|
|
443
|
+
} else {
|
|
444
|
+
if (execResult.timedOut) {
|
|
445
|
+
result.error = `Execution timed out after ${args.timeout}ms`;
|
|
446
|
+
} else {
|
|
447
|
+
result.error = 'Execution failed';
|
|
448
|
+
}
|
|
449
|
+
if (execResult.stderr) {
|
|
450
|
+
result.stderr = execResult.stderr.trim();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Write output file if requested
|
|
455
|
+
if (args.output && output) {
|
|
456
|
+
try {
|
|
457
|
+
const outputDir = path.dirname(args.output);
|
|
458
|
+
if (!fs.existsSync(outputDir)) {
|
|
459
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
460
|
+
}
|
|
461
|
+
fs.writeFileSync(args.output, output, 'utf-8');
|
|
462
|
+
console.error(`[gemini-wrapper] Output written to: ${args.output}`);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error(`[gemini-wrapper] Failed to write output file: ${error.message}`);
|
|
465
|
+
result.output_file_error = error.message;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Output JSON result to stdout
|
|
470
|
+
console.log(JSON.stringify(result, null, 2));
|
|
471
|
+
|
|
472
|
+
process.exit(result.exit_code);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Run
|
|
476
|
+
main().catch(error => {
|
|
477
|
+
const result = {
|
|
478
|
+
success: false,
|
|
479
|
+
error: 'Unexpected error: ' + error.message,
|
|
480
|
+
stack: error.stack,
|
|
481
|
+
exit_code: 1,
|
|
482
|
+
};
|
|
483
|
+
console.log(JSON.stringify(result, null, 2));
|
|
484
|
+
process.exit(1);
|
|
485
|
+
});
|
|
@@ -110,6 +110,14 @@ agents:
|
|
|
110
110
|
supported_actions: [review, create, fix, refactor, test]
|
|
111
111
|
base_confidence: 40
|
|
112
112
|
|
|
113
|
+
fe-design-expert:
|
|
114
|
+
keywords:
|
|
115
|
+
korean: [디자인, 타이포그래피, 색상, 모션, UX라이팅, 디자인시스템, 디자인리뷰]
|
|
116
|
+
english: [design, typography, color, motion, "ux writing", "design system", "design review", impeccable, "design audit", "ai slop"]
|
|
117
|
+
file_patterns: ["*.css", "*.scss", "*.less", "tailwind.config.*"]
|
|
118
|
+
supported_actions: [review, audit, critique, polish, normalize]
|
|
119
|
+
base_confidence: 40
|
|
120
|
+
|
|
113
121
|
# SW Engineers - Backend
|
|
114
122
|
be-fastapi-expert:
|
|
115
123
|
keywords:
|
|
@@ -330,7 +338,7 @@ agents:
|
|
|
330
338
|
- gather
|
|
331
339
|
base_confidence: 50
|
|
332
340
|
action_weight: 30
|
|
333
|
-
routing_rule: "MUST use codex-exec --effort xhigh when codex available, fallback to WebFetch/WebSearch"
|
|
341
|
+
routing_rule: "MUST use codex-exec --effort xhigh when codex available, or gemini-exec when gemini available, fallback to WebFetch/WebSearch"
|
|
334
342
|
|
|
335
343
|
# ---------------------------------------------------------------------------
|
|
336
344
|
# Code Generation (hybrid workflow, skill-based)
|
|
@@ -359,7 +367,7 @@ agents:
|
|
|
359
367
|
- scaffold
|
|
360
368
|
base_confidence: 30
|
|
361
369
|
action_weight: 25
|
|
362
|
-
routing_rule: "Suggest codex-exec hybrid when codex available
|
|
370
|
+
routing_rule: "Suggest codex-exec hybrid when codex available, or gemini-exec hybrid when gemini available, for new file creation tasks"
|
|
363
371
|
|
|
364
372
|
# Managers (continued)
|
|
365
373
|
mgr-gitnerd:
|
|
@@ -411,3 +419,20 @@ agents:
|
|
|
411
419
|
file_patterns: []
|
|
412
420
|
supported_actions: [manage, create, coordinate]
|
|
413
421
|
base_confidence: 40
|
|
422
|
+
|
|
423
|
+
professor-triage:
|
|
424
|
+
keywords:
|
|
425
|
+
korean: [트리아지, 이슈분석, 교차분석, 프로페서]
|
|
426
|
+
english: [triage, cross-analyze, professor, issue-analysis]
|
|
427
|
+
file_patterns: []
|
|
428
|
+
actions: [review, analyze]
|
|
429
|
+
base_confidence: 85
|
|
430
|
+
routing_rule: "MUST use professor-triage skill for cross-analyzing GitHub issues with omc_issue_analyzer comments"
|
|
431
|
+
|
|
432
|
+
rtk-exec:
|
|
433
|
+
keywords:
|
|
434
|
+
korean: [rtk, 토큰절감, 출력압축, 토큰최적화]
|
|
435
|
+
english: [rtk, "token savings", "compress output", "token optimization"]
|
|
436
|
+
file_patterns: []
|
|
437
|
+
supported_actions: [optimize, compress, proxy]
|
|
438
|
+
base_confidence: 85
|