@jspg-ai/coding-bb 0.0.3-beta.2 → 0.0.3-beta.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/cbb/lib/install/init.js +172 -45
- package/cbb/lib/install/opencode.js +39 -31
- package/cbb/worktrees/commands/worktree-close.md +3 -4
- package/cbb/worktrees/commands/worktree-init.md +4 -5
- package/cbb/worktrees/commands/worktree-push.md +1 -1
- package/cbb/worktrees/skills/cbb-worktree-close/SKILL.md +7 -7
- package/cbb/worktrees/skills/cbb-worktree-init/SKILL.md +12 -12
- package/cbb/worktrees/skills/cbb-worktree-push/SKILL.md +6 -6
- package/config/workspace-agents.sample.md +2 -2
- package/package.json +1 -1
package/cbb/lib/install/init.js
CHANGED
|
@@ -68,8 +68,8 @@ const SUPERPOWERS_WHITELIST = [
|
|
|
68
68
|
'test-driven-development', // TDD 计划结构 + TDD Evidence 方法论基底
|
|
69
69
|
];
|
|
70
70
|
|
|
71
|
-
// worktree
|
|
72
|
-
//
|
|
71
|
+
// worktree 运行时部署清单:init / close / push 三件套(SKILL.md + scripts),
|
|
72
|
+
// 部署到 .cbb/skills/<name>/(每空间一份,不分工具;命令文件统一指向此处)
|
|
73
73
|
const WORKTREE_SKILLS = ['cbb-worktree-init', 'cbb-worktree-close', 'cbb-worktree-push'];
|
|
74
74
|
|
|
75
75
|
// 装到空间的 tools skills:当前不分发。
|
|
@@ -315,56 +315,117 @@ function deployCoreRules(adapter, isUpgrade, managedPaths) {
|
|
|
315
315
|
// ── opencode instructions 登记 ────────────────────────────────
|
|
316
316
|
|
|
317
317
|
/**
|
|
318
|
-
* opencode 无原生 rules 目录:把 .opencode/
|
|
319
|
-
* opencode.
|
|
318
|
+
* opencode 无原生 rules 目录:把 rules glob 登记进 .opencode/opencode.json 的
|
|
319
|
+
* instructions(opencode V2 支持的配置位置;随 .opencode gitignore,按机器生成)。
|
|
320
|
+
* instructions 相对路径的解析基准(配置文件目录 vs 项目根)文档未写死,
|
|
321
|
+
* 故登记双 glob 对冲:任一基准下恰有一个命中,另一个空匹配。
|
|
320
322
|
* - 文件不存在:创建(记入清单,卸载时整体删除)
|
|
321
|
-
* -
|
|
322
|
-
*
|
|
323
|
+
* - 文件存在:幂等补齐缺失的 glob(保留用户已有 instructions)
|
|
324
|
+
* - JSONC(含注释)解析失败则只提示不改写
|
|
325
|
+
* 旧版登记在项目根 opencode.json,由 migrateLegacyInstructions 负责迁移清理。
|
|
323
326
|
*/
|
|
324
327
|
function injectInstructions(adapter, newPaths, managedPaths) {
|
|
325
|
-
const cfgPath = path.join(TARGET_DIR,
|
|
326
|
-
const
|
|
328
|
+
const cfgPath = path.join(TARGET_DIR, adapter.instructionsFile);
|
|
329
|
+
const patterns = adapter.instructionsPatterns;
|
|
330
|
+
let cfg = {};
|
|
331
|
+
if (fs.existsSync(cfgPath)) {
|
|
332
|
+
try {
|
|
333
|
+
cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
|
|
334
|
+
} catch (_) {
|
|
335
|
+
log(`${adapter.instructionsFile} 解析失败(可能含注释),未自动登记。请手动在 instructions 中加入 "${patterns.join('", "')}"`, 'warn');
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (!cfg || typeof cfg !== 'object') cfg = {};
|
|
339
|
+
}
|
|
340
|
+
const list = Array.isArray(cfg.instructions) ? cfg.instructions : [];
|
|
341
|
+
const merged = [...list];
|
|
342
|
+
let added = false;
|
|
343
|
+
patterns.forEach(p => {
|
|
344
|
+
if (!merged.includes(p)) {
|
|
345
|
+
merged.push(p);
|
|
346
|
+
added = true;
|
|
347
|
+
}
|
|
348
|
+
});
|
|
327
349
|
if (!fs.existsSync(cfgPath)) {
|
|
328
|
-
|
|
350
|
+
cfg.instructions = merged;
|
|
351
|
+
if (!cfg.$schema) cfg.$schema = 'https://opencode.ai/config.json';
|
|
352
|
+
fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
|
|
329
353
|
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
330
|
-
newPaths.add(
|
|
331
|
-
log(`生成
|
|
332
|
-
|
|
354
|
+
newPaths.add(adapter.instructionsFile);
|
|
355
|
+
log(`生成 ${adapter.instructionsFile} 并登记 rules 加载(instructions: ${patterns.join(' | ')})`, 'success');
|
|
356
|
+
} else if (added) {
|
|
357
|
+
cfg.instructions = merged;
|
|
358
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
359
|
+
log(`已在 ${adapter.instructionsFile} instructions 中登记 rules glob`, 'success');
|
|
333
360
|
}
|
|
361
|
+
// 本包此前创建的文件:续登清单,避免升级清理误删
|
|
362
|
+
if (managedPaths.has(adapter.instructionsFile)) newPaths.add(adapter.instructionsFile);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 旧版(≤0.0.3-beta.2)把 rules glob 登记在项目根 opencode.json;迁移为
|
|
367
|
+
* .opencode/opencode.json 后需把旧 pattern 从根文件摘除(用户其余配置保留)。
|
|
368
|
+
* 根文件若由本包创建(在清单中),升级时由清单差集自动整体删除,此处只摘 pattern。
|
|
369
|
+
*/
|
|
370
|
+
function migrateLegacyInstructions(adapter) {
|
|
371
|
+
const legacyPattern = adapter.legacyInstructionsPattern;
|
|
372
|
+
if (!legacyPattern) return;
|
|
373
|
+
const cfgPath = path.join(TARGET_DIR, 'opencode.json');
|
|
374
|
+
if (!fs.existsSync(cfgPath)) return;
|
|
334
375
|
let cfg;
|
|
335
376
|
try {
|
|
336
377
|
cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
|
|
337
378
|
} catch (_) {
|
|
338
|
-
log(
|
|
379
|
+
log('根 opencode.json 解析失败(可能含注释),未自动迁移旧登记。可手动移除 instructions 中的 "' + legacyPattern + '"', 'warn');
|
|
339
380
|
return;
|
|
340
381
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
cfg.instructions
|
|
382
|
+
if (Array.isArray(cfg.instructions) && cfg.instructions.includes(legacyPattern)) {
|
|
383
|
+
cfg.instructions = cfg.instructions.filter(p => p !== legacyPattern);
|
|
384
|
+
if (cfg.instructions.length === 0) delete cfg.instructions;
|
|
344
385
|
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
345
|
-
log(
|
|
386
|
+
log('已从根 opencode.json 移除旧版 rules 登记(迁移至 .opencode/opencode.json)', 'success');
|
|
346
387
|
}
|
|
347
|
-
// 本包此前创建的文件:续登清单,避免升级清理误删
|
|
348
|
-
if (managedPaths.has('opencode.json')) newPaths.add('opencode.json');
|
|
349
388
|
}
|
|
350
389
|
|
|
351
|
-
/** 卸载时移除本包登记的 instructions
|
|
390
|
+
/** 卸载时移除本包登记的 instructions(用户自建配置只摘本包 glob,不删文件) */
|
|
352
391
|
function removeInstructionsRegistrations() {
|
|
353
392
|
adapters.forEach(adapter => {
|
|
354
|
-
if (!adapter.
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
cfg
|
|
360
|
-
|
|
361
|
-
|
|
393
|
+
if (!adapter.instructionsFile) return;
|
|
394
|
+
const patterns = adapter.instructionsPatterns;
|
|
395
|
+
// 新位置:.opencode/opencode.json(cbb 创建的整体文件由清单删除兜底)
|
|
396
|
+
const cfgPath = path.join(TARGET_DIR, adapter.instructionsFile);
|
|
397
|
+
if (fs.existsSync(cfgPath)) {
|
|
398
|
+
let cfg;
|
|
399
|
+
try {
|
|
400
|
+
cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
|
|
401
|
+
} catch (_) {
|
|
402
|
+
cfg = null; // 不可解析的文件不动
|
|
403
|
+
}
|
|
404
|
+
if (cfg && Array.isArray(cfg.instructions)) {
|
|
405
|
+
const filtered = cfg.instructions.filter(p => !patterns.includes(p));
|
|
406
|
+
if (filtered.length !== cfg.instructions.length) {
|
|
407
|
+
if (filtered.length === 0) delete cfg.instructions;
|
|
408
|
+
else cfg.instructions = filtered;
|
|
409
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
410
|
+
log(`${adapter.instructionsFile}: 移除本包登记的 instructions,保留用户配置`, 'success');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
362
413
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
414
|
+
// 旧位置:项目根 opencode.json(未升级就卸载的老空间,或迁移残留)
|
|
415
|
+
const legacyPath = path.join(TARGET_DIR, 'opencode.json');
|
|
416
|
+
if (adapter.legacyInstructionsPattern && fs.existsSync(legacyPath)) {
|
|
417
|
+
let legacyCfg;
|
|
418
|
+
try {
|
|
419
|
+
legacyCfg = JSON.parse(fs.readFileSync(legacyPath, 'utf-8'));
|
|
420
|
+
} catch (_) {
|
|
421
|
+
return; // 不可解析的文件不动
|
|
422
|
+
}
|
|
423
|
+
if (Array.isArray(legacyCfg.instructions) && legacyCfg.instructions.includes(adapter.legacyInstructionsPattern)) {
|
|
424
|
+
legacyCfg.instructions = legacyCfg.instructions.filter(p => p !== adapter.legacyInstructionsPattern);
|
|
425
|
+
if (legacyCfg.instructions.length === 0) delete legacyCfg.instructions;
|
|
426
|
+
fs.writeFileSync(legacyPath, JSON.stringify(legacyCfg, null, 2) + '\n');
|
|
427
|
+
log('opencode.json: 移除本包登记的旧版 instructions,保留用户配置', 'success');
|
|
428
|
+
}
|
|
368
429
|
}
|
|
369
430
|
});
|
|
370
431
|
}
|
|
@@ -499,13 +560,82 @@ function deployCommandFiles(adapter, srcDir, destDir, prefix, filter) {
|
|
|
499
560
|
const srcPath = path.join(srcDir, f);
|
|
500
561
|
const destPath = path.join(destDir, prefix + f);
|
|
501
562
|
removePath(destPath);
|
|
502
|
-
const content = applyContentRewrites(adapter, fs.readFileSync(srcPath, 'utf-8'));
|
|
563
|
+
const content = rewriteCommandName(adapter, sanitizeCommandFrontmatter(applyContentRewrites(adapter, fs.readFileSync(srcPath, 'utf-8'))));
|
|
503
564
|
fs.writeFileSync(destPath, content);
|
|
504
565
|
});
|
|
505
566
|
|
|
506
567
|
return { count: commandFiles.length, names: commandFiles.map(f => prefix + f) };
|
|
507
568
|
}
|
|
508
569
|
|
|
570
|
+
/**
|
|
571
|
+
* worktree 运行时部署:SKILL.md + scripts 复制到 .cbb/skills/<name>/(每空间一份,不分工具)。
|
|
572
|
+
* 单形态 command 收敛后,worktree 不进各工具的 skills 目录——新版工具会把 skills 暴露成
|
|
573
|
+
* 斜杠命令,与命令文件在面板里重复;脚本与流程文档统一放 .cbb/skills/,命令文件指向此处。
|
|
574
|
+
*/
|
|
575
|
+
function deployWorktreeRuntime(newPaths) {
|
|
576
|
+
const srcDir = path.join(SHARED_STANDARDS_DIR, 'cbb', 'worktrees', 'skills');
|
|
577
|
+
if (!fs.existsSync(srcDir)) return 0;
|
|
578
|
+
const runtimeDir = path.join(TARGET_DIR, '.cbb', 'skills');
|
|
579
|
+
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
580
|
+
let count = 0;
|
|
581
|
+
WORKTREE_SKILLS.forEach(name => {
|
|
582
|
+
const src = path.join(srcDir, name);
|
|
583
|
+
if (!fs.existsSync(src)) return;
|
|
584
|
+
const destDir = path.join(runtimeDir, name);
|
|
585
|
+
removePath(destDir);
|
|
586
|
+
fs.cpSync(src, destDir, { recursive: true });
|
|
587
|
+
newPaths.add(`.cbb/skills/${name}`);
|
|
588
|
+
count++;
|
|
589
|
+
});
|
|
590
|
+
return count;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* 命令 frontmatter 消毒:opencode 新版校验命令配置(description 必须为 string | undefined),
|
|
595
|
+
* 上游个别命令(openspec/commands/update.md)的 description 为空值(YAML 解析为 null),
|
|
596
|
+
* 会导致 opencode 拒绝加载整个项目配置。此处对部署副本回填正文首句作为描述
|
|
597
|
+
* (超过 120 字截断),上游源文件不动(CLI 接管区)。
|
|
598
|
+
*/
|
|
599
|
+
function sanitizeCommandFrontmatter(content) {
|
|
600
|
+
const lines = content.split('\n');
|
|
601
|
+
if ((lines[0] || '').trim() !== '---') return content;
|
|
602
|
+
let end = -1;
|
|
603
|
+
for (let i = 1; i < lines.length; i++) {
|
|
604
|
+
if ((lines[i] || '').trim() === '---') { end = i; break; }
|
|
605
|
+
}
|
|
606
|
+
if (end === -1) return content;
|
|
607
|
+
let sentence = '';
|
|
608
|
+
for (let i = end + 1; i < lines.length; i++) {
|
|
609
|
+
const t = (lines[i] || '').trim();
|
|
610
|
+
if (t) { sentence = t; break; }
|
|
611
|
+
}
|
|
612
|
+
let changed = false;
|
|
613
|
+
for (let i = 1; i < end; i++) {
|
|
614
|
+
if (/^description:\s*(null|~)?\s*\r?$/.test(lines[i])) {
|
|
615
|
+
if (sentence) {
|
|
616
|
+
if (sentence.length > 120) sentence = sentence.slice(0, 117) + '...';
|
|
617
|
+
lines[i] = 'description: ' + JSON.stringify(sentence) + (lines[i].endsWith('\r') ? '\r' : '');
|
|
618
|
+
} else {
|
|
619
|
+
lines[i] = null; // 无正文可回填则移除(undefined 同样合法)
|
|
620
|
+
}
|
|
621
|
+
changed = true;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return changed ? lines.filter(l => l !== null).join('\n') : content;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* opencode 命令显示名取自 frontmatter name,上游为大写空格风格("OPSX: Propose"),
|
|
629
|
+
* 与文档写法(/opsx-propose)不一致;部署副本统一重写为 kebab-case。
|
|
630
|
+
*/
|
|
631
|
+
function rewriteCommandName(adapter, content) {
|
|
632
|
+
if (!adapter.commandNameKebab) return content;
|
|
633
|
+
return content.replace(/^name:\s*"?([A-Za-z]+):\s*([^"\r\n]+?)"?\s*\r?$/m, (m0, ns, rest) => {
|
|
634
|
+
const kebab = (ns + '-' + rest).toLowerCase().replace(/\s+/g, '-');
|
|
635
|
+
return `name: "${kebab}"`;
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
509
639
|
/** 官方 OpenSpec 命令(openspec/commands/ → adapter.commandsDir) */
|
|
510
640
|
function deployCommands(adapter, isUpgrade, managedPaths, srcDir, filter) {
|
|
511
641
|
if (!adapter.commandsDir) return { count: 0, names: [] };
|
|
@@ -871,9 +1001,10 @@ async function init(targetDir, options = {}) {
|
|
|
871
1001
|
newPaths.add(`${adapter.rulesDir}/cbb-coding-rule.md`);
|
|
872
1002
|
newPaths.add(`${adapter.rulesDir}/cbb-priority.md`);
|
|
873
1003
|
newPaths.add(`${adapter.rulesDir}/cbb-ai-behavior.md`);
|
|
874
|
-
// opencode:rules glob
|
|
875
|
-
if (adapter.
|
|
1004
|
+
// opencode:rules glob 登记进 .opencode/opencode.json instructions,并迁移根文件旧登记
|
|
1005
|
+
if (adapter.instructionsFile) {
|
|
876
1006
|
injectInstructions(adapter, newPaths, managedPaths);
|
|
1007
|
+
migrateLegacyInstructions(adapter);
|
|
877
1008
|
}
|
|
878
1009
|
});
|
|
879
1010
|
log(`核心规则 → ${selectedAdapters.map(a => a.rulesDir).join(', ')} (2 个文件)`, 'success');
|
|
@@ -883,11 +1014,10 @@ async function init(targetDir, options = {}) {
|
|
|
883
1014
|
if (skillsAdapters.length > 0) {
|
|
884
1015
|
console.log(`\n 🔧 ${action}按需加载 Skills...`);
|
|
885
1016
|
const standardsSkillsSrc = path.join(SHARED_STANDARDS_DIR, 'cbb', 'dev-standards', 'skills');
|
|
886
|
-
const openspecExtendsSkillsSrc = path.join(SHARED_STANDARDS_DIR, 'cbb', 'worktrees', 'skills');
|
|
887
1017
|
const extendsSkillsSrc = path.join(SHARED_STANDARDS_DIR, 'cbb', 'tools');
|
|
888
1018
|
|
|
889
1019
|
const skillsDests = skillsAdapters.map(a => a.skillsDir);
|
|
890
|
-
let stdCount = 0,
|
|
1020
|
+
let stdCount = 0, extCount = 0;
|
|
891
1021
|
skillsAdapters.forEach(adapter => {
|
|
892
1022
|
// 通用编码规范 Skills(cbb/dev-standards/skills)
|
|
893
1023
|
const stdResult = deploySkills(adapter, isUpgrade, managedPaths, standardsSkillsSrc);
|
|
@@ -897,12 +1027,6 @@ async function init(targetDir, options = {}) {
|
|
|
897
1027
|
}
|
|
898
1028
|
// 官方 OpenSpec Skills(openspec/skills/)不部署——工作流入口统一为 /opsx:* 斜杠命令
|
|
899
1029
|
//(openspec/commands/ 在下方 Commands 段部署),避免双形态重复
|
|
900
|
-
// worktree 扩展 Skills:init / close / push 三件套(单层安装,空间根是唯一安装点)
|
|
901
|
-
const osExtResult = deploySkills(adapter, isUpgrade, managedPaths, openspecExtendsSkillsSrc, WORKTREE_SKILLS);
|
|
902
|
-
if (osExtResult.count > 0) {
|
|
903
|
-
osCount = osExtResult.count;
|
|
904
|
-
osExtResult.names.forEach(name => newPaths.add(`${adapter.skillsDir}/${name}`));
|
|
905
|
-
}
|
|
906
1030
|
// 工具 Skills:当前不分发(恢复方式见 TOOLS_SKILLS 注释)
|
|
907
1031
|
const extResult = deploySkills(adapter, isUpgrade, managedPaths, extendsSkillsSrc, TOOLS_SKILLS);
|
|
908
1032
|
if (extResult.count > 0) {
|
|
@@ -911,7 +1035,6 @@ async function init(targetDir, options = {}) {
|
|
|
911
1035
|
}
|
|
912
1036
|
});
|
|
913
1037
|
if (stdCount > 0) log(`规范 Skills → ${skillsDests.join(', ')} (${stdCount} 个)`, 'success');
|
|
914
|
-
if (osCount > 0) log(`worktree Skills → ${skillsDests.join(', ')} (${osCount} 个)`, 'success');
|
|
915
1038
|
if (extCount > 0) log(`扩展 Skills → ${skillsDests.join(', ')} (${extCount} 个)`, 'success');
|
|
916
1039
|
|
|
917
1040
|
// 部署 Superpowers Skills(白名单:openspec 工作流依赖 + 方法论基底)
|
|
@@ -935,6 +1058,10 @@ async function init(targetDir, options = {}) {
|
|
|
935
1058
|
await detectUserLevelSuperpowers(selectedAdapters, yes);
|
|
936
1059
|
}
|
|
937
1060
|
|
|
1061
|
+
// worktree 运行时(SKILL.md + scripts)→ .cbb/skills/(每空间一份,不分工具)
|
|
1062
|
+
const runtimeCount = deployWorktreeRuntime(newPaths);
|
|
1063
|
+
if (runtimeCount > 0) log(`worktree 运行时 → .cbb/skills (${runtimeCount} 个)`, 'success');
|
|
1064
|
+
|
|
938
1065
|
// 安装 Commands(官方 11 个 → /opsx:*,onboard 不分发;worktree 管理 → /cbb:worktree-*,init/close/push 三件套)
|
|
939
1066
|
const commandsAdapters = selectedAdapters.filter(a => a.commandsDir);
|
|
940
1067
|
if (commandsAdapters.length > 0) {
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* opencode 适配器
|
|
3
|
-
*
|
|
4
|
-
* 将编码规范分发到 .opencode/ 目录(opencode 项目级配置域)。
|
|
5
|
-
*
|
|
6
|
-
* 与 Claude Code / Qoder 的映射差异:
|
|
7
|
-
* - rules:opencode 无原生 rules 目录,部署到 .opencode/rules/ 并由安装流程
|
|
8
|
-
* 将 glob
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
/**
|
|
2
|
+
* opencode 适配器
|
|
3
|
+
*
|
|
4
|
+
* 将编码规范分发到 .opencode/ 目录(opencode 项目级配置域)。
|
|
5
|
+
*
|
|
6
|
+
* 与 Claude Code / Qoder 的映射差异:
|
|
7
|
+
* - rules:opencode 无原生 rules 目录,部署到 .opencode/rules/ 并由安装流程
|
|
8
|
+
* 将 glob 登记进 .opencode/opencode.json 的 instructions 字段(opencode V2
|
|
9
|
+
* 官方支持的配置位置:全局目录 / 项目根 / .opencode 目录内均会被发现)
|
|
10
|
+
* - skills:.opencode/skills/<name>/SKILL.md,结构与 Claude 同构
|
|
11
|
+
* - commands:命令名 = 文件名(嵌套目录无命名空间,且 init.md 等会撞内置 /init),
|
|
12
|
+
* 因此扁平化为 opsx-<name>.md(/opsx-propose);worktree 管理命令同理扁平化为
|
|
13
|
+
* cbb-<name>.md(/cbb-worktree-init),部署副本中的 /opsx: 与 /cbb: 引用同步改写
|
|
14
|
+
* - hooks:opencode 无 UserPromptSubmit 等价事件,版本检查 hook 不安装(settingsFile 留空即跳过)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
name: 'opencode',
|
|
19
|
+
displayName: 'opencode',
|
|
20
|
+
rulesDir: '.opencode/rules',
|
|
21
|
+
skillsDir: '.opencode/skills',
|
|
22
|
+
commandsDir: '.opencode/commands',
|
|
23
|
+
// 部署时给命令文件名加此前缀(规避内置命令名冲突)
|
|
24
|
+
commandPrefix: 'opsx-',
|
|
25
|
+
// worktree 管理命令(非 openspec 上游)落在同一扁平目录,用 cbb- 前缀区分
|
|
26
|
+
worktreeCommandsDir: '.opencode/commands',
|
|
27
|
+
worktreeCommandPrefix: 'cbb-',
|
|
28
|
+
// 部署副本中将该引用串改写为 opencode 的命令风格(配合 commandPrefix)
|
|
29
|
+
contentRewrites: [['/opsx:', '/opsx-'], ['/cbb:', '/cbb-']],
|
|
30
|
+
// 命令显示名取自 frontmatter name,部署副本重写为 kebab-case(/opsx-propose,与文档一致)
|
|
31
|
+
commandNameKebab: true,
|
|
32
|
+
// rules 加载登记:写入 .opencode/opencode.json 的 instructions。
|
|
33
|
+
// instructions 相对路径的解析基准(配置文件目录 vs 项目根)文档未写死,
|
|
34
|
+
// 登记双 glob 对冲:任一基准下恰有一个命中,另一个空匹配。
|
|
35
|
+
instructionsFile: '.opencode/opencode.json',
|
|
36
|
+
instructionsPatterns: ['rules/*.md', '.opencode/rules/*.md'],
|
|
37
|
+
// 旧版(≤0.0.3-beta.2)曾登记在项目根 opencode.json 的 pattern,迁移/卸载时识别清理用
|
|
38
|
+
legacyInstructionsPattern: '.opencode/rules/*.md',
|
|
39
|
+
};
|
|
@@ -11,12 +11,11 @@ tags: [worktree, cleanup, worktree, git-worktree]
|
|
|
11
11
|
|
|
12
12
|
**禁止**用 `ls` / `find` / `cat` / `head` / `grep` 等 bash 命令搜索 SKILL.md。这些命令在 Agent 运行环境(Qoder Windows PowerShell 等)中经常失败。
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Claude Code 适配器:`.claude/skills/cbb-worktree-close/SKILL.md`
|
|
14
|
+
**直接路径**(各工具统一,worktree 运行时部署在空间根 `.cbb/skills/`):
|
|
15
|
+
- `.cbb/skills/cbb-worktree-close/SKILL.md`
|
|
17
16
|
|
|
18
17
|
**第一步**:
|
|
19
|
-
1. 用 Read 工具直接打开 SKILL.md
|
|
18
|
+
1. 用 Read 工具直接打开 `.cbb/skills/cbb-worktree-close/SKILL.md`
|
|
20
19
|
2. 按 SKILL.md 的 Step 0 → 1 → 2 → 3 → 4 → 5 → 6 顺序执行
|
|
21
20
|
3. 每次调用 Node.js 脚本时使用**绝对路径**
|
|
22
21
|
|
|
@@ -11,16 +11,15 @@ tags: [worktree, isolation, worktree, git-worktree]
|
|
|
11
11
|
|
|
12
12
|
**禁止**用 `ls` / `find` / `cat` / `head` / `grep` 等 bash 命令搜索 SKILL.md 是否存在。**这些命令在 Agent 运行环境(Qoder Windows PowerShell 等)中经常失败**(如 `head` 不存在、stderr 重定向无效等)。
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Claude Code 适配器:`.claude/skills/cbb-worktree-init/SKILL.md`
|
|
14
|
+
**直接路径**(各工具统一,worktree 运行时部署在空间根 `.cbb/skills/`):
|
|
15
|
+
- `.cbb/skills/cbb-worktree-init/SKILL.md`
|
|
17
16
|
|
|
18
17
|
**第一步**:
|
|
19
|
-
1. 用 Read 工具直接打开 `.
|
|
18
|
+
1. 用 Read 工具直接打开 `.cbb/skills/cbb-worktree-init/SKILL.md`
|
|
20
19
|
2. 按 SKILL.md 的 Step 0 → 1 → 2 → 3 → 4 顺序执行
|
|
21
20
|
3. 每次调用 Node.js 脚本时,使用**绝对路径**(避免依赖相对路径):
|
|
22
21
|
```
|
|
23
|
-
node "D:\workspace\<用户工作空间>\.
|
|
22
|
+
node "D:\workspace\<用户工作空间>\.cbb\skills\cbb-worktree-init\scripts\<name>.js" <参数>
|
|
24
23
|
```
|
|
25
24
|
- 参数形式:
|
|
26
25
|
- `check-env.js`:无参数
|
|
@@ -16,7 +16,7 @@ tags: [workflow, push, commit, worktree, experimental]
|
|
|
16
16
|
- 开发过程中想保存中间进度
|
|
17
17
|
- 用户口语化说"提交并push" / "推一下"
|
|
18
18
|
|
|
19
|
-
**Steps
|
|
19
|
+
**Steps 摘要**(详细流程见空间根 `.cbb/skills/cbb-worktree-push/SKILL.md`):
|
|
20
20
|
|
|
21
21
|
1. **Step 0**:定位工作空间根(find-workspace-root.js)
|
|
22
22
|
2. **Step 1**:识别目标 worktree(find-target-worktree.js)
|
|
@@ -34,7 +34,7 @@ metadata:
|
|
|
34
34
|
### 0.1 环境健康探活
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
node "D:/workspace/<ws_root>/.
|
|
37
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/check-env.js"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
**解读输出**:
|
|
@@ -45,7 +45,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-env.
|
|
|
45
45
|
### 0.2 定位工作空间根目录
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
node "D:/workspace/<ws_root>/.
|
|
48
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/find-workspace-root.js"
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
**解读输出**:
|
|
@@ -69,7 +69,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-works
|
|
|
69
69
|
3. AI agent 列出 `.worktrees/` 下的所有 worktree:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
node "D:/workspace/<ws_root>/.
|
|
72
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/find-target-worktree.js "<ws_root>"
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
> 注:本技能支持工作空间根目录或 worktree 内执行,find-target-worktree 自动从 cwd 反推或列出。
|
|
@@ -84,7 +84,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-targe
|
|
|
84
84
|
### 1.2 发现关联应用 worktree
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
node "D:/workspace/<ws_root>/.
|
|
87
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/discover-apps.js "<ws_root>" "<branch>"
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
脚本自动从 `workspace-config.json` 读取应用列表,扫描 `.worktrees/worktree-<branch>/<应用名>/` 实际存在的子目录。
|
|
@@ -98,7 +98,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/discover-a
|
|
|
98
98
|
### 1.3 检查未归档 change
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
|
-
node "D:/workspace/<ws_root>/.
|
|
101
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/check-unarchived.js "<ws_root>"
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
脚本扫描 `openspec/changes/` 目录,筛选出非 `archive/` 子目录的 change 名称列表。
|
|
@@ -117,7 +117,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-unar
|
|
|
117
117
|
对 workspace worktree 和每个关联应用 worktree,依次执行三项检查并汇总成安全状态表。**这是破坏性操作前的必经环节,结果将驱动 Step 3 的用户决策。**
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
node "D:/workspace/<ws_root>/.
|
|
120
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/safety-check.js "<ws_root>" "<branch>"
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
脚本内部对每个仓库自动执行:
|
|
@@ -289,7 +289,7 @@ node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
|
|
|
289
289
|
**顺序:先移除各应用 worktree,再移除 workspace worktree**(逆序于创建;也为后续删分支扫清"分支仍被检出"的障碍)。
|
|
290
290
|
|
|
291
291
|
```bash
|
|
292
|
-
node "D:/workspace/<ws_root>/.
|
|
292
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-close/scripts/remove-worktrees.js "<ws_root>" "<branch>" "<force>"
|
|
293
293
|
```
|
|
294
294
|
|
|
295
295
|
**三层降级策略**:`git worktree remove` 实际包含两个动作——①清理 git 元数据(`.git/worktrees/`)②删除物理目录。当 IDE 占用目录时 ② 会被 Windows 文件锁阻塞,但 ① 永远不受影响。脚本将这两个动作解耦:
|
|
@@ -28,11 +28,11 @@ metadata:
|
|
|
28
28
|
|
|
29
29
|
## 调用脚本的统一方式
|
|
30
30
|
|
|
31
|
-
**重要**:所有脚本必须用**绝对路径**调用。`./scripts/...` 相对路径在 Agent 运行环境中不适用(AI agent 的 cwd 不一定是技能目录)。
|
|
31
|
+
**重要**:所有脚本必须用**绝对路径**调用。`./scripts/...` 相对路径在 Agent 运行环境中不适用(AI agent 的 cwd 不一定是技能目录)。worktree 运行时统一部署在空间根 `.cbb/skills/` 下,各工具一致。
|
|
32
32
|
|
|
33
33
|
调用方式(推荐用绝对路径):
|
|
34
34
|
```
|
|
35
|
-
node "D:\workspace\<用户工作空间>\.
|
|
35
|
+
node "D:\workspace\<用户工作空间>\.cbb\skills\cbb-worktree-init\scripts\<name>.js" <参数>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
脚本输出:
|
|
@@ -54,7 +54,7 @@ node "D:\workspace\<用户工作空间>\.qoder\skills\cbb-worktree-init\scripts\
|
|
|
54
54
|
### 0.1 环境健康探活
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
node "D:/workspace/<ws_root>/.
|
|
57
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/check-env.js"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
**解读输出**:
|
|
@@ -67,7 +67,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/check-env.j
|
|
|
67
67
|
提前检测权限/版本问题,把可能在后续步骤暴露的失败前移到流程最开始:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
node "D:/workspace/<ws_root>/.
|
|
70
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/check-env-deep.js"
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**解读输出**:
|
|
@@ -81,7 +81,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/check-env-d
|
|
|
81
81
|
### 0.2 读取并校验 workspace-config.json
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
node "D:/workspace/<ws_root>/.
|
|
84
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/parse-config.js"
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
**解读输出**:
|
|
@@ -148,7 +148,7 @@ worktree 需要知道要关联哪些应用仓库。
|
|
|
148
148
|
|
|
149
149
|
```bash
|
|
150
150
|
# ws_root 来自 AI agent 维护的状态
|
|
151
|
-
node "D:/workspace/<ws_root>/.
|
|
151
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/sync-repos.js" "<ws_root>"
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
**用法**:
|
|
@@ -192,7 +192,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/sync-repos.
|
|
|
192
192
|
| 当前在主分支上 | ✅ | ❌ |
|
|
193
193
|
|
|
194
194
|
```bash
|
|
195
|
-
node "D:/workspace/<ws_root>/.
|
|
195
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/check-repos.js" "<ws_root>"
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
**解读输出**:
|
|
@@ -220,7 +220,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/check-repos
|
|
|
220
220
|
### 3.2 安全更新 .gitignore(前置)
|
|
221
221
|
|
|
222
222
|
```bash
|
|
223
|
-
node "D:/workspace/<ws_root>/.
|
|
223
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/update-gitignore.js "<ws_root>" "<branch>"
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
脚本内部自动完成:stash 保护 → 切分支 → 添加 `.worktrees/` 和 `.codespace/` → 提交 → 切回 → pop stash
|
|
@@ -234,7 +234,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/update-giti
|
|
|
234
234
|
### 3.3 创建分支
|
|
235
235
|
|
|
236
236
|
```bash
|
|
237
|
-
node "D:/workspace/<ws_root>/.
|
|
237
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/create-branches.js" "<ws_root>" "<branch>"
|
|
238
238
|
```
|
|
239
239
|
|
|
240
240
|
**解读输出**:
|
|
@@ -259,7 +259,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/create-bran
|
|
|
259
259
|
### 3.4 推送并建立 upstream
|
|
260
260
|
|
|
261
261
|
```bash
|
|
262
|
-
node "D:/workspace/<ws_root>/.
|
|
262
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/push-branches.js" "<ws_root>" "<branch>"
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
脚本内部自动完成:
|
|
@@ -287,7 +287,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-init/scripts/push-branch
|
|
|
287
287
|
### 3.5 创建 Worktree(含自愈清理)
|
|
288
288
|
|
|
289
289
|
```bash
|
|
290
|
-
node "D:/workspace/<ws_root>/.
|
|
290
|
+
node "D:/workspace/<ws_root>/.cbb/skills/cbb-worktree-init/scripts/create-worktrees.js" "<ws_root>" "<branch>"
|
|
291
291
|
```
|
|
292
292
|
|
|
293
293
|
脚本内部自动完成:
|
|
@@ -467,7 +467,7 @@ WT_PATH d:\workspace\xxx\.worktrees\worktree-feature-login
|
|
|
467
467
|
- workspace 仓库标准检查(当前在主分支等),app 仓库轻量检查(fetch + `origin/main` 可用即可)
|
|
468
468
|
- app 仓库分支从 `origin/main` 创建(`git branch <需求名> ${REMOTE}/${MAIN}`),不依赖本地主分支状态
|
|
469
469
|
- 严格 Step 3.3 → Step 3.4 顺序(先统一创建分支,再统一创建 worktree;workspace 工作树先建,各应用按序补建)
|
|
470
|
-
- 所有脚本调用以 `node "<WS_ROOT>\.
|
|
470
|
+
- 所有脚本调用以 `node "<WS_ROOT>\.cbb\skills\cbb-worktree-init\scripts\<name>.js" "<绝对路径>" "<branch>"` 形式发起(apps 由脚本内部从 workspace-config.json 读取),**不依赖跨调用的 cwd**
|
|
471
471
|
- **Step 3.4 push 步骤必须执行且插入在 create-branches.js (3.3) 之后、create-worktrees.js (3.5) 之前** — 失败时阻塞整个 init-worktree 流程
|
|
472
472
|
- **Step 3.4 仅覆盖创建 worktree 时的空 commit** — 开发过程中产生的本地 commit(如 apply 任务实现)必须由用户在 worktree 中手动提交并调 `cbb-worktree-push` 推送;不允许依赖 Step 3.4 之后的自动推送(不存在)
|
|
473
473
|
|
|
@@ -33,7 +33,7 @@ metadata:
|
|
|
33
33
|
**所有脚本必须用绝对路径调用**(Agent 运行环境中 AI agent 的 cwd 不一定是技能目录)。
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
node "<ws_root>/.
|
|
36
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/<name>.js" <参数>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
脚本输出:
|
|
@@ -46,7 +46,7 @@ node "<ws_root>/.qoder/skills/cbb-worktree-push/scripts/<name>.js" <参数>
|
|
|
46
46
|
## Step 0:定位工作空间
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
node "<ws_root>/.
|
|
49
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/find-workspace-root.js"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
**解读末行**:
|
|
@@ -58,7 +58,7 @@ node "<ws_root>/.qoder/skills/cbb-worktree-push/scripts/find-workspace-root.js"
|
|
|
58
58
|
## Step 1:识别目标 worktree
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
node "<ws_root>/.
|
|
61
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/find-target-worktree.js" "<ws_root>"
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
**解读末行**:
|
|
@@ -72,7 +72,7 @@ node "<ws_root>/.qoder/skills/cbb-worktree-push/scripts/find-target-worktree.js"
|
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
74
|
# 仅 commit 阶段先做 dry-run 预览
|
|
75
|
-
node "<ws_root>/.
|
|
75
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/commit-worktrees.js" "<ws_root>" "<branch>" --message "preview" --dry-run
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
**解读末行 `COMMIT_RESULT`**:每项仓库的状态
|
|
@@ -119,7 +119,7 @@ AI agent 在执行真实 commit 前**必须**在对话中向用户确认 commit
|
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
121
|
# 统一信息
|
|
122
|
-
node "<ws_root>/.
|
|
122
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/commit-worktrees.js" "<ws_root>" "<branch>" --message "feat(<branch>): 实现客户运费逻辑"
|
|
123
123
|
|
|
124
124
|
# 多仓库分别
|
|
125
125
|
node "..." "<ws_root>" "<branch>" --per-repo "ep-foo-app:feat(<branch>): 实现 saveClientExpectFreight;ep-bar-app:refactor(<branch>): 拆分 PlanService"
|
|
@@ -155,7 +155,7 @@ node "..." "<ws_root>/.worktrees/worktree-<branch>" --message "..."
|
|
|
155
155
|
## Step 5:批量 push
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
|
-
node "<ws_root>/.
|
|
158
|
+
node "<ws_root>/.cbb/skills/cbb-worktree-push/scripts/push-worktrees.js" "<ws_root>" "<branch>"
|
|
159
159
|
|
|
160
160
|
# 或 wt_path 形式
|
|
161
161
|
node "..." "<ws_root>/.worktrees/worktree-<branch>"
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
| `.codespace/` | 各关联应用的基准代码(每个应用一个子目录) | `cbb setup` 与 worktree 流程自动 clone / fetch;**勿手动编辑**;已 gitignore,不提交 |
|
|
21
21
|
| `.worktrees/` | 需求隔离开发区(每个需求一个 `worktree-<需求名>/` 目录) | worktree 命令自动创建 / 清理;不提交(首次执行 worktree 流程时自动加入 .gitignore) |
|
|
22
22
|
| `openspec/` | OpenSpec 工作流配置(`config.yaml` + `schemas/`) | 由 cbb 安装;需求变更产物(`openspec/changes/` 等)在**需求工作树内**生成,随需求分支提交 |
|
|
23
|
-
| `.claude/` `.qoder/` `.opencode/` | AI 工具适配目录:编码规范规则、OpenSpec 命令、worktree
|
|
24
|
-
| `.cbb/` | cbb
|
|
23
|
+
| `.claude/` `.qoder/` `.opencode/` | AI 工具适配目录:编码规范规则、OpenSpec 命令、worktree 管理命令 | 由 cbb 按 setup 时选择的工具安装;已 gitignore,不提交,勿手动改 |
|
|
24
|
+
| `.cbb/` | cbb 安装清单 + worktree 运行时(`.cbb/skills/`:流程文档与脚本,各工具命令共用) | 由 cbb 管理;**勿手动编辑** |
|
|
25
25
|
|
|
26
26
|
## 典型流程
|
|
27
27
|
|