oh-my-customcode 0.66.0 → 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 +8 -0
- package/templates/.claude/skills/de-lead-routing/SKILL.md +5 -2
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +5 -2
- package/templates/.claude/skills/intent-detection/patterns/agent-triggers.yaml +8 -0
- 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 +2 -1
- package/templates/manifest.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
**[한국어 문서 (Korean)](./README_ko.md)**
|
|
15
15
|
|
|
16
|
-
46 agents.
|
|
16
|
+
46 agents. 100 skills. 21 rules. One command.
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
npm install -g oh-my-customcode && cd your-project && omcustom init
|
|
@@ -146,7 +146,7 @@ Each agent declares its tools, model, memory scope, and limitations in YAML fron
|
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
149
|
-
### Skills (
|
|
149
|
+
### Skills (100)
|
|
150
150
|
|
|
151
151
|
| Category | Count | Includes |
|
|
152
152
|
|----------|-------|----------|
|
|
@@ -282,7 +282,7 @@ your-project/
|
|
|
282
282
|
├── CLAUDE.md # Entry point
|
|
283
283
|
├── .claude/
|
|
284
284
|
│ ├── agents/ # 46 agent definitions
|
|
285
|
-
│ ├── skills/ #
|
|
285
|
+
│ ├── skills/ # 100 skill modules
|
|
286
286
|
│ ├── rules/ # 21 governance rules (R000-R021)
|
|
287
287
|
│ ├── hooks/ # 15 lifecycle hook scripts
|
|
288
288
|
│ ├── schemas/ # Tool input validation schemas
|
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -31,6 +31,12 @@ if command -v gemini >/dev/null 2>&1; then
|
|
|
31
31
|
fi
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
+
# Check RTK CLI availability
|
|
35
|
+
RTK_STATUS="unavailable"
|
|
36
|
+
if command -v rtk >/dev/null 2>&1; then
|
|
37
|
+
RTK_STATUS="available"
|
|
38
|
+
fi
|
|
39
|
+
|
|
34
40
|
# Check Agent Teams availability
|
|
35
41
|
AGENT_TEAMS_STATUS="disabled"
|
|
36
42
|
if [ "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-0}" = "1" ]; then
|
|
@@ -145,6 +151,7 @@ STATUS_FILE="/tmp/.claude-env-status-${PPID}"
|
|
|
145
151
|
cat > "$STATUS_FILE" << ENVEOF
|
|
146
152
|
codex=${CODEX_STATUS}
|
|
147
153
|
gemini=${GEMINI_STATUS}
|
|
154
|
+
rtk=${RTK_STATUS}
|
|
148
155
|
agent_teams=${AGENT_TEAMS_STATUS}
|
|
149
156
|
git_branch=${CURRENT_BRANCH}
|
|
150
157
|
claude_version=${CLAUDE_VERSION}
|
|
@@ -156,6 +163,7 @@ ENVEOF
|
|
|
156
163
|
# Report to stderr (visible in conversation)
|
|
157
164
|
echo " codex CLI: ${CODEX_STATUS}" >&2
|
|
158
165
|
echo " gemini CLI: ${GEMINI_STATUS}" >&2
|
|
166
|
+
echo " RTK CLI: ${RTK_STATUS}" >&2
|
|
159
167
|
echo " Agent Teams: ${AGENT_TEAMS_STATUS}" >&2
|
|
160
168
|
echo " Claude Code: v${CLAUDE_VERSION} (${COMPAT_STATUS})" >&2
|
|
161
169
|
if [ "$COMPAT_STATUS" = "outdated" ]; then
|
|
@@ -64,7 +64,7 @@ Check if Agent Teams is available (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` or T
|
|
|
64
64
|
### Step 2: Codex-Exec Hybrid (Code Generation)
|
|
65
65
|
For **new pipeline code**, **DAG scaffolding**, or **SQL model generation**:
|
|
66
66
|
|
|
67
|
-
1. Check `/tmp/.claude-env-status-*` for codex and
|
|
67
|
+
1. Check `/tmp/.claude-env-status-*` for codex, gemini, and rtk availability
|
|
68
68
|
2. If codex available AND task involves new file creation → automatically delegate to `/codex-exec` for scaffolding:
|
|
69
69
|
- Display: `[Codex Hybrid] Delegating to codex-exec...`
|
|
70
70
|
- codex-exec generates initial code (strength: fast generation)
|
|
@@ -73,7 +73,10 @@ For **new pipeline code**, **DAG scaffolding**, or **SQL model generation**:
|
|
|
73
73
|
- Display: `[Gemini Hybrid] Delegating to gemini-exec...`
|
|
74
74
|
- gemini-exec generates initial code
|
|
75
75
|
- Selected DE expert reviews and refines output
|
|
76
|
-
4. If
|
|
76
|
+
4. If RTK available (`RTK=available` in env status) → optionally wrap DE expert output through RTK to reduce token consumption by 60-90%:
|
|
77
|
+
- Display: `[RTK Proxy] Token optimization active via rtk-exec`
|
|
78
|
+
- RTK acts as a transparent proxy — no change to expert selection
|
|
79
|
+
5. If none available → display `[External CLI] Unavailable — proceeding with {expert} directly` and use DE expert directly
|
|
77
80
|
|
|
78
81
|
**Suitable**: New DAG files, dbt model scaffolding, SQL template generation
|
|
79
82
|
**Unsuitable**: Existing pipeline modification, architecture decisions, data quality analysis
|
|
@@ -99,7 +99,7 @@ Check if Agent Teams is available (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` or T
|
|
|
99
99
|
### Step 2: Codex-Exec Hybrid (Implementation Tasks)
|
|
100
100
|
For **new file creation**, **boilerplate**, or **test code generation**:
|
|
101
101
|
|
|
102
|
-
1. Check `/tmp/.claude-env-status-*` for codex and
|
|
102
|
+
1. Check `/tmp/.claude-env-status-*` for codex, gemini, and rtk availability
|
|
103
103
|
2. If codex available AND task involves new file creation → automatically delegate to `/codex-exec` for scaffolding:
|
|
104
104
|
- Display: `[Codex Hybrid] Delegating to codex-exec...`
|
|
105
105
|
- codex-exec generates initial code (strength: fast generation)
|
|
@@ -108,7 +108,10 @@ For **new file creation**, **boilerplate**, or **test code generation**:
|
|
|
108
108
|
- Display: `[Gemini Hybrid] Delegating to gemini-exec...`
|
|
109
109
|
- gemini-exec generates initial code
|
|
110
110
|
- Selected Claude expert reviews and refines output
|
|
111
|
-
4. If
|
|
111
|
+
4. If RTK available (`RTK=available` in env status) → optionally wrap Claude expert output through RTK to reduce token consumption by 60-90%:
|
|
112
|
+
- Display: `[RTK Proxy] Token optimization active via rtk-exec`
|
|
113
|
+
- RTK acts as a transparent proxy — no change to expert selection
|
|
114
|
+
5. If none available → display `[External CLI] Unavailable — proceeding with {expert} directly` and use Claude expert directly
|
|
112
115
|
|
|
113
116
|
**Suitable**: New file creation, boilerplate, scaffolding, test code
|
|
114
117
|
**Unsuitable**: Existing code modification, architecture decisions, bug fixes
|
|
@@ -428,3 +428,11 @@ agents:
|
|
|
428
428
|
actions: [review, analyze]
|
|
429
429
|
base_confidence: 85
|
|
430
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
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rtk-exec
|
|
3
|
+
description: Execute CLI commands through RTK proxy for token-optimized output
|
|
4
|
+
scope: core
|
|
5
|
+
argument-hint: "<command> [args...] [--gain] [--version] [--init]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# RTK Exec Skill
|
|
10
|
+
|
|
11
|
+
Execute CLI commands through the RTK (Rust Token Killer) proxy to reduce LLM token consumption by 60-90%. RTK is a CLI command proxy — it wraps existing shell commands and compresses their output using smart filtering, grouping, truncation, and deduplication.
|
|
12
|
+
|
|
13
|
+
> **Important**: RTK is NOT an AI prompt tool. It is a CLI output compressor. You pass it a regular shell command and it returns compressed output.
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
<command> [args...] Required. CLI command to proxy through RTK (e.g., "ls .", "git status", "cargo test")
|
|
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
|
+
--timeout <ms> Execution timeout (default: 120000, max: 600000)
|
|
23
|
+
--working-dir <dir> Working directory for command execution
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
1. Pre-checks
|
|
30
|
+
- Verify `rtk` binary is installed (which rtk)
|
|
31
|
+
- No authentication required
|
|
32
|
+
2. Build command
|
|
33
|
+
- Special: rtk gain | rtk --version | rtk init -g
|
|
34
|
+
- Proxy: rtk <command> [args...]
|
|
35
|
+
3. Execute
|
|
36
|
+
- Run via Bash tool with timeout (default 2min, max 10min)
|
|
37
|
+
- Or use helper script: node .claude/skills/rtk-exec/scripts/rtk-wrapper.cjs
|
|
38
|
+
4. Parse output
|
|
39
|
+
- RTK always returns plain text compressed output (no JSON modes)
|
|
40
|
+
- Capture stdout and stderr
|
|
41
|
+
5. Report results
|
|
42
|
+
- Format output with execution metadata and token savings info
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Supported Commands
|
|
46
|
+
|
|
47
|
+
RTK supports 100+ CLI commands across categories:
|
|
48
|
+
|
|
49
|
+
| Category | Commands |
|
|
50
|
+
|----------|----------|
|
|
51
|
+
| File ops | `ls`, `find`, `grep`, `cat`, `tree`, `du`, `diff` |
|
|
52
|
+
| Git | `git status`, `git log`, `git diff`, `git push`, `git pull`, `git branch` |
|
|
53
|
+
| Test runners | `cargo test`, `pytest`, `vitest`, `jest`, `go test`, `bun test` |
|
|
54
|
+
| Linters | `eslint`, `tsc`, `ruff`, `clippy`, `golangci-lint`, `rubocop` |
|
|
55
|
+
| Package managers | `pnpm install`, `pip install`, `cargo build`, `npm install` |
|
|
56
|
+
| Containers | `docker build`, `docker ps`, `kubectl get`, `kubectl logs` |
|
|
57
|
+
| Build tools | `make`, `cmake`, `gradle`, `mvn` |
|
|
58
|
+
| System | `ps`, `top`, `df`, `netstat`, `env` |
|
|
59
|
+
|
|
60
|
+
## Output Format
|
|
61
|
+
|
|
62
|
+
### Success
|
|
63
|
+
```
|
|
64
|
+
[RTK Exec] Completed
|
|
65
|
+
|
|
66
|
+
Command: rtk git status
|
|
67
|
+
Duration: 0.3s
|
|
68
|
+
Working Dir: /path/to/project
|
|
69
|
+
|
|
70
|
+
--- Output ---
|
|
71
|
+
{rtk compressed output}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Success (--gain)
|
|
75
|
+
```
|
|
76
|
+
[RTK Exec] Token Savings
|
|
77
|
+
|
|
78
|
+
--- Gain Report ---
|
|
79
|
+
{rtk gain statistics showing tokens saved per command}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Failure
|
|
83
|
+
```
|
|
84
|
+
[RTK Exec] Failed
|
|
85
|
+
|
|
86
|
+
Command: rtk cargo test
|
|
87
|
+
Error: {error_message}
|
|
88
|
+
Exit Code: {code}
|
|
89
|
+
Stderr: {stderr}
|
|
90
|
+
Suggested Fix: {suggestion}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Helper Script
|
|
94
|
+
|
|
95
|
+
For complex executions or programmatic use:
|
|
96
|
+
```bash
|
|
97
|
+
node .claude/skills/rtk-exec/scripts/rtk-wrapper.cjs --command "cargo test" [options]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The wrapper provides:
|
|
101
|
+
- Binary availability check
|
|
102
|
+
- Safe command construction
|
|
103
|
+
- Timeout handling with graceful SIGTERM → SIGKILL escalation
|
|
104
|
+
- Structured JSON output for programmatic consumption
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# List files with compression
|
|
110
|
+
/rtk-exec "ls -la src/"
|
|
111
|
+
|
|
112
|
+
# Git status (often 70%+ token reduction)
|
|
113
|
+
/rtk-exec "git status"
|
|
114
|
+
|
|
115
|
+
# Run Rust tests
|
|
116
|
+
/rtk-exec "cargo test"
|
|
117
|
+
|
|
118
|
+
# Run Python tests
|
|
119
|
+
/rtk-exec "pytest tests/ -v"
|
|
120
|
+
|
|
121
|
+
# TypeScript type check
|
|
122
|
+
/rtk-exec "tsc --noEmit"
|
|
123
|
+
|
|
124
|
+
# Show token savings stats
|
|
125
|
+
/rtk-exec --gain
|
|
126
|
+
|
|
127
|
+
# Initialize RTK for project
|
|
128
|
+
/rtk-exec --init
|
|
129
|
+
|
|
130
|
+
# With timeout override
|
|
131
|
+
/rtk-exec "cargo build --release" --timeout 300000
|
|
132
|
+
|
|
133
|
+
# Specify working directory
|
|
134
|
+
/rtk-exec "git log --oneline -20" --working-dir /path/to/repo
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Integration
|
|
138
|
+
|
|
139
|
+
Works with the orchestrator pattern:
|
|
140
|
+
- Main conversation delegates CLI execution via this skill
|
|
141
|
+
- Results are returned to the main conversation for further processing
|
|
142
|
+
- Particularly effective for test runs, lint checks, and git operations where output is verbose
|
|
143
|
+
- Can be chained: rtk-exec for output collection → Claude expert for analysis
|
|
144
|
+
|
|
145
|
+
## Availability Check
|
|
146
|
+
|
|
147
|
+
rtk-exec requires the RTK binary to be installed. The skill is only usable when:
|
|
148
|
+
|
|
149
|
+
1. `rtk` binary is found in PATH (`which rtk` succeeds)
|
|
150
|
+
2. No authentication or API keys required
|
|
151
|
+
|
|
152
|
+
If the binary check fails, this skill cannot be used. Fall back to direct Bash tool execution.
|
|
153
|
+
|
|
154
|
+
> **Note**: This skill is invoked via `/rtk-exec` command or delegated by the orchestrator. It is most useful for any task that produces verbose CLI output that would otherwise consume large amounts of context tokens.
|
|
155
|
+
|
|
156
|
+
## Agent Teams Integration
|
|
157
|
+
|
|
158
|
+
When used within Agent Teams:
|
|
159
|
+
|
|
160
|
+
1. **As delegated task**: orchestrator explicitly delegates CLI execution for token-efficient output
|
|
161
|
+
2. **Hybrid workflow**: Claude team member plans → rtk-exec runs commands → Claude analyzes compressed output
|
|
162
|
+
3. **Batch execution**: Multiple rtk-exec invocations in parallel for different commands
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
Orchestrator delegates CLI task
|
|
166
|
+
→ /rtk-exec invoked with command
|
|
167
|
+
→ Compressed output returned to orchestrator
|
|
168
|
+
→ Analyst processes compressed result
|
|
169
|
+
→ Iterate if needed
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Token Savings
|
|
173
|
+
|
|
174
|
+
RTK uses four compression strategies:
|
|
175
|
+
|
|
176
|
+
| Strategy | Description | Typical Savings |
|
|
177
|
+
|----------|-------------|-----------------|
|
|
178
|
+
| Smart Filtering | Removes redundant/noise lines based on command type | 30-50% |
|
|
179
|
+
| Grouping | Collapses repeated patterns into summary counts | 20-40% |
|
|
180
|
+
| Truncation | Clips excessively long lines with ellipsis | 10-20% |
|
|
181
|
+
| Deduplication | Removes identical repeated output blocks | 15-30% |
|
|
182
|
+
|
|
183
|
+
Combined effect: **60-90% token reduction** on typical CLI output (git log, test results, lint reports).
|
|
184
|
+
|
|
185
|
+
## Installation
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# macOS via Homebrew
|
|
189
|
+
brew install rtk
|
|
190
|
+
|
|
191
|
+
# Universal install script
|
|
192
|
+
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh
|
|
193
|
+
|
|
194
|
+
# From source via Cargo
|
|
195
|
+
cargo install --git https://github.com/rtk-ai/rtk
|
|
196
|
+
|
|
197
|
+
# Verify installation
|
|
198
|
+
rtk --version
|
|
199
|
+
```
|
|
@@ -0,0 +1,377 @@
|
|
|
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
|
+
});
|
package/templates/CLAUDE.md
CHANGED
|
@@ -120,6 +120,7 @@ oh-my-customcode로 구동됩니다.
|
|
|
120
120
|
| `/omcustom-feedback` | 사용자 피드백을 GitHub Issue로 등록 |
|
|
121
121
|
| `/codex-exec` | Codex CLI 프롬프트 실행 |
|
|
122
122
|
| `/gemini-exec` | Gemini CLI 프롬프트 실행 |
|
|
123
|
+
| `/rtk-exec` | RTK CLI 프록시 실행 (토큰 압축) |
|
|
123
124
|
| `/optimize-analyze` | 번들 및 성능 분석 |
|
|
124
125
|
| `/optimize-bundle` | 번들 크기 최적화 |
|
|
125
126
|
| `/optimize-report` | 최적화 리포트 생성 |
|
|
@@ -149,7 +150,7 @@ project/
|
|
|
149
150
|
+-- CLAUDE.md # 진입점
|
|
150
151
|
+-- .claude/
|
|
151
152
|
| +-- agents/ # 서브에이전트 정의 (46 파일)
|
|
152
|
-
| +-- skills/ # 스킬 (
|
|
153
|
+
| +-- skills/ # 스킬 (100 디렉토리)
|
|
153
154
|
| +-- rules/ # 전역 규칙 (R000-R021)
|
|
154
155
|
| +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
|
|
155
156
|
| +-- contexts/ # 컨텍스트 파일 (ecomode)
|
package/templates/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.67.0",
|
|
3
3
|
"lastUpdated": "2026-03-24T00:00:00.000Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"name": "skills",
|
|
19
19
|
"path": ".claude/skills",
|
|
20
20
|
"description": "Reusable skill modules (includes slash commands)",
|
|
21
|
-
"files":
|
|
21
|
+
"files": 100
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"name": "guides",
|