jsharness 1.10.0 → 1.11.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.
Files changed (2) hide show
  1. package/lib/index.mjs +77 -19
  2. package/package.json +1 -1
package/lib/index.mjs CHANGED
@@ -107,7 +107,7 @@ export const SUPPORTED_TOOLS = [
107
107
  priority: 88,
108
108
  ruleFormat: 'markdown',
109
109
  skillFormat: 'markdown',
110
- commandFormat: 'markdown',
110
+ commandFormat: null,
111
111
  subagentFormat: 'md',
112
112
  },
113
113
  ];
@@ -345,6 +345,30 @@ const SKILL_TRANSFORMERS = {
345
345
  return { format: 'reference', files: outputs };
346
346
  },
347
347
 
348
+ copilot(skillFiles, opts) {
349
+ // Copilot 不支持独立 skills 目录,合并为 Markdown 段落
350
+ let content = '';
351
+ for (const file of skillFiles) {
352
+ const raw = fs.readFileSync(file.path, 'utf-8');
353
+ const skillName = inferSkillName(file);
354
+ const description = parseFrontmatter(raw).description || extractDescription(raw) || skillName;
355
+ content += `### ${skillName}\n\n> ${description}\n\n${extractBody(raw)}\n\n`;
356
+ }
357
+ return { format: 'merged-section', sectionName: 'Skills', content };
358
+ },
359
+
360
+ codex(skillFiles, opts) {
361
+ // Codex CLI 不支持独立 skills 目录,合并为 Markdown 段落
362
+ let content = '';
363
+ for (const file of skillFiles) {
364
+ const raw = fs.readFileSync(file.path, 'utf-8');
365
+ const skillName = inferSkillName(file);
366
+ const description = parseFrontmatter(raw).description || extractDescription(raw) || skillName;
367
+ content += `### ${skillName}\n\n> ${description}\n\n${extractBody(raw)}\n\n`;
368
+ }
369
+ return { format: 'merged-section', sectionName: 'Skills', content };
370
+ },
371
+
348
372
  qoder(skillFiles, opts) {
349
373
  const outputs = [];
350
374
  for (const file of skillFiles) {
@@ -492,16 +516,15 @@ const COMMAND_TRANSFORMERS = {
492
516
  },
493
517
 
494
518
  augment(commandFiles, opts) {
495
- const outputs = [];
519
+ // Augment 不支持独立 commands 目录,合并为 Markdown 段落
520
+ let content = '';
496
521
  for (const file of commandFiles) {
497
522
  const raw = fs.readFileSync(file.path, 'utf-8');
498
523
  const cmdName = inferCommandName(file);
499
- const existingFrontmatter = parseFrontmatter(raw);
500
- const description = existingFrontmatter.description || extractDescription(raw) || cmdName;
501
- let content = `---\nname: ${cmdName}\ndescription: ${description}\ntrigger_type: manual\n---\n\n${extractBody(raw)}`;
502
- outputs.push({ relativePath: `.augment/skills/${cmdName}/SKILL.md`, content, source: file.path });
524
+ const description = parseFrontmatter(raw).description || extractDescription(raw) || cmdName;
525
+ content += `### ${cmdName}\n\n> ${description}\n\n${extractBody(raw)}\n\n`;
503
526
  }
504
- return { format: 'directory', files: outputs };
527
+ return { format: 'merged-section', sectionName: 'Commands', content };
505
528
  },
506
529
 
507
530
  copilot(commandFiles, opts) {
@@ -928,6 +951,30 @@ function inferAgentName(file) {
928
951
  return file.name.replace(/\.md$/, '') || 'unknown';
929
952
  }
930
953
 
954
+ /**
955
+ * 将 command 内容转为 skill 格式输出(用于不支持 commands 目录的工具)
956
+ * 根据工具类型决定输出路径和 frontmatter 格式
957
+ */
958
+ function buildCommandAsSkill(toolId, cmdName, description, body) {
959
+ const skillPaths = {
960
+ 'claude-code': `.claude/skills/cmd-${cmdName}/SKILL.md`,
961
+ augment: `.augment/skills/cmd-${cmdName}/SKILL.md`,
962
+ };
963
+ const relativePath = skillPaths[toolId] || `.harness/skills/cmd-${cmdName}/SKILL.md`;
964
+
965
+ // 根据工具类型构建不同的 frontmatter
966
+ let frontmatter;
967
+ if (toolId === 'claude-code') {
968
+ frontmatter = `---\nname: cmd-${cmdName}\ndescription: ${description}\ntrigger_type: manual\n---`;
969
+ } else if (toolId === 'augment') {
970
+ frontmatter = `---\nname: cmd-${cmdName}\ndescription: ${description}\n---`;
971
+ } else {
972
+ frontmatter = `---\nname: cmd-${cmdName}\ndescription: ${description}\nalwaysApply: false\nenabled: true\n---`;
973
+ }
974
+
975
+ return { relativePath, content: `${frontmatter}\n\n${body}` };
976
+ }
977
+
931
978
  /**
932
979
  * 从 frontmatter 和原始内容构建 agent 元数据
933
980
  * 统一提取 name/description/tools/model/agentMode/enabled/enabledAutoRun/maxTurns/scope/safetyLevel
@@ -1683,10 +1730,13 @@ export async function runInit(projectDir, options = {}) {
1683
1730
  // 不支持独立 skills 的工具:合并到 rules 输出
1684
1731
  console.log(`⚡ 合并技能到规则 (${allSkills.length} 个)...`);
1685
1732
  const skillMerged = transformSkills(allSkills, tool.id, transformOpts);
1686
- if (skillMerged.files && skillMerged.files.length > 0) {
1687
- // skills 内容合并到 rules 的单文件中
1688
- const rulesOutput = outputs.find(o => o.relativePath === getRulesOutputPath(tool.id));
1689
- if (rulesOutput) {
1733
+ const rulesOutput = outputs.find(o => o.relativePath === getRulesOutputPath(tool.id));
1734
+ if (rulesOutput) {
1735
+ if (skillMerged.format === 'merged-section') {
1736
+ // merged-section 格式:直接拼接 sectionName + content
1737
+ rulesOutput.content += `\n---\n\n## ${skillMerged.sectionName}\n\n${skillMerged.content}`;
1738
+ } else if (skillMerged.files && skillMerged.files.length > 0) {
1739
+ // directory/reference 格式:逐个拼接 body 内容
1690
1740
  rulesOutput.content += '\n---\n\n## Skills\n\n';
1691
1741
  for (const sf of skillMerged.files) {
1692
1742
  rulesOutput.content += sf.content + '\n\n';
@@ -1702,21 +1752,29 @@ export async function runInit(projectDir, options = {}) {
1702
1752
  console.log(`🔧 注入命令 (${allCommandFiles.length} 个)...`);
1703
1753
  outputs.push(...transformCommands(allCommandFiles, tool.id, transformOpts).files);
1704
1754
  } else {
1705
- // 不支持独立 commands 的工具:合并到 rules 输出
1706
- console.log(`🔧 合并命令到规则 (${allCommandFiles.length} 个)...`);
1755
+ // 不支持独立 commands 的工具
1756
+ console.log(`🔧 合并命令 (${allCommandFiles.length} 个)...`);
1707
1757
  const cmdResult = transformCommands(allCommandFiles, tool.id, transformOpts);
1708
1758
  if (cmdResult.format === 'merged-section') {
1759
+ // merged-section:尝试合并到 rules 单文件
1709
1760
  const rulesOutput = outputs.find(o => o.relativePath === getRulesOutputPath(tool.id));
1710
1761
  if (rulesOutput) {
1711
1762
  rulesOutput.content += `\n---\n\n## ${cmdResult.sectionName}\n\n${cmdResult.content}`;
1712
- }
1713
- } else if (cmdResult.files && cmdResult.files.length > 0) {
1714
- const rulesOutput = outputs.find(o => o.relativePath === getRulesOutputPath(tool.id));
1715
- if (rulesOutput) {
1716
- for (const cf of cmdResult.files) {
1717
- rulesOutput.content += `\n---\n\n## Command Checks\n\n${cf.content}`;
1763
+ } else {
1764
+ // rules directory 格式,无法合并到单文件 转为独立 skill 文件输出
1765
+ console.log(` 📎 命令转存为技能文件...`);
1766
+ for (const file of allCommandFiles) {
1767
+ const raw = fs.readFileSync(file.path, 'utf-8');
1768
+ const cmdName = inferCommandName(file);
1769
+ const description = parseFrontmatter(raw).description || extractDescription(raw) || cmdName;
1770
+ const body = extractBody(raw);
1771
+ const cmdAsSkill = buildCommandAsSkill(tool.id, cmdName, description, body);
1772
+ outputs.push(cmdAsSkill);
1718
1773
  }
1719
1774
  }
1775
+ } else if (cmdResult.files && cmdResult.files.length > 0) {
1776
+ // directory 格式:直接作为独立文件输出
1777
+ outputs.push(...cmdResult.files);
1720
1778
  }
1721
1779
  }
1722
1780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsharness",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Harness Engineering - AI 编程行为工程化管控系统。将 rules/skills/commands/agents 四桶一键注入到 CodeBuddy、Cursor、Copilot、Claude Code 等 AI 工具中。",
5
5
  "main": "lib/index.mjs",
6
6
  "bin": {