oh-my-customcode 1.1.106 → 1.1.107
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 +8 -9
- package/dist/cli/index.js +227 -191
- package/dist/index.js +220 -138
- package/package.json +1 -1
- package/templates/.claude/hooks/hooks.json +0 -10
- package/templates/.claude/hooks/scripts/claude-md-reinject.sh +7 -1
- package/templates/.claude/hooks/scripts/cost-cap-advisor.sh +107 -52
- package/templates/.claude/hooks/scripts/session-env-check.sh +0 -8
- package/templates/.claude/hooks/scripts/session-reflection.sh +60 -15
- package/templates/.claude/hooks/scripts/stuck-detector.sh +110 -14
- package/templates/.claude/rules/MAY-optimization.md +1 -1
- package/templates/.claude/rules/MUST-agent-design.md +1 -1
- package/templates/.claude/rules/SHOULD-memory-integration.md +1 -1
- package/templates/.claude/rules/SHOULD-verification-ladder.md +9 -0
- package/templates/.claude/settings.json +0 -10
- package/templates/.claude/skills/intent-detection/patterns/agent-triggers.yaml +0 -8
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +3 -1
- package/templates/CLAUDE.md +1 -1
- package/templates/CLAUDE.md.en +1 -1
- package/templates/CLAUDE.md.ko +1 -1
- package/templates/README.md +4 -4
- package/templates/guides/external-tools/ecc-absorption-decisions.md +2 -0
- package/templates/guides/multi-provider-exec/README.md +7 -18
- package/templates/manifest.json +3 -3
- package/templates/workflows/auto-dev.yaml +3 -1
- package/templates/.claude/hooks/scripts/rtk-intercept.sh +0 -77
- package/templates/.claude/skills/rtk-exec/SKILL.md +0 -199
- package/templates/.claude/skills/rtk-exec/scripts/rtk-wrapper.cjs +0 -377
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* rtk-wrapper.cjs
|
|
5
|
-
*
|
|
6
|
-
* Node.js wrapper for RTK (Rust Token Killer) CLI proxy.
|
|
7
|
-
* Executes shell commands through RTK for token-optimized compressed output.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* node rtk-wrapper.cjs --command "git status" [options]
|
|
11
|
-
* node rtk-wrapper.cjs --gain
|
|
12
|
-
* node rtk-wrapper.cjs --version
|
|
13
|
-
* node rtk-wrapper.cjs --init
|
|
14
|
-
*
|
|
15
|
-
* Options:
|
|
16
|
-
* --command <cmd> Required (unless --gain/--version/--init): CLI command to proxy through RTK
|
|
17
|
-
* --timeout <ms> Execution timeout in milliseconds (default: 120000, max: 600000)
|
|
18
|
-
* --working-dir <dir> Set working directory for execution
|
|
19
|
-
* --gain Show token savings statistics (rtk gain)
|
|
20
|
-
* --version Show RTK version (rtk --version)
|
|
21
|
-
* --init Initialize RTK for current project (rtk init -g)
|
|
22
|
-
*
|
|
23
|
-
* Output (JSON to stdout):
|
|
24
|
-
* Success: { "success": true, "output": "...", "duration_ms": 1234, "command": "..." }
|
|
25
|
-
* Failure: { "success": false, "error": "...", "stderr": "...", "exit_code": 1 }
|
|
26
|
-
*
|
|
27
|
-
* Exit codes:
|
|
28
|
-
* 0 = success
|
|
29
|
-
* 1 = execution error
|
|
30
|
-
* 2 = validation error (missing binary or invalid arguments)
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
const { spawn, execFileSync } = require('child_process');
|
|
34
|
-
const fs = require('fs');
|
|
35
|
-
const path = require('path');
|
|
36
|
-
const os = require('os');
|
|
37
|
-
|
|
38
|
-
// Configuration
|
|
39
|
-
const DEFAULT_TIMEOUT_MS = 120000; // 2 minutes
|
|
40
|
-
const MAX_TIMEOUT_MS = 600000; // 10 minutes
|
|
41
|
-
const KILL_GRACE_PERIOD_MS = 5000; // 5 seconds for graceful shutdown
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Parse command line arguments
|
|
45
|
-
* @returns {Object} Parsed arguments
|
|
46
|
-
*/
|
|
47
|
-
function parseArgs() {
|
|
48
|
-
const args = {
|
|
49
|
-
command: null,
|
|
50
|
-
timeout: DEFAULT_TIMEOUT_MS,
|
|
51
|
-
workingDir: null,
|
|
52
|
-
gain: false,
|
|
53
|
-
version: false,
|
|
54
|
-
init: false,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
for (let i = 2; i < process.argv.length; i++) {
|
|
58
|
-
const arg = process.argv[i];
|
|
59
|
-
|
|
60
|
-
switch (arg) {
|
|
61
|
-
case '--command':
|
|
62
|
-
if (i + 1 < process.argv.length) {
|
|
63
|
-
args.command = process.argv[++i];
|
|
64
|
-
}
|
|
65
|
-
break;
|
|
66
|
-
case '--timeout':
|
|
67
|
-
if (i + 1 < process.argv.length) {
|
|
68
|
-
const timeoutValue = parseInt(process.argv[++i], 10);
|
|
69
|
-
if (!isNaN(timeoutValue)) {
|
|
70
|
-
args.timeout = Math.min(timeoutValue, MAX_TIMEOUT_MS);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
break;
|
|
74
|
-
case '--working-dir':
|
|
75
|
-
if (i + 1 < process.argv.length) {
|
|
76
|
-
args.workingDir = process.argv[++i];
|
|
77
|
-
}
|
|
78
|
-
break;
|
|
79
|
-
case '--gain':
|
|
80
|
-
args.gain = true;
|
|
81
|
-
break;
|
|
82
|
-
case '--version':
|
|
83
|
-
args.version = true;
|
|
84
|
-
break;
|
|
85
|
-
case '--init':
|
|
86
|
-
args.init = true;
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return args;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Validate environment for RTK execution
|
|
96
|
-
* No auth required — only checks binary availability
|
|
97
|
-
* @returns {Object} Validation result { valid: boolean, errors: string[], rtkPath: string|null }
|
|
98
|
-
*/
|
|
99
|
-
function validateEnvironment() {
|
|
100
|
-
const errors = [];
|
|
101
|
-
let rtkPath = null;
|
|
102
|
-
|
|
103
|
-
// Check for rtk binary
|
|
104
|
-
try {
|
|
105
|
-
const result = execFileSync('which', ['rtk'], { stdio: 'pipe' });
|
|
106
|
-
rtkPath = result.toString().trim();
|
|
107
|
-
} catch (error) {
|
|
108
|
-
// Try common installation paths
|
|
109
|
-
const commonPaths = [
|
|
110
|
-
'/usr/local/bin/rtk',
|
|
111
|
-
path.join(os.homedir(), '.local', 'bin', 'rtk'),
|
|
112
|
-
path.join(os.homedir(), 'bin', 'rtk'),
|
|
113
|
-
path.join(os.homedir(), '.cargo', 'bin', 'rtk'),
|
|
114
|
-
'/opt/homebrew/bin/rtk',
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
const foundPath = commonPaths.find(p => fs.existsSync(p));
|
|
118
|
-
if (foundPath) {
|
|
119
|
-
rtkPath = foundPath;
|
|
120
|
-
} else {
|
|
121
|
-
errors.push(
|
|
122
|
-
'rtk binary not found in PATH or common locations. ' +
|
|
123
|
-
'Install with: brew install rtk, ' +
|
|
124
|
-
'curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh, ' +
|
|
125
|
-
'or cargo install --git https://github.com/rtk-ai/rtk'
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
valid: errors.length === 0,
|
|
132
|
-
errors,
|
|
133
|
-
rtkPath,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Build RTK command array
|
|
139
|
-
* @param {Object} options - Parsed arguments
|
|
140
|
-
* @returns {Object} Command structure { binary: string, args: string[], label: string }
|
|
141
|
-
*/
|
|
142
|
-
function buildCommand(options) {
|
|
143
|
-
// Special commands
|
|
144
|
-
if (options.gain) {
|
|
145
|
-
return { binary: 'rtk', args: ['gain'], label: 'rtk gain' };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (options.version) {
|
|
149
|
-
return { binary: 'rtk', args: ['--version'], label: 'rtk --version' };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (options.init) {
|
|
153
|
-
return { binary: 'rtk', args: ['init', '-g'], label: 'rtk init -g' };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Proxy command: rtk <command_parts...>
|
|
157
|
-
// Split the command string into parts for safe argv construction
|
|
158
|
-
const commandParts = splitCommand(options.command);
|
|
159
|
-
return {
|
|
160
|
-
binary: 'rtk',
|
|
161
|
-
args: commandParts,
|
|
162
|
-
label: `rtk ${options.command}`,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Split a command string into argv parts, respecting quoted strings
|
|
168
|
-
* Simple shell-like splitting (no full POSIX parsing)
|
|
169
|
-
* @param {string} cmd - Command string
|
|
170
|
-
* @returns {string[]} Argv parts
|
|
171
|
-
*/
|
|
172
|
-
function splitCommand(cmd) {
|
|
173
|
-
const parts = [];
|
|
174
|
-
let current = '';
|
|
175
|
-
let inSingleQuote = false;
|
|
176
|
-
let inDoubleQuote = false;
|
|
177
|
-
|
|
178
|
-
for (let i = 0; i < cmd.length; i++) {
|
|
179
|
-
const ch = cmd[i];
|
|
180
|
-
|
|
181
|
-
if (ch === "'" && !inDoubleQuote) {
|
|
182
|
-
inSingleQuote = !inSingleQuote;
|
|
183
|
-
} else if (ch === '"' && !inSingleQuote) {
|
|
184
|
-
inDoubleQuote = !inDoubleQuote;
|
|
185
|
-
} else if (ch === ' ' && !inSingleQuote && !inDoubleQuote) {
|
|
186
|
-
if (current.length > 0) {
|
|
187
|
-
parts.push(current);
|
|
188
|
-
current = '';
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
current += ch;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (current.length > 0) {
|
|
196
|
-
parts.push(current);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return parts;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Execute RTK command
|
|
204
|
-
* @param {string} binary - Binary to execute (rtk)
|
|
205
|
-
* @param {string[]} args - Command arguments
|
|
206
|
-
* @param {number} timeout - Timeout in milliseconds
|
|
207
|
-
* @param {string|null} workingDir - Working directory
|
|
208
|
-
* @returns {Promise<Object>} Execution result
|
|
209
|
-
*/
|
|
210
|
-
function executeRtk(binary, args, timeout, workingDir = null) {
|
|
211
|
-
return new Promise((resolve) => {
|
|
212
|
-
const startTime = Date.now();
|
|
213
|
-
let stdout = '';
|
|
214
|
-
let stderr = '';
|
|
215
|
-
let timedOut = false;
|
|
216
|
-
|
|
217
|
-
const spawnOptions = {
|
|
218
|
-
cwd: workingDir || process.cwd(),
|
|
219
|
-
env: process.env,
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const child = spawn(binary, args, spawnOptions);
|
|
223
|
-
|
|
224
|
-
// Collect output
|
|
225
|
-
child.stdout.on('data', (data) => {
|
|
226
|
-
stdout += data.toString();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
child.stderr.on('data', (data) => {
|
|
230
|
-
stderr += data.toString();
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// Set timeout
|
|
234
|
-
const timeoutHandle = setTimeout(() => {
|
|
235
|
-
timedOut = true;
|
|
236
|
-
console.error('[rtk-wrapper] Timeout reached, terminating process...');
|
|
237
|
-
|
|
238
|
-
// Graceful termination attempt
|
|
239
|
-
child.kill('SIGTERM');
|
|
240
|
-
|
|
241
|
-
// Force kill after grace period
|
|
242
|
-
setTimeout(() => {
|
|
243
|
-
if (!child.killed) {
|
|
244
|
-
console.error('[rtk-wrapper] Force killing process...');
|
|
245
|
-
child.kill('SIGKILL');
|
|
246
|
-
}
|
|
247
|
-
}, KILL_GRACE_PERIOD_MS);
|
|
248
|
-
}, timeout);
|
|
249
|
-
|
|
250
|
-
// Handle process exit
|
|
251
|
-
child.on('close', (exitCode) => {
|
|
252
|
-
clearTimeout(timeoutHandle);
|
|
253
|
-
const durationMs = Date.now() - startTime;
|
|
254
|
-
|
|
255
|
-
resolve({
|
|
256
|
-
exitCode: exitCode !== null ? exitCode : 1,
|
|
257
|
-
stdout,
|
|
258
|
-
stderr,
|
|
259
|
-
timedOut,
|
|
260
|
-
durationMs,
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Handle spawn errors
|
|
265
|
-
child.on('error', (error) => {
|
|
266
|
-
clearTimeout(timeoutHandle);
|
|
267
|
-
const durationMs = Date.now() - startTime;
|
|
268
|
-
|
|
269
|
-
resolve({
|
|
270
|
-
exitCode: 1,
|
|
271
|
-
stdout,
|
|
272
|
-
stderr: stderr + '\nSpawn error: ' + error.message,
|
|
273
|
-
timedOut: false,
|
|
274
|
-
durationMs,
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Main execution function
|
|
282
|
-
*/
|
|
283
|
-
async function main() {
|
|
284
|
-
const args = parseArgs();
|
|
285
|
-
|
|
286
|
-
// Determine which mode we're running in
|
|
287
|
-
const isSpecialCommand = args.gain || args.version || args.init;
|
|
288
|
-
|
|
289
|
-
// Validate required arguments
|
|
290
|
-
if (!isSpecialCommand && !args.command) {
|
|
291
|
-
const result = {
|
|
292
|
-
success: false,
|
|
293
|
-
error: 'Missing required argument: --command (or use --gain, --version, --init)',
|
|
294
|
-
exit_code: 2,
|
|
295
|
-
};
|
|
296
|
-
console.log(JSON.stringify(result, null, 2));
|
|
297
|
-
process.exit(2);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Validate environment (binary check only, no auth)
|
|
301
|
-
const validation = validateEnvironment();
|
|
302
|
-
if (!validation.valid) {
|
|
303
|
-
const result = {
|
|
304
|
-
success: false,
|
|
305
|
-
error: 'RTK binary not found',
|
|
306
|
-
validation_errors: validation.errors,
|
|
307
|
-
exit_code: 2,
|
|
308
|
-
};
|
|
309
|
-
console.log(JSON.stringify(result, null, 2));
|
|
310
|
-
process.exit(2);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
console.error(`[rtk-wrapper] RTK binary: ${validation.rtkPath}`);
|
|
314
|
-
console.error(`[rtk-wrapper] Executing with timeout: ${args.timeout}ms`);
|
|
315
|
-
if (args.workingDir) {
|
|
316
|
-
console.error(`[rtk-wrapper] Working directory: ${args.workingDir}`);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Build command
|
|
320
|
-
const command = buildCommand(args);
|
|
321
|
-
console.error(`[rtk-wrapper] Command: ${command.binary} ${command.args.join(' ')}`);
|
|
322
|
-
|
|
323
|
-
// Execute
|
|
324
|
-
const execResult = await executeRtk(
|
|
325
|
-
command.binary,
|
|
326
|
-
command.args,
|
|
327
|
-
args.timeout,
|
|
328
|
-
args.workingDir
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
// Determine success
|
|
332
|
-
const success = execResult.exitCode === 0 && !execResult.timedOut;
|
|
333
|
-
|
|
334
|
-
// Build result object
|
|
335
|
-
const result = {
|
|
336
|
-
success,
|
|
337
|
-
duration_ms: execResult.durationMs,
|
|
338
|
-
exit_code: execResult.exitCode,
|
|
339
|
-
command: command.label,
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
if (success) {
|
|
343
|
-
result.output = execResult.stdout.trim();
|
|
344
|
-
if (execResult.stderr.trim()) {
|
|
345
|
-
result.stderr = execResult.stderr.trim();
|
|
346
|
-
}
|
|
347
|
-
} else {
|
|
348
|
-
if (execResult.timedOut) {
|
|
349
|
-
result.error = `Execution timed out after ${args.timeout}ms`;
|
|
350
|
-
} else {
|
|
351
|
-
result.error = 'Execution failed';
|
|
352
|
-
}
|
|
353
|
-
if (execResult.stderr.trim()) {
|
|
354
|
-
result.stderr = execResult.stderr.trim();
|
|
355
|
-
}
|
|
356
|
-
if (execResult.stdout.trim()) {
|
|
357
|
-
result.partial_output = execResult.stdout.trim();
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Output JSON result to stdout
|
|
362
|
-
console.log(JSON.stringify(result, null, 2));
|
|
363
|
-
|
|
364
|
-
process.exit(result.exit_code);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Run
|
|
368
|
-
main().catch(error => {
|
|
369
|
-
const result = {
|
|
370
|
-
success: false,
|
|
371
|
-
error: 'Unexpected error: ' + error.message,
|
|
372
|
-
stack: error.stack,
|
|
373
|
-
exit_code: 1,
|
|
374
|
-
};
|
|
375
|
-
console.log(JSON.stringify(result, null, 2));
|
|
376
|
-
process.exit(1);
|
|
377
|
-
});
|