oh-my-customcode 0.65.2 → 0.66.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 +12 -0
- package/templates/.claude/skills/de-lead-routing/SKILL.md +6 -2
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +6 -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 +19 -2
- package/templates/CLAUDE.md +13 -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. 99 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 (99)
|
|
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/ # 99 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
|
@@ -21,6 +21,16 @@ if command -v codex >/dev/null 2>&1; then
|
|
|
21
21
|
fi
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
+
# Check Gemini CLI availability
|
|
25
|
+
GEMINI_STATUS="unavailable"
|
|
26
|
+
if command -v gemini >/dev/null 2>&1; then
|
|
27
|
+
if [ -n "${GOOGLE_API_KEY:-}" ] || [ -n "${GEMINI_API_KEY:-}" ]; then
|
|
28
|
+
GEMINI_STATUS="available (authenticated)"
|
|
29
|
+
else
|
|
30
|
+
GEMINI_STATUS="installed (gcloud auth may be available)"
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
24
34
|
# Check Agent Teams availability
|
|
25
35
|
AGENT_TEAMS_STATUS="disabled"
|
|
26
36
|
if [ "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-0}" = "1" ]; then
|
|
@@ -134,6 +144,7 @@ fi
|
|
|
134
144
|
STATUS_FILE="/tmp/.claude-env-status-${PPID}"
|
|
135
145
|
cat > "$STATUS_FILE" << ENVEOF
|
|
136
146
|
codex=${CODEX_STATUS}
|
|
147
|
+
gemini=${GEMINI_STATUS}
|
|
137
148
|
agent_teams=${AGENT_TEAMS_STATUS}
|
|
138
149
|
git_branch=${CURRENT_BRANCH}
|
|
139
150
|
claude_version=${CLAUDE_VERSION}
|
|
@@ -144,6 +155,7 @@ ENVEOF
|
|
|
144
155
|
|
|
145
156
|
# Report to stderr (visible in conversation)
|
|
146
157
|
echo " codex CLI: ${CODEX_STATUS}" >&2
|
|
158
|
+
echo " gemini CLI: ${GEMINI_STATUS}" >&2
|
|
147
159
|
echo " Agent Teams: ${AGENT_TEAMS_STATUS}" >&2
|
|
148
160
|
echo " Claude Code: v${CLAUDE_VERSION} (${COMPAT_STATUS})" >&2
|
|
149
161
|
if [ "$COMPAT_STATUS" = "outdated" ]; then
|
|
@@ -64,12 +64,16 @@ 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 availability
|
|
67
|
+
1. Check `/tmp/.claude-env-status-*` for codex and gemini 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)
|
|
71
71
|
- Selected DE expert reviews and refines codex output (strength: reasoning, quality)
|
|
72
|
-
3. If codex unavailable
|
|
72
|
+
3. If codex unavailable but gemini available → delegate to `/gemini-exec` for scaffolding:
|
|
73
|
+
- Display: `[Gemini Hybrid] Delegating to gemini-exec...`
|
|
74
|
+
- gemini-exec generates initial code
|
|
75
|
+
- Selected DE expert reviews and refines output
|
|
76
|
+
4. If neither available → display `[External CLI] Unavailable — proceeding with {expert} directly` and use DE expert directly
|
|
73
77
|
|
|
74
78
|
**Suitable**: New DAG files, dbt model scaffolding, SQL template generation
|
|
75
79
|
**Unsuitable**: Existing pipeline modification, architecture decisions, data quality analysis
|
|
@@ -99,12 +99,16 @@ 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 availability
|
|
102
|
+
1. Check `/tmp/.claude-env-status-*` for codex and gemini 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)
|
|
106
106
|
- Selected Claude expert reviews and refines codex output (strength: reasoning, quality)
|
|
107
|
-
3. If codex unavailable
|
|
107
|
+
3. If codex unavailable but gemini available → delegate to `/gemini-exec` for scaffolding:
|
|
108
|
+
- Display: `[Gemini Hybrid] Delegating to gemini-exec...`
|
|
109
|
+
- gemini-exec generates initial code
|
|
110
|
+
- Selected Claude expert reviews and refines output
|
|
111
|
+
4. If neither available → display `[External CLI] Unavailable — proceeding with {expert} directly` and use Claude expert directly
|
|
108
112
|
|
|
109
113
|
**Suitable**: New file creation, boilerplate, scaffolding, test code
|
|
110
114
|
**Unsuitable**: Existing code modification, architecture decisions, bug fixes
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gemini-exec
|
|
3
|
+
description: Execute Gemini CLI prompts and return results
|
|
4
|
+
scope: core
|
|
5
|
+
argument-hint: "<prompt> [--json] [--stream-json] [--output <path>] [--model <name>] [--timeout <ms>] [--sandbox] [--plan]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Gemini Exec Skill
|
|
10
|
+
|
|
11
|
+
Execute Google Gemini CLI prompts in non-interactive mode and return structured results. Enables Claude + Gemini hybrid workflows.
|
|
12
|
+
|
|
13
|
+
## Options
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
<prompt> Required. The prompt to send to Gemini CLI
|
|
17
|
+
--json Return structured JSON output (-o json)
|
|
18
|
+
--stream-json Return streaming JSON events (-o stream-json)
|
|
19
|
+
--output <path> Save response to file
|
|
20
|
+
--model <name> Model override (default: Gemini CLI default)
|
|
21
|
+
--timeout <ms> Execution timeout (default: 120000, max: 600000)
|
|
22
|
+
--yolo Enable auto-approval mode (gemini -y)
|
|
23
|
+
--sandbox Run in sandbox mode (gemini -s)
|
|
24
|
+
--plan Use plan approval mode (--approval-mode plan)
|
|
25
|
+
--working-dir Working directory for Gemini execution
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
1. Pre-checks
|
|
32
|
+
- Verify `gemini` binary is installed (which gemini)
|
|
33
|
+
- Verify authentication (GOOGLE_API_KEY, GEMINI_API_KEY, or gcloud auth)
|
|
34
|
+
2. Build command
|
|
35
|
+
- Base: gemini -p "<prompt>"
|
|
36
|
+
- Apply options: -o json, -m <model>, -y, -s, --approval-mode plan
|
|
37
|
+
3. Execute
|
|
38
|
+
- Run via Bash tool with timeout (default 2min, max 10min)
|
|
39
|
+
- Or use helper script: node .claude/skills/gemini-exec/scripts/gemini-wrapper.cjs
|
|
40
|
+
4. Parse output
|
|
41
|
+
- Text mode: return raw stdout
|
|
42
|
+
- JSON mode: parse single JSON object, extract response field
|
|
43
|
+
- Stream-JSON mode: parse event stream, extract final assistant message
|
|
44
|
+
5. Report results
|
|
45
|
+
- Format output with execution metadata
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Safety Defaults
|
|
49
|
+
|
|
50
|
+
- `-p` flag: Non-interactive prompt mode (no session persistence)
|
|
51
|
+
- Default mode: Normal approval (Gemini prompts for confirmation)
|
|
52
|
+
- Override with `--yolo` only when explicitly requested
|
|
53
|
+
- Sandbox mode (`-s`) available for isolated execution
|
|
54
|
+
|
|
55
|
+
## Output Format
|
|
56
|
+
|
|
57
|
+
### Success (Text Mode)
|
|
58
|
+
```
|
|
59
|
+
[Gemini Exec] Completed
|
|
60
|
+
|
|
61
|
+
Model: (default)
|
|
62
|
+
Duration: 23.4s
|
|
63
|
+
Working Dir: /path/to/project
|
|
64
|
+
|
|
65
|
+
--- Output ---
|
|
66
|
+
{gemini response text}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Success (JSON Mode)
|
|
70
|
+
```
|
|
71
|
+
[Gemini Exec] Completed (JSON)
|
|
72
|
+
|
|
73
|
+
Model: (default)
|
|
74
|
+
Duration: 23.4s
|
|
75
|
+
|
|
76
|
+
--- Response ---
|
|
77
|
+
{extracted response from JSON}
|
|
78
|
+
|
|
79
|
+
--- Stats ---
|
|
80
|
+
{token usage and other stats}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Success (Stream-JSON Mode)
|
|
84
|
+
```
|
|
85
|
+
[Gemini Exec] Completed (Stream-JSON)
|
|
86
|
+
|
|
87
|
+
Model: (default)
|
|
88
|
+
Duration: 23.4s
|
|
89
|
+
Events: 12
|
|
90
|
+
|
|
91
|
+
--- Final Message ---
|
|
92
|
+
{extracted final assistant message}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Failure
|
|
96
|
+
```
|
|
97
|
+
[Gemini Exec] Failed
|
|
98
|
+
|
|
99
|
+
Error: {error_message}
|
|
100
|
+
Exit Code: {code}
|
|
101
|
+
Suggested Fix: {suggestion}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Helper Script
|
|
105
|
+
|
|
106
|
+
For complex executions, use the wrapper script:
|
|
107
|
+
```bash
|
|
108
|
+
node .claude/skills/gemini-exec/scripts/gemini-wrapper.cjs --prompt "your prompt" [options]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The wrapper provides:
|
|
112
|
+
- Environment validation (binary + auth checks)
|
|
113
|
+
- Safe command construction
|
|
114
|
+
- JSON and stream-JSON parsing with response extraction
|
|
115
|
+
- Structured JSON output
|
|
116
|
+
- Timeout handling with graceful termination
|
|
117
|
+
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Simple text prompt
|
|
122
|
+
gemini-exec "explain what this project does"
|
|
123
|
+
|
|
124
|
+
# JSON output with model override
|
|
125
|
+
gemini-exec "list all TODO items" --json --model gemini-2.5-pro
|
|
126
|
+
|
|
127
|
+
# Stream-JSON for detailed event tracking
|
|
128
|
+
gemini-exec "analyze the codebase" --stream-json
|
|
129
|
+
|
|
130
|
+
# Save output to file
|
|
131
|
+
gemini-exec "generate a README" --output ./README.md
|
|
132
|
+
|
|
133
|
+
# Sandbox mode with auto-approval
|
|
134
|
+
gemini-exec "fix the failing tests" --yolo --sandbox
|
|
135
|
+
|
|
136
|
+
# Plan mode for careful execution
|
|
137
|
+
gemini-exec "refactor the auth module" --plan
|
|
138
|
+
|
|
139
|
+
# Specify working directory
|
|
140
|
+
gemini-exec "analyze the codebase" --working-dir /path/to/project
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Integration
|
|
144
|
+
|
|
145
|
+
Works with the orchestrator pattern:
|
|
146
|
+
- Main conversation delegates Gemini execution via this skill
|
|
147
|
+
- Results are returned to the main conversation for further processing
|
|
148
|
+
- Can be chained with other skills (e.g., dev-review after Gemini generates code)
|
|
149
|
+
|
|
150
|
+
## Availability Check
|
|
151
|
+
|
|
152
|
+
gemini-exec requires the Gemini CLI binary to be installed and authenticated. The skill is only usable when:
|
|
153
|
+
|
|
154
|
+
1. `gemini` binary is found in PATH (`which gemini` succeeds)
|
|
155
|
+
2. Authentication is valid (GOOGLE_API_KEY, GEMINI_API_KEY set, or gcloud auth active)
|
|
156
|
+
|
|
157
|
+
If either check fails, this skill cannot be used. Fall back to Claude agents for the task.
|
|
158
|
+
|
|
159
|
+
> **Note**: This skill is invoked via `/gemini-exec` command, delegated by the orchestrator, or suggested by routing skills when gemini is available. The intent-detection system can trigger it for research and code generation hybrid workflows.
|
|
160
|
+
|
|
161
|
+
## Agent Teams Integration
|
|
162
|
+
|
|
163
|
+
When used within Agent Teams (requires explicit invocation):
|
|
164
|
+
|
|
165
|
+
1. **As delegated task**: orchestrator explicitly delegates gemini-exec for code generation
|
|
166
|
+
2. **Hybrid workflow**: Claude team member analyzes → orchestrator invokes gemini-exec → Claude reviews
|
|
167
|
+
3. **Iteration**: Team messaging enables review-fix cycles between Claude and Gemini outputs
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Orchestrator delegates generation task
|
|
171
|
+
→ /gemini-exec invoked explicitly
|
|
172
|
+
→ Output returned to orchestrator
|
|
173
|
+
→ Reviewer validates quality
|
|
174
|
+
→ Iterate if needed
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Research Workflow
|
|
178
|
+
|
|
179
|
+
When the orchestrator or intent-detection detects a research request:
|
|
180
|
+
|
|
181
|
+
1. **Check Gemini availability**: Verify `gemini` binary and auth
|
|
182
|
+
2. **If available**: Execute prompt for research
|
|
183
|
+
3. **If unavailable**: Fall back to Claude's WebFetch/WebSearch
|
|
184
|
+
|
|
185
|
+
### Research Command Pattern
|
|
186
|
+
```
|
|
187
|
+
/gemini-exec "Research and analyze: {topic}. Provide structured findings with sources." --json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Code Generation Workflow
|
|
191
|
+
|
|
192
|
+
When routing skills detect a code generation task and gemini is available:
|
|
193
|
+
|
|
194
|
+
1. **Check availability**: Verify gemini CLI via `/tmp/.claude-env-status-*`
|
|
195
|
+
2. **If available + new file creation**: Suggest hybrid workflow
|
|
196
|
+
3. **Hybrid pattern**:
|
|
197
|
+
- gemini-exec generates initial code (fast, broad generation)
|
|
198
|
+
- Claude expert reviews for quality, patterns, best practices
|
|
199
|
+
- Iterate if needed
|
|
200
|
+
|
|
201
|
+
### Suitable Tasks
|
|
202
|
+
- New file scaffolding
|
|
203
|
+
- Boilerplate generation
|
|
204
|
+
- Test stub creation
|
|
205
|
+
- Documentation generation
|
|
206
|
+
|
|
207
|
+
### Unsuitable Tasks
|
|
208
|
+
- Modifying existing code (Claude expert better at understanding context)
|
|
209
|
+
- Architecture decisions (requires reasoning, not generation)
|
|
210
|
+
- Bug fixes (requires deep code understanding)
|
|
211
|
+
|
|
212
|
+
### Code Generation Command Pattern
|
|
213
|
+
```
|
|
214
|
+
/gemini-exec "Generate {description} following {framework} best practices" --yolo
|
|
215
|
+
```
|
|
@@ -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,12 @@ 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"
|
package/templates/CLAUDE.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- omcustom:start -->
|
|
1
2
|
# AI 에이전트 시스템
|
|
2
3
|
|
|
3
4
|
oh-my-customcode로 구동됩니다.
|
|
@@ -118,6 +119,7 @@ oh-my-customcode로 구동됩니다.
|
|
|
118
119
|
| `/omcustom-release-notes` | 릴리즈 노트 생성 (git 히스토리 기반) |
|
|
119
120
|
| `/omcustom-feedback` | 사용자 피드백을 GitHub Issue로 등록 |
|
|
120
121
|
| `/codex-exec` | Codex CLI 프롬프트 실행 |
|
|
122
|
+
| `/gemini-exec` | Gemini CLI 프롬프트 실행 |
|
|
121
123
|
| `/optimize-analyze` | 번들 및 성능 분석 |
|
|
122
124
|
| `/optimize-bundle` | 번들 크기 최적화 |
|
|
123
125
|
| `/optimize-report` | 최적화 리포트 생성 |
|
|
@@ -125,11 +127,19 @@ oh-my-customcode로 구동됩니다.
|
|
|
125
127
|
| `/scout` | 외부 URL 분석 및 프로젝트 적합성 평가 |
|
|
126
128
|
| `/deep-plan` | 연구 검증 기반 계획 수립 (research → plan → verify) |
|
|
127
129
|
| `/deep-verify` | 다중 관점 릴리즈 품질 검증 |
|
|
130
|
+
| `/professor-triage` | 이슈 교차 분석 트리아지 (omc_issue_analyzer 댓글 기반) |
|
|
131
|
+
| `/release-plan` | verify-done 이슈 릴리즈 유닛 계획 생성 |
|
|
132
|
+
| `/omcustom:workflow` | YAML 워크플로우 실행 (예: /omcustom:workflow auto-dev) |
|
|
133
|
+
| `/omcustom:workflow:resume` | 중단된 워크플로우 재개 |
|
|
128
134
|
| `/omcustom:sauron-watch` | 전체 R017 검증 |
|
|
135
|
+
| `/sdd-dev` | Spec-Driven Development 워크플로우 (sdd/ 폴더 기반) |
|
|
129
136
|
| `/structured-dev-cycle` | 6단계 구조적 개발 사이클 (Plan → Verify → Implement → Verify → Compound → Done) |
|
|
130
137
|
| `/omcustom:loop` | 백그라운드 에이전트 자동 계속 실행 |
|
|
131
138
|
| `/omcustom:lists` | 모든 사용 가능한 커맨드 표시 |
|
|
132
139
|
| `/omcustom:status` | 시스템 상태 표시 |
|
|
140
|
+
| `/omcustom-web` | 내장 Web UI 제어 및 검사 |
|
|
141
|
+
| `/skills-sh-search` | skills.sh 마켓플레이스 스킬 검색 및 설치 |
|
|
142
|
+
| `/vercel-deploy` | Vercel 배포 자동화 |
|
|
133
143
|
| `/omcustom:help` | 도움말 표시 |
|
|
134
144
|
|
|
135
145
|
## 프로젝트 구조
|
|
@@ -139,7 +149,7 @@ project/
|
|
|
139
149
|
+-- CLAUDE.md # 진입점
|
|
140
150
|
+-- .claude/
|
|
141
151
|
| +-- agents/ # 서브에이전트 정의 (46 파일)
|
|
142
|
-
| +-- skills/ # 스킬 (
|
|
152
|
+
| +-- skills/ # 스킬 (99 디렉토리)
|
|
143
153
|
| +-- rules/ # 전역 규칙 (R000-R021)
|
|
144
154
|
| +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
|
|
145
155
|
| +-- contexts/ # 컨텍스트 파일 (ecomode)
|
|
@@ -278,3 +288,5 @@ claude-mem setup
|
|
|
278
288
|
```
|
|
279
289
|
|
|
280
290
|
<!-- omcustom:git-workflow -->
|
|
291
|
+
|
|
292
|
+
<!-- omcustom:end -->
|
package/templates/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.66.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": 99
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"name": "guides",
|