Skip to content

Commit 233f913

Browse files
Merge pull request #6 from phuthuycoding/fix/skill-flatten
fix: flatten nested skills to /group-action so Claude Code discovers them
2 parents 508f38a + af88cbb commit 233f913

6 files changed

Lines changed: 122 additions & 106 deletions

File tree

CLAUDE.md

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -57,34 +57,34 @@ moicle/
5757
│ │ ├── bootstrap.md
5858
│ │ ├── brainstorm.md
5959
│ │ └── marketing.md
60-
│ └── skills/ # Nested namespace: /group:action
60+
│ └── skills/ # nested in repo, flattened to /group-action on install
6161
│ ├── feature/
62-
│ │ ├── new/ # /feature:new
63-
│ │ ├── refactor/ # /feature:refactor
64-
│ │ ├── api/ # /feature:api
65-
│ │ └── deprecate/ # /feature:deprecate
62+
│ │ ├── new/ # /feature-new
63+
│ │ ├── refactor/ # /feature-refactor
64+
│ │ ├── api/ # /feature-api
65+
│ │ └── deprecate/ # /feature-deprecate
6666
│ ├── fix/
67-
│ │ ├── hotfix/ # /fix:hotfix
68-
│ │ ├── root-cause/ # /fix:root-cause
69-
│ │ ├── incident/ # /fix:incident
70-
│ │ └── pr-comment/ # /fix:pr-comment
67+
│ │ ├── hotfix/ # /fix-hotfix
68+
│ │ ├── root-cause/ # /fix-root-cause
69+
│ │ ├── incident/ # /fix-incident
70+
│ │ └── pr-comment/ # /fix-pr-comment
7171
│ ├── review/
72-
│ │ ├── branch/ # /review:branch
73-
│ │ ├── pr/ # /review:pr
74-
│ │ ├── architect/ # /review:architect
75-
│ │ └── tdd/ # /review:tdd
72+
│ │ ├── branch/ # /review-branch
73+
│ │ ├── pr/ # /review-pr
74+
│ │ ├── architect/ # /review-architect
75+
│ │ └── tdd/ # /review-tdd
7676
│ ├── research/
77-
│ │ ├── web/ # /research:web
78-
│ │ ├── spike/ # /research:spike
79-
│ │ └── onboarding/ # /research:onboarding
77+
│ │ ├── web/ # /research-web
78+
│ │ ├── spike/ # /research-spike
79+
│ │ └── onboarding/ # /research-onboarding
8080
│ ├── docs/
81-
│ │ ├── write/ # /docs:write
82-
│ │ └── sync/ # /docs:sync
81+
│ │ ├── write/ # /docs-write
82+
│ │ └── sync/ # /docs-sync
8383
│ └── marketing/
84-
│ ├── content/ # /marketing:content
85-
│ ├── seo-blog/ # /marketing:seo-blog
86-
│ ├── logo/ # /marketing:logo
87-
│ └── video/ # /marketing:video
84+
│ ├── content/ # /marketing-content
85+
│ ├── seo-blog/ # /marketing-seo-blog
86+
│ ├── logo/ # /marketing-logo
87+
│ └── video/ # /marketing-video
8888
├── package.json
8989
└── README.md
9090
```
@@ -144,54 +144,54 @@ Comprehensive marketing plan wizard - combines logo design, video content, and c
144144

145145
## Skills (21)
146146

147-
Skills are nested under 5 namespaces. Folder path becomes the trigger: `skills/<group>/<action>/SKILL.md``/<group>:<action>`. Old trigger phrases stay in `description` so Claude still auto-invokes the right skill from natural language.
147+
Skills are organized into 6 groups in this repo as `skills/<group>/<action>/SKILL.md`. Claude Code only scans skills **one level deep** and uses the **folder name** as the slash-command name — it does not recurse into nested group folders. So the installer **flattens** each nested skill to a single-level `<group>-<action>` entry: `skills/fix/root-cause/``~/.claude/skills/fix-root-cause/``/fix-root-cause`. A hyphen is used (not a colon) because `:` is an invalid filename character on Windows. Old trigger phrases stay in `description` so Claude still auto-invokes the right skill from natural language.
148148

149149
See `README.md` for the decision matrix when multiple skills overlap.
150150

151-
### `/feature:*` — Build & Change
151+
### `/feature-*` — Build & Change
152152
| Skill | Trigger phrases (auto-invoke) |
153153
|-------|------------------------------|
154-
| `/feature:new` | "implement feature", "add feature", "build feature", "create feature" |
155-
| `/feature:refactor` | "refactor", "clean up", "improve code", "restructure", "migrate to ddd" |
156-
| `/feature:api` | "integrate api", "add endpoint", "new api", "connect api" |
157-
| `/feature:deprecate` | "deprecate", "remove feature", "sunset", "phase out" |
154+
| `/feature-new` | "implement feature", "add feature", "build feature", "create feature" |
155+
| `/feature-refactor` | "refactor", "clean up", "improve code", "restructure", "migrate to ddd" |
156+
| `/feature-api` | "integrate api", "add endpoint", "new api", "connect api" |
157+
| `/feature-deprecate` | "deprecate", "remove feature", "sunset", "phase out" |
158158

159-
### `/fix:*` — Bugs & Incidents
159+
### `/fix-*` — Bugs & Incidents
160160
| Skill | Trigger phrases (auto-invoke) |
161161
|-------|------------------------------|
162-
| `/fix:hotfix` | "fix bug", "hotfix", "urgent fix", "production issue" |
163-
| `/fix:root-cause` | "deep debug", "trace bug", "find root cause", "hard bug" |
164-
| `/fix:incident` | "incident", "outage", "production down", "service down" |
165-
| `/fix:pr-comment` | "fix pr comment", "fix review comment", "address pr feedback" |
162+
| `/fix-hotfix` | "fix bug", "hotfix", "urgent fix", "production issue" |
163+
| `/fix-root-cause` | "deep debug", "trace bug", "find root cause", "hard bug" |
164+
| `/fix-incident` | "incident", "outage", "production down", "service down" |
165+
| `/fix-pr-comment` | "fix pr comment", "fix review comment", "address pr feedback" |
166166

167-
### `/review:*` — Review & Quality
167+
### `/review-*` — Review & Quality
168168
| Skill | Trigger phrases (auto-invoke) |
169169
|-------|------------------------------|
170-
| `/review:branch` | "review changes", "review branch", "check branch", "review before pr" |
171-
| `/review:pr` | "review pr", "check pr", "review code", "pr review" |
172-
| `/review:architect` | "architect-review", "architecture review", "review ddd" |
173-
| `/review:tdd` | "tdd", "test first", "test driven", "red green refactor" |
170+
| `/review-branch` | "review changes", "review branch", "check branch", "review before pr" |
171+
| `/review-pr` | "review pr", "check pr", "review code", "pr review" |
172+
| `/review-architect` | "architect-review", "architecture review", "review ddd" |
173+
| `/review-tdd` | "tdd", "test first", "test driven", "red green refactor" |
174174

175-
### `/research:*` — Explore & Learn
175+
### `/research-*` — Explore & Learn
176176
| Skill | Trigger phrases (auto-invoke) |
177177
|-------|------------------------------|
178-
| `/research:web` | "research", "tìm giải pháp", "find best practice", "so sánh giải pháp" |
179-
| `/research:spike` | "spike", "prototype", "poc", "explore" |
180-
| `/research:onboarding` | "explain codebase", "onboard", "new to project", "understand project" |
178+
| `/research-web` | "research", "tìm giải pháp", "find best practice", "so sánh giải pháp" |
179+
| `/research-spike` | "spike", "prototype", "poc", "explore" |
180+
| `/research-onboarding` | "explain codebase", "onboard", "new to project", "understand project" |
181181

182-
### `/docs:*` — Project Documentation
182+
### `/docs-*` — Project Documentation
183183
| Skill | Trigger phrases (auto-invoke) |
184184
|-------|------------------------------|
185-
| `/docs:write` | "document", "generate docs", "write docs" |
186-
| `/docs:sync` | "sync docs", "sync documentation", "doc sync" |
185+
| `/docs-write` | "document", "generate docs", "write docs" |
186+
| `/docs-sync` | "sync docs", "sync documentation", "doc sync" |
187187

188-
### `/marketing:*` — Brand & Content (wrapped by `/marketing` command)
188+
### `/marketing-*` — Brand & Content (wrapped by `/marketing` command)
189189
| Skill | Trigger phrases (auto-invoke) |
190190
|-------|------------------------------|
191-
| `/marketing:content` | "write content", "content strategy", "content plan", "newsletter" |
192-
| `/marketing:seo-blog` | "write seo blog", "seo blog", "evergreen post", "compare post", "blog for AI" |
193-
| `/marketing:logo` | "design logo", "create logo", "brand identity" |
194-
| `/marketing:video` | "create video", "video content", "video script" |
191+
| `/marketing-content` | "write content", "content strategy", "content plan", "newsletter" |
192+
| `/marketing-seo-blog` | "write seo blog", "seo blog", "evergreen post", "compare post", "blog for AI" |
193+
| `/marketing-logo` | "design logo", "create logo", "brand identity" |
194+
| `/marketing-video` | "create video", "video content", "video script" |
195195

196196
## Development
197197

README.md

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -118,78 +118,78 @@ moicle install --target antigravity --global
118118

119119
### Skills (21)
120120

121-
Skills are grouped into 6 namespaces. Type `/<group>:<tab>` in Claude Code to see all skills in a group.
121+
Skills are grouped by a `<group>-` prefix. Type `/<group>-` then `Tab` in Claude Code to see all skills in a group.
122122

123-
**`/feature:*` — Build & Change**
123+
**`/feature-*` — Build & Change**
124124

125125
| Skill | When to use |
126126
|-------|-------------|
127-
| `/feature:new` | Build a new feature end-to-end following DDD |
128-
| `/feature:refactor` | Restructure existing module to DDD or improve internals |
129-
| `/feature:api` | Add a new endpoint or integrate an external API |
130-
| `/feature:deprecate` | Safely sunset a feature, API, or module |
127+
| `/feature-new` | Build a new feature end-to-end following DDD |
128+
| `/feature-refactor` | Restructure existing module to DDD or improve internals |
129+
| `/feature-api` | Add a new endpoint or integrate an external API |
130+
| `/feature-deprecate` | Safely sunset a feature, API, or module |
131131

132-
**`/fix:*` — Bugs & Incidents**
132+
**`/fix-*` — Bugs & Incidents**
133133

134134
| Skill | When to use |
135135
|-------|-------------|
136-
| `/fix:hotfix` | Fix a bug fast with a rollback plan |
137-
| `/fix:root-cause` | Hard-to-trace bug that has been "fixed" multiple times |
138-
| `/fix:incident` | Production outage / on-call workflow |
139-
| `/fix:pr-comment` | Address review comments on an existing PR |
136+
| `/fix-hotfix` | Fix a bug fast with a rollback plan |
137+
| `/fix-root-cause` | Hard-to-trace bug that has been "fixed" multiple times |
138+
| `/fix-incident` | Production outage / on-call workflow |
139+
| `/fix-pr-comment` | Address review comments on an existing PR |
140140

141-
**`/review:*` — Review & Quality**
141+
**`/review-*` — Review & Quality**
142142

143143
| Skill | When to use |
144144
|-------|-------------|
145-
| `/review:branch` | Self-review your branch BEFORE pushing / opening PR |
146-
| `/review:pr` | Review someone else's open PR |
147-
| `/review:architect` | DDD compliance check (called by `/feature:new` / `/feature:refactor`) |
148-
| `/review:tdd` | Drive implementation with test-first discipline |
145+
| `/review-branch` | Self-review your branch BEFORE pushing / opening PR |
146+
| `/review-pr` | Review someone else's open PR |
147+
| `/review-architect` | DDD compliance check (called by `/feature-new` / `/feature-refactor`) |
148+
| `/review-tdd` | Drive implementation with test-first discipline |
149149

150-
**`/research:*` — Explore & Learn**
150+
**`/research-*` — Explore & Learn**
151151

152152
| Skill | When to use |
153153
|-------|-------------|
154-
| `/research:web` | Search the web for solutions / best practices |
155-
| `/research:spike` | Time-boxed prototype to learn / decide |
156-
| `/research:onboarding` | Get up to speed on a new codebase |
154+
| `/research-web` | Search the web for solutions / best practices |
155+
| `/research-spike` | Time-boxed prototype to learn / decide |
156+
| `/research-onboarding` | Get up to speed on a new codebase |
157157

158-
**`/docs:*` — Project Documentation**
158+
**`/docs-*` — Project Documentation**
159159

160160
| Skill | When to use |
161161
|-------|-------------|
162-
| `/docs:write` | Author docs manually (README / API / ARCH / CONTRIB) |
163-
| `/docs:sync` | Auto-generate structured docs from codebase with review loop |
162+
| `/docs-write` | Author docs manually (README / API / ARCH / CONTRIB) |
163+
| `/docs-sync` | Auto-generate structured docs from codebase with review loop |
164164

165-
**`/marketing:*` — Brand & Content** (wrapped by the `/marketing` command)
165+
**`/marketing-*` — Brand & Content** (wrapped by the `/marketing` command)
166166

167167
| Skill | When to use |
168168
|-------|-------------|
169-
| `/marketing:content` | Multi-post content strategy (pillars, calendar, channels) |
170-
| `/marketing:seo-blog` | Write ONE evergreen blog post optimized for Search + AI tools |
171-
| `/marketing:logo` | Logo + brand identity specification |
172-
| `/marketing:video` | Video script, storyboard, production plan |
169+
| `/marketing-content` | Multi-post content strategy (pillars, calendar, channels) |
170+
| `/marketing-seo-blog` | Write ONE evergreen blog post optimized for Search + AI tools |
171+
| `/marketing-logo` | Logo + brand identity specification |
172+
| `/marketing-video` | Video script, storyboard, production plan |
173173

174174
### Skill decision matrix
175175

176176
When more than one skill could fit, use this matrix:
177177

178178
| Situation | Use | Not |
179179
|-----------|-----|-----|
180-
| Bug just happened in prod, need fix in <1h | `/fix:hotfix` | `/fix:root-cause` (too slow) |
181-
| Bug keeps coming back after "fixes" | `/fix:root-cause` | `/fix:hotfix` (will repeat) |
182-
| About to push / open PR | `/review:branch` | `/review:pr` (that's for others') |
183-
| Reviewing teammate's PR | `/review:pr` | `/review:branch` (that's for own branch) |
184-
| Want to verify DDD compliance only | `/review:architect` | `/review:pr` (broader scope) |
185-
| Don't know the right solution yet | `/research:web` | `/research:spike` (skip if you can decide from docs) |
186-
| Need to validate an idea by building | `/research:spike` | `/feature:new` (commit only after spike) |
187-
| Writing README / API docs by hand | `/docs:write` | `/docs:sync` (overkill for single file) |
188-
| Generating full docs site from codebase | `/docs:sync` | `/docs:write` (manual is slower) |
180+
| Bug just happened in prod, need fix in <1h | `/fix-hotfix` | `/fix-root-cause` (too slow) |
181+
| Bug keeps coming back after "fixes" | `/fix-root-cause` | `/fix-hotfix` (will repeat) |
182+
| About to push / open PR | `/review-branch` | `/review-pr` (that's for others') |
183+
| Reviewing teammate's PR | `/review-pr` | `/review-branch` (that's for own branch) |
184+
| Want to verify DDD compliance only | `/review-architect` | `/review-pr` (broader scope) |
185+
| Don't know the right solution yet | `/research-web` | `/research-spike` (skip if you can decide from docs) |
186+
| Need to validate an idea by building | `/research-spike` | `/feature-new` (commit only after spike) |
187+
| Writing README / API docs by hand | `/docs-write` | `/docs-sync` (overkill for single file) |
188+
| Generating full docs site from codebase | `/docs-sync` | `/docs-write` (manual is slower) |
189189

190190
### Backward compatibility
191191

192-
Old trigger phrases still work — they're kept in the skill `description` so Claude auto-invokes the right skill when the user says e.g. "deep debug", "hotfix", "review changes". The namespace `/group:action` is the new explicit invocation form.
192+
Old trigger phrases still work — they're kept in the skill `description` so Claude auto-invokes the right skill when the user says e.g. "deep debug", "hotfix", "review changes". The flattened name `/group-action` is the explicit invocation form.
193193

194194
## Architecture-First Approach
195195

src/commands/install/native.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
getArchitectureDir,
1515
getClaudeDir,
1616
getFiles,
17-
getDirs,
17+
listSkillsNested,
1818
} from '../../utils/symlink.js';
1919
import { printInstalled } from './print.js';
2020

@@ -55,10 +55,14 @@ const installCommands = (targetDir: string, useSymlink: boolean): FileResult[] =
5555
const installSkills = (targetDir: string, useSymlink: boolean): FileResult[] => {
5656
ensureDir(targetDir);
5757
const skillsDir = path.join(ASSETS_DIR, 'skills');
58+
// Claude Code scans skills one level deep and uses the folder name as the
59+
// slash-command name. Nested groups (skills/<group>/<action>/SKILL.md) are
60+
// flattened to a single-level "<group>-<action>" entry so each becomes
61+
// /<group>-<action>.
5862
const results = fs.existsSync(skillsDir)
59-
? getDirs(skillsDir).map((dir) => {
60-
const target = path.join(targetDir, path.basename(dir));
61-
return useSymlink ? createSymlink(dir, target) : copyDir(dir, target);
63+
? listSkillsNested(skillsDir).map((skill) => {
64+
const target = path.join(targetDir, skill.name);
65+
return useSymlink ? createSymlink(skill.path, target) : copyDir(skill.path, target);
6266
})
6367
: [];
6468
printInstalled('Skills', targetDir, results);

src/commands/install/skill-editor.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import chalk from 'chalk';
22
import path from 'path';
33
import fs from 'fs';
44
import type { FileResult, Scope } from '../../types.js';
5-
import { ASSETS_DIR, ensureDir, getEditorConfig, getEditorDir, getFiles, getDirs } from '../../utils/symlink.js';
5+
import { ASSETS_DIR, ensureDir, getEditorConfig, getEditorDir, getFiles, listSkillsNested } from '../../utils/symlink.js';
66
import { printSummary } from './print.js';
77
import {
88
type SkillEditorTarget,
@@ -41,9 +41,9 @@ const ensureSkillDir = (baseDir: string, name: string): string => {
4141
const installSkillFolder = (
4242
sourceDir: string,
4343
targetSkillsDir: string,
44-
target: SkillEditorTarget
44+
target: SkillEditorTarget,
45+
skillName: string = path.basename(sourceDir)
4546
): FileResult => {
46-
const skillName = path.basename(sourceDir);
4747
const targetDir = ensureSkillDir(targetSkillsDir, skillName);
4848

4949
let status: FileResult['status'] = 'created';
@@ -111,11 +111,12 @@ const installEditorSkills = (targetDir: string, target: SkillEditorTarget): File
111111
const targetSkillsDir = path.join(targetDir, 'skills');
112112
ensureDir(targetSkillsDir);
113113

114-
// 1. Skill folders → copied verbatim (paths rewritten).
114+
// 1. Skill folders → copied verbatim (paths rewritten). Nested groups are
115+
// flattened to a single-level "<group>-<action>" skill folder.
115116
const skillsDir = path.join(ASSETS_DIR, 'skills');
116117
if (fs.existsSync(skillsDir)) {
117-
for (const dir of getDirs(skillsDir)) {
118-
results.push(installSkillFolder(dir, targetSkillsDir, target));
118+
for (const skill of listSkillsNested(skillsDir)) {
119+
results.push(installSkillFolder(skill.path, targetSkillsDir, target, skill.name));
119120
}
120121
}
121122

src/commands/uninstall.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
EDITOR_CONFIGS,
1010
removeItem,
1111
listItems,
12+
listSkillsNested,
1213
getAgentsDir,
1314
getCommandsDir,
1415
getSkillsDir,
@@ -43,7 +44,9 @@ const getKitFiles = (): Set<string> => {
4344
addFilesFromDir(developersDir);
4445
addFilesFromDir(utilitiesDir);
4546
addFilesFromDir(commandsDir);
46-
addFilesFromDir(skillsDir);
47+
addFilesFromDir(skillsDir); // group folder names, cleans up legacy nested installs
48+
// Flattened skill names (<group>-<action>) created by the current installer.
49+
listSkillsNested(skillsDir).forEach((s) => files.add(s.name));
4750

4851
return files;
4952
};
@@ -101,7 +104,8 @@ const getCodexManagedNames = (): { architecture: string[]; skills: string[] } =>
101104

102105
const skillsDir = path.join(ASSETS_DIR, 'skills');
103106
if (fs.existsSync(skillsDir)) {
104-
fs.readdirSync(skillsDir).forEach((name) => skills.push(name));
107+
fs.readdirSync(skillsDir).forEach((name) => skills.push(name)); // legacy group folders
108+
listSkillsNested(skillsDir).forEach((s) => skills.push(s.name)); // flattened skills
105109
}
106110

107111
const commandsDir = path.join(ASSETS_DIR, 'commands');
@@ -158,7 +162,8 @@ const getAntigravityManagedNames = (): { architecture: string[]; skills: string[
158162

159163
const skillsDir = path.join(ASSETS_DIR, 'skills');
160164
if (fs.existsSync(skillsDir)) {
161-
fs.readdirSync(skillsDir).forEach((name) => skills.push(name));
165+
fs.readdirSync(skillsDir).forEach((name) => skills.push(name)); // legacy group folders
166+
listSkillsNested(skillsDir).forEach((s) => skills.push(s.name)); // flattened skills
162167
}
163168

164169
const commandsDir = path.join(ASSETS_DIR, 'commands');

0 commit comments

Comments
 (0)