cc-devflow 2.4.6 → 4.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.
Files changed (191) hide show
  1. package/.claude/CLAUDE.md +1065 -48
  2. package/.claude/agents/dev-implementer.md +195 -0
  3. package/.claude/commands/{flow-archive.md → flow/archive.md} +46 -11
  4. package/.claude/commands/flow/context.md +150 -0
  5. package/.claude/commands/flow/delta.md +245 -0
  6. package/.claude/commands/{flow-dev.md → flow/dev.md} +112 -11
  7. package/.claude/commands/flow/init.md +45 -0
  8. package/.claude/commands/flow/quality.md +159 -0
  9. package/.claude/commands/flow/spec.md +186 -0
  10. package/.claude/commands/flow/workspace.md +146 -0
  11. package/.claude/commands/{cancel-ralph.md → util/cancel-ralph.md} +1 -0
  12. package/.claude/config/quality-gates.yml +305 -0
  13. package/.claude/docs/guides/TEAM_MODE_GUIDE.md +313 -0
  14. package/.claude/docs/templates/DELTA_SPEC_TEMPLATE.md +91 -0
  15. package/.claude/docs/templates/DESIGN_DECISIONS_TEMPLATE.md +151 -0
  16. package/.claude/docs/templates/JOURNAL_TEMPLATE.md +75 -0
  17. package/.claude/docs/templates/_shared/CLAUDE.md +36 -0
  18. package/.claude/docs/templates/_shared/CONSTITUTION_CHECK.md +125 -0
  19. package/.claude/docs/templates/_shared/VALIDATION_CHECKLIST.md +187 -0
  20. package/.claude/docs/templates/_shared/YAML_FRONTMATTER.md +164 -0
  21. package/.claude/docs/templates/context/dev.jsonl.template +6 -0
  22. package/.claude/docs/templates/context/epic.jsonl.template +5 -0
  23. package/.claude/docs/templates/context/prd.jsonl.template +4 -0
  24. package/.claude/docs/templates/context/research.jsonl.template +4 -0
  25. package/.claude/docs/templates/context/review.jsonl.template +5 -0
  26. package/.claude/docs/templates/context/tech.jsonl.template +5 -0
  27. package/.claude/hooks/CLAUDE.md +342 -0
  28. package/.claude/hooks/inject-agent-context.ts +480 -0
  29. package/.claude/hooks/inject-skill-context.ts +359 -0
  30. package/.claude/hooks/ralph-loop.ts +931 -0
  31. package/.claude/hooks/task-completed-hook.ts +593 -0
  32. package/.claude/hooks/teammate-idle-hook.ts +690 -0
  33. package/.claude/hooks/types/team-types.d.ts +238 -0
  34. package/.claude/rules/devflow-conventions.md +82 -9
  35. package/.claude/scripts/archive-requirement.sh +44 -1
  36. package/.claude/scripts/common.sh +670 -3
  37. package/.claude/scripts/delta-parser.ts +527 -0
  38. package/.claude/scripts/detect-file-conflicts.sh +151 -0
  39. package/.claude/scripts/flow-context-add.sh +134 -0
  40. package/.claude/scripts/flow-context-init.sh +133 -0
  41. package/.claude/scripts/flow-context-validate.sh +144 -0
  42. package/.claude/scripts/flow-delta-apply.sh +297 -0
  43. package/.claude/scripts/flow-delta-archive.sh +71 -0
  44. package/.claude/scripts/flow-delta-create.sh +202 -0
  45. package/.claude/scripts/flow-delta-list.sh +142 -0
  46. package/.claude/scripts/flow-delta-status.sh +235 -0
  47. package/.claude/scripts/flow-quality-full.sh +184 -0
  48. package/.claude/scripts/flow-quality-quick.sh +64 -0
  49. package/.claude/scripts/flow-workspace-init.sh +117 -0
  50. package/.claude/scripts/flow-workspace-record.sh +164 -0
  51. package/.claude/scripts/flow-workspace-start.sh +88 -0
  52. package/.claude/scripts/get-workflow-status.sh +415 -0
  53. package/.claude/scripts/parse-task-dependencies.js +334 -0
  54. package/.claude/scripts/record-quality-error.sh +165 -0
  55. package/.claude/scripts/run-quality-gates.sh +242 -0
  56. package/.claude/scripts/team-dev-init.sh +319 -0
  57. package/.claude/scripts/team-state-recovery.sh +229 -0
  58. package/.claude/scripts/workflow-status.ts +433 -0
  59. package/.claude/settings.json +19 -0
  60. package/.claude/skills/cc-devflow-orchestrator/SKILL.md +85 -200
  61. package/.claude/skills/domain/using-git-worktrees/SKILL.md +252 -0
  62. package/.claude/skills/domain/using-git-worktrees/assets/SHELL_ALIASES.md +133 -0
  63. package/.claude/skills/domain/using-git-worktrees/context.jsonl +4 -0
  64. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-cleanup.sh +218 -0
  65. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-create.sh +232 -0
  66. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-list.sh +130 -0
  67. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-status.sh +140 -0
  68. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-switch.sh +70 -0
  69. package/.claude/skills/skill-rules.json +72 -1
  70. package/.claude/skills/utility/journey-checker/SKILL.md +199 -0
  71. package/.claude/skills/utility/journey-checker/pressure-scenarios.md +164 -0
  72. package/.claude/skills/utility/skill-creator/LICENSE.txt +202 -0
  73. package/.claude/skills/utility/skill-creator/SKILL.md +356 -0
  74. package/.claude/skills/utility/skill-creator/references/output-patterns.md +82 -0
  75. package/.claude/skills/utility/skill-creator/references/workflows.md +28 -0
  76. package/.claude/skills/utility/skill-creator/scripts/init_skill.py +303 -0
  77. package/.claude/skills/utility/skill-creator/scripts/package_skill.py +110 -0
  78. package/.claude/skills/utility/skill-creator/scripts/quick_validate.py +95 -0
  79. package/.claude/skills/workflow/flow-dev/CLAUDE.md +78 -0
  80. package/.claude/skills/workflow/flow-dev/SKILL.md +96 -0
  81. package/.claude/skills/workflow/flow-dev/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +71 -0
  82. package/.claude/skills/workflow/flow-dev/context.jsonl +8 -0
  83. package/.claude/skills/workflow/flow-dev/dev-implementer.jsonl +8 -0
  84. package/.claude/skills/workflow/flow-dev/scripts/entry-gate.sh +116 -0
  85. package/.claude/skills/workflow/flow-dev/scripts/exit-gate.sh +101 -0
  86. package/.claude/skills/workflow/flow-dev/scripts/task-orchestrator.sh +106 -0
  87. package/.claude/skills/workflow/flow-fix/SKILL.md +105 -0
  88. package/.claude/skills/workflow/flow-fix/context.jsonl +6 -0
  89. package/.claude/skills/workflow/flow-fix/references/bug-analyzer.md +381 -0
  90. package/.claude/skills/workflow/flow-init/SKILL.md +211 -0
  91. package/.claude/skills/workflow/flow-init/assets/BRAINSTORM_TEMPLATE.md +148 -0
  92. package/.claude/skills/workflow/flow-init/assets/INIT_FLOW_TEMPLATE.md +198 -0
  93. package/.claude/skills/workflow/flow-init/assets/RESEARCH_TEMPLATE.md +276 -0
  94. package/.claude/skills/workflow/flow-init/context.jsonl +5 -0
  95. package/.claude/skills/workflow/flow-init/references/flow-researcher.md +132 -0
  96. package/.claude/skills/workflow/flow-init/scripts/check-prerequisites.sh +232 -0
  97. package/.claude/skills/workflow/flow-init/scripts/consolidate-research.sh +182 -0
  98. package/.claude/skills/workflow/flow-init/scripts/create-requirement.sh +515 -0
  99. package/.claude/skills/workflow/flow-init/scripts/generate-research-tasks.sh +157 -0
  100. package/.claude/skills/workflow/flow-init/scripts/populate-research-tasks.sh +284 -0
  101. package/.claude/skills/workflow/flow-init/scripts/validate-research.sh +332 -0
  102. package/.claude/skills/workflow/flow-quality/SKILL.md +94 -0
  103. package/.claude/skills/workflow/flow-quality/context.jsonl +6 -0
  104. package/.claude/skills/workflow/flow-quality/references/code-quality-reviewer.md +205 -0
  105. package/.claude/skills/workflow/flow-quality/references/qa-tester.md +313 -0
  106. package/.claude/skills/workflow/flow-quality/references/security-reviewer.md +314 -0
  107. package/.claude/skills/workflow/flow-quality/references/spec-reviewer.md +221 -0
  108. package/.claude/skills/workflow/flow-release/SKILL.md +126 -0
  109. package/.claude/skills/workflow/flow-release/context.jsonl +7 -0
  110. package/.claude/skills/workflow/flow-release/references/release-manager.md +295 -0
  111. package/.claude/skills/workflow/flow-spec/CLAUDE.md +103 -0
  112. package/.claude/skills/workflow/flow-spec/SKILL.md +545 -0
  113. package/.claude/skills/workflow/flow-spec/context.jsonl +7 -0
  114. package/.claude/skills/workflow/flow-spec/scripts/entry-gate.sh +194 -0
  115. package/.claude/skills/workflow/flow-spec/scripts/exit-gate.sh +244 -0
  116. package/.claude/skills/workflow/flow-spec/scripts/parallel-orchestrator.sh +205 -0
  117. package/.claude/skills/workflow/flow-spec/scripts/team-communication.sh +353 -0
  118. package/.claude/skills/workflow/flow-spec/scripts/team-init.sh +195 -0
  119. package/.claude/skills/workflow/flow-spec/scripts/test-team-mode.sh +496 -0
  120. package/.claude/skills/workflow/flow-spec/team-config.json +165 -0
  121. package/.claude/skills/workflow.yaml +417 -0
  122. package/CHANGELOG.md +254 -0
  123. package/README.md +193 -33
  124. package/README.zh-CN.md +206 -46
  125. package/lib/compiler/CLAUDE.md +77 -46
  126. package/lib/compiler/__tests__/multi-module-emitters.test.js +508 -0
  127. package/lib/compiler/context-expander.js +179 -0
  128. package/lib/compiler/emitters/antigravity-emitter.js +195 -5
  129. package/lib/compiler/emitters/base-emitter.js +217 -2
  130. package/lib/compiler/emitters/codex-emitter.js +200 -4
  131. package/lib/compiler/emitters/cursor-emitter.js +307 -3
  132. package/lib/compiler/emitters/qwen-emitter.js +196 -4
  133. package/lib/compiler/index.js +197 -2
  134. package/lib/compiler/platforms.js +270 -21
  135. package/package.json +1 -1
  136. package/.claude/commands/flow-epic.md +0 -183
  137. package/.claude/commands/flow-init.md +0 -370
  138. package/.claude/commands/flow-prd.md +0 -144
  139. package/.claude/commands/flow-qa.md +0 -93
  140. package/.claude/commands/flow-review.md +0 -257
  141. package/.claude/commands/flow-tech.md +0 -142
  142. package/.claude/commands/flow-ui.md +0 -189
  143. package/.claude/skills/file-header-guardian/SKILL.md +0 -56
  144. package/.claude/skills/skill-developer/ADVANCED.md +0 -197
  145. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
  146. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
  147. package/.claude/skills/skill-developer/SKILL.md +0 -426
  148. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
  149. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
  150. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
  151. package/.claude/skills/writing-skills/SKILL.md +0 -655
  152. package/.claude/skills/writing-skills/anthropic-best-practices.md +0 -1150
  153. package/.claude/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
  154. package/.claude/skills/writing-skills/graphviz-conventions.dot +0 -172
  155. package/.claude/skills/writing-skills/persuasion-principles.md +0 -187
  156. package/.claude/skills/writing-skills/render-graphs.js +0 -168
  157. package/.claude/skills/writing-skills/testing-skills-with-subagents.md +0 -384
  158. package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +0 -1
  159. package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +0 -1
  160. /package/.claude/commands/{core-architecture.md → core/architecture.md} +0 -0
  161. /package/.claude/commands/{core-guidelines.md → core/guidelines.md} +0 -0
  162. /package/.claude/commands/{core-roadmap.md → core/roadmap.md} +0 -0
  163. /package/.claude/commands/{core-style.md → core/style.md} +0 -0
  164. /package/.claude/commands/{flow-checklist.md → flow/checklist.md} +0 -0
  165. /package/.claude/commands/{flow-clarify.md → flow/clarify.md} +0 -0
  166. /package/.claude/commands/{flow-constitution.md → flow/constitution.md} +0 -0
  167. /package/.claude/commands/{flow-fix.md → flow/fix.md} +0 -0
  168. /package/.claude/commands/{flow-ideate.md → flow/ideate.md} +0 -0
  169. /package/.claude/commands/{flow-new.md → flow/new.md} +0 -0
  170. /package/.claude/commands/{flow-release.md → flow/release.md} +0 -0
  171. /package/.claude/commands/{flow-restart.md → flow/restart.md} +0 -0
  172. /package/.claude/commands/{flow-status.md → flow/status.md} +0 -0
  173. /package/.claude/commands/{flow-update.md → flow/update.md} +0 -0
  174. /package/.claude/commands/{flow-upgrade.md → flow/upgrade.md} +0 -0
  175. /package/.claude/commands/{flow-verify.md → flow/verify.md} +0 -0
  176. /package/.claude/commands/{code-review-high.md → util/code-review.md} +0 -0
  177. /package/.claude/commands/{git-commit.md → util/git-commit.md} +0 -0
  178. /package/.claude/commands/{problem-analyzer.md → util/problem-analyzer.md} +0 -0
  179. /package/.claude/skills/{flow-attention-refresh → domain/attention-refresh}/SKILL.md +0 -0
  180. /package/.claude/skills/{flow-brainstorming → domain/brainstorming}/SKILL.md +0 -0
  181. /package/.claude/skills/{flow-debugging → domain/debugging}/SKILL.md +0 -0
  182. /package/.claude/skills/{flow-finishing-branch → domain/finishing-branch}/SKILL.md +0 -0
  183. /package/.claude/skills/{flow-receiving-review → domain/receiving-review}/SKILL.md +0 -0
  184. /package/.claude/skills/{flow-tdd → domain/tdd}/SKILL.md +0 -0
  185. /package/.claude/skills/{verification-before-completion → domain/verification}/SKILL.md +0 -0
  186. /package/.claude/skills/{constitution-guardian → guardrail/constitution-guardian}/SKILL.md +0 -0
  187. /package/.claude/skills/{devflow-tdd-enforcer → guardrail/tdd-enforcer}/SKILL.md +0 -0
  188. /package/.claude/skills/{devflow-constitution-quick-ref → utility/constitution-quick-ref}/SKILL.md +0 -0
  189. /package/.claude/skills/{devflow-file-standards → utility/file-standards}/SKILL.md +0 -0
  190. /package/.claude/skills/{fractal-docs-generator → utility/fractal-docs}/SKILL.md +0 -0
  191. /package/.claude/skills/{npm-release → utility/npm-release}/SKILL.md +0 -0
@@ -1,12 +1,25 @@
1
1
  /**
2
- * T032: CodexEmitter - Codex 平台输出
2
+ * T032: CodexEmitter - Codex 平台输出 (v2.0)
3
3
  *
4
- * 输出格式: Markdown + YAML frontmatter
5
- * 目录: .codex/prompts/
6
- * Frontmatter 字段: description, argument-hint
4
+ * [INPUT]: CommandIR, SKILL.md, agents/*.md, rules/*.md
5
+ * [OUTPUT]: .codex/prompts/*.md, .codex/skills/*, AGENTS.md
6
+ * [POS]: Codex CLI 平台编译器,支持完整功能模块
7
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
8
+ *
9
+ * 输出格式:
10
+ * - Commands: Markdown + YAML frontmatter → .codex/prompts/
11
+ * - Skills: SKILL.md (YAML frontmatter) → .codex/skills/
12
+ * - Agents: 合并到 AGENTS.md
13
+ * - Rules: 合并到 AGENTS.md
14
+ *
15
+ * v2.0: 支持多模块编译
7
16
  */
17
+ const fs = require('fs');
18
+ const path = require('path');
8
19
  const yaml = require('js-yaml');
20
+ const matter = require('gray-matter');
9
21
  const BaseEmitter = require('./base-emitter.js');
22
+ const { ContextExpander } = require('../context-expander.js');
10
23
 
11
24
  class CodexEmitter extends BaseEmitter {
12
25
  get name() {
@@ -47,6 +60,189 @@ class CodexEmitter extends BaseEmitter {
47
60
 
48
61
  return `---\n${yamlStr}---\n\n${transformedContent}`;
49
62
  }
63
+
64
+ // ----------------------------------------------------------
65
+ // Multi-Module Emit Methods (v2.0)
66
+ // ----------------------------------------------------------
67
+
68
+ /**
69
+ * 编译 Skills 模块
70
+ * .claude/skills/[name]/SKILL.md -> .codex/skills/[name]/SKILL.md
71
+ */
72
+ async emitSkills(sourceDir, targetDir) {
73
+ const results = [];
74
+
75
+ if (!fs.existsSync(sourceDir)) {
76
+ return results;
77
+ }
78
+
79
+ // 扫描技能分组目录 (workflow/, domain/, utility/, guardrail/)
80
+ const groupDirs = await fs.promises.readdir(sourceDir, { withFileTypes: true });
81
+
82
+ for (const groupEntry of groupDirs) {
83
+ if (!groupEntry.isDirectory() || groupEntry.name.startsWith('_')) {
84
+ continue;
85
+ }
86
+
87
+ const groupDir = path.join(sourceDir, groupEntry.name);
88
+ const skillEntries = await fs.promises.readdir(groupDir, { withFileTypes: true });
89
+
90
+ for (const skillEntry of skillEntries) {
91
+ if (!skillEntry.isDirectory() || skillEntry.name.startsWith('_')) {
92
+ continue;
93
+ }
94
+
95
+ const skillDir = path.join(groupDir, skillEntry.name);
96
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
97
+
98
+ if (!fs.existsSync(skillMdPath)) {
99
+ continue;
100
+ }
101
+
102
+ try {
103
+ const result = await this._emitSingleSkill(
104
+ skillEntry.name,
105
+ skillDir,
106
+ skillMdPath,
107
+ targetDir
108
+ );
109
+ results.push(result);
110
+ } catch (error) {
111
+ console.warn(`Warning: Failed to emit skill ${skillEntry.name}: ${error.message}`);
112
+ }
113
+ }
114
+ }
115
+
116
+ return results;
117
+ }
118
+
119
+ /**
120
+ * 编译单个 Skill
121
+ */
122
+ async _emitSingleSkill(skillName, skillDir, skillMdPath, targetDir) {
123
+ const content = await fs.promises.readFile(skillMdPath, 'utf8');
124
+ const parsed = matter(content);
125
+
126
+ // 展开 context.jsonl
127
+ const contextExpanded = ContextExpander.expandFromSkillDir(skillDir, 'codex');
128
+
129
+ // 确保 frontmatter 格式正确
130
+ const frontmatterData = {
131
+ name: parsed.data.name || skillName,
132
+ description: parsed.data.description || ''
133
+ };
134
+
135
+ const yamlStr = yaml.dump(frontmatterData, {
136
+ lineWidth: -1,
137
+ quotingType: '"',
138
+ forceQuotes: false
139
+ });
140
+
141
+ // 组合最终内容:frontmatter + context + body
142
+ let body = parsed.content;
143
+ if (contextExpanded) {
144
+ body = contextExpanded + '\n' + body;
145
+ }
146
+
147
+ const finalContent = `---\n${yamlStr}---\n\n${body}`;
148
+
149
+ // 输出到 .codex/skills/<skill-name>/SKILL.md
150
+ const targetSkillDir = path.join(targetDir, skillName);
151
+ const targetPath = path.join(targetSkillDir, 'SKILL.md');
152
+
153
+ const result = await this.emitToPath(targetPath, finalContent);
154
+
155
+ // 复制 scripts/ 和 references/ 目录
156
+ await this._copySkillResources(skillDir, targetSkillDir);
157
+
158
+ return { ...result, skillName };
159
+ }
160
+
161
+ /**
162
+ * 复制 Skill 资源目录
163
+ */
164
+ async _copySkillResources(sourceSkillDir, targetSkillDir) {
165
+ const resourceDirs = ['scripts', 'references', 'assets'];
166
+
167
+ for (const dir of resourceDirs) {
168
+ const sourceResDir = path.join(sourceSkillDir, dir);
169
+ const targetResDir = path.join(targetSkillDir, dir);
170
+
171
+ if (fs.existsSync(sourceResDir)) {
172
+ await this._copyDir(sourceResDir, targetResDir);
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 递归复制目录
179
+ */
180
+ async _copyDir(src, dest) {
181
+ await fs.promises.mkdir(dest, { recursive: true, mode: 0o755 });
182
+ const entries = await fs.promises.readdir(src, { withFileTypes: true });
183
+
184
+ for (const entry of entries) {
185
+ const srcPath = path.join(src, entry.name);
186
+ const destPath = path.join(dest, entry.name);
187
+
188
+ if (entry.isDirectory()) {
189
+ await this._copyDir(srcPath, destPath);
190
+ } else {
191
+ await fs.promises.copyFile(srcPath, destPath);
192
+ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 编译 Agents 模块
198
+ * .claude/agents/[name].md -> AGENTS.md (合并)
199
+ */
200
+ async emitAgents(sourceDir, targetPath) {
201
+ return this._defaultAgentsEmit(sourceDir, targetPath);
202
+ }
203
+
204
+ /**
205
+ * 编译 Rules 模块
206
+ * .claude/rules/[name].md -> AGENTS.md (追加)
207
+ */
208
+ async emitRules(sourceDir, targetPath) {
209
+ const results = [];
210
+
211
+ if (!fs.existsSync(sourceDir)) {
212
+ return results;
213
+ }
214
+
215
+ const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
216
+ const sections = [];
217
+
218
+ for (const entry of entries) {
219
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
220
+ continue;
221
+ }
222
+
223
+ const filePath = path.join(sourceDir, entry.name);
224
+ const content = await fs.promises.readFile(filePath, 'utf8');
225
+ const ruleName = entry.name.replace('.md', '');
226
+
227
+ sections.push(`### ${ruleName}\n\n${content}`);
228
+ }
229
+
230
+ if (sections.length > 0) {
231
+ // 追加到现有 AGENTS.md
232
+ let existingContent = '';
233
+ if (fs.existsSync(targetPath)) {
234
+ existingContent = await fs.promises.readFile(targetPath, 'utf8');
235
+ }
236
+
237
+ const rulesSection = `\n\n## Rules\n\n${sections.join('\n\n---\n\n')}`;
238
+ const merged = existingContent + rulesSection;
239
+
240
+ const result = await this.emitToPath(targetPath, merged);
241
+ results.push(result);
242
+ }
243
+
244
+ return results;
245
+ }
50
246
  }
51
247
 
52
248
  module.exports = CodexEmitter;
@@ -1,10 +1,26 @@
1
1
  /**
2
- * T033: CursorEmitter - Cursor 平台输出
2
+ * T033: CursorEmitter - Cursor 平台输出 (v2.0)
3
3
  *
4
- * 输出格式: Markdown (无 frontmatter)
5
- * 目录: .cursor/commands/
4
+ * [INPUT]: CommandIR, SKILL.md, agents/*.md, rules/*.md, hooks/*.ts
5
+ * [OUTPUT]: .cursor/commands/*.md, .cursor/rules/*.mdc, .cursor/subagents/*.md, hooks.json
6
+ * [POS]: Cursor IDE 平台编译器,支持完整功能模块
7
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
8
+ *
9
+ * 输出格式:
10
+ * - Commands: 纯 Markdown → .cursor/commands/
11
+ * - Skills: MDC (YAML frontmatter) → .cursor/rules/
12
+ * - Agents: Subagents (YAML frontmatter) → .cursor/subagents/
13
+ * - Rules: MDC → .cursor/rules/
14
+ * - Hooks: hooks.json + hooks/*.sh
15
+ *
16
+ * v2.0: 支持多模块编译
6
17
  */
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const yaml = require('js-yaml');
21
+ const matter = require('gray-matter');
7
22
  const BaseEmitter = require('./base-emitter.js');
23
+ const { ContextExpander } = require('../context-expander.js');
8
24
 
9
25
  class CursorEmitter extends BaseEmitter {
10
26
  get name() {
@@ -26,6 +42,294 @@ class CursorEmitter extends BaseEmitter {
26
42
  format(ir, transformedContent) {
27
43
  return transformedContent;
28
44
  }
45
+
46
+ // ----------------------------------------------------------
47
+ // Multi-Module Emit Methods (v2.0)
48
+ // ----------------------------------------------------------
49
+
50
+ /**
51
+ * 编译 Skills 模块
52
+ * .claude/skills/[name]/SKILL.md -> .cursor/rules/[name].mdc
53
+ */
54
+ async emitSkills(sourceDir, targetDir) {
55
+ const results = [];
56
+
57
+ if (!fs.existsSync(sourceDir)) {
58
+ return results;
59
+ }
60
+
61
+ // 扫描技能分组目录
62
+ const groupDirs = await fs.promises.readdir(sourceDir, { withFileTypes: true });
63
+
64
+ for (const groupEntry of groupDirs) {
65
+ if (!groupEntry.isDirectory() || groupEntry.name.startsWith('_')) {
66
+ continue;
67
+ }
68
+
69
+ const groupDir = path.join(sourceDir, groupEntry.name);
70
+ const skillEntries = await fs.promises.readdir(groupDir, { withFileTypes: true });
71
+
72
+ for (const skillEntry of skillEntries) {
73
+ if (!skillEntry.isDirectory() || skillEntry.name.startsWith('_')) {
74
+ continue;
75
+ }
76
+
77
+ const skillDir = path.join(groupDir, skillEntry.name);
78
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
79
+
80
+ if (!fs.existsSync(skillMdPath)) {
81
+ continue;
82
+ }
83
+
84
+ try {
85
+ const result = await this._emitSkillAsRule(
86
+ skillEntry.name,
87
+ skillDir,
88
+ skillMdPath,
89
+ targetDir
90
+ );
91
+ results.push(result);
92
+ } catch (error) {
93
+ console.warn(`Warning: Failed to emit skill ${skillEntry.name}: ${error.message}`);
94
+ }
95
+ }
96
+ }
97
+
98
+ return results;
99
+ }
100
+
101
+ /**
102
+ * 将 Skill 转换为 Cursor Rule (.mdc)
103
+ */
104
+ async _emitSkillAsRule(skillName, skillDir, skillMdPath, targetDir) {
105
+ let content = await fs.promises.readFile(skillMdPath, 'utf8');
106
+ const parsed = matter(content);
107
+
108
+ // 展开 context.jsonl (Cursor 使用 @file 引用)
109
+ const contextExpanded = ContextExpander.expandFromSkillDir(skillDir, 'cursor');
110
+
111
+ // 构建 MDC frontmatter
112
+ const frontmatterData = {
113
+ description: parsed.data.description || `${skillName} skill`,
114
+ globs: this._inferGlobs(skillName, parsed.data),
115
+ alwaysApply: parsed.data.alwaysApply || false
116
+ };
117
+
118
+ const yamlStr = yaml.dump(frontmatterData, {
119
+ lineWidth: -1,
120
+ quotingType: '"',
121
+ forceQuotes: false
122
+ });
123
+
124
+ // 组合最终内容
125
+ let body = parsed.content;
126
+ if (contextExpanded) {
127
+ body = contextExpanded + '\n' + body;
128
+ }
129
+
130
+ const finalContent = `---\n${yamlStr}---\n\n${body}`;
131
+
132
+ // 输出到 .cursor/rules/<skill-name>.mdc
133
+ const targetPath = path.join(targetDir, `${skillName}.mdc`);
134
+ const result = await this.emitToPath(targetPath, finalContent);
135
+
136
+ return { ...result, skillName };
137
+ }
138
+
139
+ /**
140
+ * 推断 globs 模式
141
+ */
142
+ _inferGlobs(skillName, frontmatter) {
143
+ if (frontmatter.globs) {
144
+ return frontmatter.globs;
145
+ }
146
+
147
+ // 根据技能名推断
148
+ if (skillName.includes('flow-')) {
149
+ return ['devflow/**/*', '.claude/**/*'];
150
+ }
151
+
152
+ return ['**/*'];
153
+ }
154
+
155
+ /**
156
+ * 编译 Agents 模块
157
+ * .claude/agents/[name].md -> .cursor/subagents/[name].md
158
+ */
159
+ async emitAgents(sourceDir, targetDir) {
160
+ const results = [];
161
+
162
+ if (!fs.existsSync(sourceDir)) {
163
+ return results;
164
+ }
165
+
166
+ const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
167
+
168
+ for (const entry of entries) {
169
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
170
+ continue;
171
+ }
172
+
173
+ const filePath = path.join(sourceDir, entry.name);
174
+ const content = await fs.promises.readFile(filePath, 'utf8');
175
+ const parsed = matter(content);
176
+ const agentName = entry.name.replace('.md', '');
177
+
178
+ // 构建 Cursor subagent frontmatter
179
+ const frontmatterData = {
180
+ name: parsed.data.name || agentName,
181
+ description: parsed.data.description || `${agentName} agent`
182
+ };
183
+
184
+ // 添加 tools 字段(如果有)
185
+ if (parsed.data.tools) {
186
+ frontmatterData.tools = parsed.data.tools;
187
+ }
188
+
189
+ const yamlStr = yaml.dump(frontmatterData, {
190
+ lineWidth: -1,
191
+ quotingType: '"',
192
+ forceQuotes: false
193
+ });
194
+
195
+ const finalContent = `---\n${yamlStr}---\n\n${parsed.content}`;
196
+
197
+ const targetPath = path.join(targetDir, `${agentName}.md`);
198
+ const result = await this.emitToPath(targetPath, finalContent);
199
+ results.push({ ...result, agentName });
200
+ }
201
+
202
+ return results;
203
+ }
204
+
205
+ /**
206
+ * 编译 Rules 模块
207
+ * .claude/rules/[name].md -> .cursor/rules/[name].mdc
208
+ */
209
+ async emitRules(sourceDir, targetDir) {
210
+ const results = [];
211
+
212
+ if (!fs.existsSync(sourceDir)) {
213
+ return results;
214
+ }
215
+
216
+ const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
217
+
218
+ for (const entry of entries) {
219
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
220
+ continue;
221
+ }
222
+
223
+ const filePath = path.join(sourceDir, entry.name);
224
+ const content = await fs.promises.readFile(filePath, 'utf8');
225
+ const parsed = matter(content);
226
+ const ruleName = entry.name.replace('.md', '');
227
+
228
+ // 构建 MDC frontmatter
229
+ const frontmatterData = {
230
+ description: parsed.data.description || `${ruleName} rule`,
231
+ alwaysApply: parsed.data.alwaysApply || true
232
+ };
233
+
234
+ if (parsed.data.globs) {
235
+ frontmatterData.globs = parsed.data.globs;
236
+ }
237
+
238
+ const yamlStr = yaml.dump(frontmatterData, {
239
+ lineWidth: -1,
240
+ quotingType: '"',
241
+ forceQuotes: false
242
+ });
243
+
244
+ const finalContent = `---\n${yamlStr}---\n\n${parsed.content}`;
245
+
246
+ const targetPath = path.join(targetDir, `${ruleName}.mdc`);
247
+ const result = await this.emitToPath(targetPath, finalContent);
248
+ results.push({ ...result, ruleName });
249
+ }
250
+
251
+ return results;
252
+ }
253
+
254
+ /**
255
+ * 编译 Hooks 模块
256
+ * .claude/hooks/[name].ts -> .cursor/hooks.json + .cursor/hooks/[name].sh
257
+ */
258
+ async emitHooks(sourceDir, targetDir) {
259
+ const results = [];
260
+
261
+ if (!fs.existsSync(sourceDir)) {
262
+ return results;
263
+ }
264
+
265
+ const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
266
+ const hooksConfig = { version: 1, hooks: {} };
267
+
268
+ // Claude hook 事件 → Cursor hook 事件映射
269
+ const eventMap = {
270
+ 'PreToolUse': 'preToolUse',
271
+ 'PostToolUse': 'postToolUse',
272
+ 'UserPromptSubmit': 'sessionStart'
273
+ };
274
+
275
+ for (const entry of entries) {
276
+ if (!entry.isFile()) {
277
+ continue;
278
+ }
279
+
280
+ // 跳过 TypeScript 类型定义文件
281
+ if (entry.name.endsWith('.d.ts')) {
282
+ continue;
283
+ }
284
+
285
+ const filePath = path.join(sourceDir, entry.name);
286
+ const hookName = entry.name.replace(/\.(ts|js)$/, '');
287
+
288
+ // 尝试从文件名推断事件类型
289
+ let event = null;
290
+ for (const [claudeEvent, cursorEvent] of Object.entries(eventMap)) {
291
+ if (hookName.toLowerCase().includes(claudeEvent.toLowerCase())) {
292
+ event = cursorEvent;
293
+ break;
294
+ }
295
+ }
296
+
297
+ if (!event) {
298
+ // 默认为 sessionStart
299
+ event = 'sessionStart';
300
+ }
301
+
302
+ // 创建 shell wrapper
303
+ const shellScript = `#!/bin/bash
304
+ # Auto-generated from ${entry.name}
305
+ # Original hook: ${filePath}
306
+
307
+ # Note: TypeScript hooks need to be manually converted
308
+ echo "Hook ${hookName} triggered"
309
+ `;
310
+
311
+ const shellPath = path.join(targetDir, 'hooks', `${hookName}.sh`);
312
+ await this.emitToPath(shellPath, shellScript);
313
+
314
+ // 添加到 hooks.json
315
+ if (!hooksConfig.hooks[event]) {
316
+ hooksConfig.hooks[event] = [];
317
+ }
318
+ hooksConfig.hooks[event].push({
319
+ command: `./hooks/${hookName}.sh`
320
+ });
321
+ }
322
+
323
+ // 写入 hooks.json
324
+ if (Object.keys(hooksConfig.hooks).length > 0) {
325
+ const configPath = path.join(targetDir, 'hooks.json');
326
+ const configContent = JSON.stringify(hooksConfig, null, 2);
327
+ const result = await this.emitToPath(configPath, configContent);
328
+ results.push(result);
329
+ }
330
+
331
+ return results;
332
+ }
29
333
  }
30
334
 
31
335
  module.exports = CursorEmitter;