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,9 +1,9 @@
1
1
  # Command Emitter - Compiler Module Architecture
2
2
 
3
3
  ## Purpose
4
- Multi-platform command compiler that transforms `.claude/commands/*.md` (SSOT) into native formats for Codex, Cursor, Qwen, and Antigravity platforms.
4
+ Multi-platform compiler that transforms `.claude/` modules (commands, skills, agents, rules, hooks) into native formats for Codex, Cursor, Qwen, and Antigravity platforms.
5
5
 
6
- ## Architecture
6
+ ## Architecture (v3.0)
7
7
 
8
8
  ```
9
9
  lib/compiler/
@@ -13,63 +13,90 @@ lib/compiler/
13
13
  ├── schemas.js # Zod validation schemas (CommandIR, Manifest)
14
14
  ├── errors.js # Custom error types (MissingFrontmatter, UnknownAlias, etc.)
15
15
  ├── skills-registry.js # Generate skills registry from .claude/skills/
16
+ ├── platforms.js # Platform configuration registry (v2.0)
17
+ ├── context-expander.js # context.jsonl expansion (v3.0)
16
18
  ├── index.js # Compiler entry point, orchestrates pipeline
17
- └── emitters/
18
- ├── base-emitter.js # Abstract base class
19
- ├── codex-emitter.js # .codex/prompts/*.md (YAML frontmatter)
20
- ├── cursor-emitter.js # .cursor/commands/*.md (pure Markdown)
21
- ├── qwen-emitter.js # .qwen/commands/*.toml (TOML format)
22
- ├── antigravity-emitter.js # .agent/workflows/*.md (12K limit, auto-split)
23
- └── index.js # Emitter factory
19
+ ├── emitters/
20
+ ├── base-emitter.js # Abstract base class (v2.0: multi-module support)
21
+ ├── codex-emitter.js # .codex/prompts/*.md, .codex/skills/*, AGENTS.md
22
+ ├── cursor-emitter.js # .cursor/commands/*.md, .cursor/rules/*.mdc, subagents/
23
+ ├── qwen-emitter.js # .qwen/commands/*.toml, .qwen/agents/*, CONTEXT.md
24
+ ├── antigravity-emitter.js # .agent/workflows/*.md, .agent/skills/*, rules/
25
+ └── index.js # Emitter factory
26
+ └── rules-emitters/ # Legacy rules emitters (deprecated)
27
+ └── ...
24
28
  ```
25
29
 
26
- ## Data Flow
30
+ ## Multi-Module Compilation (v3.0)
31
+
32
+ ### Supported Modules
33
+
34
+ | Module | Source | Codex | Cursor | Qwen | Antigravity |
35
+ |--------|--------|-------|--------|------|-------------|
36
+ | skills | `.claude/skills/` | `.codex/skills/` | `.cursor/rules/*.mdc` | `.qwen/commands/*.toml` | `.agent/skills/` |
37
+ | commands | `.claude/commands/` | `.codex/prompts/` | `.cursor/commands/` | `.qwen/commands/` | `.agent/workflows/` |
38
+ | agents | `.claude/agents/` | `AGENTS.md` | `.cursor/subagents/` | `.qwen/agents/` | `AGENTS.md` |
39
+ | rules | `.claude/rules/` | `AGENTS.md` | `.cursor/rules/*.mdc` | `CONTEXT.md` | `.agent/rules/` |
40
+ | hooks | `.claude/hooks/` | ❌ | `hooks.json` + `hooks/` | ❌ | ❌ |
41
+
42
+ ### Data Flow (v3.0)
27
43
 
28
44
  ```
29
- .claude/commands/*.md
45
+ .claude/
46
+ ├── skills/*/SKILL.md
47
+ ├── commands/*.md
48
+ ├── agents/*.md
49
+ ├── rules/*.md
50
+ └── hooks/*.ts
30
51
 
31
52
 
32
- ┌─────────────────┐
33
- Parser gray-matter CommandIR
34
- (parser.js) - Extract frontmatter
35
- │ │ - Detect placeholders
36
- │ │ - Compute SHA-256 hash
37
- └────────┬────────┘
38
- │ CommandIR[]
39
-
40
- ┌─────────────────┐
41
- │ Transformer │ Platform-specific transforms
42
- │(transformer.js) │ - {SCRIPT:alias} → "bash <path>"
43
- │ │ - $ARGUMENTS → {{args}} / [arguments]
44
- │ │ - {AGENT_SCRIPT} + __AGENT__ substitution
45
- └────────┬────────┘
46
- │ TransformedContent
47
-
48
- ┌─────────────────┐
49
- │ Emitters │ Platform format + file write
50
- │ (emitters/*.js) │ - Codex: MD + YAML frontmatter
51
- │ │ - Cursor: pure MD
52
- │ │ - Qwen: TOML
53
- │ │ - Antigravity: MD + YAML (12K limit)
54
- └────────┬────────┘
53
+ ┌─────────────────────┐
54
+ compileMultiModule Orchestrates all modules
55
+ (index.js)
56
+ └────────┬────────────┘
55
57
 
56
-
57
- ┌─────────────────┐
58
- │ Manifest │ devflow/.generated/manifest.json
59
- (manifest.js) - Track source/target hashes
60
- - Enable incremental compilation
61
- │ │ - Detect drift
62
- └─────────────────┘
58
+ ┌────┴────┬────────┬────────┬────────┐
59
+ ▼ ▼ ▼ ▼ ▼
60
+ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
61
+ Skills│Commands│ │Agents │Rules │ │Hooks │
62
+ Emitter│Emitter │Emitter│ │Emitter│ │Emitter│
63
+ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘
64
+ │ │ │ │ │
65
+ ▼ ▼ ▼ ▼ ▼
66
+ Platform-specific output directories
63
67
  ```
64
68
 
65
69
  ## CLI Usage
66
70
 
67
71
  ```bash
72
+ # Legacy: Compile commands only
68
73
  npm run adapt # Compile all platforms
69
74
  npm run adapt -- --platform codex # Compile single platform
70
75
  npm run adapt -- --check # Drift detection only
71
- npm run adapt -- --skills # Generate skills-registry.json
72
- npm run adapt -- --verbose # Detailed output
76
+
77
+ # v3.0: Multi-module compilation
78
+ npm run adapt -- --modules skills,commands,agents,rules
79
+ npm run adapt -- --modules skills --platform cursor
80
+ ```
81
+
82
+ ## Key APIs
83
+
84
+ ### compile(options) - Legacy
85
+ Compiles commands only (backward compatible).
86
+
87
+ ### compileMultiModule(options) - v3.0
88
+ Compiles all specified modules.
89
+
90
+ ```javascript
91
+ const { compileMultiModule } = require('./lib/compiler');
92
+
93
+ await compileMultiModule({
94
+ sourceBaseDir: '.claude/',
95
+ outputBaseDir: '.',
96
+ platforms: ['codex', 'cursor', 'qwen', 'antigravity'],
97
+ modules: ['skills', 'commands', 'agents', 'rules'],
98
+ verbose: true
99
+ });
73
100
  ```
74
101
 
75
102
  ## Key Schemas
@@ -77,16 +104,20 @@ npm run adapt -- --verbose # Detailed output
77
104
  - **CommandIR**: Intermediate representation after parsing
78
105
  - **ManifestEntry**: Single compilation record (source, target, hash, platform)
79
106
  - **Manifest**: Complete compilation history
107
+ - **PlatformConfig**: Platform-specific configuration (v2.0)
80
108
 
81
109
  ## Dependencies
82
110
 
83
111
  - `gray-matter`: Frontmatter parsing
84
112
  - `@iarna/toml`: TOML serialization (Qwen)
85
- - `js-yaml`: YAML serialization (Codex, Antigravity)
113
+ - `js-yaml`: YAML serialization (Codex, Antigravity, Cursor)
86
114
  - `zod`: Schema validation
87
115
 
88
116
  ---
89
117
 
90
118
  **Created**: 2025-12-18
91
- **REQ**: REQ-005 (RM-007)
92
- **Version**: 1.0.0
119
+ **Updated**: 2026-02-07
120
+ **REQ**: REQ-005 (RM-007), Multi-Platform Adaptation
121
+ **Version**: 3.0.0
122
+
123
+ [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Multi-Module Emitters Test Suite
3
+ *
4
+ * [INPUT]: 测试 fixtures
5
+ * [OUTPUT]: 测试结果
6
+ * [POS]: 编译器测试套件,验证多模块编译功能
7
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
8
+ *
9
+ * 测试覆盖:
10
+ * - context-expander.js
11
+ * - 各平台 Emitter 的多模块方法
12
+ * - compileMultiModule 集成测试
13
+ */
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+
18
+ const { ContextExpander } = require('../context-expander.js');
19
+ const CodexEmitter = require('../emitters/codex-emitter.js');
20
+ const CursorEmitter = require('../emitters/cursor-emitter.js');
21
+ const QwenEmitter = require('../emitters/qwen-emitter.js');
22
+ const AntigravityEmitter = require('../emitters/antigravity-emitter.js');
23
+ const { compileMultiModule, PLATFORMS, DEFAULT_MODULES } = require('../index.js');
24
+
25
+ // ============================================================
26
+ // Test Fixtures
27
+ // ============================================================
28
+ const FIXTURES_DIR = path.join(__dirname, 'fixtures', 'multi-module');
29
+
30
+ // 创建临时目录
31
+ function createTempDir() {
32
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-test-'));
33
+ }
34
+
35
+ // 清理临时目录
36
+ function cleanupTempDir(dir) {
37
+ if (fs.existsSync(dir)) {
38
+ fs.rmSync(dir, { recursive: true, force: true });
39
+ }
40
+ }
41
+
42
+ // 创建测试 fixtures
43
+ function setupFixtures(tempDir) {
44
+ // 创建 skills 目录结构
45
+ const skillsDir = path.join(tempDir, '.claude', 'skills', 'workflow', 'test-skill');
46
+ fs.mkdirSync(skillsDir, { recursive: true });
47
+
48
+ // 创建 SKILL.md
49
+ fs.writeFileSync(path.join(skillsDir, 'SKILL.md'), `---
50
+ name: test-skill
51
+ description: A test skill for unit testing
52
+ ---
53
+
54
+ # Test Skill
55
+
56
+ This is a test skill.
57
+
58
+ ## Usage
59
+
60
+ Use this skill for testing.
61
+ `);
62
+
63
+ // 创建 context.jsonl
64
+ fs.writeFileSync(path.join(skillsDir, 'context.jsonl'), `{"file": "devflow/requirements/REQ-001/PRD.md", "reason": "Product requirements"}
65
+ {"file": "devflow/spec/frontend/index.md", "reason": "Frontend conventions", "optional": true}
66
+ `);
67
+
68
+ // 创建 agents 目录
69
+ const agentsDir = path.join(tempDir, '.claude', 'agents');
70
+ fs.mkdirSync(agentsDir, { recursive: true });
71
+
72
+ fs.writeFileSync(path.join(agentsDir, 'test-agent.md'), `---
73
+ name: test-agent
74
+ description: A test agent
75
+ tools: Read, Write
76
+ ---
77
+
78
+ # Test Agent
79
+
80
+ This agent is for testing.
81
+ `);
82
+
83
+ // 创建 rules 目录
84
+ const rulesDir = path.join(tempDir, '.claude', 'rules');
85
+ fs.mkdirSync(rulesDir, { recursive: true });
86
+
87
+ fs.writeFileSync(path.join(rulesDir, 'test-rule.md'), `---
88
+ description: A test rule
89
+ alwaysApply: true
90
+ ---
91
+
92
+ # Test Rule
93
+
94
+ This rule is for testing.
95
+ `);
96
+
97
+ // 创建 hooks 目录
98
+ const hooksDir = path.join(tempDir, '.claude', 'hooks');
99
+ fs.mkdirSync(hooksDir, { recursive: true });
100
+
101
+ fs.writeFileSync(path.join(hooksDir, 'preToolUse-test.ts'), `// Test hook
102
+ export default function preToolUseTest() {
103
+ console.log('Hook triggered');
104
+ }
105
+ `);
106
+
107
+ return tempDir;
108
+ }
109
+
110
+ // ============================================================
111
+ // ContextExpander Tests
112
+ // ============================================================
113
+ describe('ContextExpander', () => {
114
+ describe('parseJsonl', () => {
115
+ test('parses valid JSONL content', () => {
116
+ const jsonl = `{"file": "a.md", "reason": "test"}
117
+ {"file": "b.md", "reason": "test2", "optional": true}`;
118
+
119
+ const result = ContextExpander.parseJsonl(jsonl);
120
+
121
+ expect(result).toHaveLength(2);
122
+ expect(result[0]).toEqual({ file: 'a.md', reason: 'test', optional: false });
123
+ expect(result[1]).toEqual({ file: 'b.md', reason: 'test2', optional: true });
124
+ });
125
+
126
+ test('handles empty content', () => {
127
+ expect(ContextExpander.parseJsonl('')).toEqual([]);
128
+ expect(ContextExpander.parseJsonl(' ')).toEqual([]);
129
+ });
130
+
131
+ test('skips invalid lines', () => {
132
+ const jsonl = `{"file": "a.md", "reason": "test"}
133
+ invalid json
134
+ {"file": "b.md", "reason": "test2"}`;
135
+
136
+ const result = ContextExpander.parseJsonl(jsonl);
137
+ expect(result).toHaveLength(2);
138
+ });
139
+ });
140
+
141
+ describe('expand', () => {
142
+ const contexts = [
143
+ { file: 'a.md', reason: 'test', optional: false },
144
+ { file: 'b.md', reason: 'test2', optional: true }
145
+ ];
146
+
147
+ test('expands for Cursor with @file references', () => {
148
+ const result = ContextExpander.expand(contexts, 'cursor');
149
+
150
+ expect(result).toContain('@a.md');
151
+ expect(result).toContain('@b.md (optional)');
152
+ expect(result).toContain('## Context Files');
153
+ });
154
+
155
+ test('expands for Codex with Required Context section', () => {
156
+ const result = ContextExpander.expand(contexts, 'codex');
157
+
158
+ expect(result).toContain('## Required Context');
159
+ expect(result).toContain('`a.md`');
160
+ expect(result).toContain('`b.md`');
161
+ });
162
+
163
+ test('expands for Qwen with Required Context section', () => {
164
+ const result = ContextExpander.expand(contexts, 'qwen');
165
+
166
+ expect(result).toContain('## Required Context');
167
+ });
168
+
169
+ test('expands for Antigravity with Required Context section', () => {
170
+ const result = ContextExpander.expand(contexts, 'antigravity');
171
+
172
+ expect(result).toContain('## Required Context');
173
+ });
174
+
175
+ test('returns empty string for empty contexts', () => {
176
+ expect(ContextExpander.expand([], 'cursor')).toBe('');
177
+ expect(ContextExpander.expand(null, 'cursor')).toBe('');
178
+ });
179
+ });
180
+ });
181
+
182
+ // ============================================================
183
+ // CodexEmitter Multi-Module Tests
184
+ // ============================================================
185
+ describe('CodexEmitter Multi-Module', () => {
186
+ let tempDir;
187
+ let emitter;
188
+
189
+ beforeEach(() => {
190
+ tempDir = createTempDir();
191
+ setupFixtures(tempDir);
192
+ emitter = new CodexEmitter();
193
+ });
194
+
195
+ afterEach(() => {
196
+ cleanupTempDir(tempDir);
197
+ });
198
+
199
+ test('emitSkills creates SKILL.md in target directory', async () => {
200
+ const sourceDir = path.join(tempDir, '.claude', 'skills');
201
+ const targetDir = path.join(tempDir, '.codex', 'skills');
202
+
203
+ const results = await emitter.emitSkills(sourceDir, targetDir);
204
+
205
+ expect(results.length).toBeGreaterThan(0);
206
+ expect(results[0].skillName).toBe('test-skill');
207
+
208
+ const targetPath = path.join(targetDir, 'test-skill', 'SKILL.md');
209
+ expect(fs.existsSync(targetPath)).toBe(true);
210
+
211
+ const content = fs.readFileSync(targetPath, 'utf8');
212
+ expect(content).toContain('name: test-skill');
213
+ expect(content).toContain('## Required Context');
214
+ });
215
+
216
+ test('emitAgents merges to AGENTS.md', async () => {
217
+ const sourceDir = path.join(tempDir, '.claude', 'agents');
218
+ const targetPath = path.join(tempDir, 'AGENTS.md');
219
+
220
+ const results = await emitter.emitAgents(sourceDir, targetPath);
221
+
222
+ expect(results.length).toBe(1);
223
+ expect(fs.existsSync(targetPath)).toBe(true);
224
+
225
+ const content = fs.readFileSync(targetPath, 'utf8');
226
+ expect(content).toContain('# Agents');
227
+ expect(content).toContain('## test-agent');
228
+ });
229
+
230
+ test('emitRules appends to AGENTS.md', async () => {
231
+ const sourceDir = path.join(tempDir, '.claude', 'rules');
232
+ const targetPath = path.join(tempDir, 'AGENTS.md');
233
+
234
+ // 先创建 AGENTS.md
235
+ fs.writeFileSync(targetPath, '# Agents\n\nExisting content.\n');
236
+
237
+ const results = await emitter.emitRules(sourceDir, targetPath);
238
+
239
+ expect(results.length).toBe(1);
240
+
241
+ const content = fs.readFileSync(targetPath, 'utf8');
242
+ expect(content).toContain('# Agents');
243
+ expect(content).toContain('## Rules');
244
+ expect(content).toContain('### test-rule');
245
+ });
246
+ });
247
+
248
+ // ============================================================
249
+ // CursorEmitter Multi-Module Tests
250
+ // ============================================================
251
+ describe('CursorEmitter Multi-Module', () => {
252
+ let tempDir;
253
+ let emitter;
254
+
255
+ beforeEach(() => {
256
+ tempDir = createTempDir();
257
+ setupFixtures(tempDir);
258
+ emitter = new CursorEmitter();
259
+ });
260
+
261
+ afterEach(() => {
262
+ cleanupTempDir(tempDir);
263
+ });
264
+
265
+ test('emitSkills creates .mdc files in rules directory', async () => {
266
+ const sourceDir = path.join(tempDir, '.claude', 'skills');
267
+ const targetDir = path.join(tempDir, '.cursor', 'rules');
268
+
269
+ const results = await emitter.emitSkills(sourceDir, targetDir);
270
+
271
+ expect(results.length).toBeGreaterThan(0);
272
+
273
+ const targetPath = path.join(targetDir, 'test-skill.mdc');
274
+ expect(fs.existsSync(targetPath)).toBe(true);
275
+
276
+ const content = fs.readFileSync(targetPath, 'utf8');
277
+ expect(content).toContain('description:');
278
+ expect(content).toContain('globs:');
279
+ expect(content).toContain('@devflow/requirements/REQ-001/PRD.md');
280
+ });
281
+
282
+ test('emitAgents creates subagent files', async () => {
283
+ const sourceDir = path.join(tempDir, '.claude', 'agents');
284
+ const targetDir = path.join(tempDir, '.cursor', 'subagents');
285
+
286
+ const results = await emitter.emitAgents(sourceDir, targetDir);
287
+
288
+ expect(results.length).toBe(1);
289
+
290
+ const targetPath = path.join(targetDir, 'test-agent.md');
291
+ expect(fs.existsSync(targetPath)).toBe(true);
292
+
293
+ const content = fs.readFileSync(targetPath, 'utf8');
294
+ expect(content).toContain('name: test-agent');
295
+ expect(content).toContain('description:');
296
+ });
297
+
298
+ test('emitRules creates .mdc files', async () => {
299
+ const sourceDir = path.join(tempDir, '.claude', 'rules');
300
+ const targetDir = path.join(tempDir, '.cursor', 'rules');
301
+
302
+ const results = await emitter.emitRules(sourceDir, targetDir);
303
+
304
+ expect(results.length).toBe(1);
305
+
306
+ const targetPath = path.join(targetDir, 'test-rule.mdc');
307
+ expect(fs.existsSync(targetPath)).toBe(true);
308
+
309
+ const content = fs.readFileSync(targetPath, 'utf8');
310
+ expect(content).toContain('description:');
311
+ expect(content).toContain('alwaysApply:');
312
+ });
313
+
314
+ test('emitHooks creates hooks.json and shell scripts', async () => {
315
+ const sourceDir = path.join(tempDir, '.claude', 'hooks');
316
+ const targetDir = path.join(tempDir, '.cursor');
317
+
318
+ const results = await emitter.emitHooks(sourceDir, targetDir);
319
+
320
+ expect(results.length).toBeGreaterThan(0);
321
+
322
+ const configPath = path.join(targetDir, 'hooks.json');
323
+ expect(fs.existsSync(configPath)).toBe(true);
324
+
325
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
326
+ expect(config.version).toBe(1);
327
+ expect(config.hooks).toBeDefined();
328
+ });
329
+ });
330
+
331
+ // ============================================================
332
+ // QwenEmitter Multi-Module Tests
333
+ // ============================================================
334
+ describe('QwenEmitter Multi-Module', () => {
335
+ let tempDir;
336
+ let emitter;
337
+
338
+ beforeEach(() => {
339
+ tempDir = createTempDir();
340
+ setupFixtures(tempDir);
341
+ emitter = new QwenEmitter();
342
+ });
343
+
344
+ afterEach(() => {
345
+ cleanupTempDir(tempDir);
346
+ });
347
+
348
+ test('emitSkills creates .toml files', async () => {
349
+ const sourceDir = path.join(tempDir, '.claude', 'skills');
350
+ const targetDir = path.join(tempDir, '.qwen', 'commands');
351
+
352
+ const results = await emitter.emitSkills(sourceDir, targetDir);
353
+
354
+ expect(results.length).toBeGreaterThan(0);
355
+
356
+ const targetPath = path.join(targetDir, 'test-skill.toml');
357
+ expect(fs.existsSync(targetPath)).toBe(true);
358
+
359
+ const content = fs.readFileSync(targetPath, 'utf8');
360
+ expect(content).toContain('description =');
361
+ expect(content).toContain('prompt =');
362
+ });
363
+
364
+ test('emitAgents creates agent files', async () => {
365
+ const sourceDir = path.join(tempDir, '.claude', 'agents');
366
+ const targetDir = path.join(tempDir, '.qwen', 'agents');
367
+
368
+ const results = await emitter.emitAgents(sourceDir, targetDir);
369
+
370
+ expect(results.length).toBe(1);
371
+
372
+ const targetPath = path.join(targetDir, 'test-agent.md');
373
+ expect(fs.existsSync(targetPath)).toBe(true);
374
+ });
375
+
376
+ test('emitRules creates CONTEXT.md', async () => {
377
+ const sourceDir = path.join(tempDir, '.claude', 'rules');
378
+ const targetPath = path.join(tempDir, 'CONTEXT.md');
379
+
380
+ const results = await emitter.emitRules(sourceDir, targetPath);
381
+
382
+ expect(results.length).toBe(1);
383
+ expect(fs.existsSync(targetPath)).toBe(true);
384
+
385
+ const content = fs.readFileSync(targetPath, 'utf8');
386
+ expect(content).toContain('# Project Context');
387
+ expect(content).toContain('## test-rule');
388
+ });
389
+ });
390
+
391
+ // ============================================================
392
+ // AntigravityEmitter Multi-Module Tests
393
+ // ============================================================
394
+ describe('AntigravityEmitter Multi-Module', () => {
395
+ let tempDir;
396
+ let emitter;
397
+
398
+ beforeEach(() => {
399
+ tempDir = createTempDir();
400
+ setupFixtures(tempDir);
401
+ emitter = new AntigravityEmitter();
402
+ });
403
+
404
+ afterEach(() => {
405
+ cleanupTempDir(tempDir);
406
+ });
407
+
408
+ test('emitSkills creates SKILL.md in skills directory', async () => {
409
+ const sourceDir = path.join(tempDir, '.claude', 'skills');
410
+ const targetDir = path.join(tempDir, '.agent', 'skills');
411
+
412
+ const results = await emitter.emitSkills(sourceDir, targetDir);
413
+
414
+ expect(results.length).toBeGreaterThan(0);
415
+
416
+ const targetPath = path.join(targetDir, 'test-skill', 'SKILL.md');
417
+ expect(fs.existsSync(targetPath)).toBe(true);
418
+ });
419
+
420
+ test('emitRules creates rule files', async () => {
421
+ const sourceDir = path.join(tempDir, '.claude', 'rules');
422
+ const targetDir = path.join(tempDir, '.agent', 'rules');
423
+
424
+ const results = await emitter.emitRules(sourceDir, targetDir);
425
+
426
+ expect(results.length).toBe(1);
427
+
428
+ const targetPath = path.join(targetDir, 'test-rule.md');
429
+ expect(fs.existsSync(targetPath)).toBe(true);
430
+ });
431
+ });
432
+
433
+ // ============================================================
434
+ // compileMultiModule Integration Tests
435
+ // ============================================================
436
+ describe('compileMultiModule Integration', () => {
437
+ let tempDir;
438
+
439
+ beforeEach(() => {
440
+ tempDir = createTempDir();
441
+ setupFixtures(tempDir);
442
+
443
+ // 创建 devflow/.generated 目录
444
+ fs.mkdirSync(path.join(tempDir, 'devflow', '.generated'), { recursive: true });
445
+ });
446
+
447
+ afterEach(() => {
448
+ cleanupTempDir(tempDir);
449
+ });
450
+
451
+ test('compiles all modules for all platforms', async () => {
452
+ const result = await compileMultiModule({
453
+ sourceBaseDir: path.join(tempDir, '.claude'),
454
+ outputBaseDir: tempDir,
455
+ platforms: ['codex', 'cursor'],
456
+ modules: ['skills', 'agents', 'rules'],
457
+ verbose: false
458
+ });
459
+
460
+ expect(result.success).toBe(true);
461
+ expect(result.skillsEmitted).toBeGreaterThan(0);
462
+ expect(result.agentsEmitted).toBeGreaterThan(0);
463
+ expect(result.rulesEmitted).toBeGreaterThan(0);
464
+ });
465
+
466
+ test('compiles only specified modules', async () => {
467
+ const result = await compileMultiModule({
468
+ sourceBaseDir: path.join(tempDir, '.claude'),
469
+ outputBaseDir: tempDir,
470
+ platforms: ['codex'],
471
+ modules: ['skills'],
472
+ verbose: false
473
+ });
474
+
475
+ expect(result.success).toBe(true);
476
+ expect(result.skillsEmitted).toBeGreaterThan(0);
477
+ expect(result.agentsEmitted).toBe(0);
478
+ expect(result.rulesEmitted).toBe(0);
479
+ });
480
+
481
+ test('handles missing source directories gracefully', async () => {
482
+ const emptyDir = createTempDir();
483
+
484
+ try {
485
+ const result = await compileMultiModule({
486
+ sourceBaseDir: path.join(emptyDir, '.claude'),
487
+ outputBaseDir: emptyDir,
488
+ platforms: ['codex'],
489
+ modules: ['skills', 'agents', 'rules'],
490
+ verbose: false
491
+ });
492
+
493
+ expect(result.success).toBe(true);
494
+ expect(result.skillsEmitted).toBe(0);
495
+ expect(result.agentsEmitted).toBe(0);
496
+ expect(result.rulesEmitted).toBe(0);
497
+ } finally {
498
+ cleanupTempDir(emptyDir);
499
+ }
500
+ });
501
+
502
+ test('exports DEFAULT_MODULES', () => {
503
+ expect(DEFAULT_MODULES).toContain('skills');
504
+ expect(DEFAULT_MODULES).toContain('commands');
505
+ expect(DEFAULT_MODULES).toContain('agents');
506
+ expect(DEFAULT_MODULES).toContain('rules');
507
+ });
508
+ });