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.
- package/.claude/CLAUDE.md +1065 -48
- package/.claude/agents/dev-implementer.md +195 -0
- package/.claude/commands/{flow-archive.md → flow/archive.md} +46 -11
- package/.claude/commands/flow/context.md +150 -0
- package/.claude/commands/flow/delta.md +245 -0
- package/.claude/commands/{flow-dev.md → flow/dev.md} +112 -11
- package/.claude/commands/flow/init.md +45 -0
- package/.claude/commands/flow/quality.md +159 -0
- package/.claude/commands/flow/spec.md +186 -0
- package/.claude/commands/flow/workspace.md +146 -0
- package/.claude/commands/{cancel-ralph.md → util/cancel-ralph.md} +1 -0
- package/.claude/config/quality-gates.yml +305 -0
- package/.claude/docs/guides/TEAM_MODE_GUIDE.md +313 -0
- package/.claude/docs/templates/DELTA_SPEC_TEMPLATE.md +91 -0
- package/.claude/docs/templates/DESIGN_DECISIONS_TEMPLATE.md +151 -0
- package/.claude/docs/templates/JOURNAL_TEMPLATE.md +75 -0
- package/.claude/docs/templates/_shared/CLAUDE.md +36 -0
- package/.claude/docs/templates/_shared/CONSTITUTION_CHECK.md +125 -0
- package/.claude/docs/templates/_shared/VALIDATION_CHECKLIST.md +187 -0
- package/.claude/docs/templates/_shared/YAML_FRONTMATTER.md +164 -0
- package/.claude/docs/templates/context/dev.jsonl.template +6 -0
- package/.claude/docs/templates/context/epic.jsonl.template +5 -0
- package/.claude/docs/templates/context/prd.jsonl.template +4 -0
- package/.claude/docs/templates/context/research.jsonl.template +4 -0
- package/.claude/docs/templates/context/review.jsonl.template +5 -0
- package/.claude/docs/templates/context/tech.jsonl.template +5 -0
- package/.claude/hooks/CLAUDE.md +342 -0
- package/.claude/hooks/inject-agent-context.ts +480 -0
- package/.claude/hooks/inject-skill-context.ts +359 -0
- package/.claude/hooks/ralph-loop.ts +931 -0
- package/.claude/hooks/task-completed-hook.ts +593 -0
- package/.claude/hooks/teammate-idle-hook.ts +690 -0
- package/.claude/hooks/types/team-types.d.ts +238 -0
- package/.claude/rules/devflow-conventions.md +82 -9
- package/.claude/scripts/archive-requirement.sh +44 -1
- package/.claude/scripts/common.sh +670 -3
- package/.claude/scripts/delta-parser.ts +527 -0
- package/.claude/scripts/detect-file-conflicts.sh +151 -0
- package/.claude/scripts/flow-context-add.sh +134 -0
- package/.claude/scripts/flow-context-init.sh +133 -0
- package/.claude/scripts/flow-context-validate.sh +144 -0
- package/.claude/scripts/flow-delta-apply.sh +297 -0
- package/.claude/scripts/flow-delta-archive.sh +71 -0
- package/.claude/scripts/flow-delta-create.sh +202 -0
- package/.claude/scripts/flow-delta-list.sh +142 -0
- package/.claude/scripts/flow-delta-status.sh +235 -0
- package/.claude/scripts/flow-quality-full.sh +184 -0
- package/.claude/scripts/flow-quality-quick.sh +64 -0
- package/.claude/scripts/flow-workspace-init.sh +117 -0
- package/.claude/scripts/flow-workspace-record.sh +164 -0
- package/.claude/scripts/flow-workspace-start.sh +88 -0
- package/.claude/scripts/get-workflow-status.sh +415 -0
- package/.claude/scripts/parse-task-dependencies.js +334 -0
- package/.claude/scripts/record-quality-error.sh +165 -0
- package/.claude/scripts/run-quality-gates.sh +242 -0
- package/.claude/scripts/team-dev-init.sh +319 -0
- package/.claude/scripts/team-state-recovery.sh +229 -0
- package/.claude/scripts/workflow-status.ts +433 -0
- package/.claude/settings.json +19 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +85 -200
- package/.claude/skills/domain/using-git-worktrees/SKILL.md +252 -0
- package/.claude/skills/domain/using-git-worktrees/assets/SHELL_ALIASES.md +133 -0
- package/.claude/skills/domain/using-git-worktrees/context.jsonl +4 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-cleanup.sh +218 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-create.sh +232 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-list.sh +130 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-status.sh +140 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-switch.sh +70 -0
- package/.claude/skills/skill-rules.json +72 -1
- package/.claude/skills/utility/journey-checker/SKILL.md +199 -0
- package/.claude/skills/utility/journey-checker/pressure-scenarios.md +164 -0
- package/.claude/skills/utility/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/utility/skill-creator/SKILL.md +356 -0
- package/.claude/skills/utility/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/utility/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/utility/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/utility/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/utility/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/workflow/flow-dev/CLAUDE.md +78 -0
- package/.claude/skills/workflow/flow-dev/SKILL.md +96 -0
- package/.claude/skills/workflow/flow-dev/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +71 -0
- package/.claude/skills/workflow/flow-dev/context.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/dev-implementer.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/scripts/entry-gate.sh +116 -0
- package/.claude/skills/workflow/flow-dev/scripts/exit-gate.sh +101 -0
- package/.claude/skills/workflow/flow-dev/scripts/task-orchestrator.sh +106 -0
- package/.claude/skills/workflow/flow-fix/SKILL.md +105 -0
- package/.claude/skills/workflow/flow-fix/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-fix/references/bug-analyzer.md +381 -0
- package/.claude/skills/workflow/flow-init/SKILL.md +211 -0
- package/.claude/skills/workflow/flow-init/assets/BRAINSTORM_TEMPLATE.md +148 -0
- package/.claude/skills/workflow/flow-init/assets/INIT_FLOW_TEMPLATE.md +198 -0
- package/.claude/skills/workflow/flow-init/assets/RESEARCH_TEMPLATE.md +276 -0
- package/.claude/skills/workflow/flow-init/context.jsonl +5 -0
- package/.claude/skills/workflow/flow-init/references/flow-researcher.md +132 -0
- package/.claude/skills/workflow/flow-init/scripts/check-prerequisites.sh +232 -0
- package/.claude/skills/workflow/flow-init/scripts/consolidate-research.sh +182 -0
- package/.claude/skills/workflow/flow-init/scripts/create-requirement.sh +515 -0
- package/.claude/skills/workflow/flow-init/scripts/generate-research-tasks.sh +157 -0
- package/.claude/skills/workflow/flow-init/scripts/populate-research-tasks.sh +284 -0
- package/.claude/skills/workflow/flow-init/scripts/validate-research.sh +332 -0
- package/.claude/skills/workflow/flow-quality/SKILL.md +94 -0
- package/.claude/skills/workflow/flow-quality/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-quality/references/code-quality-reviewer.md +205 -0
- package/.claude/skills/workflow/flow-quality/references/qa-tester.md +313 -0
- package/.claude/skills/workflow/flow-quality/references/security-reviewer.md +314 -0
- package/.claude/skills/workflow/flow-quality/references/spec-reviewer.md +221 -0
- package/.claude/skills/workflow/flow-release/SKILL.md +126 -0
- package/.claude/skills/workflow/flow-release/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-release/references/release-manager.md +295 -0
- package/.claude/skills/workflow/flow-spec/CLAUDE.md +103 -0
- package/.claude/skills/workflow/flow-spec/SKILL.md +545 -0
- package/.claude/skills/workflow/flow-spec/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-spec/scripts/entry-gate.sh +194 -0
- package/.claude/skills/workflow/flow-spec/scripts/exit-gate.sh +244 -0
- package/.claude/skills/workflow/flow-spec/scripts/parallel-orchestrator.sh +205 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-communication.sh +353 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-init.sh +195 -0
- package/.claude/skills/workflow/flow-spec/scripts/test-team-mode.sh +496 -0
- package/.claude/skills/workflow/flow-spec/team-config.json +165 -0
- package/.claude/skills/workflow.yaml +417 -0
- package/CHANGELOG.md +254 -0
- package/README.md +193 -33
- package/README.zh-CN.md +206 -46
- package/lib/compiler/CLAUDE.md +77 -46
- package/lib/compiler/__tests__/multi-module-emitters.test.js +508 -0
- package/lib/compiler/context-expander.js +179 -0
- package/lib/compiler/emitters/antigravity-emitter.js +195 -5
- package/lib/compiler/emitters/base-emitter.js +217 -2
- package/lib/compiler/emitters/codex-emitter.js +200 -4
- package/lib/compiler/emitters/cursor-emitter.js +307 -3
- package/lib/compiler/emitters/qwen-emitter.js +196 -4
- package/lib/compiler/index.js +197 -2
- package/lib/compiler/platforms.js +270 -21
- package/package.json +1 -1
- package/.claude/commands/flow-epic.md +0 -183
- package/.claude/commands/flow-init.md +0 -370
- package/.claude/commands/flow-prd.md +0 -144
- package/.claude/commands/flow-qa.md +0 -93
- package/.claude/commands/flow-review.md +0 -257
- package/.claude/commands/flow-tech.md +0 -142
- package/.claude/commands/flow-ui.md +0 -189
- package/.claude/skills/file-header-guardian/SKILL.md +0 -56
- package/.claude/skills/skill-developer/ADVANCED.md +0 -197
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
- package/.claude/skills/skill-developer/SKILL.md +0 -426
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
- package/.claude/skills/writing-skills/SKILL.md +0 -655
- package/.claude/skills/writing-skills/anthropic-best-practices.md +0 -1150
- package/.claude/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/.claude/skills/writing-skills/graphviz-conventions.dot +0 -172
- package/.claude/skills/writing-skills/persuasion-principles.md +0 -187
- package/.claude/skills/writing-skills/render-graphs.js +0 -168
- package/.claude/skills/writing-skills/testing-skills-with-subagents.md +0 -384
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +0 -1
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +0 -1
- /package/.claude/commands/{core-architecture.md → core/architecture.md} +0 -0
- /package/.claude/commands/{core-guidelines.md → core/guidelines.md} +0 -0
- /package/.claude/commands/{core-roadmap.md → core/roadmap.md} +0 -0
- /package/.claude/commands/{core-style.md → core/style.md} +0 -0
- /package/.claude/commands/{flow-checklist.md → flow/checklist.md} +0 -0
- /package/.claude/commands/{flow-clarify.md → flow/clarify.md} +0 -0
- /package/.claude/commands/{flow-constitution.md → flow/constitution.md} +0 -0
- /package/.claude/commands/{flow-fix.md → flow/fix.md} +0 -0
- /package/.claude/commands/{flow-ideate.md → flow/ideate.md} +0 -0
- /package/.claude/commands/{flow-new.md → flow/new.md} +0 -0
- /package/.claude/commands/{flow-release.md → flow/release.md} +0 -0
- /package/.claude/commands/{flow-restart.md → flow/restart.md} +0 -0
- /package/.claude/commands/{flow-status.md → flow/status.md} +0 -0
- /package/.claude/commands/{flow-update.md → flow/update.md} +0 -0
- /package/.claude/commands/{flow-upgrade.md → flow/upgrade.md} +0 -0
- /package/.claude/commands/{flow-verify.md → flow/verify.md} +0 -0
- /package/.claude/commands/{code-review-high.md → util/code-review.md} +0 -0
- /package/.claude/commands/{git-commit.md → util/git-commit.md} +0 -0
- /package/.claude/commands/{problem-analyzer.md → util/problem-analyzer.md} +0 -0
- /package/.claude/skills/{flow-attention-refresh → domain/attention-refresh}/SKILL.md +0 -0
- /package/.claude/skills/{flow-brainstorming → domain/brainstorming}/SKILL.md +0 -0
- /package/.claude/skills/{flow-debugging → domain/debugging}/SKILL.md +0 -0
- /package/.claude/skills/{flow-finishing-branch → domain/finishing-branch}/SKILL.md +0 -0
- /package/.claude/skills/{flow-receiving-review → domain/receiving-review}/SKILL.md +0 -0
- /package/.claude/skills/{flow-tdd → domain/tdd}/SKILL.md +0 -0
- /package/.claude/skills/{verification-before-completion → domain/verification}/SKILL.md +0 -0
- /package/.claude/skills/{constitution-guardian → guardrail/constitution-guardian}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-tdd-enforcer → guardrail/tdd-enforcer}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-constitution-quick-ref → utility/constitution-quick-ref}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-file-standards → utility/file-standards}/SKILL.md +0 -0
- /package/.claude/skills/{fractal-docs-generator → utility/fractal-docs}/SKILL.md +0 -0
- /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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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;
|