@tinkcarlos/skillora 0.2.3 โ 0.2.4
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/CLAUDE.md +215 -0
- package/lib/init.js +239 -131
- package/package.json +2 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## ๐ ่ฏญ่จ่ฎพ็ฝฎ (Language Setting)
|
|
4
|
+
|
|
5
|
+
**ๅผบๅถ่ฆๆฑ**: ๆๆ่พๅบๅ
ๅฎนๅฟ
้กปไฝฟ็จ**็ฎไฝไธญๆ**๏ผๅ
ๆฌไฝไธ้ไบ๏ผ
|
|
6
|
+
- ไปปๅกๅๆๅ่ฎกๅ (Task Analysis & Plan)
|
|
7
|
+
- Todoๅ่กจ (Todo List)
|
|
8
|
+
- ๆ่ฝๅๆข้็ฅ (Skill Switch Notification)
|
|
9
|
+
- ไปปๅกๅฎๆๆฅๅ (Completion Report)
|
|
10
|
+
- ้ฎ้ขๆพๆธ
ๅ็กฎ่ฎค (Clarification & Confirmation)
|
|
11
|
+
|
|
12
|
+
## Core Decision Flow (Execute on EVERY user input)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Step 1: Task Type Matching (Highest Priority)
|
|
16
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
17
|
+
โ Keywords โ Force Skill (regardless of complexity) โ
|
|
18
|
+
โ โโ fix/bug/error/exception/broken โ @bug-fixing โ
|
|
19
|
+
โ โโ develop/feature/implement/add โ @fullstack-developer โ
|
|
20
|
+
โ โโ optimize/refactor/improve โ @fullstack-developer โ
|
|
21
|
+
โ โโ review/check code/PR โ @code-review โ
|
|
22
|
+
โ โโ frontend/UI/interface/style โ @frontend-design โ
|
|
23
|
+
โ โโ Other โ Go to Step 2 โ
|
|
24
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
25
|
+
โ
|
|
26
|
+
Step 2: Complexity Assessment (Only when Step 1 doesn't match)
|
|
27
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
28
|
+
โ Steps โ Action โ
|
|
29
|
+
โ โโ 1-2 steps โ Simple โ Execute directly โ
|
|
30
|
+
โ โโ 3-5 steps โ Medium โ Must create Todo + Recommend Skill โ
|
|
31
|
+
โ โโ 5+ steps โ Complex โ Must Todo + Must Skill + Must MCP โ
|
|
32
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Skill Mapping Table
|
|
36
|
+
|
|
37
|
+
### Mandatory Skills (Must invoke when matched)
|
|
38
|
+
|
|
39
|
+
| Task Type | Skill | MCP Tools | Closed-loop |
|
|
40
|
+
|-----------|-------|-----------|-------------|
|
|
41
|
+
| Bug Fix | `@bug-fixing` | serena | โ @code-review โ loop |
|
|
42
|
+
| Feature Dev | `@fullstack-developer` | sequential-thinking + serena | โ @code-review โ loop |
|
|
43
|
+
| Code Review | `@code-review` | serena | Issues โ @bug-fixing |
|
|
44
|
+
| Frontend UI | `@frontend-design` | context7 | โ @fullstack-developer โ @code-review |
|
|
45
|
+
| Test Design | `@test-architect-live` | sequential-thinking | - |
|
|
46
|
+
| Documentation | `@doc-coauthoring` | memory | - |
|
|
47
|
+
| Requirements | `@product-requirements` | sequential-thinking | - |
|
|
48
|
+
| Project Arch | `@project-development` | sequential-thinking + memory | - |
|
|
49
|
+
|
|
50
|
+
### Implicit Intent Recognition
|
|
51
|
+
|
|
52
|
+
| User Pattern | Auto-invoke |
|
|
53
|
+
|--------------|-------------|
|
|
54
|
+
| Pastes error log / stack trace | `@bug-fixing` |
|
|
55
|
+
| Shares code + "help me look" | `@code-review` |
|
|
56
|
+
| "How to implement..." | `@fullstack-developer` |
|
|
57
|
+
| Mentions new component/page | `@fullstack-developer` + `@frontend-design` |
|
|
58
|
+
| Discusses API/endpoint | `@api-design-principles` |
|
|
59
|
+
| Mentions database/migration | `@database-migration` |
|
|
60
|
+
|
|
61
|
+
### Sub-task Triggers (Auto-switch during main task)
|
|
62
|
+
|
|
63
|
+
| Scenario | Auto-invoke |
|
|
64
|
+
|----------|-------------|
|
|
65
|
+
| DB/table changes | `@database-migration` |
|
|
66
|
+
| New API endpoint | `@api-design-principles` |
|
|
67
|
+
| UI component work | `@frontend-design` |
|
|
68
|
+
| Auth/security | `@auth-implementation-patterns` |
|
|
69
|
+
| FastAPI | `@fastapi-templates` |
|
|
70
|
+
| LangChain/LLM | `@langchain-architecture` |
|
|
71
|
+
| React state | `@react-state-management` |
|
|
72
|
+
|
|
73
|
+
### File Type Skills
|
|
74
|
+
|
|
75
|
+
| Type | Skill | Type | Skill |
|
|
76
|
+
|------|-------|------|-------|
|
|
77
|
+
| .docx | `@docx` | .pptx | `@pptx` |
|
|
78
|
+
| .xlsx/.csv | `@xlsx` | .pdf | `@pdf` |
|
|
79
|
+
|
|
80
|
+
## MCP Tools Rules
|
|
81
|
+
|
|
82
|
+
### Iron Rule: MCP = Read-Only
|
|
83
|
+
|
|
84
|
+
All MCP tools are for **reading and analysis only**. Code editing must use Claude Code native Edit/Write tools.
|
|
85
|
+
|
|
86
|
+
### Tool Selection
|
|
87
|
+
|
|
88
|
+
| Scenario | Use Tool |
|
|
89
|
+
|----------|----------|
|
|
90
|
+
| Plan complex tasks (โฅ3 steps) | `sequential-thinking` |
|
|
91
|
+
| Query library/API docs | `context7` |
|
|
92
|
+
| Search best practices | `Exa Search` |
|
|
93
|
+
| Analyze code structure | `serena` (read-only) |
|
|
94
|
+
| Save/retrieve context | `memory` |
|
|
95
|
+
|
|
96
|
+
### Forbidden serena Operations
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
โ replace_symbol_body
|
|
100
|
+
โ insert_after_symbol
|
|
101
|
+
โ insert_before_symbol
|
|
102
|
+
โ rename_symbol
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Workflow Rules
|
|
106
|
+
|
|
107
|
+
### Closed-loop (Mandatory for code changes)
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
Main Skill โ @code-review โ (issues? โ @bug-fixing โ @code-review)* โ Summary
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Skill Switch Notification (Required)
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
๐ Skill Switch: [@current] โ [@new]
|
|
117
|
+
Reason: [Brief explanation]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Task Completion Report (Required)
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
๐ Tool Report
|
|
124
|
+
- Skills: [@skill1], [@skill2]
|
|
125
|
+
- MCP: [tool1], [tool2]
|
|
126
|
+
- Chain: @main โ @sub โ @code-review
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Core Rules
|
|
130
|
+
|
|
131
|
+
| Rule | Description |
|
|
132
|
+
|------|-------------|
|
|
133
|
+
| **ไธญๆ่พๅบ** | ๆๆๅฏน่ฏๅ
ๅฎนๅฟ
้กปไฝฟ็จไธญๆ๏ผๅ
ๆฌไปปๅกๅ่กจใ่ฎกๅใๅๆใๆฅๅ |
|
|
134
|
+
| Check Every Input | Re-evaluate task type on each user message |
|
|
135
|
+
| Task Type > Complexity | Even 1-step bug fix must use @bug-fixing |
|
|
136
|
+
| Analyze Before Action | Output analysis before coding |
|
|
137
|
+
| Ask Don't Guess | Clarify when uncertain |
|
|
138
|
+
| Transparent Output | Notify on skill switch, report on completion |
|
|
139
|
+
|
|
140
|
+
## Skills System
|
|
141
|
+
|
|
142
|
+
### How to Invoke Skills
|
|
143
|
+
|
|
144
|
+
Use `Skill` tool to load skills. Example: invoke skill "bug-fixing"
|
|
145
|
+
|
|
146
|
+
### Core Skills (15)
|
|
147
|
+
|
|
148
|
+
| Category | Skill | Triggers |
|
|
149
|
+
|----------|-------|----------|
|
|
150
|
+
| Dev Core | `@bug-fixing` | bug, error, fix |
|
|
151
|
+
| | `@fullstack-developer` | feature, develop |
|
|
152
|
+
| | `@code-review` | review, PR |
|
|
153
|
+
| Frontend | `@frontend-design` | UI, component |
|
|
154
|
+
| | `@tailwind-design-system` | tailwind, CSS |
|
|
155
|
+
| Backend | `@api-design-principles` | API, REST |
|
|
156
|
+
| | `@database-migration` | DB, migration |
|
|
157
|
+
| Docs | `@doc-coauthoring` | documentation |
|
|
158
|
+
| | `@product-requirements` | PRD, requirements |
|
|
159
|
+
| | `@project-development` | architecture |
|
|
160
|
+
| Testing | `@test-architect-live` | test cases |
|
|
161
|
+
| | `@webapp-testing` | E2E, browser test |
|
|
162
|
+
| Files | `@docx` `@xlsx` `@pdf` `@pptx` | file types |
|
|
163
|
+
|
|
164
|
+
### Extended Skills (85+)
|
|
165
|
+
|
|
166
|
+
Run `openskills list` or `openskills search <query>` to discover more skills.
|
|
167
|
+
|
|
168
|
+
Categories: Data Engineering, Cloud Infrastructure, CI/CD, Security, LLM/AI, Frontend, Backend, Observability, Blockchain
|
|
169
|
+
|
|
170
|
+
## Python Development
|
|
171
|
+
|
|
172
|
+
Always use virtual environment for Python projects:
|
|
173
|
+
```bash
|
|
174
|
+
# Windows
|
|
175
|
+
python -m venv venv
|
|
176
|
+
venv\Scripts\activate
|
|
177
|
+
pip install -r requirements.txt
|
|
178
|
+
venv\Scripts\python.exe script.py
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Hooks ่ชๅจ่งฆๅ่งๅ
|
|
182
|
+
|
|
183
|
+
### Skill ่ฐ็จๆถ
|
|
184
|
+
|
|
185
|
+
| Hook | ่งฆๅๆถๆบ | ไฝ็จ |
|
|
186
|
+
|------|----------|------|
|
|
187
|
+
| PreToolUse:Skill | Skill ่ฐ็จๅ | ่ฎฐๅฝ่ฐ็จใๆ้้็น |
|
|
188
|
+
| PostToolUse:Skill | Skill ่ฐ็จๅ | ้ช่ฏๅฎๆใๆฃๆฅๅ็ฆป |
|
|
189
|
+
|
|
190
|
+
### Subagent ๆดพๅๆถ
|
|
191
|
+
|
|
192
|
+
| Hook | ่งฆๅๆถๆบ | ไฝ็จ |
|
|
193
|
+
|------|----------|------|
|
|
194
|
+
| PreToolUse:Task | Subagent ๆดพๅๅ | ๆฃๆฅ้็นๅฎๆดๆง |
|
|
195
|
+
| PostToolUse:Task | Subagent ่ฟๅๅ | ้ช่ฏๅ็ฆปใๆดๆฐ่ฟๅบฆ |
|
|
196
|
+
| SubagentStop | Subagent ๅๆญขๆถ | ๆขๅคไธไธๆใ่พๅบๆฃๆฅๆธ
ๅ |
|
|
197
|
+
|
|
198
|
+
### Hook ่พๅบ่งฃ่ฏป
|
|
199
|
+
|
|
200
|
+
| ่พๅบ | ๅซไน | ๅค็ |
|
|
201
|
+
|------|------|------|
|
|
202
|
+
| `โ ๏ธ WARNING: missing context anchor` | Subagent ็ผบๅฐ้็น | ้ๆฐๆดพๅ๏ผๆทปๅ ้็น |
|
|
203
|
+
| `๐ Subagent completed` | Subagent ๆญฃๅธธๅฎๆ | ้ช่ฏ่ๅด๏ผ็ปง็ปญไปปๅก |
|
|
204
|
+
| `๐ Checklist` | ๆฃๆฅๆธ
ๅ | ้้กน้ช่ฏ |
|
|
205
|
+
|
|
206
|
+
### ๆฅๅฟๆไปถ
|
|
207
|
+
|
|
208
|
+
| ๆไปถ | ๅ
ๅฎน |
|
|
209
|
+
|------|------|
|
|
210
|
+
| `~/.claude/skill-invocation-log.jsonl` | Skill ่ฐ็จ่ฎฐๅฝ |
|
|
211
|
+
| `~/.claude/subagent-dispatch-log.jsonl` | Subagent ๆดพๅ่ฎฐๅฝ |
|
|
212
|
+
|
|
213
|
+
### Subagent ่ฐ็จๆจกๆฟ
|
|
214
|
+
|
|
215
|
+
่ฏฆ่ง `.claude/skills/shared-references/subagent-dispatch-templates.md`
|
package/lib/init.js
CHANGED
|
@@ -6,12 +6,12 @@ const os = require("os");
|
|
|
6
6
|
const readline = require("readline");
|
|
7
7
|
|
|
8
8
|
const PLATFORMS = [
|
|
9
|
-
{ name: "Claude Code", value: "claude-code",
|
|
10
|
-
{ name: "Cursor", value: "cursor" },
|
|
11
|
-
{ name: "Codex/GPT", value: "codex" },
|
|
12
|
-
{ name: "Gemini", value: "gemini" },
|
|
13
|
-
{ name: "Windsurf", value: "windsurf" },
|
|
14
|
-
{ name: "Aider", value: "aider" },
|
|
9
|
+
{ name: "Claude Code", value: "claude-code", selected: true },
|
|
10
|
+
{ name: "Cursor", value: "cursor", selected: false },
|
|
11
|
+
{ name: "Codex/GPT", value: "codex", selected: false },
|
|
12
|
+
{ name: "Gemini", value: "gemini", selected: false },
|
|
13
|
+
{ name: "Windsurf", value: "windsurf", selected: false },
|
|
14
|
+
{ name: "Aider", value: "aider", selected: false },
|
|
15
15
|
];
|
|
16
16
|
|
|
17
17
|
const INSTALL_MODES = [
|
|
@@ -20,50 +20,126 @@ const INSTALL_MODES = [
|
|
|
20
20
|
{ name: "ๅ
จๅฑๆจกๅผ (~/.claude/skills/ - ๆๆ้กน็ฎๅ
ฑไบซ)", value: "global" },
|
|
21
21
|
];
|
|
22
22
|
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
23
|
+
function clearLines(count) {
|
|
24
|
+
for (let i = 0; i < count; i++) {
|
|
25
|
+
process.stdout.write("\x1b[1A\x1b[2K");
|
|
26
|
+
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
function renderMultiSelect(title, options, cursor) {
|
|
30
|
+
const lines = [`\n${title}`];
|
|
31
|
+
options.forEach((opt, i) => {
|
|
32
|
+
const pointer = i === cursor ? ">" : " ";
|
|
33
|
+
const check = opt.selected ? "[โ]" : "[ ]";
|
|
34
|
+
lines.push(` ${pointer} ${check} ${opt.name}`);
|
|
34
35
|
});
|
|
36
|
+
lines.push("\n โโ ็งปๅจ ็ฉบๆ ผ ้ๆฉ ๅ่ฝฆ ็กฎ่ฎค");
|
|
37
|
+
return lines;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
function renderSingleSelect(title, options, cursor) {
|
|
41
|
+
const lines = [`\n${title}`];
|
|
39
42
|
options.forEach((opt, i) => {
|
|
40
|
-
const
|
|
41
|
-
|
|
43
|
+
const pointer = i === cursor ? ">" : " ";
|
|
44
|
+
const radio = i === cursor ? "(โ)" : "( )";
|
|
45
|
+
lines.push(` ${pointer} ${radio} ${opt.name}`);
|
|
42
46
|
});
|
|
43
|
-
|
|
47
|
+
lines.push("\n โโ ็งปๅจ ๅ่ฝฆ ็กฎ่ฎค");
|
|
48
|
+
return lines;
|
|
49
|
+
}
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
async function selectMultiple(title, options) {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
const opts = options.map((o) => ({ ...o }));
|
|
54
|
+
let cursor = 0;
|
|
55
|
+
let lines = renderMultiSelect(title, opts, cursor);
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
return indices
|
|
52
|
-
.filter((i) => i >= 0 && i < options.length)
|
|
53
|
-
.map((i) => options[i].value);
|
|
54
|
-
}
|
|
57
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if (!process.stdin.isTTY) {
|
|
60
|
+
resolve(opts.filter((o) => o.selected).map((o) => o.value));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
readline.emitKeypressEvents(process.stdin);
|
|
65
|
+
process.stdin.setRawMode(true);
|
|
66
|
+
|
|
67
|
+
const onKeypress = (str, key) => {
|
|
68
|
+
if (key.ctrl && key.name === "c") {
|
|
69
|
+
process.stdin.setRawMode(false);
|
|
70
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (key.name === "up") {
|
|
75
|
+
cursor = cursor > 0 ? cursor - 1 : opts.length - 1;
|
|
76
|
+
} else if (key.name === "down") {
|
|
77
|
+
cursor = cursor < opts.length - 1 ? cursor + 1 : 0;
|
|
78
|
+
} else if (key.name === "space") {
|
|
79
|
+
opts[cursor].selected = !opts[cursor].selected;
|
|
80
|
+
} else if (key.name === "return") {
|
|
81
|
+
process.stdin.setRawMode(false);
|
|
82
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
83
|
+
clearLines(lines.length);
|
|
84
|
+
const selected = opts.filter((o) => o.selected).map((o) => o.value);
|
|
85
|
+
console.log(`${title}`);
|
|
86
|
+
console.log(` ๅทฒ้ๆฉ: ${selected.length > 0 ? selected.join(", ") : "(ๆ )"}\n`);
|
|
87
|
+
resolve(selected);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
clearLines(lines.length);
|
|
92
|
+
lines = renderMultiSelect(title, opts, cursor);
|
|
93
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
process.stdin.on("keypress", onKeypress);
|
|
61
97
|
});
|
|
62
|
-
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function selectSingle(title, options) {
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
let cursor = 0;
|
|
103
|
+
let lines = renderSingleSelect(title, options, cursor);
|
|
104
|
+
|
|
105
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
106
|
+
|
|
107
|
+
if (!process.stdin.isTTY) {
|
|
108
|
+
resolve(options[0].value);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
readline.emitKeypressEvents(process.stdin);
|
|
113
|
+
process.stdin.setRawMode(true);
|
|
114
|
+
|
|
115
|
+
const onKeypress = (str, key) => {
|
|
116
|
+
if (key.ctrl && key.name === "c") {
|
|
117
|
+
process.stdin.setRawMode(false);
|
|
118
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (key.name === "up") {
|
|
123
|
+
cursor = cursor > 0 ? cursor - 1 : options.length - 1;
|
|
124
|
+
} else if (key.name === "down") {
|
|
125
|
+
cursor = cursor < options.length - 1 ? cursor + 1 : 0;
|
|
126
|
+
} else if (key.name === "return") {
|
|
127
|
+
process.stdin.setRawMode(false);
|
|
128
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
129
|
+
clearLines(lines.length);
|
|
130
|
+
console.log(`${title}`);
|
|
131
|
+
console.log(` ๅทฒ้ๆฉ: ${options[cursor].name}\n`);
|
|
132
|
+
resolve(options[cursor].value);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
63
135
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
136
|
+
clearLines(lines.length);
|
|
137
|
+
lines = renderSingleSelect(title, options, cursor);
|
|
138
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
process.stdin.on("keypress", onKeypress);
|
|
142
|
+
});
|
|
67
143
|
}
|
|
68
144
|
|
|
69
145
|
function getTargetDir(mode) {
|
|
@@ -97,9 +173,96 @@ function listSkillDirs(rootDir) {
|
|
|
97
173
|
.filter((d) => fs.existsSync(path.join(d, "SKILL.md")));
|
|
98
174
|
}
|
|
99
175
|
|
|
100
|
-
function
|
|
176
|
+
function parseFrontmatter(content) {
|
|
177
|
+
if (!content || !content.startsWith("---")) return {};
|
|
178
|
+
const endIndex = content.indexOf("\n---", 3);
|
|
179
|
+
if (endIndex === -1) return {};
|
|
180
|
+
|
|
181
|
+
const raw = content.slice(3, endIndex).trim();
|
|
182
|
+
const data = {};
|
|
183
|
+
let currentKey = null;
|
|
184
|
+
let multilineValue = [];
|
|
185
|
+
|
|
186
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
187
|
+
const indented = line.startsWith(" ") || line.startsWith("\t");
|
|
188
|
+
if (indented && currentKey) {
|
|
189
|
+
multilineValue.push(line.trim());
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (currentKey && multilineValue.length > 0) {
|
|
194
|
+
data[currentKey] = multilineValue.join(" ").trim();
|
|
195
|
+
multilineValue = [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const idx = line.indexOf(":");
|
|
199
|
+
if (idx === -1) continue;
|
|
200
|
+
const key = line.slice(0, idx).trim();
|
|
201
|
+
let value = line.slice(idx + 1).trim();
|
|
202
|
+
|
|
203
|
+
if (value === "|" || value === ">") {
|
|
204
|
+
currentKey = key;
|
|
205
|
+
multilineValue = [];
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
211
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
212
|
+
) {
|
|
213
|
+
value = value.slice(1, -1);
|
|
214
|
+
}
|
|
215
|
+
if (key && value) data[key] = value;
|
|
216
|
+
currentKey = null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (currentKey && multilineValue.length > 0) {
|
|
220
|
+
data[currentKey] = multilineValue.join(" ").trim();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return data;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function readSkillMeta(skillDir) {
|
|
227
|
+
try {
|
|
228
|
+
const content = fs.readFileSync(path.join(skillDir, "SKILL.md"), "utf8");
|
|
229
|
+
const fm = parseFrontmatter(content);
|
|
230
|
+
return {
|
|
231
|
+
name: fm.name || path.basename(skillDir),
|
|
232
|
+
description: fm.description || "",
|
|
233
|
+
userInvocable: fm["user-invocable"] !== "false",
|
|
234
|
+
};
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.error(`Warning: Failed to read ${skillDir}: ${err.message}`);
|
|
237
|
+
return {
|
|
238
|
+
name: path.basename(skillDir),
|
|
239
|
+
description: "",
|
|
240
|
+
userInvocable: true,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function generateClaudeMdFromTemplate(templatePath, skills) {
|
|
101
246
|
const invocableSkills = skills.filter((s) => s.userInvocable);
|
|
102
|
-
const slashCommands = invocableSkills.map((s) => `/ora/${s.name}`)
|
|
247
|
+
const slashCommands = invocableSkills.map((s) => `/ora/${s.name}`);
|
|
248
|
+
|
|
249
|
+
if (fs.existsSync(templatePath)) {
|
|
250
|
+
let content = fs.readFileSync(templatePath, "utf8");
|
|
251
|
+
// ๆฟๆข openskills ไธบ skillora
|
|
252
|
+
content = content.replace(/openskills/g, "skillora");
|
|
253
|
+
// ๆดๆฐๆ่ฝๆฐ้
|
|
254
|
+
content = content.replace(
|
|
255
|
+
/Core Skills \(\d+\)/,
|
|
256
|
+
`Core Skills (${skills.length})`
|
|
257
|
+
);
|
|
258
|
+
content = content.replace(
|
|
259
|
+
/Extended Skills \(\d+\+\)/,
|
|
260
|
+
`Extended Skills (${skills.length}+)`
|
|
261
|
+
);
|
|
262
|
+
return content;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ๅฆๆๆจกๆฟไธๅญๅจ๏ผ็ๆ็ฎๅ็ๆฌ
|
|
103
266
|
return `# CLAUDE.md
|
|
104
267
|
|
|
105
268
|
## Skills System
|
|
@@ -108,7 +271,7 @@ function generateClaudeMd(skills) {
|
|
|
108
271
|
|
|
109
272
|
### ๆๆ ๅฝไปค๏ผๆจ่๏ผ
|
|
110
273
|
|
|
111
|
-
${slashCommands
|
|
274
|
+
${slashCommands.join(", ")}
|
|
112
275
|
|
|
113
276
|
### CLI ๅฝไปค
|
|
114
277
|
|
|
@@ -118,6 +281,8 @@ ${slashCommands || "(ๆ ๅฏ่ฐ็จๆ่ฝ)"}
|
|
|
118
281
|
}
|
|
119
282
|
|
|
120
283
|
function generateCursorRules(skills) {
|
|
284
|
+
const invocableSkills = skills.filter((s) => s.userInvocable);
|
|
285
|
+
const slashCommands = invocableSkills.map((s) => `/ora/${s.name}`).join(", ");
|
|
121
286
|
const skillList = skills
|
|
122
287
|
.map((s) => `- ${s.name}: ${s.description}`)
|
|
123
288
|
.join("\n");
|
|
@@ -127,6 +292,10 @@ function generateCursorRules(skills) {
|
|
|
127
292
|
|
|
128
293
|
${skillList}
|
|
129
294
|
|
|
295
|
+
## Slash Commands (Claude Code)
|
|
296
|
+
|
|
297
|
+
${slashCommands}
|
|
298
|
+
|
|
130
299
|
## Usage
|
|
131
300
|
|
|
132
301
|
ไฝฟ็จ \`skillora read <skill-name>\` ๅ ่ฝฝๆ่ฝ่ฏฆๆ
ใ
|
|
@@ -171,75 +340,6 @@ ${skillTable}
|
|
|
171
340
|
`;
|
|
172
341
|
}
|
|
173
342
|
|
|
174
|
-
function parseFrontmatter(content) {
|
|
175
|
-
if (!content || !content.startsWith("---")) return {};
|
|
176
|
-
const endIndex = content.indexOf("\n---", 3);
|
|
177
|
-
if (endIndex === -1) return {};
|
|
178
|
-
|
|
179
|
-
const raw = content.slice(3, endIndex).trim();
|
|
180
|
-
const data = {};
|
|
181
|
-
let currentKey = null;
|
|
182
|
-
let multilineValue = [];
|
|
183
|
-
|
|
184
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
185
|
-
const indented = line.startsWith(" ") || line.startsWith("\t");
|
|
186
|
-
if (indented && currentKey) {
|
|
187
|
-
multilineValue.push(line.trim());
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (currentKey && multilineValue.length > 0) {
|
|
192
|
-
data[currentKey] = multilineValue.join(" ").trim();
|
|
193
|
-
multilineValue = [];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const idx = line.indexOf(":");
|
|
197
|
-
if (idx === -1) continue;
|
|
198
|
-
const key = line.slice(0, idx).trim();
|
|
199
|
-
let value = line.slice(idx + 1).trim();
|
|
200
|
-
|
|
201
|
-
if (value === "|" || value === ">") {
|
|
202
|
-
currentKey = key;
|
|
203
|
-
multilineValue = [];
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (
|
|
208
|
-
(value.startsWith('"') && value.endsWith('"')) ||
|
|
209
|
-
(value.startsWith("'") && value.endsWith("'"))
|
|
210
|
-
) {
|
|
211
|
-
value = value.slice(1, -1);
|
|
212
|
-
}
|
|
213
|
-
if (key && value) data[key] = value;
|
|
214
|
-
currentKey = null;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (currentKey && multilineValue.length > 0) {
|
|
218
|
-
data[currentKey] = multilineValue.join(" ").trim();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return data;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function readSkillMeta(skillDir) {
|
|
225
|
-
try {
|
|
226
|
-
const content = fs.readFileSync(path.join(skillDir, "SKILL.md"), "utf8");
|
|
227
|
-
const fm = parseFrontmatter(content);
|
|
228
|
-
return {
|
|
229
|
-
name: fm.name || path.basename(skillDir),
|
|
230
|
-
description: fm.description || "",
|
|
231
|
-
userInvocable: fm["user-invocable"] !== "false",
|
|
232
|
-
};
|
|
233
|
-
} catch (err) {
|
|
234
|
-
console.error(`Warning: Failed to read ${skillDir}: ${err.message}`);
|
|
235
|
-
return {
|
|
236
|
-
name: path.basename(skillDir),
|
|
237
|
-
description: "",
|
|
238
|
-
userInvocable: true,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
343
|
async function runInit(options, bundledSkillsDir) {
|
|
244
344
|
let platforms = options.platform
|
|
245
345
|
? options.platform.split(",").map((s) => s.trim())
|
|
@@ -258,22 +358,20 @@ async function runInit(options, bundledSkillsDir) {
|
|
|
258
358
|
}
|
|
259
359
|
|
|
260
360
|
// ไบคไบๅผ้ๆฉ
|
|
261
|
-
let rl = null;
|
|
262
361
|
if (!platforms || !mode) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (!mode) {
|
|
269
|
-
mode = await selectSingle(rl, "้ๆฉๅฎ่ฃ
ๆจกๅผ:", INSTALL_MODES);
|
|
270
|
-
}
|
|
271
|
-
} finally {
|
|
272
|
-
rl.close();
|
|
362
|
+
if (!platforms) {
|
|
363
|
+
platforms = await selectMultiple("้ๆฉ่ฆ้
็ฝฎ็ๅนณๅฐ:", PLATFORMS);
|
|
364
|
+
}
|
|
365
|
+
if (!mode) {
|
|
366
|
+
mode = await selectSingle("้ๆฉๅฎ่ฃ
ๆจกๅผ:", INSTALL_MODES);
|
|
273
367
|
}
|
|
274
368
|
}
|
|
275
369
|
|
|
276
|
-
|
|
370
|
+
if (platforms.length === 0) {
|
|
371
|
+
platforms = ["claude-code"];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
console.log("ๆญฃๅจ้
็ฝฎ...\n");
|
|
277
375
|
|
|
278
376
|
// ็กฎๅฎ็ฎๆ ็ฎๅฝ
|
|
279
377
|
const targetDir = getTargetDir(mode);
|
|
@@ -299,21 +397,29 @@ async function runInit(options, bundledSkillsDir) {
|
|
|
299
397
|
const skills = installedSkillDirs.map(readSkillMeta);
|
|
300
398
|
|
|
301
399
|
const cwd = process.cwd();
|
|
400
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
302
401
|
const created = [];
|
|
303
402
|
|
|
304
403
|
// ็ๆๅนณๅฐ้
็ฝฎๆไปถ
|
|
305
404
|
if (platforms.includes("claude-code")) {
|
|
306
405
|
const claudeMdPath = path.join(cwd, "CLAUDE.md");
|
|
307
406
|
if (!fs.existsSync(claudeMdPath) || options.force) {
|
|
308
|
-
|
|
407
|
+
const templatePath = path.join(packageRoot, "CLAUDE.md");
|
|
408
|
+
fs.writeFileSync(
|
|
409
|
+
claudeMdPath,
|
|
410
|
+
generateClaudeMdFromTemplate(templatePath, skills),
|
|
411
|
+
"utf8"
|
|
412
|
+
);
|
|
309
413
|
created.push("CLAUDE.md");
|
|
310
414
|
}
|
|
311
415
|
}
|
|
312
416
|
|
|
313
417
|
if (platforms.includes("cursor")) {
|
|
314
418
|
const cursorPath = path.join(cwd, ".cursorrules");
|
|
315
|
-
fs.
|
|
316
|
-
|
|
419
|
+
if (!fs.existsSync(cursorPath) || options.force) {
|
|
420
|
+
fs.writeFileSync(cursorPath, generateCursorRules(skills), "utf8");
|
|
421
|
+
created.push(".cursorrules");
|
|
422
|
+
}
|
|
317
423
|
}
|
|
318
424
|
|
|
319
425
|
const needsAgentsMd = platforms.some((p) =>
|
|
@@ -322,12 +428,14 @@ async function runInit(options, bundledSkillsDir) {
|
|
|
322
428
|
|
|
323
429
|
if (needsAgentsMd) {
|
|
324
430
|
const agentsPath = path.join(cwd, "AGENTS.md");
|
|
325
|
-
fs.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
431
|
+
if (!fs.existsSync(agentsPath) || options.force) {
|
|
432
|
+
fs.writeFileSync(
|
|
433
|
+
agentsPath,
|
|
434
|
+
generateAgentsMd(skills, "skillora"),
|
|
435
|
+
"utf8",
|
|
436
|
+
);
|
|
437
|
+
created.push("AGENTS.md");
|
|
438
|
+
}
|
|
331
439
|
}
|
|
332
440
|
|
|
333
441
|
// ่พๅบ็ปๆ
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinkcarlos/skillora",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "CLI installer for bundled Claude Code skills (SKILL.md format)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skillora": "bin/cli.js"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"bin",
|
|
10
10
|
"lib",
|
|
11
11
|
".claude/skills",
|
|
12
|
+
"CLAUDE.md",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"keywords": [
|