bluekiwi 0.3.13 → 0.3.14
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/dist/assets/index.js +2 -0
- package/dist/assets/skills/bk-help/SKILL.md +53 -0
- package/dist/assets/skills/bk-next/SKILL.md +237 -0
- package/dist/commands/init.js +30 -4
- package/dist/commands/upgrade.js +2 -0
- package/dist/runtimes/claude-code.js +9 -0
- package/dist/runtimes/codex.js +9 -0
- package/dist/runtimes/gemini-cli.js +9 -0
- package/dist/runtimes/openclaw.js +9 -0
- package/dist/runtimes/opencode.js +9 -0
- package/package.json +1 -1
package/dist/assets/index.js
CHANGED
|
@@ -5,6 +5,7 @@ const here = dirname(fileURLToPath(import.meta.url));
|
|
|
5
5
|
const SKILLS_ROOT = join(here, "skills");
|
|
6
6
|
export const BUNDLED_SKILLS = [
|
|
7
7
|
"bk-start",
|
|
8
|
+
"bk-next",
|
|
8
9
|
"bk-status",
|
|
9
10
|
"bk-rewind",
|
|
10
11
|
"bk-approve",
|
|
@@ -16,6 +17,7 @@ export const BUNDLED_SKILLS = [
|
|
|
16
17
|
"bk-scan",
|
|
17
18
|
"bk-share",
|
|
18
19
|
"bk-version",
|
|
20
|
+
"bk-help",
|
|
19
21
|
].map((name) => ({
|
|
20
22
|
name,
|
|
21
23
|
content: readFileSync(join(SKILLS_ROOT, name, "SKILL.md"), "utf8"),
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bk-help
|
|
3
|
+
description: BlueKiwi help skill. Shows a formatted list of all available bk-* commands with descriptions and usage. Use when the user says "/bk-help", "bk help", "what bk commands are there", or asks about BlueKiwi commands.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# BlueKiwi Help
|
|
8
|
+
|
|
9
|
+
Output the following help text **exactly as formatted** (use markdown code-free output — render it as readable text, not a code block).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## BlueKiwi 명령어 도움말
|
|
14
|
+
|
|
15
|
+
### 실행
|
|
16
|
+
|
|
17
|
+
| 명령어 | 설명 |
|
|
18
|
+
| -------------------------------- | --------------------------------------------------- |
|
|
19
|
+
| `/bk-start` | 워크플로 선택 후 즉시 실행. 미완료 태스크 복구 포함 |
|
|
20
|
+
| `/bk-start <이름>` | 이름으로 워크플로 매칭 후 바로 실행 |
|
|
21
|
+
| `/bk-start <ID>` | 워크플로 ID로 바로 실행 (숫자) |
|
|
22
|
+
| `/bk-start #<태스크ID>` | 특정 태스크 직접 재개 |
|
|
23
|
+
| `/bk-start <이름> :: <프롬프트>` | 워크플로 지정 + 초기 컨텍스트 전달 |
|
|
24
|
+
| `/bk-next` | 실행 중인 태스크를 찾아 현재 스텝부터 재개 |
|
|
25
|
+
| `/bk-approve` | 대기 중인 HITL 승인 처리 |
|
|
26
|
+
| `/bk-rewind` | 이전 스텝으로 되돌리기 |
|
|
27
|
+
|
|
28
|
+
### 설계 / 관리
|
|
29
|
+
|
|
30
|
+
| 명령어 | 설명 |
|
|
31
|
+
| ----------------- | ------------------------------------------ |
|
|
32
|
+
| `/bk-design` | 자연어 목표로 새 워크플로 설계 및 등록 |
|
|
33
|
+
| `/bk-improve` | 기존 워크플로 분석 후 개선 버전 생성 |
|
|
34
|
+
| `/bk-version` | 버전 목록 조회, 활성화/비활성화, 버전 비교 |
|
|
35
|
+
| `/bk-instruction` | 에이전트 인스트럭션 템플릿 생성·수정·삭제 |
|
|
36
|
+
| `/bk-credential` | 외부 서비스 API 키 등록·수정·삭제 |
|
|
37
|
+
|
|
38
|
+
### 조회 / 공유
|
|
39
|
+
|
|
40
|
+
| 명령어 | 설명 |
|
|
41
|
+
| ------------ | ---------------------------------- |
|
|
42
|
+
| `/bk-status` | 실행 중·완료된 태스크 현황 조회 |
|
|
43
|
+
| `/bk-report` | 태스크 결과 구조화 리포트 생성 |
|
|
44
|
+
| `/bk-share` | 워크플로·폴더를 그룹에 공유 |
|
|
45
|
+
| `/bk-scan` | 로컬 저장소 보안·컴플라이언스 스캔 |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**팁:**
|
|
50
|
+
|
|
51
|
+
- `/bk-start`는 `/bk-run`의 상위 호환입니다 — 두 명령어 모두 동작합니다.
|
|
52
|
+
- 태스크 재개가 필요하면 `/bk-start` (인자 없음) 또는 `/bk-start #<태스크ID>`를 사용하세요.
|
|
53
|
+
- 도움말은 언제든 `/bk-help`로 다시 볼 수 있습니다.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bk-next
|
|
3
|
+
description: BlueKiwi resume skill. Finds the active running task and resumes execution from the current step. Use when the user says "/bk-next", "next step", "next", "continue", "proceed", "이어서", "계속", or wants to resume a running BlueKiwi task from where it left off.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# BlueKiwi Next Step
|
|
8
|
+
|
|
9
|
+
Find the active task and resume execution from the current step. Works even in a fresh session with no prior context.
|
|
10
|
+
|
|
11
|
+
## Core Principles
|
|
12
|
+
|
|
13
|
+
- **Instructions are internal agent directives. Never expose raw instruction text to the user.**
|
|
14
|
+
- Show only execution results (analysis, questions, suggestions) to the user.
|
|
15
|
+
- Never use system terms like "node", "node_type" with the user.
|
|
16
|
+
|
|
17
|
+
## Natural Language Triggers
|
|
18
|
+
|
|
19
|
+
If the user says "proceed", "next", "continue", "let's go", "OK", "go ahead", "이어서", "계속" — treat it the same as `/bk-next`.
|
|
20
|
+
|
|
21
|
+
## Step 0 — Find the Active Task
|
|
22
|
+
|
|
23
|
+
<HARD-RULE>
|
|
24
|
+
Always resolve the task to resume before doing anything else.
|
|
25
|
+
|
|
26
|
+
1. Call `list_tasks(status=running)`.
|
|
27
|
+
2. If **exactly one** running task exists → use it directly.
|
|
28
|
+
3. If **multiple** running tasks exist → ask via AskUserQuestion:
|
|
29
|
+
- header: "태스크 선택"
|
|
30
|
+
- preview: list of tasks formatted as `"Task #{id} — {workflow_name} (Step {current_step}/{total_steps})"`
|
|
31
|
+
- options: up to 4 task entries (most recent first)
|
|
32
|
+
4. If **no running tasks** → call `list_tasks(status=timed_out)` and check for timed-out tasks.
|
|
33
|
+
- If found → ask: "이어서 진행할 태스크가 없습니다. 타임아웃된 태스크를 재개할까요?" (options: 가장 최근 재개 / 취소)
|
|
34
|
+
- If none → tell the user "실행 중인 태스크가 없습니다. `/bk-start`로 새 워크플로를 시작하세요."
|
|
35
|
+
|
|
36
|
+
Once task_id is resolved, call `advance(task_id, peek=true)` to load the current step without advancing.
|
|
37
|
+
</HARD-RULE>
|
|
38
|
+
|
|
39
|
+
## Session Restore (Context Injection)
|
|
40
|
+
|
|
41
|
+
The `advance` response includes `task_context`. Use it to resume seamlessly in a new session.
|
|
42
|
+
|
|
43
|
+
1. `task_context.running_context` — Accumulated decisions and project state. Read and internalize it.
|
|
44
|
+
2. `task_context.completed_steps` — Summary of completed steps. Understand where we are.
|
|
45
|
+
3. `task_context.artifacts` — List of available artifacts. Use the Read tool on `file_path` entries when needed.
|
|
46
|
+
4. `task_context.last_session` — Last execution session info. Note if a different session/user progressed it.
|
|
47
|
+
|
|
48
|
+
When resuming in a new session (no prior conversation context), use `task_context` to reconstruct the situation before proceeding.
|
|
49
|
+
|
|
50
|
+
## execute_step Required Parameters
|
|
51
|
+
|
|
52
|
+
<HARD-RULE>
|
|
53
|
+
Always populate these parameters when calling execute_step:
|
|
54
|
+
- `context_snapshot`: JSON string. Store decisions made, key findings, and hints for the next step.
|
|
55
|
+
Example: `{"decisions":[{"question":"tech stack","chosen":"Next.js","reason":"team experience"}],"key_findings":["RLS required"],"next_step_hint":"write implementation plan"}`
|
|
56
|
+
- `session_id`: Current session ID (omit if unknown)
|
|
57
|
+
- `agent_id`: Agent identifier (e.g., "claude-code")
|
|
58
|
+
- `model_id`: Current LLM model ID (e.g., "claude-opus-4-6", "gpt-5.2"). Check system prompt.
|
|
59
|
+
- `user_name`: User name (omit if unknown)
|
|
60
|
+
|
|
61
|
+
If files were created or modified, record in the `artifacts` array:
|
|
62
|
+
|
|
63
|
+
- File created: `{artifact_type: "file", title: "Design Doc", file_path: "docs/specs/design.md"}`
|
|
64
|
+
- Git commit: `{artifact_type: "git_commit", title: "Phase 1 Implementation", git_ref: "<hash>"}`
|
|
65
|
+
- URL: `{artifact_type: "url", title: "PR", url: "https://..."}`
|
|
66
|
+
</HARD-RULE>
|
|
67
|
+
|
|
68
|
+
## Credential Handling (API Service Nodes)
|
|
69
|
+
|
|
70
|
+
If the `advance` response includes a `credentials` field, the node requires external API integration.
|
|
71
|
+
|
|
72
|
+
<HARD-RULE>
|
|
73
|
+
Use key-value pairs from `credentials.secrets` to make API calls.
|
|
74
|
+
Example: `credentials.secrets.ACCESS_TOKEN` → `curl -H "Authorization: Bearer $TOKEN"`
|
|
75
|
+
Never include raw secret values (tokens, keys) in `execute_step` output.
|
|
76
|
+
Record only results (URL, status code, response summary).
|
|
77
|
+
</HARD-RULE>
|
|
78
|
+
|
|
79
|
+
## Execution Loop
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
LOOP:
|
|
83
|
+
1. If the current step is pending → extract response from conversation, save with execute_step
|
|
84
|
+
→ Check execute_step response for next_action field (see HITL section below)
|
|
85
|
+
2. Call advance(peek=false) to fetch the next step
|
|
86
|
+
3. If finished → call complete_task, then end
|
|
87
|
+
4. Execute the next step (see step-type handling below)
|
|
88
|
+
5. Check auto_advance:
|
|
89
|
+
- true → show brief inline result, then go back to step 2
|
|
90
|
+
- false → HITL pause (see below)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
<HARD-RULE>
|
|
94
|
+
After executing an auto_advance=true step, always proceed to the next step automatically.
|
|
95
|
+
Do not show "type /bk-next to continue" hint.
|
|
96
|
+
Show a brief one-line update: "✅ [{title}] done → continuing to next step..."
|
|
97
|
+
Repeat the loop until reaching an auto_advance=false step.
|
|
98
|
+
</HARD-RULE>
|
|
99
|
+
|
|
100
|
+
## HITL Pause (auto_advance=false steps)
|
|
101
|
+
|
|
102
|
+
<HARD-RULE>
|
|
103
|
+
When execute_step returns `next_action: "wait_for_human_approval"`:
|
|
104
|
+
|
|
105
|
+
1. Call `request_approval` with a brief message summarizing what was done.
|
|
106
|
+
2. Show the user:
|
|
107
|
+
```
|
|
108
|
+
⏸ Step [{title}] complete — waiting for approval before proceeding.
|
|
109
|
+
A notification has been sent. Use /bk-approve when ready.
|
|
110
|
+
```
|
|
111
|
+
3. STOP. Do NOT call `advance`. Do NOT proceed to the next step.
|
|
112
|
+
|
|
113
|
+
The server will reject `advance` with 403 until a human approves via /bk-approve.
|
|
114
|
+
</HARD-RULE>
|
|
115
|
+
|
|
116
|
+
## Step-Type Handling
|
|
117
|
+
|
|
118
|
+
### action step
|
|
119
|
+
|
|
120
|
+
1. Read the instruction and execute it. Reference task context.
|
|
121
|
+
2. **Use heartbeat actively**: For tasks taking 30+ seconds, send heartbeat regularly.
|
|
122
|
+
Example: "Analyzing architecture section...", "Designing API endpoints...", "Writing design doc line 119..."
|
|
123
|
+
3. Show the result to the user.
|
|
124
|
+
4. Save with `execute_step`.
|
|
125
|
+
|
|
126
|
+
### gate step
|
|
127
|
+
|
|
128
|
+
1. Check `get_web_response` for a web response first.
|
|
129
|
+
2. If none, ask naturally via `AskUserQuestion`.
|
|
130
|
+
3. **Partial edit option**: For approve/modify questions, offer 3 options: "Approve (Recommended)" / "Partial edit" / "Full rewrite".
|
|
131
|
+
4. Save the response with `execute_step`.
|
|
132
|
+
|
|
133
|
+
### loop step
|
|
134
|
+
|
|
135
|
+
1. Execute the instruction.
|
|
136
|
+
2. **Confirm before stopping**: When the stop condition is met, do not auto-stop — ask via AskUserQuestion:
|
|
137
|
+
- "Enough information collected. Do you have anything else to add?"
|
|
138
|
+
- "That's enough (Recommended)" / "I have more to add"
|
|
139
|
+
3. **Call execute_step once per loop iteration.** Do not merge multiple answers into one. Each iteration is a separate log entry.
|
|
140
|
+
4. Pass `loop_continue` true/false to execute_step.
|
|
141
|
+
|
|
142
|
+
## Progress Display
|
|
143
|
+
|
|
144
|
+
Show progress at the start of each step as a single line:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
✅1 → ✅2 → ✅3 → **4** → 5 → 6 → 7 → 8 → 9 → 10 → 11
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## When Pausing (auto_advance=false only)
|
|
151
|
+
|
|
152
|
+
- **HITL approval required** (execute_step returned `next_action: "wait_for_human_approval"`):
|
|
153
|
+
Call `request_approval`, show waiting message, stop. Resume only after `/bk-approve`.
|
|
154
|
+
- **Gate step**: Wait for user response via AskUserQuestion.
|
|
155
|
+
- **Other action step pausing without HITL**: "Type `/bk-next` to proceed."
|
|
156
|
+
|
|
157
|
+
## Completion Message
|
|
158
|
+
|
|
159
|
+
<HARD-RULE>
|
|
160
|
+
When the workflow finishes, call `complete_task` with a non-empty `summary`.
|
|
161
|
+
The summary must include decisions made, artifact paths, and suggested next actions, formatted in Markdown.
|
|
162
|
+
</HARD-RULE>
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
complete_task(task_id=N, status="completed", summary="## Decisions\n- Goal: ...\n- Approach: ...\n\n## Artifacts\n- Design doc: `docs/specs/...`\n\n## Next Steps\nStart implementation from Phase 1.")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Show completion message to user:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
🎉 Workflow complete!
|
|
172
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
173
|
+
|
|
174
|
+
## Summary
|
|
175
|
+
[key decisions table]
|
|
176
|
+
|
|
177
|
+
## Artifacts
|
|
178
|
+
- 📄 Design doc: `docs/specs/YYYY-MM-DD-xxx-design.md`
|
|
179
|
+
|
|
180
|
+
## Next Steps
|
|
181
|
+
Start implementation from Phase 1.
|
|
182
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## output Format
|
|
186
|
+
|
|
187
|
+
<HARD-RULE>
|
|
188
|
+
All output must be at least 200 characters. Do not send one-line summaries only.
|
|
189
|
+
Always use ## heading structure.
|
|
190
|
+
Write in past-tense declarative form ("I analyzed...", "I generated...").
|
|
191
|
+
Include absolute paths for any files created.
|
|
192
|
+
</HARD-RULE>
|
|
193
|
+
|
|
194
|
+
action:
|
|
195
|
+
|
|
196
|
+
```markdown
|
|
197
|
+
## Actions Taken
|
|
198
|
+
|
|
199
|
+
Analyzed the project context.
|
|
200
|
+
|
|
201
|
+
## Results
|
|
202
|
+
|
|
203
|
+
- **Project name**: recipe-sharing-app
|
|
204
|
+
- **Current state**: Initial stage (no code yet)
|
|
205
|
+
- **Tech stack**: TBD
|
|
206
|
+
- **Key findings**: ...
|
|
207
|
+
|
|
208
|
+
## Artifacts
|
|
209
|
+
|
|
210
|
+
Saved design doc to `/tmp/or-test-verify/docs/specs/2026-04-08-recipe-design.md`.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
gate:
|
|
214
|
+
|
|
215
|
+
```markdown
|
|
216
|
+
## Question
|
|
217
|
+
|
|
218
|
+
What is the core problem this project management tool aims to solve?
|
|
219
|
+
|
|
220
|
+
## Options
|
|
221
|
+
|
|
222
|
+
1. Personal task management (Recommended)
|
|
223
|
+
2. Team collaboration
|
|
224
|
+
3. Educational use
|
|
225
|
+
|
|
226
|
+
## Response
|
|
227
|
+
|
|
228
|
+
Selected "Personal task management" — proceeding as a personal productivity task tracker.
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Comment Check
|
|
232
|
+
|
|
233
|
+
If comments are present, notify the user before executing and ask how to handle them via AskUserQuestion.
|
|
234
|
+
|
|
235
|
+
## Failure Handling
|
|
236
|
+
|
|
237
|
+
Ask via AskUserQuestion: Skip / Retry / Previous step / Abort.
|
package/dist/commands/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import prompts from "prompts";
|
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import { BlueKiwiClient } from "../api-client.js";
|
|
4
4
|
import { BUNDLED_MCP_PATH, BUNDLED_SKILLS } from "../assets/index.js";
|
|
5
|
-
import { saveConfig } from "../config.js";
|
|
5
|
+
import { loadConfig, saveConfig } from "../config.js";
|
|
6
6
|
import { detectInstalledAdapters, getAllAdapters } from "../runtimes/detect.js";
|
|
7
7
|
function normalizeEnvValue(value) {
|
|
8
8
|
const trimmed = value?.trim();
|
|
@@ -30,8 +30,15 @@ function uniquePreserveOrder(values) {
|
|
|
30
30
|
}
|
|
31
31
|
return unique;
|
|
32
32
|
}
|
|
33
|
+
function maskApiKey(key) {
|
|
34
|
+
if (key.length <= 8)
|
|
35
|
+
return key;
|
|
36
|
+
return key.slice(0, 8) + "***";
|
|
37
|
+
}
|
|
33
38
|
export async function initCommand(options = {}) {
|
|
34
39
|
const isNonInteractive = options.yes === true || process.stdin.isTTY !== true;
|
|
40
|
+
// Load existing config for pre-filling prompts
|
|
41
|
+
const existingCfg = loadConfig();
|
|
35
42
|
let server = normalizeEnvValue(options.server) ??
|
|
36
43
|
normalizeEnvValue(process.env.BLUEKIWI_SERVER);
|
|
37
44
|
let apiKey = normalizeEnvValue(options.apiKey) ??
|
|
@@ -43,18 +50,37 @@ export async function initCommand(options = {}) {
|
|
|
43
50
|
type: "text",
|
|
44
51
|
name: "server",
|
|
45
52
|
message: "BlueKiwi server URL",
|
|
53
|
+
initial: existingCfg?.server_url,
|
|
46
54
|
});
|
|
47
55
|
}
|
|
48
56
|
if (apiKey === undefined) {
|
|
57
|
+
const maskedKey = existingCfg?.api_key
|
|
58
|
+
? maskApiKey(existingCfg.api_key)
|
|
59
|
+
: undefined;
|
|
49
60
|
questions.push({
|
|
50
|
-
type: "
|
|
61
|
+
type: "text",
|
|
51
62
|
name: "apiKey",
|
|
52
|
-
message:
|
|
63
|
+
message: maskedKey
|
|
64
|
+
? `API key (press Enter to keep ${maskedKey})`
|
|
65
|
+
: "API key (bk_...)",
|
|
66
|
+
initial: maskedKey,
|
|
53
67
|
});
|
|
54
68
|
}
|
|
55
69
|
const answers = await prompts(questions);
|
|
56
70
|
server ??= answers.server;
|
|
57
|
-
|
|
71
|
+
// If user kept the masked key (just pressed Enter), restore original
|
|
72
|
+
if (apiKey === undefined) {
|
|
73
|
+
const entered = answers.apiKey;
|
|
74
|
+
const maskedKey = existingCfg?.api_key
|
|
75
|
+
? maskApiKey(existingCfg.api_key)
|
|
76
|
+
: undefined;
|
|
77
|
+
if (entered && maskedKey && entered === maskedKey) {
|
|
78
|
+
apiKey = existingCfg.api_key;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
apiKey = entered;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
58
84
|
}
|
|
59
85
|
if (server === undefined || apiKey === undefined) {
|
|
60
86
|
if (isNonInteractive) {
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -13,10 +13,12 @@ export async function upgradeCommand() {
|
|
|
13
13
|
console.log(pc.yellow("No config found. Run `bluekiwi accept` or `bluekiwi init` next."));
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
+
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
16
17
|
for (const adapter of getAllAdapters()) {
|
|
17
18
|
if (!cfg.runtimes.includes(adapter.name))
|
|
18
19
|
continue;
|
|
19
20
|
adapter.installSkills(BUNDLED_SKILLS);
|
|
21
|
+
adapter.pruneSkills(bundledNames);
|
|
20
22
|
adapter.installMcp({
|
|
21
23
|
command: "node",
|
|
22
24
|
args: [BUNDLED_MCP_PATH],
|
|
@@ -25,6 +25,15 @@ export class ClaudeCodeAdapter {
|
|
|
25
25
|
writeFileSync(join(dir, "SKILL.md"), skill.content);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
pruneSkills(keep) {
|
|
29
|
+
if (!existsSync(SKILLS_DIR))
|
|
30
|
+
return;
|
|
31
|
+
for (const entry of readdirSync(SKILLS_DIR)) {
|
|
32
|
+
if (entry.startsWith("bk-") && !keep.has(entry)) {
|
|
33
|
+
rmSync(join(SKILLS_DIR, entry), { recursive: true, force: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
28
37
|
installMcp(config) {
|
|
29
38
|
let existing = {};
|
|
30
39
|
if (existsSync(MCP_CONFIG)) {
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -24,6 +24,15 @@ export class CodexAdapter {
|
|
|
24
24
|
writeFileSync(join(dir, "SKILL.md"), skill.content);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
pruneSkills(keep) {
|
|
28
|
+
if (!existsSync(SKILLS_DIR))
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of readdirSync(SKILLS_DIR)) {
|
|
31
|
+
if (entry.startsWith("bk-") && !keep.has(entry)) {
|
|
32
|
+
rmSync(join(SKILLS_DIR, entry), { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
installMcp(config) {
|
|
28
37
|
mkdirSync(BASE, { recursive: true });
|
|
29
38
|
const envLines = Object.entries(config.env)
|
|
@@ -24,6 +24,15 @@ export class GeminiCliAdapter {
|
|
|
24
24
|
writeFileSync(join(dir, "SKILL.md"), skill.content);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
pruneSkills(keep) {
|
|
28
|
+
if (!existsSync(SKILLS_DIR))
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of readdirSync(SKILLS_DIR)) {
|
|
31
|
+
if (entry.startsWith("bk-") && !keep.has(entry)) {
|
|
32
|
+
rmSync(join(SKILLS_DIR, entry), { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
installMcp(config) {
|
|
28
37
|
mkdirSync(BASE, { recursive: true });
|
|
29
38
|
let existing = {};
|
|
@@ -24,6 +24,15 @@ export class OpenClawAdapter {
|
|
|
24
24
|
writeFileSync(join(dir, "SKILL.md"), skill.content);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
pruneSkills(keep) {
|
|
28
|
+
if (!existsSync(SKILLS_DIR))
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of readdirSync(SKILLS_DIR)) {
|
|
31
|
+
if (entry.startsWith("bk-") && !keep.has(entry)) {
|
|
32
|
+
rmSync(join(SKILLS_DIR, entry), { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
installMcp(config) {
|
|
28
37
|
mkdirSync(BASE, { recursive: true });
|
|
29
38
|
let existing = {};
|
|
@@ -24,6 +24,15 @@ export class OpenCodeAdapter {
|
|
|
24
24
|
writeFileSync(join(dir, "SKILL.md"), skill.content);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
pruneSkills(keep) {
|
|
28
|
+
if (!existsSync(SKILLS_DIR))
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of readdirSync(SKILLS_DIR)) {
|
|
31
|
+
if (entry.startsWith("bk-") && !keep.has(entry)) {
|
|
32
|
+
rmSync(join(SKILLS_DIR, entry), { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
installMcp(config) {
|
|
28
37
|
mkdirSync(BASE, { recursive: true });
|
|
29
38
|
let existing = {};
|