@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.
@@ -68,8 +68,8 @@ const SUPERPOWERS_WHITELIST = [
68
68
  'test-driven-development', // TDD 计划结构 + TDD Evidence 方法论基底
69
69
  ];
70
70
 
71
- // worktree 扩展 skills 部署白名单:init / close / push 三件套
72
- // (单层安装:业务空间根是唯一安装点,worktree 管理入口 init 同样部署在根)
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/rules/*.md glob 登记进项目根
319
- * opencode.json 的 instructions(官方支持的加载方式)。
318
+ * opencode 无原生 rules 目录:把 rules glob 登记进 .opencode/opencode.json
319
+ * instructions(opencode V2 支持的配置位置;随 .opencode gitignore,按机器生成)。
320
+ * instructions 相对路径的解析基准(配置文件目录 vs 项目根)文档未写死,
321
+ * 故登记双 glob 对冲:任一基准下恰有一个命中,另一个空匹配。
320
322
  * - 文件不存在:创建(记入清单,卸载时整体删除)
321
- * - 文件存在:仅追加 pattern(幂等);若清单显示该文件本由本包创建,则续登
322
- * newPaths 防止升级清理误删;JSONC(含注释)解析失败则只提示不改写
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, 'opencode.json');
326
- const pattern = adapter.instructionsPattern;
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
- const cfg = { $schema: 'https://opencode.ai/config.json', instructions: [pattern] };
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('opencode.json');
331
- log(`生成 opencode.json 并登记 rules 加载(instructions: ${pattern})`, 'success');
332
- return;
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(`opencode.json 解析失败(可能含注释),未自动登记。请手动在 instructions 中加入 "${pattern}"`, 'warn');
379
+ log('根 opencode.json 解析失败(可能含注释),未自动迁移旧登记。可手动移除 instructions 中的 "' + legacyPattern + '"', 'warn');
339
380
  return;
340
381
  }
341
- const list = Array.isArray(cfg.instructions) ? cfg.instructions : [];
342
- if (!list.includes(pattern)) {
343
- cfg.instructions = [...list, pattern];
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(`已在 opencode.json instructions 中登记 ${pattern}`, 'success');
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 pattern(用户创建的 opencode.json 只删 pattern 不删文件) */
390
+ /** 卸载时移除本包登记的 instructions(用户自建配置只摘本包 glob,不删文件) */
352
391
  function removeInstructionsRegistrations() {
353
392
  adapters.forEach(adapter => {
354
- if (!adapter.instructionsPattern) return;
355
- const cfgPath = path.join(TARGET_DIR, 'opencode.json');
356
- if (!fs.existsSync(cfgPath)) return;
357
- let cfg;
358
- try {
359
- cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
360
- } catch (_) {
361
- return; // 不可解析的文件不动
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
- if (Array.isArray(cfg.instructions) && cfg.instructions.includes(adapter.instructionsPattern)) {
364
- cfg.instructions = cfg.instructions.filter(p => p !== adapter.instructionsPattern);
365
- if (cfg.instructions.length === 0) delete cfg.instructions;
366
- fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
367
- log('opencode.json: 移除本包登记的 instructions,保留用户配置', 'success');
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 登记进项目根 opencode.json instructions
875
- if (adapter.instructionsPattern) {
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, osCount = 0, extCount = 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 注册进项目根 opencode.json 的 instructions 字段(官方支持的加载方式)
9
- * - skills:.opencode/skills/<name>/SKILL.md,结构与 Claude 同构
10
- * - commands:命令名 = 文件名(嵌套目录无命名空间,且 init.md 等会撞内置 /init),
11
- * 因此扁平化为 opsx-<name>.md(/opsx-propose);worktree 管理命令同理扁平化为
12
- * cbb-<name>.md(/cbb-worktree-init),部署副本中的 /opsx: 与 /cbb: 引用同步改写
13
- * - hooks:opencode UserPromptSubmit 等价事件,版本检查 hook 不安装(settingsFile 留空即跳过)
14
- */
15
-
16
- module.exports = {
17
- name: 'opencode',
18
- displayName: 'opencode',
19
- rulesDir: '.opencode/rules',
20
- skillsDir: '.opencode/skills',
21
- commandsDir: '.opencode/commands',
22
- // 部署时给命令文件名加此前缀(规避内置命令名冲突)
23
- commandPrefix: 'opsx-',
24
- // worktree 管理命令(非 openspec 上游)落在同一扁平目录,用 cbb- 前缀区分
25
- worktreeCommandsDir: '.opencode/commands',
26
- worktreeCommandPrefix: 'cbb-',
27
- // 部署副本中将该引用串改写为 opencode 的命令风格(配合 commandPrefix)
28
- contentRewrites: [['/opsx:', '/opsx-'], ['/cbb:', '/cbb-']],
29
- // rules 加载登记:注入项目根 opencode.json 的 instructions(glob,相对项目根)
30
- instructionsPattern: '.opencode/rules/*.md',
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
- - Qoder 适配器:`.qoder/skills/cbb-worktree-close/SKILL.md`
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
- - Qoder 适配器:`.qoder/skills/cbb-worktree-init/SKILL.md`
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 工具直接打开 `.qoder/skills/cbb-worktree-init/SKILL.md`(或 `.claude/skills/...`)
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\<用户工作空间>\.qoder\skills\cbb-worktree-init\scripts\<name>.js" <参数>
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 摘要**(详细见 [cbb-worktree-push SKILL.md](../../skills/cbb-worktree-push/SKILL.md)):
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>/.qoder/skills/cbb-worktree-close/scripts/check-env.js"
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>/.qoder/skills/cbb-worktree-close/scripts/find-workspace-root.js"
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>/.qoder/skills/cbb-worktree-close/scripts/find-target-worktree.js "<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>/.qoder/skills/cbb-worktree-close/scripts/discover-apps.js "<ws_root>" "<branch>"
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>/.qoder/skills/cbb-worktree-close/scripts/check-unarchived.js "<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>/.qoder/skills/cbb-worktree-close/scripts/safety-check.js "<ws_root>" "<branch>"
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>/.qoder/skills/cbb-worktree-close/scripts/remove-worktrees.js "<ws_root>" "<branch>" "<force>"
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\<用户工作空间>\.qoder\skills\cbb-worktree-init\scripts\<name>.js" <参数>
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>/.qoder/skills/cbb-worktree-init/scripts/check-env.js"
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>/.qoder/skills/cbb-worktree-init/scripts/check-env-deep.js"
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>/.qoder/skills/cbb-worktree-init/scripts/parse-config.js"
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>/.qoder/skills/cbb-worktree-init/scripts/sync-repos.js" "<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>/.qoder/skills/cbb-worktree-init/scripts/check-repos.js" "<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>/.qoder/skills/cbb-worktree-init/scripts/update-gitignore.js "<ws_root>" "<branch>"
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>/.qoder/skills/cbb-worktree-init/scripts/create-branches.js" "<ws_root>" "<branch>"
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>/.qoder/skills/cbb-worktree-init/scripts/push-branches.js" "<ws_root>" "<branch>"
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>/.qoder/skills/cbb-worktree-init/scripts/create-worktrees.js" "<ws_root>" "<branch>"
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>\.qoder\skills\cbb-worktree-init\scripts\<name>.js" "<绝对路径>" "<branch>"` 形式发起(apps 由脚本内部从 workspace-config.json 读取),**不依赖跨调用的 cwd**
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>/.qoder/skills/cbb-worktree-push/scripts/<name>.js" <参数>
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>/.qoder/skills/cbb-worktree-push/scripts/find-workspace-root.js"
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>/.qoder/skills/cbb-worktree-push/scripts/find-target-worktree.js" "<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>/.qoder/skills/cbb-worktree-push/scripts/commit-worktrees.js" "<ws_root>" "<branch>" --message "preview" --dry-run
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>/.qoder/skills/cbb-worktree-push/scripts/commit-worktrees.js" "<ws_root>" "<branch>" --message "feat(<branch>): 实现客户运费逻辑"
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>/.qoder/skills/cbb-worktree-push/scripts/push-worktrees.js" "<ws_root>" "<branch>"
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 管理技能与命令 | 由 cbb 按 setup 时选择的工具安装;已 gitignore,不提交,勿手动改 |
24
- | `.cbb/` | 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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jspg-ai/coding-bb",
3
- "version": "0.0.3-beta.2",
3
+ "version": "0.0.3-beta.4",
4
4
  "description": "整合业界热门且高价值的工具、框架与技能,为 AI CODING AGENT 提供统一的行为准则与工作流,辅助开发者将需求高效落地为符合规范的代码",
5
5
  "main": "cbb/lib/install/init.js",
6
6
  "bin": {