code-abyss 1.7.7 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -8
- package/bin/adapters/codex.js +3 -3
- package/bin/install.js +142 -69
- package/config/codex-config.example.toml +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,8 +111,8 @@ Code Abyss 是一套 **Claude Code / Codex CLI 个性化配置包**,一条命
|
|
|
111
111
|
```
|
|
112
112
|
~/.claude/(Claude Code) ~/.codex/(Codex CLI)
|
|
113
113
|
├── CLAUDE.md 道典 ├── AGENTS.md 道典+风格
|
|
114
|
-
├── output-styles/ 输出风格 ├──
|
|
115
|
-
│ └── abyss-cultivator.md
|
|
114
|
+
├── output-styles/ 输出风格 ├── config.toml 推荐配置
|
|
115
|
+
│ └── abyss-cultivator.md ├── prompts/ custom prompts
|
|
116
116
|
├── settings.json
|
|
117
117
|
└── skills/ 56 篇秘典
|
|
118
118
|
|
|
@@ -187,15 +187,19 @@ Code Abyss 是一套 **Claude Code / Codex CLI 个性化配置包**,一条命
|
|
|
187
187
|
|
|
188
188
|
### Codex `config.toml` 推荐模板
|
|
189
189
|
|
|
190
|
-
安装 `--target codex`(尤其 `-y
|
|
190
|
+
安装 `--target codex`(尤其 `-y`)时会写入以下 **safe 默认档** 到 `~/.codex/config.toml`:
|
|
191
191
|
|
|
192
192
|
```toml
|
|
193
193
|
model_provider = "custom"
|
|
194
194
|
model = "gpt-5.2-codex"
|
|
195
195
|
model_reasoning_effort = "high"
|
|
196
|
+
approval_policy = "on-request"
|
|
197
|
+
sandbox_mode = "workspace-write"
|
|
198
|
+
disable_response_storage = true
|
|
199
|
+
|
|
200
|
+
[profiles.full_access]
|
|
196
201
|
approval_policy = "never"
|
|
197
202
|
sandbox_mode = "danger-full-access"
|
|
198
|
-
disable_response_storage = true
|
|
199
203
|
|
|
200
204
|
[model_providers.custom]
|
|
201
205
|
name = "custom"
|
|
@@ -210,12 +214,16 @@ web_search = true
|
|
|
210
214
|
multi_agent = true
|
|
211
215
|
```
|
|
212
216
|
|
|
217
|
+
- 日常交互默认使用 `on-request + workspace-write`,更贴近当前 Codex CLI 的低摩擦安全姿态
|
|
218
|
+
- 需要高自动化时可显式切到 `full_access`:`codex -p full_access`
|
|
213
219
|
|
|
214
220
|
### 兼容性说明
|
|
215
221
|
|
|
216
|
-
- 模板已对齐新版 Codex
|
|
217
|
-
-
|
|
218
|
-
-
|
|
222
|
+
- 模板已对齐新版 Codex 配置风格:root keys、`[profiles.*]`、`[tools].web_search` 与 `[features].multi_agent`
|
|
223
|
+
- `Codex` 当前支持 `~/.codex/prompts/*.md` 作为 custom prompts;Code Abyss 会继续安装 `~/.codex/skills/`,并从 `user-invocable` skills 自动生成对应的 `prompts/`
|
|
224
|
+
- 安装器不会再为 Codex 写入伪配置 `~/.codex/settings.json`;若检测到旧版遗留文件,会在安装时备份后移除,卸载时恢复
|
|
225
|
+
- 若你本地已有旧配置,安装器不会强制覆盖;会自动补齐 safe root 默认项、清理 removed feature、迁移 deprecated `web_search_*` 到 `[tools].web_search`,并仅在 `danger-full-access` 下清理 `projects.*.trust_level`
|
|
226
|
+
- 建议升级后执行一次 `codex --help`,或用 `codex -p full_access --help` 校验 profile 可见性
|
|
219
227
|
|
|
220
228
|
---
|
|
221
229
|
|
|
@@ -228,7 +236,7 @@ multi_agent = true
|
|
|
228
236
|
- `bin/lib/ccline.js`:Claude 侧状态栏与 ccline 集成
|
|
229
237
|
- `bin/adapters/codex.js`:Codex 侧认证检测、核心文件映射、config 模板流程
|
|
230
238
|
|
|
231
|
-
当前 Claude/Codex 安装映射分别由 `getClaudeCoreFiles()` 与 `getCodexCoreFiles()`
|
|
239
|
+
当前 Claude/Codex 安装映射分别由 `getClaudeCoreFiles()` 与 `getCodexCoreFiles()` 提供;Claude 额外生成 `commands/`,Codex 保持 `skills/ + config.toml` 的官方主路径,避免在主流程硬编码目标细节。
|
|
232
240
|
|
|
233
241
|
---
|
|
234
242
|
|
package/bin/adapters/codex.js
CHANGED
|
@@ -4,8 +4,8 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const CODEX_DEFAULTS = {
|
|
7
|
-
approvalPolicy: '
|
|
8
|
-
sandboxMode: '
|
|
7
|
+
approvalPolicy: 'on-request',
|
|
8
|
+
sandboxMode: 'workspace-write',
|
|
9
9
|
featureFlag: 'multi_agent',
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -179,7 +179,7 @@ function removeKeyAssignmentsInNonRootSections(content, key) {
|
|
|
179
179
|
function removeProjectTrustSectionsForFullAccess(content) {
|
|
180
180
|
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
181
181
|
const sandboxMode = readRootStringKey(content, 'sandbox_mode');
|
|
182
|
-
if (sandboxMode !==
|
|
182
|
+
if (sandboxMode !== 'danger-full-access') {
|
|
183
183
|
return { merged: content, removed: false };
|
|
184
184
|
}
|
|
185
185
|
|
package/bin/install.js
CHANGED
|
@@ -186,92 +186,154 @@ function scanInvocableSkills(skillsDir) {
|
|
|
186
186
|
return results;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
function
|
|
203
|
-
const
|
|
189
|
+
const INVOCABLE_TARGETS = {
|
|
190
|
+
claude: {
|
|
191
|
+
dir: 'commands',
|
|
192
|
+
label: '斜杠命令',
|
|
193
|
+
skillRoot: '~/.claude/skills',
|
|
194
|
+
},
|
|
195
|
+
codex: {
|
|
196
|
+
dir: 'prompts',
|
|
197
|
+
label: 'custom prompts',
|
|
198
|
+
skillRoot: '~/.codex/skills',
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
function getInvocableTarget(targetName) {
|
|
203
|
+
const targetCfg = INVOCABLE_TARGETS[targetName];
|
|
204
|
+
if (!targetCfg) throw new Error(`不支持的 invocable target: ${targetName}`);
|
|
205
|
+
return targetCfg;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getSkillPath(skillRoot, skillRelPath) {
|
|
209
|
+
return skillRelPath
|
|
210
|
+
? `${skillRoot}/${skillRelPath}/SKILL.md`
|
|
211
|
+
: `${skillRoot}/SKILL.md`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function buildCommandFrontmatter(meta) {
|
|
204
215
|
const desc = (meta.description || '').replace(/"/g, '\\"');
|
|
205
216
|
const argHint = meta['argument-hint'];
|
|
206
217
|
const tools = meta['allowed-tools'] || 'Read';
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
: '~/.claude/skills/SKILL.md';
|
|
210
|
-
|
|
211
|
-
const lines = [
|
|
212
|
-
'---',
|
|
213
|
-
`name: ${name}`,
|
|
214
|
-
`description: "${desc}"`,
|
|
215
|
-
];
|
|
218
|
+
const lines = ['---', `name: ${meta.name}`, `description: "${desc}"`];
|
|
219
|
+
|
|
216
220
|
if (argHint) lines.push(`argument-hint: "${argHint}"`);
|
|
217
221
|
lines.push(`allowed-tools: ${tools}`);
|
|
218
|
-
lines.push('---');
|
|
219
|
-
lines
|
|
222
|
+
lines.push('---', '');
|
|
223
|
+
return lines;
|
|
224
|
+
}
|
|
220
225
|
|
|
226
|
+
function buildClaudeBody(skillPath, meta, hasScripts) {
|
|
227
|
+
const lines = [];
|
|
221
228
|
if (hasScripts) {
|
|
222
|
-
|
|
223
|
-
lines.push('以下所有步骤一气呵成,不要在步骤间停顿等待用户输入:');
|
|
224
|
-
lines.push('');
|
|
229
|
+
lines.push('以下所有步骤一气呵成,不要在步骤间停顿等待用户输入:', '');
|
|
225
230
|
lines.push(`1. 读取规范:${skillPath}`);
|
|
226
|
-
lines.push(`2. 执行命令:\`node ~/.claude/skills/run_skill.js ${name} $ARGUMENTS\``);
|
|
227
|
-
lines.push('3. 按规范分析输出,完成后续动作');
|
|
228
|
-
lines.push('');
|
|
231
|
+
lines.push(`2. 执行命令:\`node ~/.claude/skills/run_skill.js ${meta.name} $ARGUMENTS\``);
|
|
232
|
+
lines.push('3. 按规范分析输出,完成后续动作', '');
|
|
229
233
|
lines.push('全程不要停顿,不要询问是否继续。');
|
|
230
|
-
|
|
231
|
-
// ── 无脚本的 skill:知识库模式 ──
|
|
232
|
-
lines.push('读取以下秘典,根据内容为用户提供专业指导:');
|
|
233
|
-
lines.push('');
|
|
234
|
-
lines.push('```');
|
|
235
|
-
lines.push(skillPath);
|
|
236
|
-
lines.push('```');
|
|
234
|
+
return lines;
|
|
237
235
|
}
|
|
238
236
|
|
|
239
|
-
lines.push('');
|
|
240
|
-
|
|
237
|
+
lines.push('读取以下秘典,根据内容为用户提供专业指导:', '');
|
|
238
|
+
lines.push('```', skillPath, '```');
|
|
239
|
+
return lines;
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
function buildCodexPromptBody(skillPath, meta, hasScripts) {
|
|
243
|
+
const lines = [];
|
|
244
|
+
if (meta['argument-hint']) lines.push(`Arguments: ${meta['argument-hint']}`, '');
|
|
245
|
+
lines.push(`Read \`${skillPath}\` before acting.`, '');
|
|
246
|
+
if (hasScripts) {
|
|
247
|
+
lines.push(`Then run \`node ~/.codex/skills/run_skill.js ${meta.name} $ARGUMENTS\`.`);
|
|
248
|
+
lines.push('Do not stop between steps unless blocked by permissions or missing required inputs.');
|
|
249
|
+
lines.push('Use the skill guidance plus script output to complete the task end-to-end.');
|
|
250
|
+
return lines;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
lines.push('Use that skill as the authoritative playbook for the task.');
|
|
254
|
+
lines.push('Respond with concrete actions instead of generic advice.');
|
|
255
|
+
return lines;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function generateInvocableContent(meta, skillRelPath, hasScripts, targetName) {
|
|
259
|
+
const targetCfg = getInvocableTarget(targetName);
|
|
260
|
+
const skillPath = getSkillPath(targetCfg.skillRoot, skillRelPath);
|
|
261
|
+
const lines = targetName === 'claude' ? buildCommandFrontmatter(meta) : [];
|
|
262
|
+
const body = targetName === 'claude'
|
|
263
|
+
? buildClaudeBody(skillPath, meta, hasScripts)
|
|
264
|
+
: buildCodexPromptBody(skillPath, meta, hasScripts);
|
|
265
|
+
return [...lines, ...body, ''].join('\n');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function generateCommandContent(meta, skillRelPath, hasScripts) {
|
|
269
|
+
return generateInvocableContent(meta, skillRelPath, hasScripts, 'claude');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function generatePromptContent(meta, skillRelPath, hasScripts) {
|
|
273
|
+
return generateInvocableContent(meta, skillRelPath, hasScripts, 'codex');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function installGeneratedArtifacts(skillsSrcDir, targetDir, backupDir, manifest, targetName) {
|
|
247
277
|
const skills = scanInvocableSkills(skillsSrcDir);
|
|
248
278
|
if (skills.length === 0) return 0;
|
|
249
279
|
|
|
250
|
-
const
|
|
251
|
-
|
|
280
|
+
const targetCfg = getInvocableTarget(targetName);
|
|
281
|
+
const installDir = path.join(targetDir, targetCfg.dir);
|
|
282
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
252
283
|
|
|
253
284
|
skills.forEach(({ meta, relPath, hasScripts }) => {
|
|
254
285
|
const fileName = `${meta.name}.md`;
|
|
255
|
-
const destFile = path.join(
|
|
256
|
-
const relFile = path.posix.join(
|
|
286
|
+
const destFile = path.join(installDir, fileName);
|
|
287
|
+
const relFile = path.posix.join(targetCfg.dir, fileName);
|
|
257
288
|
|
|
258
289
|
if (fs.existsSync(destFile)) {
|
|
259
|
-
const
|
|
260
|
-
fs.mkdirSync(
|
|
261
|
-
fs.copyFileSync(destFile, path.join(
|
|
290
|
+
const backupSubdir = path.join(backupDir, targetCfg.dir);
|
|
291
|
+
fs.mkdirSync(backupSubdir, { recursive: true });
|
|
292
|
+
fs.copyFileSync(destFile, path.join(backupSubdir, fileName));
|
|
262
293
|
manifest.backups.push(relFile);
|
|
263
294
|
info(`备份: ${c.d(relFile)}`);
|
|
264
295
|
}
|
|
265
296
|
|
|
266
|
-
const content =
|
|
297
|
+
const content = generateInvocableContent(meta, relPath, hasScripts, targetName);
|
|
267
298
|
fs.writeFileSync(destFile, content);
|
|
268
299
|
manifest.installed.push(relFile);
|
|
269
300
|
});
|
|
270
301
|
|
|
271
|
-
ok(
|
|
302
|
+
ok(`${targetCfg.dir}/ ${c.d(`(自动生成 ${skills.length} 个 ${targetCfg.label})`)}`);
|
|
272
303
|
return skills.length;
|
|
273
304
|
}
|
|
274
305
|
|
|
306
|
+
function installGeneratedCommands(skillsSrcDir, targetDir, backupDir, manifest) {
|
|
307
|
+
return installGeneratedArtifacts(skillsSrcDir, targetDir, backupDir, manifest, 'claude');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function installGeneratedPrompts(skillsSrcDir, targetDir, backupDir, manifest) {
|
|
311
|
+
return installGeneratedArtifacts(skillsSrcDir, targetDir, backupDir, manifest, 'codex');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function backupPathIfExists(targetDir, backupDir, relPath, manifest) {
|
|
315
|
+
const targetPath = path.join(targetDir, relPath);
|
|
316
|
+
if (!fs.existsSync(targetPath)) return false;
|
|
317
|
+
|
|
318
|
+
const backupPath = path.join(backupDir, relPath);
|
|
319
|
+
rmSafe(backupPath);
|
|
320
|
+
copyRecursive(targetPath, backupPath);
|
|
321
|
+
manifest.backups.push(relPath);
|
|
322
|
+
info(`备份: ${c.d(relPath)}`);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function pruneLegacyCodexSettings(targetDir, backupDir, manifest) {
|
|
327
|
+
const relPath = 'settings.json';
|
|
328
|
+
const settingsPath = path.join(targetDir, relPath);
|
|
329
|
+
if (!fs.existsSync(settingsPath)) return null;
|
|
330
|
+
|
|
331
|
+
backupPathIfExists(targetDir, backupDir, relPath, manifest);
|
|
332
|
+
rmSafe(settingsPath);
|
|
333
|
+
warn('移除 legacy settings.json(Codex 已改用 config.toml)');
|
|
334
|
+
return settingsPath;
|
|
335
|
+
}
|
|
336
|
+
|
|
275
337
|
function installCore(tgt) {
|
|
276
338
|
const targetDir = path.join(HOME, `.${tgt}`);
|
|
277
339
|
const backupDir = path.join(targetDir, '.sage-backup');
|
|
@@ -309,30 +371,37 @@ function installCore(tgt) {
|
|
|
309
371
|
rmSafe(destPath); copyRecursive(srcPath, destPath); manifest.installed.push(dest);
|
|
310
372
|
});
|
|
311
373
|
|
|
312
|
-
//
|
|
374
|
+
// 为目标 CLI 自动生成 user-invocable artifacts
|
|
313
375
|
if (tgt === 'claude') {
|
|
314
376
|
const skillsSrc = path.join(PKG_ROOT, 'skills');
|
|
315
377
|
installGeneratedCommands(skillsSrc, targetDir, backupDir, manifest);
|
|
378
|
+
} else if (tgt === 'codex') {
|
|
379
|
+
const skillsSrc = path.join(PKG_ROOT, 'skills');
|
|
380
|
+
installGeneratedPrompts(skillsSrc, targetDir, backupDir, manifest);
|
|
316
381
|
}
|
|
317
382
|
|
|
318
|
-
|
|
383
|
+
let settingsPath = null;
|
|
319
384
|
let settings = {};
|
|
320
|
-
if (fs.existsSync(settingsPath)) {
|
|
321
|
-
try {
|
|
322
|
-
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
323
|
-
} catch (e) {
|
|
324
|
-
warn(`settings.json 解析失败,将使用空配置`);
|
|
325
|
-
settings = {};
|
|
326
|
-
}
|
|
327
|
-
fs.copyFileSync(settingsPath, path.join(backupDir, 'settings.json'));
|
|
328
|
-
manifest.backups.push('settings.json');
|
|
329
|
-
}
|
|
330
385
|
if (tgt === 'claude') {
|
|
386
|
+
settingsPath = path.join(targetDir, 'settings.json');
|
|
387
|
+
if (fs.existsSync(settingsPath)) {
|
|
388
|
+
try {
|
|
389
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
390
|
+
} catch (e) {
|
|
391
|
+
warn('settings.json 解析失败,将使用空配置');
|
|
392
|
+
settings = {};
|
|
393
|
+
}
|
|
394
|
+
fs.copyFileSync(settingsPath, path.join(backupDir, 'settings.json'));
|
|
395
|
+
manifest.backups.push('settings.json');
|
|
396
|
+
}
|
|
331
397
|
settings.outputStyle = 'abyss-cultivator';
|
|
332
398
|
ok(`outputStyle = ${c.mag('abyss-cultivator')}`);
|
|
399
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
400
|
+
manifest.installed.push('settings.json');
|
|
401
|
+
} else {
|
|
402
|
+
pruneLegacyCodexSettings(targetDir, backupDir, manifest);
|
|
333
403
|
}
|
|
334
|
-
|
|
335
|
-
manifest.installed.push('settings.json');
|
|
404
|
+
|
|
336
405
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
337
406
|
|
|
338
407
|
const uSrc = path.join(PKG_ROOT, 'bin', 'uninstall.js');
|
|
@@ -438,5 +507,9 @@ if (require.main === module) {
|
|
|
438
507
|
module.exports = {
|
|
439
508
|
deepMergeNew, detectClaudeAuth, detectCodexAuth,
|
|
440
509
|
detectCclineBin, copyRecursive, shouldSkip, SETTINGS_TEMPLATE,
|
|
441
|
-
scanInvocableSkills,
|
|
510
|
+
scanInvocableSkills,
|
|
511
|
+
generateCommandContent,
|
|
512
|
+
generatePromptContent,
|
|
513
|
+
installGeneratedCommands,
|
|
514
|
+
installGeneratedPrompts,
|
|
442
515
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
model_provider = "custom"
|
|
2
2
|
model = "gpt-5.2-codex"
|
|
3
3
|
model_reasoning_effort = "high"
|
|
4
|
+
approval_policy = "on-request"
|
|
5
|
+
sandbox_mode = "workspace-write"
|
|
6
|
+
disable_response_storage = true
|
|
7
|
+
|
|
8
|
+
[profiles.full_access]
|
|
4
9
|
approval_policy = "never"
|
|
5
10
|
sandbox_mode = "danger-full-access"
|
|
6
|
-
disable_response_storage = true
|
|
7
11
|
|
|
8
12
|
[model_providers.custom]
|
|
9
13
|
name = "custom"
|