@jefferylau/euphony-skills 0.1.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/LICENSE +21 -0
- package/README.md +231 -0
- package/README_CN.md +231 -0
- package/bin/euphony-skills.mjs +221 -0
- package/package.json +25 -0
- package/scripts/install-codebuddy.sh +4 -0
- package/scripts/install-codex.sh +4 -0
- package/skills/codebuddy-euphony/SKILL.md +81 -0
- package/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs +646 -0
- package/skills/codex-euphony/SKILL.md +146 -0
- package/skills/codex-euphony/agents/openai.yaml +4 -0
- package/skills/codex-euphony/scripts/codex-euphony.mjs +468 -0
- package/skills/codex-euphony/scripts/codex-euphony.sh +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Euphony Skills
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [简体中文](README_CN.md)
|
|
4
|
+
|
|
5
|
+
Install local Euphony viewer skills for Codex and CodeBuddy.
|
|
6
|
+
|
|
7
|
+
This repository contains two independent skills with one npm-based installer:
|
|
8
|
+
|
|
9
|
+
- `skills/codex-euphony`: opens local Codex session JSONL logs in OpenAI Euphony.
|
|
10
|
+
- `skills/codebuddy-euphony`: converts local CodeBuddy session JSONL logs into an Euphony-compatible shape and opens them in OpenAI Euphony.
|
|
11
|
+
|
|
12
|
+
Each skill directory is self-contained, so it can still be installed by path. The npm CLI is the recommended install path because it handles both host applications consistently.
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Node.js 18 or newer.
|
|
17
|
+
- `git`, used when a skill needs to clone the Euphony runtime checkout.
|
|
18
|
+
- `pnpm` or `corepack`, used to install and run Euphony.
|
|
19
|
+
- macOS, Linux, or Windows with a local browser opener.
|
|
20
|
+
|
|
21
|
+
The installer itself does not install Euphony dependencies. The selected skill does that lazily the first time it starts Euphony.
|
|
22
|
+
At runtime the skill prefers an installed `pnpm`; when only Corepack is available, it uses `corepack pnpm` and sets `COREPACK_INTEGRITY_KEYS=0` for that subprocess to avoid known Corepack pnpm signature bootstrap failures.
|
|
23
|
+
|
|
24
|
+
## Quick Install
|
|
25
|
+
|
|
26
|
+
Install from npm after the package is published:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @jefferylau/euphony-skills install codex
|
|
30
|
+
npx @jefferylau/euphony-skills install codebuddy
|
|
31
|
+
npx @jefferylau/euphony-skills install all
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Replace an existing install:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx @jefferylau/euphony-skills install all --force
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Remove installed skills:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx @jefferylau/euphony-skills uninstall all
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check local state:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @jefferylau/euphony-skills doctor
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Restart Codex or CodeBuddy after installing so the host application reloads skills.
|
|
53
|
+
|
|
54
|
+
## Local Checkout Install
|
|
55
|
+
|
|
56
|
+
From a cloned repository:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/liiujinfu/euphony-skills.git
|
|
60
|
+
cd euphony-skills
|
|
61
|
+
node bin/euphony-skills.mjs install all --force
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For local development, install with symlinks so edits in this repository are picked up immediately:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
node bin/euphony-skills.mjs install all --force --link
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For normal end users, prefer copy install without `--link`.
|
|
71
|
+
|
|
72
|
+
## Install Targets
|
|
73
|
+
|
|
74
|
+
Codex installs to:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
${CODEX_HOME:-~/.codex}/skills/codex-euphony
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
CodeBuddy installs to:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
${CODEBUDDY_HOME:-~/.codebuddy}/skills/codebuddy-euphony
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Override host homes when needed:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
CODEX_HOME=/custom/.codex npx @jefferylau/euphony-skills install codex
|
|
90
|
+
CODEBUDDY_HOME=/custom/.codebuddy npx @jefferylau/euphony-skills install codebuddy
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CLI Reference
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
euphony-skills install codex [--force] [--link]
|
|
97
|
+
euphony-skills install codebuddy [--force] [--link]
|
|
98
|
+
euphony-skills install all [--force] [--link]
|
|
99
|
+
euphony-skills uninstall codex
|
|
100
|
+
euphony-skills uninstall codebuddy
|
|
101
|
+
euphony-skills uninstall all
|
|
102
|
+
euphony-skills doctor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Options:
|
|
106
|
+
|
|
107
|
+
- `--force`: replace an existing install.
|
|
108
|
+
- `--link`: create a symlink from the host skill directory to this checkout. Use this for development only.
|
|
109
|
+
|
|
110
|
+
## Using The Skills
|
|
111
|
+
|
|
112
|
+
After installing and restarting the host app, ask the assistant to open the latest session with Euphony.
|
|
113
|
+
|
|
114
|
+
Codex examples:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
Use codex-euphony to open the latest Codex session.
|
|
118
|
+
Open this Codex conversation in Euphony.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
CodeBuddy examples:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
Use codebuddy-euphony to open the latest CodeBuddy session.
|
|
125
|
+
Open this CodeBuddy conversation in Euphony.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
You can also run the scripts directly.
|
|
129
|
+
|
|
130
|
+
Codex:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs open
|
|
134
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs status
|
|
135
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs stop
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
CodeBuddy:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs open
|
|
142
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs status
|
|
143
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs stop
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Runtime Behavior
|
|
147
|
+
|
|
148
|
+
The installer only copies or links skill folders. It does not copy session logs, generated JSONL files, local caches, or Euphony checkouts.
|
|
149
|
+
|
|
150
|
+
At runtime:
|
|
151
|
+
|
|
152
|
+
- Codex uses `${CODEX_HOME:-~/.codex}/cache/euphony`.
|
|
153
|
+
- CodeBuddy uses `${CODEBUDDY_HOME:-~/.codebuddy}/cache/euphony`.
|
|
154
|
+
- If the cache is deleted, the skill recreates it on the next command that needs Euphony.
|
|
155
|
+
- The local Euphony server binds to `127.0.0.1`.
|
|
156
|
+
- The default port is `3000`.
|
|
157
|
+
- Codex staging uses a symlink on macOS/Linux and a copy on Windows by default. Set `EUPHONY_STAGE_MODE=copy` to force snapshot staging everywhere.
|
|
158
|
+
- Background servers are tracked with a pid file under the Euphony cache, so `stop` only controls servers started by the same skill script.
|
|
159
|
+
|
|
160
|
+
If port `3000` is already occupied, use another port:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
EUPHONY_PORT=3001 node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs open
|
|
164
|
+
EUPHONY_PORT=3001 ~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs open
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Windows PowerShell example:
|
|
168
|
+
|
|
169
|
+
```powershell
|
|
170
|
+
$env:EUPHONY_PORT = "3001"
|
|
171
|
+
node "$env:USERPROFILE\.codex\skills\codex-euphony\scripts\codex-euphony.mjs" open
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Privacy
|
|
175
|
+
|
|
176
|
+
Session JSONL files can contain prompts, paths, tool output, and secrets that appeared in conversations. The skills serve staged session data only from a local `127.0.0.1` Euphony instance.
|
|
177
|
+
|
|
178
|
+
Do not commit generated session files, staged JSONL output, `.env` files, or Euphony cache directories. This repository's package contents are limited to the CLI, README files, LICENSE, and skill source files.
|
|
179
|
+
|
|
180
|
+
## Troubleshooting
|
|
181
|
+
|
|
182
|
+
If a host app does not see the skill, restart Codex or CodeBuddy after installation.
|
|
183
|
+
|
|
184
|
+
If installation says the skill already exists, rerun with `--force`:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx @jefferylau/euphony-skills install codebuddy --force
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If Euphony fails to start, check prerequisites:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npx @jefferylau/euphony-skills doctor
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If `npm` reports cache permission errors, use a temporary cache or repair the npm cache ownership:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm_config_cache=/tmp/euphony-skills-npm-cache npx @jefferylau/euphony-skills doctor
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
If multiple Euphony servers are running, stop the relevant one or choose a different port:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs stop
|
|
206
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs stop
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
Run checks:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm run check
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Test install behavior without touching real homes:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
CODEX_HOME=/tmp/euphony-test-codex \
|
|
221
|
+
CODEBUDDY_HOME=/tmp/euphony-test-codebuddy \
|
|
222
|
+
node bin/euphony-skills.mjs install all --force
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Test symlink install behavior:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
CODEX_HOME=/tmp/euphony-test-codex \
|
|
229
|
+
CODEBUDDY_HOME=/tmp/euphony-test-codebuddy \
|
|
230
|
+
node bin/euphony-skills.mjs install all --force --link
|
|
231
|
+
```
|
package/README_CN.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Euphony Skills
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [简体中文](README_CN.md)
|
|
4
|
+
|
|
5
|
+
为 Codex 和 CodeBuddy 安装本地 Euphony 会话查看 skill。
|
|
6
|
+
|
|
7
|
+
这个仓库包含两个独立 skill,并提供一个 npm 安装器:
|
|
8
|
+
|
|
9
|
+
- `skills/codex-euphony`:用 OpenAI Euphony 打开本地 Codex 会话 JSONL。
|
|
10
|
+
- `skills/codebuddy-euphony`:把本地 CodeBuddy 会话 JSONL 转成 Euphony 兼容格式后打开。
|
|
11
|
+
|
|
12
|
+
每个 skill 目录都是自包含的,也可以按路径单独安装。推荐使用 npm CLI,因为它能统一处理 Codex 和 CodeBuddy 两种宿主。
|
|
13
|
+
|
|
14
|
+
## 环境要求
|
|
15
|
+
|
|
16
|
+
- Node.js 18 或更高版本。
|
|
17
|
+
- `git`,skill 首次拉取 Euphony 运行时 checkout 时使用。
|
|
18
|
+
- `pnpm` 或 `corepack`,用于安装和运行 Euphony。
|
|
19
|
+
- macOS、Linux 或 Windows,并支持本地浏览器打开命令。
|
|
20
|
+
|
|
21
|
+
安装器本身不会安装 Euphony 依赖;对应 skill 会在第一次启动 Euphony 时按需安装。
|
|
22
|
+
运行时会优先使用已安装的 `pnpm`;如果只有 Corepack,则使用 `corepack pnpm`,并仅对该子进程设置 `COREPACK_INTEGRITY_KEYS=0`,避开常见的 Corepack pnpm 签名 bootstrap 失败。
|
|
23
|
+
|
|
24
|
+
## 快速安装
|
|
25
|
+
|
|
26
|
+
发布到 npm 后,可以这样安装:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @jefferylau/euphony-skills install codex
|
|
30
|
+
npx @jefferylau/euphony-skills install codebuddy
|
|
31
|
+
npx @jefferylau/euphony-skills install all
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
替换已有安装:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx @jefferylau/euphony-skills install all --force
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
卸载已安装的 skill:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx @jefferylau/euphony-skills uninstall all
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
检查本机状态:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @jefferylau/euphony-skills doctor
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
安装后需要重启 Codex 或 CodeBuddy,让宿主重新加载 skill。
|
|
53
|
+
|
|
54
|
+
## 本地 Checkout 安装
|
|
55
|
+
|
|
56
|
+
从本地 clone 安装:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/liiujinfu/euphony-skills.git
|
|
60
|
+
cd euphony-skills
|
|
61
|
+
node bin/euphony-skills.mjs install all --force
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
本地开发时可以使用软链接安装,这样修改仓库里的代码会立即生效:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
node bin/euphony-skills.mjs install all --force --link
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
普通用户建议使用默认复制安装,不要加 `--link`。
|
|
71
|
+
|
|
72
|
+
## 安装位置
|
|
73
|
+
|
|
74
|
+
Codex 安装到:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
${CODEX_HOME:-~/.codex}/skills/codex-euphony
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
CodeBuddy 安装到:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
${CODEBUDDY_HOME:-~/.codebuddy}/skills/codebuddy-euphony
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
需要时可以覆盖宿主 home 目录:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
CODEX_HOME=/custom/.codex npx @jefferylau/euphony-skills install codex
|
|
90
|
+
CODEBUDDY_HOME=/custom/.codebuddy npx @jefferylau/euphony-skills install codebuddy
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CLI 参考
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
euphony-skills install codex [--force] [--link]
|
|
97
|
+
euphony-skills install codebuddy [--force] [--link]
|
|
98
|
+
euphony-skills install all [--force] [--link]
|
|
99
|
+
euphony-skills uninstall codex
|
|
100
|
+
euphony-skills uninstall codebuddy
|
|
101
|
+
euphony-skills uninstall all
|
|
102
|
+
euphony-skills doctor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
参数:
|
|
106
|
+
|
|
107
|
+
- `--force`:替换已有安装。
|
|
108
|
+
- `--link`:从宿主 skill 目录创建到当前 checkout 的软链接,只建议开发时使用。
|
|
109
|
+
|
|
110
|
+
## 使用方式
|
|
111
|
+
|
|
112
|
+
安装并重启宿主后,可以直接让助手用 Euphony 打开最新会话。
|
|
113
|
+
|
|
114
|
+
Codex 示例:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
Use codex-euphony to open the latest Codex session.
|
|
118
|
+
Open this Codex conversation in Euphony.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
CodeBuddy 示例:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
Use codebuddy-euphony to open the latest CodeBuddy session.
|
|
125
|
+
Open this CodeBuddy conversation in Euphony.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
也可以直接运行脚本。
|
|
129
|
+
|
|
130
|
+
Codex:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs open
|
|
134
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs status
|
|
135
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs stop
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
CodeBuddy:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs open
|
|
142
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs status
|
|
143
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs stop
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 运行时行为
|
|
147
|
+
|
|
148
|
+
安装器只复制或链接 skill 目录,不会复制会话日志、生成的 JSONL、本地缓存或 Euphony checkout。
|
|
149
|
+
|
|
150
|
+
运行时:
|
|
151
|
+
|
|
152
|
+
- Codex 使用 `${CODEX_HOME:-~/.codex}/cache/euphony`。
|
|
153
|
+
- CodeBuddy 使用 `${CODEBUDDY_HOME:-~/.codebuddy}/cache/euphony`。
|
|
154
|
+
- 如果缓存被删除,下一次需要 Euphony 时会自动重建。
|
|
155
|
+
- 本地 Euphony 服务绑定到 `127.0.0.1`。
|
|
156
|
+
- 默认端口是 `3000`。
|
|
157
|
+
- Codex staging 在 macOS/Linux 默认使用软链接,在 Windows 默认复制文件。设置 `EUPHONY_STAGE_MODE=copy` 可以在所有平台强制使用快照复制。
|
|
158
|
+
- 后台服务通过 Euphony cache 下的 pid 文件跟踪,所以 `stop` 只会停止同一个 skill 脚本启动的服务。
|
|
159
|
+
|
|
160
|
+
如果 `3000` 端口已被占用,可以换端口:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
EUPHONY_PORT=3001 node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs open
|
|
164
|
+
EUPHONY_PORT=3001 ~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs open
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Windows PowerShell 示例:
|
|
168
|
+
|
|
169
|
+
```powershell
|
|
170
|
+
$env:EUPHONY_PORT = "3001"
|
|
171
|
+
node "$env:USERPROFILE\.codex\skills\codex-euphony\scripts\codex-euphony.mjs" open
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 隐私
|
|
175
|
+
|
|
176
|
+
会话 JSONL 可能包含提示词、路径、工具输出,以及对话中出现过的敏感信息。skill 只会通过本地 `127.0.0.1` Euphony 实例提供临时 staged 数据。
|
|
177
|
+
|
|
178
|
+
不要提交生成的会话文件、staged JSONL 输出、`.env` 文件或 Euphony 缓存目录。这个仓库的 npm 包内容只包含 CLI、README、LICENSE 和 skill 源码。
|
|
179
|
+
|
|
180
|
+
## 排错
|
|
181
|
+
|
|
182
|
+
如果宿主没有识别到 skill,安装后先重启 Codex 或 CodeBuddy。
|
|
183
|
+
|
|
184
|
+
如果安装提示 skill 已存在,使用 `--force`:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx @jefferylau/euphony-skills install codebuddy --force
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
如果 Euphony 启动失败,先检查依赖和安装状态:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npx @jefferylau/euphony-skills doctor
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
如果 `npm` 报 cache 权限错误,可以使用临时 cache,或修复 npm cache 目录权限:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm_config_cache=/tmp/euphony-skills-npm-cache npx @jefferylau/euphony-skills doctor
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
如果有多个 Euphony 服务在跑,停止对应服务或换端口:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
node ~/.codex/skills/codex-euphony/scripts/codex-euphony.mjs stop
|
|
206
|
+
~/.codebuddy/skills/codebuddy-euphony/scripts/codebuddy-euphony.mjs stop
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## 开发
|
|
210
|
+
|
|
211
|
+
运行检查:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm run check
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
不影响真实 home 目录的安装测试:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
CODEX_HOME=/tmp/euphony-test-codex \
|
|
221
|
+
CODEBUDDY_HOME=/tmp/euphony-test-codebuddy \
|
|
222
|
+
node bin/euphony-skills.mjs install all --force
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
测试软链接安装:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
CODEX_HOME=/tmp/euphony-test-codex \
|
|
229
|
+
CODEBUDDY_HOME=/tmp/euphony-test-codebuddy \
|
|
230
|
+
node bin/euphony-skills.mjs install all --force --link
|
|
231
|
+
```
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
const isWindows = process.platform === 'win32';
|
|
11
|
+
|
|
12
|
+
const targets = {
|
|
13
|
+
codex: {
|
|
14
|
+
label: 'Codex',
|
|
15
|
+
homeEnv: 'CODEX_HOME',
|
|
16
|
+
defaultHome: path.join(home, '.codex'),
|
|
17
|
+
skillName: 'codex-euphony',
|
|
18
|
+
executables: ['scripts/codex-euphony.mjs', 'scripts/codex-euphony.sh']
|
|
19
|
+
},
|
|
20
|
+
codebuddy: {
|
|
21
|
+
label: 'CodeBuddy',
|
|
22
|
+
homeEnv: 'CODEBUDDY_HOME',
|
|
23
|
+
defaultHome: path.join(home, '.codebuddy'),
|
|
24
|
+
skillName: 'codebuddy-euphony',
|
|
25
|
+
executables: ['scripts/codebuddy-euphony.mjs']
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function usage() {
|
|
30
|
+
console.log(`Usage: euphony-skills <command> [target] [options]
|
|
31
|
+
|
|
32
|
+
Commands:
|
|
33
|
+
install codex|codebuddy|all Install skill(s).
|
|
34
|
+
uninstall codex|codebuddy|all Remove installed skill(s).
|
|
35
|
+
doctor Check local install prerequisites and state.
|
|
36
|
+
help Show this help.
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
--force Replace an existing install.
|
|
40
|
+
--link Symlink from this checkout instead of copying.
|
|
41
|
+
|
|
42
|
+
Environment:
|
|
43
|
+
CODEX_HOME Override Codex home. Default: ~/.codex
|
|
44
|
+
CODEBUDDY_HOME Override CodeBuddy home. Default: ~/.codebuddy`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fail(message) {
|
|
48
|
+
console.error(message);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function commandExists(command) {
|
|
53
|
+
try {
|
|
54
|
+
if (isWindows) {
|
|
55
|
+
execFileSync('where', [command], { stdio: 'ignore', shell: true });
|
|
56
|
+
} else {
|
|
57
|
+
execFileSync('which', [command], { stdio: 'ignore' });
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseArgs(argv) {
|
|
66
|
+
const args = [...argv];
|
|
67
|
+
const flags = new Set(args.filter(arg => arg.startsWith('--')));
|
|
68
|
+
const positional = args.filter(arg => !arg.startsWith('--'));
|
|
69
|
+
return {
|
|
70
|
+
command: positional[0] || 'help',
|
|
71
|
+
target: positional[1],
|
|
72
|
+
force: flags.has('--force'),
|
|
73
|
+
link: flags.has('--link')
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function expandTargets(target) {
|
|
78
|
+
if (target === 'all') return Object.keys(targets);
|
|
79
|
+
if (target && targets[target]) return [target];
|
|
80
|
+
fail(`Unknown target: ${target || '(missing)'}
|
|
81
|
+
|
|
82
|
+
Expected one of: codex, codebuddy, all`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sourceDirFor(targetKey) {
|
|
86
|
+
return path.join(rootDir, 'skills', targets[targetKey].skillName);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function homeDirFor(targetKey) {
|
|
90
|
+
const target = targets[targetKey];
|
|
91
|
+
return process.env[target.homeEnv] || target.defaultHome;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function installDirFor(targetKey) {
|
|
95
|
+
return path.join(homeDirFor(targetKey), 'skills', targets[targetKey].skillName);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function removeExisting(dest) {
|
|
99
|
+
if (!pathExists(dest)) return;
|
|
100
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function pathExists(file) {
|
|
104
|
+
return fs.lstatSync(file, { throwIfNoEntry: false }) !== undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function copyRecursive(src, dest) {
|
|
108
|
+
fs.cpSync(src, dest, {
|
|
109
|
+
recursive: true,
|
|
110
|
+
dereference: false,
|
|
111
|
+
filter: source => !source.split(path.sep).includes('.git')
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function chmodExecutables(targetKey) {
|
|
116
|
+
if (isWindows) return;
|
|
117
|
+
const target = targets[targetKey];
|
|
118
|
+
const dest = installDirFor(targetKey);
|
|
119
|
+
for (const relative of target.executables) {
|
|
120
|
+
const file = path.join(dest, relative);
|
|
121
|
+
if (fs.existsSync(file)) fs.chmodSync(file, 0o755);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function installOne(targetKey, options) {
|
|
126
|
+
const source = sourceDirFor(targetKey);
|
|
127
|
+
const dest = installDirFor(targetKey);
|
|
128
|
+
if (!fs.existsSync(path.join(source, 'SKILL.md'))) {
|
|
129
|
+
fail(`Missing skill source: ${source}`);
|
|
130
|
+
}
|
|
131
|
+
if (pathExists(dest)) {
|
|
132
|
+
if (!options.force) {
|
|
133
|
+
fail(`${targets[targetKey].label} skill already exists: ${dest}
|
|
134
|
+
Use --force to replace it.`);
|
|
135
|
+
}
|
|
136
|
+
removeExisting(dest);
|
|
137
|
+
}
|
|
138
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
139
|
+
if (options.link) {
|
|
140
|
+
fs.symlinkSync(source, dest, 'dir');
|
|
141
|
+
} else {
|
|
142
|
+
copyRecursive(source, dest);
|
|
143
|
+
}
|
|
144
|
+
if (!options.link) chmodExecutables(targetKey);
|
|
145
|
+
console.log(`Installed ${targets[targetKey].label}: ${dest}${options.link ? ` -> ${source}` : ''}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function uninstallOne(targetKey) {
|
|
149
|
+
const dest = installDirFor(targetKey);
|
|
150
|
+
if (!pathExists(dest)) {
|
|
151
|
+
console.log(`${targets[targetKey].label} skill is not installed: ${dest}`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
removeExisting(dest);
|
|
155
|
+
console.log(`Removed ${targets[targetKey].label}: ${dest}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function statInstall(targetKey) {
|
|
159
|
+
const target = targets[targetKey];
|
|
160
|
+
const source = sourceDirFor(targetKey);
|
|
161
|
+
const dest = installDirFor(targetKey);
|
|
162
|
+
const installed = pathExists(dest);
|
|
163
|
+
const linkTarget = installed && fs.lstatSync(dest).isSymbolicLink() ? fs.readlinkSync(dest) : null;
|
|
164
|
+
const executableStatus = target.executables.map(relative => {
|
|
165
|
+
const file = path.join(dest, relative);
|
|
166
|
+
if (!fs.existsSync(file)) return `${relative}: missing`;
|
|
167
|
+
const mode = fs.statSync(file).mode;
|
|
168
|
+
return `${relative}: ${(mode & 0o111) !== 0 ? 'executable' : 'not executable'}`;
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
target,
|
|
172
|
+
source,
|
|
173
|
+
dest,
|
|
174
|
+
installed,
|
|
175
|
+
linkTarget,
|
|
176
|
+
executableStatus
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function doctor() {
|
|
181
|
+
console.log(`Node: ${process.version}`);
|
|
182
|
+
for (const command of ['git', 'pnpm', 'corepack']) {
|
|
183
|
+
console.log(`${command}: ${commandExists(command) ? 'found' : 'missing'}`);
|
|
184
|
+
}
|
|
185
|
+
console.log(`package runner: ${commandExists('pnpm') ? 'pnpm' : commandExists('corepack') ? 'corepack pnpm' : 'missing'}`);
|
|
186
|
+
for (const key of Object.keys(targets)) {
|
|
187
|
+
const status = statInstall(key);
|
|
188
|
+
console.log(`\n${status.target.label}`);
|
|
189
|
+
console.log(` source: ${status.source}`);
|
|
190
|
+
console.log(` install: ${status.dest}`);
|
|
191
|
+
console.log(` installed: ${status.installed ? 'yes' : 'no'}`);
|
|
192
|
+
if (status.linkTarget) console.log(` link: ${status.linkTarget}`);
|
|
193
|
+
for (const line of status.executableStatus) console.log(` ${line}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function main() {
|
|
198
|
+
const args = parseArgs(process.argv.slice(2));
|
|
199
|
+
switch (args.command) {
|
|
200
|
+
case 'install':
|
|
201
|
+
for (const targetKey of expandTargets(args.target)) installOne(targetKey, args);
|
|
202
|
+
console.log('Restart Codex or CodeBuddy to pick up newly installed skills.');
|
|
203
|
+
break;
|
|
204
|
+
case 'uninstall':
|
|
205
|
+
for (const targetKey of expandTargets(args.target)) uninstallOne(targetKey);
|
|
206
|
+
break;
|
|
207
|
+
case 'doctor':
|
|
208
|
+
doctor();
|
|
209
|
+
break;
|
|
210
|
+
case 'help':
|
|
211
|
+
case '--help':
|
|
212
|
+
case '-h':
|
|
213
|
+
usage();
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
usage();
|
|
217
|
+
process.exitCode = 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
main();
|