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.
Files changed (28) hide show
  1. package/README.md +8 -9
  2. package/dist/cli/index.js +227 -191
  3. package/dist/index.js +220 -138
  4. package/package.json +1 -1
  5. package/templates/.claude/hooks/hooks.json +0 -10
  6. package/templates/.claude/hooks/scripts/claude-md-reinject.sh +7 -1
  7. package/templates/.claude/hooks/scripts/cost-cap-advisor.sh +107 -52
  8. package/templates/.claude/hooks/scripts/session-env-check.sh +0 -8
  9. package/templates/.claude/hooks/scripts/session-reflection.sh +60 -15
  10. package/templates/.claude/hooks/scripts/stuck-detector.sh +110 -14
  11. package/templates/.claude/rules/MAY-optimization.md +1 -1
  12. package/templates/.claude/rules/MUST-agent-design.md +1 -1
  13. package/templates/.claude/rules/SHOULD-memory-integration.md +1 -1
  14. package/templates/.claude/rules/SHOULD-verification-ladder.md +9 -0
  15. package/templates/.claude/settings.json +0 -10
  16. package/templates/.claude/skills/intent-detection/patterns/agent-triggers.yaml +0 -8
  17. package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +3 -1
  18. package/templates/CLAUDE.md +1 -1
  19. package/templates/CLAUDE.md.en +1 -1
  20. package/templates/CLAUDE.md.ko +1 -1
  21. package/templates/README.md +4 -4
  22. package/templates/guides/external-tools/ecc-absorption-decisions.md +2 -0
  23. package/templates/guides/multi-provider-exec/README.md +7 -18
  24. package/templates/manifest.json +3 -3
  25. package/templates/workflows/auto-dev.yaml +3 -1
  26. package/templates/.claude/hooks/scripts/rtk-intercept.sh +0 -77
  27. package/templates/.claude/skills/rtk-exec/SKILL.md +0 -199
  28. 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
- });