cc-devflow 1.0.1

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 (277) hide show
  1. package/.claude/CLAUDE.md +83 -0
  2. package/.claude/agents/architecture-designer.md +443 -0
  3. package/.claude/agents/bug-analyzer.md +382 -0
  4. package/.claude/agents/checklist-agent.md +175 -0
  5. package/.claude/agents/clarify-analyst.md +50 -0
  6. package/.claude/agents/code-reviewer.md +71 -0
  7. package/.claude/agents/codex-analyzer.md +39 -0
  8. package/.claude/agents/compatibility-checker.md +580 -0
  9. package/.claude/agents/consistency-checker.md +532 -0
  10. package/.claude/agents/impact-analyzer.md +441 -0
  11. package/.claude/agents/planner.md +230 -0
  12. package/.claude/agents/prd-writer.md +320 -0
  13. package/.claude/agents/project-guidelines-generator.md +1329 -0
  14. package/.claude/agents/qa-tester.md +313 -0
  15. package/.claude/agents/release-manager.md +295 -0
  16. package/.claude/agents/security-reviewer.md +314 -0
  17. package/.claude/agents/style-guide-generator.md +458 -0
  18. package/.claude/agents/tech-architect.md +516 -0
  19. package/.claude/agents/ui-designer.md +485 -0
  20. package/.claude/commands/code-review-high.md +58 -0
  21. package/.claude/commands/core-architecture.md +429 -0
  22. package/.claude/commands/core-guidelines.md +486 -0
  23. package/.claude/commands/core-roadmap.md +439 -0
  24. package/.claude/commands/core-style.md +293 -0
  25. package/.claude/commands/flow-archive.md +245 -0
  26. package/.claude/commands/flow-checklist.md +260 -0
  27. package/.claude/commands/flow-clarify.md +136 -0
  28. package/.claude/commands/flow-constitution.md +82 -0
  29. package/.claude/commands/flow-dev.md +134 -0
  30. package/.claude/commands/flow-epic.md +150 -0
  31. package/.claude/commands/flow-fix.md +104 -0
  32. package/.claude/commands/flow-ideate.md +214 -0
  33. package/.claude/commands/flow-init.md +313 -0
  34. package/.claude/commands/flow-new.md +394 -0
  35. package/.claude/commands/flow-prd.md +131 -0
  36. package/.claude/commands/flow-qa.md +93 -0
  37. package/.claude/commands/flow-release.md +92 -0
  38. package/.claude/commands/flow-restart.md +98 -0
  39. package/.claude/commands/flow-status.md +64 -0
  40. package/.claude/commands/flow-tech.md +142 -0
  41. package/.claude/commands/flow-ui.md +189 -0
  42. package/.claude/commands/flow-update.md +111 -0
  43. package/.claude/commands/flow-upgrade.md +115 -0
  44. package/.claude/commands/flow-verify.md +96 -0
  45. package/.claude/commands/problem-analyzer.md +60 -0
  46. package/.claude/config/quality-rules.yml +161 -0
  47. package/.claude/docs/SPEC_KIT_CONSTITUTION_ANALYSIS.md +426 -0
  48. package/.claude/docs/design/consistency-conflict-detection-algorithms.md +658 -0
  49. package/.claude/docs/design/intent-driven-input-design.md +380 -0
  50. package/.claude/docs/design/prd-version-management-design.md +437 -0
  51. package/.claude/docs/guides/INIT_TROUBLESHOOTING.md +117 -0
  52. package/.claude/docs/guides/NEW_TROUBLESHOOTING.md +151 -0
  53. package/.claude/docs/guides/ROADMAP_TROUBLESHOOTING.md +188 -0
  54. package/.claude/docs/guides/TASK_COMPLETION_MARKING.md +338 -0
  55. package/.claude/docs/templates/ARCHITECTURE_TEMPLATE.md +633 -0
  56. package/.claude/docs/templates/BACKLOG_TEMPLATE.md +261 -0
  57. package/.claude/docs/templates/CHECKLIST_TEMPLATE.md +52 -0
  58. package/.claude/docs/templates/CLARIFICATION_REPORT_TEMPLATE.md +206 -0
  59. package/.claude/docs/templates/CODE_REVIEW_TEMPLATE.md +71 -0
  60. package/.claude/docs/templates/EPIC_TEMPLATE.md +805 -0
  61. package/.claude/docs/templates/INIT_FLOW_TEMPLATE.md +213 -0
  62. package/.claude/docs/templates/INTENT_CLARIFICATION_TEMPLATE.md +57 -0
  63. package/.claude/docs/templates/NEW_ORCHESTRATION_TEMPLATE.md +148 -0
  64. package/.claude/docs/templates/PRD_TEMPLATE.md +562 -0
  65. package/.claude/docs/templates/RESEARCH_TEMPLATE.md +276 -0
  66. package/.claude/docs/templates/REVIEW-HIGH.md +57 -0
  67. package/.claude/docs/templates/ROADMAP_DIALOGUE_TEMPLATE.md +198 -0
  68. package/.claude/docs/templates/ROADMAP_TEMPLATE.md +310 -0
  69. package/.claude/docs/templates/STYLE_TEMPLATE.md +1266 -0
  70. package/.claude/docs/templates/TASKS_TEMPLATE.md +523 -0
  71. package/.claude/docs/templates/TECH_DESIGN_TEMPLATE.md +1019 -0
  72. package/.claude/docs/templates/UI_PROTOTYPE_TEMPLATE.md +1436 -0
  73. package/.claude/guides/agent-guides/agent-coordination-guide.md +459 -0
  74. package/.claude/guides/project-guidelines-system.md +463 -0
  75. package/.claude/guides/technical-guides/datetime-handling-guide.md +563 -0
  76. package/.claude/guides/technical-guides/git-github-guide.md +642 -0
  77. package/.claude/guides/technical-guides/test-execution-guide.md +618 -0
  78. package/.claude/guides/workflow-guides/bug-fix-orchestrator.md +217 -0
  79. package/.claude/guides/workflow-guides/flow-orchestrator.md +282 -0
  80. package/.claude/hooks/checklist-gate.js +397 -0
  81. package/.claude/hooks/error-handling-reminder.sh +12 -0
  82. package/.claude/hooks/error-handling-reminder.ts +459 -0
  83. package/.claude/hooks/post-tool-use-tracker.sh +280 -0
  84. package/.claude/hooks/pre-tool-use-guardrail.sh +36 -0
  85. package/.claude/hooks/pre-tool-use-guardrail.ts +342 -0
  86. package/.claude/hooks/skill-activation-prompt.sh +36 -0
  87. package/.claude/hooks/skill-activation-prompt.ts +214 -0
  88. package/.claude/hooks/state/skills-used-test-guard.json +3 -0
  89. package/.claude/rules/devflow-conventions.md +305 -0
  90. package/.claude/rules/project-constitution.md +748 -0
  91. package/.claude/schemas/constitution.schema.json +43 -0
  92. package/.claude/scripts/analyze-upgrade-impact.sh +200 -0
  93. package/.claude/scripts/archive-requirement.sh +351 -0
  94. package/.claude/scripts/calculate-checklist-completion.sh +243 -0
  95. package/.claude/scripts/calculate-quarter.sh +206 -0
  96. package/.claude/scripts/check-dependencies.sh +409 -0
  97. package/.claude/scripts/check-prerequisites.sh +232 -0
  98. package/.claude/scripts/check-task-status.sh +264 -0
  99. package/.claude/scripts/checklist-errors.sh +131 -0
  100. package/.claude/scripts/common.sh +570 -0
  101. package/.claude/scripts/consolidate-research.sh +182 -0
  102. package/.claude/scripts/create-requirement.sh +426 -0
  103. package/.claude/scripts/export-contracts.sh +117 -0
  104. package/.claude/scripts/extract-data-model.sh +78 -0
  105. package/.claude/scripts/generate-clarification-questions.sh +377 -0
  106. package/.claude/scripts/generate-clarification-report.sh +463 -0
  107. package/.claude/scripts/generate-quickstart.sh +146 -0
  108. package/.claude/scripts/generate-research-tasks.sh +157 -0
  109. package/.claude/scripts/generate-status-report.sh +523 -0
  110. package/.claude/scripts/generate-tech-analysis.sh +46 -0
  111. package/.claude/scripts/locate-requirement-in-roadmap.sh +233 -0
  112. package/.claude/scripts/manage-constitution.sh +602 -0
  113. package/.claude/scripts/mark-task-complete.sh +198 -0
  114. package/.claude/scripts/populate-research-tasks.sh +259 -0
  115. package/.claude/scripts/recover-workflow.sh +460 -0
  116. package/.claude/scripts/run-clarify-scan.sh +601 -0
  117. package/.claude/scripts/run-high-review.sh +62 -0
  118. package/.claude/scripts/run-problem-analysis.sh +68 -0
  119. package/.claude/scripts/setup-epic.sh +173 -0
  120. package/.claude/scripts/sync-roadmap-progress.sh +300 -0
  121. package/.claude/scripts/sync-task-marks.sh +199 -0
  122. package/.claude/scripts/test-clarify-scan.sh +515 -0
  123. package/.claude/scripts/update-agent-context.sh +806 -0
  124. package/.claude/scripts/validate-constitution.sh +567 -0
  125. package/.claude/scripts/validate-hooks.sh +487 -0
  126. package/.claude/scripts/validate-research.sh +332 -0
  127. package/.claude/scripts/validate-scope-boundary.sh +493 -0
  128. package/.claude/scripts/verify-setup.sh +37 -0
  129. package/.claude/settings.json +76 -0
  130. package/.claude/skills/_reference-implementations/README.md +96 -0
  131. package/.claude/skills/_reference-implementations/backend-express-prisma/SKILL.md +302 -0
  132. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/architecture-overview.md +451 -0
  133. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/async-and-errors.md +307 -0
  134. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/complete-examples.md +638 -0
  135. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/configuration.md +275 -0
  136. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/database-patterns.md +224 -0
  137. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/middleware-guide.md +213 -0
  138. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/routing-and-controllers.md +756 -0
  139. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/sentry-and-monitoring.md +336 -0
  140. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/services-and-repositories.md +789 -0
  141. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/testing-guide.md +235 -0
  142. package/.claude/skills/_reference-implementations/backend-express-prisma/resources/validation-patterns.md +754 -0
  143. package/.claude/skills/_reference-implementations/frontend-react-mui/SKILL.md +399 -0
  144. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/common-patterns.md +331 -0
  145. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/complete-examples.md +872 -0
  146. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/component-patterns.md +502 -0
  147. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/data-fetching.md +767 -0
  148. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/file-organization.md +502 -0
  149. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/loading-and-error-states.md +501 -0
  150. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/performance.md +406 -0
  151. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/routing-guide.md +364 -0
  152. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/styling-guide.md +428 -0
  153. package/.claude/skills/_reference-implementations/frontend-react-mui/resources/typescript-standards.md +418 -0
  154. package/.claude/skills/cc-devflow-orchestrator/SKILL.md +229 -0
  155. package/.claude/skills/constitution-guardian/SKILL.md +306 -0
  156. package/.claude/skills/devflow-constitution-quick-ref/SKILL.md +374 -0
  157. package/.claude/skills/devflow-file-standards/SKILL.md +353 -0
  158. package/.claude/skills/devflow-tdd-enforcer/SKILL.md +192 -0
  159. package/.claude/skills/skill-developer/ADVANCED.md +197 -0
  160. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
  161. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
  162. package/.claude/skills/skill-developer/SKILL.md +426 -0
  163. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
  164. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
  165. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
  166. package/.claude/skills/skill-rules.json +213 -0
  167. package/.claude/tests/README.md +300 -0
  168. package/.claude/tests/TODO.md +69 -0
  169. package/.claude/tests/__pycache__/test_analyze_upgrade_impact.cpython-311-pytest-7.2.2.pyc +0 -0
  170. package/.claude/tests/__pycache__/test_consolidate_research.cpython-311-pytest-7.2.2.pyc +0 -0
  171. package/.claude/tests/__pycache__/test_export_contracts.cpython-311-pytest-7.2.2.pyc +0 -0
  172. package/.claude/tests/__pycache__/test_extract_data_model.cpython-311-pytest-7.2.2.pyc +0 -0
  173. package/.claude/tests/__pycache__/test_generate_quickstart.cpython-311-pytest-7.2.2.pyc +0 -0
  174. package/.claude/tests/__pycache__/test_generate_research_tasks.cpython-311-pytest-7.2.2.pyc +0 -0
  175. package/.claude/tests/constitution/run_all_constitution_tests.sh +111 -0
  176. package/.claude/tests/constitution/test_agent_assignment.sh +207 -0
  177. package/.claude/tests/constitution/test_article_coverage.sh +201 -0
  178. package/.claude/tests/constitution/test_template_completeness.sh +150 -0
  179. package/.claude/tests/constitution/test_version_consistency.sh +120 -0
  180. package/.claude/tests/fixtures/spec_delta_full.md +16 -0
  181. package/.claude/tests/fixtures/tasks_progress_sample.md +5 -0
  182. package/.claude/tests/run-all-tests.sh +229 -0
  183. package/.claude/tests/scripts/run.sh +30 -0
  184. package/.claude/tests/scripts/test-framework.sh +128 -0
  185. package/.claude/tests/scripts/test_check_prerequisites.sh +511 -0
  186. package/.claude/tests/scripts/test_check_prerequisites.sh.bak +504 -0
  187. package/.claude/tests/scripts/test_check_prerequisites.sh.bak2 +505 -0
  188. package/.claude/tests/scripts/test_check_prerequisites.sh.bak3 +506 -0
  189. package/.claude/tests/scripts/test_check_prerequisites.sh.bak4 +507 -0
  190. package/.claude/tests/scripts/test_check_prerequisites.sh.bak5 +508 -0
  191. package/.claude/tests/scripts/test_check_task_status.sh +499 -0
  192. package/.claude/tests/scripts/test_common.sh +244 -0
  193. package/.claude/tests/scripts/test_generate_status_report.sh +71 -0
  194. package/.claude/tests/scripts/test_mark_task_complete.sh +441 -0
  195. package/.claude/tests/scripts/test_mark_task_complete.sh.backup +410 -0
  196. package/.claude/tests/scripts/test_recover_workflow.sh +304 -0
  197. package/.claude/tests/scripts/test_setup_epic.sh +437 -0
  198. package/.claude/tests/scripts/test_sync_task_marks.sh +196 -0
  199. package/.claude/tests/scripts/test_validate_constitution.sh +74 -0
  200. package/.claude/tests/scripts/test_validate_research.sh +462 -0
  201. package/.claude/tests/slugify.bats +82 -0
  202. package/.claude/tests/test-framework.sh +732 -0
  203. package/.claude/tests/test_analyze_upgrade_impact.py +34 -0
  204. package/.claude/tests/test_consolidate_research.py +48 -0
  205. package/.claude/tests/test_export_contracts.py +43 -0
  206. package/.claude/tests/test_extract_data_model.py +33 -0
  207. package/.claude/tests/test_generate_quickstart.py +50 -0
  208. package/.claude/tests/test_generate_research_tasks.py +52 -0
  209. package/.claude/tsc-cache/6e64f818-6398-49ca-8623-581a9af85c44/edited-files.log +1 -0
  210. package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +1 -0
  211. package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/edited-files.log +1 -0
  212. package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +1 -0
  213. package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/edited-files.log +1 -0
  214. package/CHANGELOG.md +507 -0
  215. package/LICENSE +21 -0
  216. package/README.md +534 -0
  217. package/README.zh-CN.md +530 -0
  218. package/bin/adapt.js +240 -0
  219. package/bin/cc-devflow-cli.js +185 -0
  220. package/bin/cc-devflow.js +78 -0
  221. package/config/adapters.yml +5 -0
  222. package/config/schema/adapters.schema.json +44 -0
  223. package/docs/CLAUDE.md +26 -0
  224. package/docs/commands/README.md +61 -0
  225. package/docs/commands/README.zh-CN.md +55 -0
  226. package/docs/commands/core-roadmap.md +106 -0
  227. package/docs/commands/core-roadmap.zh-CN.md +102 -0
  228. package/docs/commands/core-style.md +405 -0
  229. package/docs/commands/core-style.zh-CN.md +405 -0
  230. package/docs/commands/flow-init.md +134 -0
  231. package/docs/commands/flow-init.zh-CN.md +163 -0
  232. package/docs/commands/flow-new.md +274 -0
  233. package/docs/commands/flow-new.zh-CN.md +270 -0
  234. package/docs/guides/getting-started.md +204 -0
  235. package/docs/guides/getting-started.zh-CN.md +152 -0
  236. package/lib/adapters/adapter-interface.js +57 -0
  237. package/lib/adapters/claude-adapter.js +74 -0
  238. package/lib/adapters/codex-adapter.js +40 -0
  239. package/lib/adapters/config-validator.js +68 -0
  240. package/lib/adapters/logger.js +42 -0
  241. package/lib/adapters/registry.js +153 -0
  242. package/lib/compiler/CLAUDE.md +92 -0
  243. package/lib/compiler/__tests__/drift.test.js +215 -0
  244. package/lib/compiler/__tests__/errors.test.js +184 -0
  245. package/lib/compiler/__tests__/incremental.test.js +174 -0
  246. package/lib/compiler/__tests__/integration.test.js +174 -0
  247. package/lib/compiler/__tests__/manifest.test.js +233 -0
  248. package/lib/compiler/__tests__/parser.test.js +456 -0
  249. package/lib/compiler/__tests__/schemas.test.js +301 -0
  250. package/lib/compiler/__tests__/skills-registry.test.js +125 -0
  251. package/lib/compiler/__tests__/transformer.test.js +286 -0
  252. package/lib/compiler/emitters/antigravity-emitter.js +171 -0
  253. package/lib/compiler/emitters/base-emitter.js +73 -0
  254. package/lib/compiler/emitters/codex-emitter.js +52 -0
  255. package/lib/compiler/emitters/cursor-emitter.js +31 -0
  256. package/lib/compiler/emitters/index.js +50 -0
  257. package/lib/compiler/emitters/qwen-emitter.js +39 -0
  258. package/lib/compiler/errors.js +119 -0
  259. package/lib/compiler/index.js +256 -0
  260. package/lib/compiler/manifest.js +242 -0
  261. package/lib/compiler/parser.js +258 -0
  262. package/lib/compiler/platforms.js +113 -0
  263. package/lib/compiler/resource-copier.js +320 -0
  264. package/lib/compiler/rules-emitters/__tests__/antigravity-rules-emitter.test.js +191 -0
  265. package/lib/compiler/rules-emitters/__tests__/codex-rules-emitter.test.js +109 -0
  266. package/lib/compiler/rules-emitters/__tests__/cursor-rules-emitter.test.js +123 -0
  267. package/lib/compiler/rules-emitters/__tests__/qwen-rules-emitter.test.js +123 -0
  268. package/lib/compiler/rules-emitters/antigravity-rules-emitter.js +253 -0
  269. package/lib/compiler/rules-emitters/base-rules-emitter.js +83 -0
  270. package/lib/compiler/rules-emitters/codex-rules-emitter.js +116 -0
  271. package/lib/compiler/rules-emitters/cursor-rules-emitter.js +98 -0
  272. package/lib/compiler/rules-emitters/index.js +71 -0
  273. package/lib/compiler/rules-emitters/qwen-rules-emitter.js +70 -0
  274. package/lib/compiler/schemas.js +144 -0
  275. package/lib/compiler/skills-registry.js +225 -0
  276. package/lib/compiler/transformer.js +236 -0
  277. package/package.json +50 -0
@@ -0,0 +1,456 @@
1
+ /**
2
+ * T011: Parser Tests
3
+ * Tests for parsing .claude/commands/*.md files
4
+ * Expected: All tests FAIL (parser not implemented)
5
+ */
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const os = require('os');
9
+
10
+ // ============================================================
11
+ // Parser imports - will fail until lib/compiler/parser.js exists
12
+ // ============================================================
13
+ let parser;
14
+ let errors;
15
+ try {
16
+ parser = require('../parser.js');
17
+ errors = require('../errors.js');
18
+ } catch (e) {
19
+ parser = null;
20
+ errors = null;
21
+ }
22
+
23
+ // Helper to create temp files for testing
24
+ const createTempFile = (content) => {
25
+ const tmpDir = os.tmpdir();
26
+ const tmpFile = path.join(tmpDir, `test-${Date.now()}-${Math.random().toString(36).slice(2)}.md`);
27
+ fs.writeFileSync(tmpFile, content);
28
+ return tmpFile;
29
+ };
30
+
31
+ const cleanup = (filePath) => {
32
+ try {
33
+ fs.unlinkSync(filePath);
34
+ } catch (e) {
35
+ // ignore
36
+ }
37
+ };
38
+
39
+ describe('Parser Module', () => {
40
+ beforeEach(() => {
41
+ if (!parser) {
42
+ throw new Error('parser.js not implemented');
43
+ }
44
+ });
45
+
46
+ // ----------------------------------------------------------
47
+ // AC1: Parse file with YAML frontmatter
48
+ // ----------------------------------------------------------
49
+ describe('AC1: Parse YAML frontmatter', () => {
50
+ it('should parse file with valid frontmatter and return IR', () => {
51
+ const content = `---
52
+ name: flow-prd
53
+ description: Generate PRD document
54
+ scripts:
55
+ prereq: .claude/scripts/check-prerequisites.sh
56
+ ---
57
+ # Flow PRD Command
58
+
59
+ Run {SCRIPT:prereq}
60
+ `;
61
+ const tmpFile = createTempFile(content);
62
+ try {
63
+ const ir = parser.parseCommand(tmpFile);
64
+ expect(ir.frontmatter.name).toBe('flow-prd');
65
+ expect(ir.frontmatter.description).toBe('Generate PRD document');
66
+ expect(ir.frontmatter.scripts).toBeDefined();
67
+ expect(ir.frontmatter.scripts.prereq).toBe('.claude/scripts/check-prerequisites.sh');
68
+ } finally {
69
+ cleanup(tmpFile);
70
+ }
71
+ });
72
+
73
+ it('should extract body content without frontmatter', () => {
74
+ const content = `---
75
+ name: test-cmd
76
+ description: Test command
77
+ ---
78
+ # Test Command
79
+
80
+ Body content here
81
+ `;
82
+ const tmpFile = createTempFile(content);
83
+ try {
84
+ const ir = parser.parseCommand(tmpFile);
85
+ expect(ir.body).toContain('# Test Command');
86
+ expect(ir.body).toContain('Body content here');
87
+ expect(ir.body).not.toContain('---');
88
+ expect(ir.body).not.toContain('name: test-cmd');
89
+ } finally {
90
+ cleanup(tmpFile);
91
+ }
92
+ });
93
+ });
94
+
95
+ // ----------------------------------------------------------
96
+ // AC2: Parse frontmatter scripts mapping
97
+ // ----------------------------------------------------------
98
+ describe('AC2: Parse scripts alias-path mapping', () => {
99
+ it('should parse multiple scripts aliases', () => {
100
+ const content = `---
101
+ name: flow-test
102
+ description: Test
103
+ scripts:
104
+ prereq: .claude/scripts/prereq.sh
105
+ validate: .claude/scripts/validate.sh
106
+ check_tasks: .claude/scripts/check-tasks.sh
107
+ ---
108
+ Body
109
+ `;
110
+ const tmpFile = createTempFile(content);
111
+ try {
112
+ const ir = parser.parseCommand(tmpFile);
113
+ expect(Object.keys(ir.frontmatter.scripts)).toHaveLength(3);
114
+ expect(ir.frontmatter.scripts.prereq).toBe('.claude/scripts/prereq.sh');
115
+ expect(ir.frontmatter.scripts.validate).toBe('.claude/scripts/validate.sh');
116
+ expect(ir.frontmatter.scripts.check_tasks).toBe('.claude/scripts/check-tasks.sh');
117
+ } finally {
118
+ cleanup(tmpFile);
119
+ }
120
+ });
121
+
122
+ it('should handle frontmatter without scripts', () => {
123
+ const content = `---
124
+ name: simple-cmd
125
+ description: Simple command without scripts
126
+ ---
127
+ Body
128
+ `;
129
+ const tmpFile = createTempFile(content);
130
+ try {
131
+ const ir = parser.parseCommand(tmpFile);
132
+ expect(ir.frontmatter.scripts).toBeUndefined();
133
+ } finally {
134
+ cleanup(tmpFile);
135
+ }
136
+ });
137
+ });
138
+
139
+ // ----------------------------------------------------------
140
+ // AC3: Detect {SCRIPT:*} placeholders in body
141
+ // ----------------------------------------------------------
142
+ describe('AC3: Detect SCRIPT placeholders', () => {
143
+ it('should detect single {SCRIPT:alias} placeholder', () => {
144
+ const content = `---
145
+ name: test
146
+ description: Test
147
+ scripts:
148
+ prereq: .claude/scripts/prereq.sh
149
+ ---
150
+ Run {SCRIPT:prereq}
151
+ `;
152
+ const tmpFile = createTempFile(content);
153
+ try {
154
+ const ir = parser.parseCommand(tmpFile);
155
+ const scriptPlaceholders = ir.placeholders.filter(p => p.type === 'SCRIPT');
156
+ expect(scriptPlaceholders).toHaveLength(1);
157
+ expect(scriptPlaceholders[0].alias).toBe('prereq');
158
+ expect(scriptPlaceholders[0].raw).toBe('{SCRIPT:prereq}');
159
+ } finally {
160
+ cleanup(tmpFile);
161
+ }
162
+ });
163
+
164
+ it('should detect multiple {SCRIPT:*} placeholders', () => {
165
+ const content = `---
166
+ name: test
167
+ description: Test
168
+ scripts:
169
+ first: .claude/scripts/first.sh
170
+ second: .claude/scripts/second.sh
171
+ ---
172
+ Run {SCRIPT:first} then {SCRIPT:second}
173
+ `;
174
+ const tmpFile = createTempFile(content);
175
+ try {
176
+ const ir = parser.parseCommand(tmpFile);
177
+ const scriptPlaceholders = ir.placeholders.filter(p => p.type === 'SCRIPT');
178
+ expect(scriptPlaceholders).toHaveLength(2);
179
+ expect(scriptPlaceholders.map(p => p.alias).sort()).toEqual(['first', 'second']);
180
+ } finally {
181
+ cleanup(tmpFile);
182
+ }
183
+ });
184
+
185
+ it('should detect {AGENT_SCRIPT} placeholder', () => {
186
+ const content = `---
187
+ name: test
188
+ description: Test
189
+ agent_scripts:
190
+ sh: echo "test"
191
+ ---
192
+ Execute {AGENT_SCRIPT}
193
+ `;
194
+ const tmpFile = createTempFile(content);
195
+ try {
196
+ const ir = parser.parseCommand(tmpFile);
197
+ const agentPlaceholders = ir.placeholders.filter(p => p.type === 'AGENT_SCRIPT');
198
+ expect(agentPlaceholders).toHaveLength(1);
199
+ expect(agentPlaceholders[0].raw).toBe('{AGENT_SCRIPT}');
200
+ } finally {
201
+ cleanup(tmpFile);
202
+ }
203
+ });
204
+
205
+ it('should detect $ARGUMENTS placeholder', () => {
206
+ const content = `---
207
+ name: test
208
+ description: Test
209
+ ---
210
+ Input: $ARGUMENTS
211
+ `;
212
+ const tmpFile = createTempFile(content);
213
+ try {
214
+ const ir = parser.parseCommand(tmpFile);
215
+ const argPlaceholders = ir.placeholders.filter(p => p.type === 'ARGUMENTS');
216
+ expect(argPlaceholders).toHaveLength(1);
217
+ expect(argPlaceholders[0].raw).toBe('$ARGUMENTS');
218
+ } finally {
219
+ cleanup(tmpFile);
220
+ }
221
+ });
222
+
223
+ it('should record placeholder positions', () => {
224
+ const content = `---
225
+ name: test
226
+ description: Test
227
+ scripts:
228
+ x: .claude/scripts/test.sh
229
+ ---
230
+ Start {SCRIPT:x} end
231
+ `;
232
+ const tmpFile = createTempFile(content);
233
+ try {
234
+ const ir = parser.parseCommand(tmpFile);
235
+ const placeholder = ir.placeholders.find(p => p.type === 'SCRIPT');
236
+ expect(placeholder.position).toBeDefined();
237
+ expect(typeof placeholder.position.start).toBe('number');
238
+ expect(typeof placeholder.position.end).toBe('number');
239
+ expect(placeholder.position.end).toBeGreaterThan(placeholder.position.start);
240
+ } finally {
241
+ cleanup(tmpFile);
242
+ }
243
+ });
244
+ });
245
+
246
+ // ----------------------------------------------------------
247
+ // AC4: Throw MissingFrontmatterError
248
+ // ----------------------------------------------------------
249
+ describe('AC4: Error on missing frontmatter', () => {
250
+ it('should throw MissingFrontmatterError for file without frontmatter', () => {
251
+ const content = `# No Frontmatter
252
+
253
+ Just markdown content
254
+ `;
255
+ const tmpFile = createTempFile(content);
256
+ try {
257
+ expect(() => parser.parseCommand(tmpFile)).toThrow(errors.MissingFrontmatterError);
258
+ } finally {
259
+ cleanup(tmpFile);
260
+ }
261
+ });
262
+
263
+ it('should throw MissingFrontmatterError for empty file', () => {
264
+ const tmpFile = createTempFile('');
265
+ try {
266
+ expect(() => parser.parseCommand(tmpFile)).toThrow(errors.MissingFrontmatterError);
267
+ } finally {
268
+ cleanup(tmpFile);
269
+ }
270
+ });
271
+
272
+ it('should throw InvalidFrontmatterError for missing required fields', () => {
273
+ const content = `---
274
+ name: test-only-name
275
+ ---
276
+ Body
277
+ `;
278
+ const tmpFile = createTempFile(content);
279
+ try {
280
+ expect(() => parser.parseCommand(tmpFile)).toThrow(errors.InvalidFrontmatterError);
281
+ } finally {
282
+ cleanup(tmpFile);
283
+ }
284
+ });
285
+ });
286
+
287
+ // ----------------------------------------------------------
288
+ // AC5: Throw UnknownAliasError for undefined script alias
289
+ // ----------------------------------------------------------
290
+ describe('AC5: Error on unknown script alias', () => {
291
+ it('should throw UnknownAliasError for undefined alias in body', () => {
292
+ const content = `---
293
+ name: test
294
+ description: Test
295
+ scripts:
296
+ known: .claude/scripts/known.sh
297
+ ---
298
+ Run {SCRIPT:unknown}
299
+ `;
300
+ const tmpFile = createTempFile(content);
301
+ try {
302
+ expect(() => parser.parseCommand(tmpFile)).toThrow(errors.UnknownAliasError);
303
+ } finally {
304
+ cleanup(tmpFile);
305
+ }
306
+ });
307
+
308
+ it('should throw UnknownAliasError when scripts is undefined but alias used', () => {
309
+ const content = `---
310
+ name: test
311
+ description: Test
312
+ ---
313
+ Run {SCRIPT:prereq}
314
+ `;
315
+ const tmpFile = createTempFile(content);
316
+ try {
317
+ expect(() => parser.parseCommand(tmpFile)).toThrow(errors.UnknownAliasError);
318
+ } finally {
319
+ cleanup(tmpFile);
320
+ }
321
+ });
322
+ });
323
+
324
+ // ----------------------------------------------------------
325
+ // parseCommand() specific tests
326
+ // ----------------------------------------------------------
327
+ describe('parseCommand()', () => {
328
+ it('should return valid CommandIR structure', () => {
329
+ const content = `---
330
+ name: valid
331
+ description: Valid command
332
+ ---
333
+ Content
334
+ `;
335
+ const tmpFile = createTempFile(content);
336
+ try {
337
+ const ir = parser.parseCommand(tmpFile);
338
+ expect(ir).toHaveProperty('source');
339
+ expect(ir).toHaveProperty('frontmatter');
340
+ expect(ir).toHaveProperty('body');
341
+ expect(ir).toHaveProperty('placeholders');
342
+ expect(ir.source).toHaveProperty('path');
343
+ expect(ir.source).toHaveProperty('filename');
344
+ expect(ir.source).toHaveProperty('hash');
345
+ } finally {
346
+ cleanup(tmpFile);
347
+ }
348
+ });
349
+
350
+ it('should compute SHA-256 hash', () => {
351
+ const content = `---
352
+ name: hash-test
353
+ description: Test hash
354
+ ---
355
+ Content
356
+ `;
357
+ const tmpFile = createTempFile(content);
358
+ try {
359
+ const ir = parser.parseCommand(tmpFile);
360
+ expect(ir.source.hash).toMatch(/^[a-f0-9]{64}$/);
361
+ } finally {
362
+ cleanup(tmpFile);
363
+ }
364
+ });
365
+
366
+ it('should extract filename without extension', () => {
367
+ const content = `---
368
+ name: test
369
+ description: Test
370
+ ---
371
+ Body
372
+ `;
373
+ const tmpFile = createTempFile(content);
374
+ try {
375
+ const ir = parser.parseCommand(tmpFile);
376
+ expect(ir.source.filename).not.toContain('.md');
377
+ } finally {
378
+ cleanup(tmpFile);
379
+ }
380
+ });
381
+ });
382
+
383
+ // ----------------------------------------------------------
384
+ // parseAllCommands() tests
385
+ // ----------------------------------------------------------
386
+ describe('parseAllCommands()', () => {
387
+ it('should return array of CommandIR', async () => {
388
+ // 使用临时目录测试,避免依赖真实代码库状态
389
+ const tmpDir = path.join(os.tmpdir(), `test-array-${Date.now()}`);
390
+ fs.mkdirSync(tmpDir, { recursive: true });
391
+ fs.writeFileSync(path.join(tmpDir, 'test.md'), `---
392
+ name: test
393
+ description: Test command
394
+ ---
395
+ Body`);
396
+ try {
397
+ const result = await parser.parseAllCommands(tmpDir);
398
+ expect(Array.isArray(result)).toBe(true);
399
+ } finally {
400
+ fs.rmSync(tmpDir, { recursive: true, force: true });
401
+ }
402
+ });
403
+
404
+ it('should parse multiple .md files in directory', async () => {
405
+ const tmpDir = path.join(os.tmpdir(), `test-multi-${Date.now()}`);
406
+ fs.mkdirSync(tmpDir, { recursive: true });
407
+ fs.writeFileSync(path.join(tmpDir, 'cmd1.md'), `---
408
+ name: cmd1
409
+ description: Command 1
410
+ ---
411
+ Body 1`);
412
+ fs.writeFileSync(path.join(tmpDir, 'cmd2.md'), `---
413
+ name: cmd2
414
+ description: Command 2
415
+ ---
416
+ Body 2`);
417
+ try {
418
+ const result = await parser.parseAllCommands(tmpDir);
419
+ expect(result.length).toBe(2);
420
+ result.forEach(ir => {
421
+ expect(ir).toHaveProperty('source');
422
+ expect(ir).toHaveProperty('frontmatter');
423
+ expect(ir).toHaveProperty('body');
424
+ });
425
+ } finally {
426
+ fs.rmSync(tmpDir, { recursive: true, force: true });
427
+ }
428
+ });
429
+
430
+ it('should skip non-.md files', async () => {
431
+ // Create temp dir with mixed files
432
+ const tmpDir = path.join(os.tmpdir(), `test-dir-${Date.now()}`);
433
+ fs.mkdirSync(tmpDir, { recursive: true });
434
+
435
+ const mdFile = path.join(tmpDir, 'test.md');
436
+ const txtFile = path.join(tmpDir, 'test.txt');
437
+
438
+ fs.writeFileSync(mdFile, `---
439
+ name: test
440
+ description: Test
441
+ ---
442
+ Body`);
443
+ fs.writeFileSync(txtFile, 'not markdown');
444
+
445
+ try {
446
+ const result = await parser.parseAllCommands(tmpDir);
447
+ expect(result).toHaveLength(1);
448
+ expect(result[0].source.filename).toBe('test');
449
+ } finally {
450
+ fs.unlinkSync(mdFile);
451
+ fs.unlinkSync(txtFile);
452
+ fs.rmdirSync(tmpDir);
453
+ }
454
+ });
455
+ });
456
+ });