@einja/dev-cli 0.1.9 → 0.1.11

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 (251) hide show
  1. package/README.md +30 -2
  2. package/dist/cli.js +3 -6
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +11 -6
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/list.js +1 -1
  8. package/dist/commands/list.js.map +1 -1
  9. package/dist/commands/sync.d.ts.map +1 -1
  10. package/dist/commands/sync.js +69 -7
  11. package/dist/commands/sync.js.map +1 -1
  12. package/dist/commands/sync.test.js +11 -25
  13. package/dist/commands/sync.test.js.map +1 -1
  14. package/dist/commands/task-loop/index.d.ts.map +1 -1
  15. package/dist/commands/task-loop/index.js +5 -2
  16. package/dist/commands/task-loop/index.js.map +1 -1
  17. package/dist/commands/task-loop/lib/__mocks__/child-process.mock.d.ts +227 -0
  18. package/dist/commands/task-loop/lib/__mocks__/child-process.mock.d.ts.map +1 -0
  19. package/dist/commands/task-loop/lib/__mocks__/child-process.mock.js +351 -0
  20. package/dist/commands/task-loop/lib/__mocks__/child-process.mock.js.map +1 -0
  21. package/dist/commands/task-loop/lib/__mocks__/sample-issues.d.ts +46 -0
  22. package/dist/commands/task-loop/lib/__mocks__/sample-issues.d.ts.map +1 -0
  23. package/dist/commands/task-loop/lib/__mocks__/sample-issues.js +224 -0
  24. package/dist/commands/task-loop/lib/__mocks__/sample-issues.js.map +1 -0
  25. package/dist/commands/task-loop/lib/branch-manager.d.ts.map +1 -1
  26. package/dist/commands/task-loop/lib/branch-manager.js +14 -8
  27. package/dist/commands/task-loop/lib/branch-manager.js.map +1 -1
  28. package/dist/commands/task-loop/lib/branch-manager.test.d.ts +2 -0
  29. package/dist/commands/task-loop/lib/branch-manager.test.d.ts.map +1 -0
  30. package/dist/commands/task-loop/lib/branch-manager.test.js +539 -0
  31. package/dist/commands/task-loop/lib/branch-manager.test.js.map +1 -0
  32. package/dist/commands/task-loop/lib/conflict-handler.js +1 -1
  33. package/dist/commands/task-loop/lib/conflict-handler.js.map +1 -1
  34. package/dist/commands/task-loop/lib/dependency-resolver.test.d.ts +2 -0
  35. package/dist/commands/task-loop/lib/dependency-resolver.test.d.ts.map +1 -0
  36. package/dist/commands/task-loop/lib/dependency-resolver.test.js +1129 -0
  37. package/dist/commands/task-loop/lib/dependency-resolver.test.js.map +1 -0
  38. package/dist/commands/task-loop/lib/gh-setup.d.ts.map +1 -1
  39. package/dist/commands/task-loop/lib/gh-setup.js.map +1 -1
  40. package/dist/commands/task-loop/lib/github-client.d.ts.map +1 -1
  41. package/dist/commands/task-loop/lib/github-client.js +3 -3
  42. package/dist/commands/task-loop/lib/github-client.js.map +1 -1
  43. package/dist/commands/task-loop/lib/github-client.test.d.ts +2 -0
  44. package/dist/commands/task-loop/lib/github-client.test.d.ts.map +1 -0
  45. package/dist/commands/task-loop/lib/github-client.test.js +377 -0
  46. package/dist/commands/task-loop/lib/github-client.test.js.map +1 -0
  47. package/dist/commands/task-loop/lib/issue-parser.js +4 -4
  48. package/dist/commands/task-loop/lib/issue-parser.js.map +1 -1
  49. package/dist/commands/task-loop/lib/issue-parser.test.d.ts +2 -0
  50. package/dist/commands/task-loop/lib/issue-parser.test.d.ts.map +1 -0
  51. package/dist/commands/task-loop/lib/issue-parser.test.js +854 -0
  52. package/dist/commands/task-loop/lib/issue-parser.test.js.map +1 -0
  53. package/dist/commands/task-loop/lib/pull-request-manager.d.ts +35 -0
  54. package/dist/commands/task-loop/lib/pull-request-manager.d.ts.map +1 -0
  55. package/dist/commands/task-loop/lib/pull-request-manager.js +150 -0
  56. package/dist/commands/task-loop/lib/pull-request-manager.js.map +1 -0
  57. package/dist/commands/task-loop/lib/task-number-utils.d.ts +10 -4
  58. package/dist/commands/task-loop/lib/task-number-utils.d.ts.map +1 -1
  59. package/dist/commands/task-loop/lib/task-number-utils.js +19 -10
  60. package/dist/commands/task-loop/lib/task-number-utils.js.map +1 -1
  61. package/dist/commands/task-loop/lib/task-number-utils.test.d.ts +2 -0
  62. package/dist/commands/task-loop/lib/task-number-utils.test.d.ts.map +1 -0
  63. package/dist/commands/task-loop/lib/task-number-utils.test.js +379 -0
  64. package/dist/commands/task-loop/lib/task-number-utils.test.js.map +1 -0
  65. package/dist/commands/task-loop/lib/task-state-manager.d.ts.map +1 -1
  66. package/dist/commands/task-loop/lib/task-state-manager.js +1 -1
  67. package/dist/commands/task-loop/lib/task-state-manager.js.map +1 -1
  68. package/dist/commands/task-loop/lib/task-state-manager.test.d.ts +2 -0
  69. package/dist/commands/task-loop/lib/task-state-manager.test.d.ts.map +1 -0
  70. package/dist/commands/task-loop/lib/task-state-manager.test.js +541 -0
  71. package/dist/commands/task-loop/lib/task-state-manager.test.js.map +1 -0
  72. package/dist/lib/file-system.js +1 -1
  73. package/dist/lib/file-system.js.map +1 -1
  74. package/dist/lib/mcp-config.d.ts.map +1 -1
  75. package/dist/lib/mcp-config.js +8 -4
  76. package/dist/lib/mcp-config.js.map +1 -1
  77. package/dist/lib/mcp-config.test.js +2 -2
  78. package/dist/lib/mcp-config.test.js.map +1 -1
  79. package/dist/lib/merger.d.ts.map +1 -1
  80. package/dist/lib/merger.js.map +1 -1
  81. package/dist/lib/preset-update/cli-repo-detector.test.js.map +1 -1
  82. package/dist/lib/preset-update/file-copier.d.ts +2 -0
  83. package/dist/lib/preset-update/file-copier.d.ts.map +1 -1
  84. package/dist/lib/preset-update/file-copier.js +12 -8
  85. package/dist/lib/preset-update/file-copier.js.map +1 -1
  86. package/dist/lib/preset-update/file-copier.test.js +36 -5
  87. package/dist/lib/preset-update/file-copier.test.js.map +1 -1
  88. package/dist/lib/preset-update/preset-finder.d.ts +1 -1
  89. package/dist/lib/preset-update/preset-finder.d.ts.map +1 -1
  90. package/dist/lib/preset-update/preset-finder.js +1 -1
  91. package/dist/lib/preset-update/preset-finder.js.map +1 -1
  92. package/dist/lib/preset-update/preset-finder.test.js +11 -11
  93. package/dist/lib/preset-update/preset-finder.test.js.map +1 -1
  94. package/dist/lib/preset.js +3 -3
  95. package/dist/lib/preset.js.map +1 -1
  96. package/dist/lib/sync/backup-manager.d.ts.map +1 -1
  97. package/dist/lib/sync/backup-manager.js +1 -1
  98. package/dist/lib/sync/backup-manager.js.map +1 -1
  99. package/dist/lib/sync/backup-manager.test.js +2 -2
  100. package/dist/lib/sync/backup-manager.test.js.map +1 -1
  101. package/dist/lib/sync/batch-processor.d.ts.map +1 -1
  102. package/dist/lib/sync/batch-processor.js.map +1 -1
  103. package/dist/lib/sync/batch-processor.test.js.map +1 -1
  104. package/dist/lib/sync/category-validator.d.ts.map +1 -1
  105. package/dist/lib/sync/category-validator.js.map +1 -1
  106. package/dist/lib/sync/category-validator.test.js +2 -11
  107. package/dist/lib/sync/category-validator.test.js.map +1 -1
  108. package/dist/lib/sync/conflict-reporter.d.ts.map +1 -1
  109. package/dist/lib/sync/conflict-reporter.js +1 -2
  110. package/dist/lib/sync/conflict-reporter.js.map +1 -1
  111. package/dist/lib/sync/conflict-reporter.test.js +2 -7
  112. package/dist/lib/sync/conflict-reporter.test.js.map +1 -1
  113. package/dist/lib/sync/diff-engine.d.ts.map +1 -1
  114. package/dist/lib/sync/diff-engine.js +2 -4
  115. package/dist/lib/sync/diff-engine.js.map +1 -1
  116. package/dist/lib/sync/diff-engine.test.js.map +1 -1
  117. package/dist/lib/sync/file-filter.d.ts.map +1 -1
  118. package/dist/lib/sync/file-filter.js +26 -3
  119. package/dist/lib/sync/file-filter.js.map +1 -1
  120. package/dist/lib/sync/file-filter.test.js +26 -2
  121. package/dist/lib/sync/file-filter.test.js.map +1 -1
  122. package/dist/lib/sync/hash-cache.d.ts.map +1 -1
  123. package/dist/lib/sync/hash-cache.js.map +1 -1
  124. package/dist/lib/sync/hash-cache.test.js +2 -2
  125. package/dist/lib/sync/hash-cache.test.js.map +1 -1
  126. package/dist/lib/sync/integration.test.js +289 -2
  127. package/dist/lib/sync/integration.test.js.map +1 -1
  128. package/dist/lib/sync/marker-processor.d.ts +34 -10
  129. package/dist/lib/sync/marker-processor.d.ts.map +1 -1
  130. package/dist/lib/sync/marker-processor.js +142 -41
  131. package/dist/lib/sync/marker-processor.js.map +1 -1
  132. package/dist/lib/sync/marker-processor.test.js +134 -1
  133. package/dist/lib/sync/marker-processor.test.js.map +1 -1
  134. package/dist/lib/sync/metadata-manager.d.ts.map +1 -1
  135. package/dist/lib/sync/metadata-manager.js.map +1 -1
  136. package/dist/lib/sync/metadata-manager.test.js +4 -6
  137. package/dist/lib/sync/metadata-manager.test.js.map +1 -1
  138. package/dist/lib/sync/performance.test.js +2 -2
  139. package/dist/lib/sync/performance.test.js.map +1 -1
  140. package/dist/lib/sync/seed-synchronizer.d.ts +27 -0
  141. package/dist/lib/sync/seed-synchronizer.d.ts.map +1 -0
  142. package/dist/lib/sync/seed-synchronizer.js +72 -0
  143. package/dist/lib/sync/seed-synchronizer.js.map +1 -0
  144. package/dist/lib/sync/seed-synchronizer.test.d.ts +2 -0
  145. package/dist/lib/sync/seed-synchronizer.test.d.ts.map +1 -0
  146. package/dist/lib/sync/seed-synchronizer.test.js +147 -0
  147. package/dist/lib/sync/seed-synchronizer.test.js.map +1 -0
  148. package/dist/types/index.d.ts.map +1 -1
  149. package/dist/types/preset-update.d.ts +1 -1
  150. package/dist/types/sync.d.ts +4 -2
  151. package/dist/types/sync.d.ts.map +1 -1
  152. package/dist/types/sync.js.map +1 -1
  153. package/package.json +1 -2
  154. package/presets/default/.claude/agents/einja/backend-architect.md +1131 -0
  155. package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/design-engineer.md +1 -1
  156. package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/frontend-architect.md +1 -1
  157. package/presets/{minimal/.claude/agents/einja/frontend → default/.claude/agents/einja}/frontend-coder.md +1 -37
  158. package/presets/{minimal → default}/.claude/agents/einja/task/task-committer.md +12 -6
  159. package/presets/{minimal → default}/.claude/agents/einja/task/task-executer.md +9 -9
  160. package/presets/{minimal → default}/.claude/commands/einja/frontend-implement.md +1 -1
  161. package/presets/{minimal → default}/.claude/commands/einja/update-docs-by-task-specs.md +6 -6
  162. package/presets/{minimal/.claude/skills/einja/api-development → default/.claude/skills/einja-api-development}/SKILL.md +5 -5
  163. package/presets/{minimal/.claude/skills/einja/backend-architecture → default/.claude/skills/einja-backend-architecture}/SKILL.md +5 -5
  164. package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/SKILL.md +6 -6
  165. package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/SKILL.md +6 -6
  166. package/presets/{minimal/.claude/skills/einja/frontend-development → default/.claude/skills/einja-frontend-development}/SKILL.md +5 -5
  167. package/presets/{minimal/.claude/skills/einja/output-format → default/.claude/skills/einja-output-format}/SKILL.md +54 -5
  168. package/presets/{minimal → default}/preset.yaml +1 -1
  169. package/presets/{minimal → default}/symlinks.json +10 -10
  170. package/scaffolds/cli/preset.yaml +110 -0
  171. package/scaffolds/example/README.md +35 -0
  172. package/scaffolds/example/specs/issues/issue999-example-task/design.md +879 -0
  173. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/README.md +150 -0
  174. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase1/1-1.md +268 -0
  175. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase1/1-2.md +179 -0
  176. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase1/1-3.md +392 -0
  177. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase1/evidence/.gitkeep +0 -0
  178. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase2/2-1.md +459 -0
  179. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/phase2/evidence/.gitkeep +0 -0
  180. package/scaffolds/example/specs/issues/issue999-example-task/qa-tests/scenarios.md +125 -0
  181. package/scaffolds/example/specs/issues/issue999-example-task/requirements.md +494 -0
  182. package/scaffolds/example/specs/issues/issue999-example-task/tasks.md +212 -0
  183. package/scaffolds/instructions/deployment-setup.md +458 -0
  184. package/scaffolds/instructions/environment-setup.md +509 -0
  185. package/scaffolds/instructions/local-server-environment-and-worktree.md +539 -0
  186. package/scaffolds/instructions/task-execute.md +649 -0
  187. package/scaffolds/instructions/task-vibe-kanban-loop.md +495 -0
  188. package/scaffolds/memory/archive/.gitkeep +0 -0
  189. package/scaffolds/memory/decisions.md +35 -0
  190. package/scaffolds/memory/patterns.md +37 -0
  191. package/scaffolds/steering/README.md +42 -0
  192. package/scaffolds/steering/acceptance-criteria-and-qa-guide.md +11 -0
  193. package/scaffolds/steering/architecture.md +11 -0
  194. package/scaffolds/steering/branch-strategy.md +11 -0
  195. package/scaffolds/steering/commit-rules.md +12 -1
  196. package/scaffolds/steering/db-schema-design.md +11 -0
  197. package/scaffolds/steering/development/api-development.md +15 -4
  198. package/scaffolds/steering/development/backend-architecture.md +11 -0
  199. package/scaffolds/steering/development/frontend-development.md +11 -0
  200. package/scaffolds/steering/development/review-guidelines.md +11 -0
  201. package/scaffolds/steering/development/testing-strategy.md +85 -0
  202. package/scaffolds/steering/development-workflow.md +11 -0
  203. package/scaffolds/steering/infrastructure/deployment.md +11 -0
  204. package/scaffolds/steering/infrastructure/environment-variables.md +11 -0
  205. package/scaffolds/steering/product.md +11 -0
  206. package/scaffolds/steering/task-management.md +11 -0
  207. package/scaffolds/CLAUDE.md.template +0 -386
  208. /package/presets/{minimal → default}/.claude/agents/einja/docs/docs-updater.md +0 -0
  209. /package/presets/{minimal → default}/.claude/agents/einja/git/conflict-resolver.md +0 -0
  210. /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-design-generator.md +0 -0
  211. /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-qa-generator.md +0 -0
  212. /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-requirements-generator.md +0 -0
  213. /package/presets/{minimal → default}/.claude/agents/einja/specs/spec-tasks-generator.md +0 -0
  214. /package/presets/{minimal → default}/.claude/agents/einja/task/task-modification-analyzer.md +0 -0
  215. /package/presets/{minimal → default}/.claude/agents/einja/task/task-qa.md +0 -0
  216. /package/presets/{minimal → default}/.claude/agents/einja/task/task-reviewer.md +0 -0
  217. /package/presets/{minimal → default}/.claude/commands/einja/spec-create.md +0 -0
  218. /package/presets/{minimal → default}/.claude/commands/einja/start-dev.md +0 -0
  219. /package/presets/{minimal → default}/.claude/commands/einja/sync-cursor-commands.md +0 -0
  220. /package/presets/{minimal → default}/.claude/commands/einja/task-exec.md +0 -0
  221. /package/presets/{minimal → default}/.claude/hooks/einja/biome-format.sh +0 -0
  222. /package/presets/{minimal → default}/.claude/hooks/einja/design-doc-check.sh +0 -0
  223. /package/presets/{minimal → default}/.claude/hooks/einja/detect-secrets.sh +0 -0
  224. /package/presets/{minimal → default}/.claude/hooks/einja/large-file-warning.sh +0 -0
  225. /package/presets/{minimal → default}/.claude/hooks/einja/playwright-resize.sh +0 -0
  226. /package/presets/{minimal → default}/.claude/hooks/einja/typecheck.sh +0 -0
  227. /package/presets/{minimal → default}/.claude/hooks/einja/unset-volta-recursion.sh +0 -0
  228. /package/presets/{minimal → default}/.claude/hooks/einja/validate-git-commit.sh +0 -0
  229. /package/presets/{minimal → default}/.claude/hooks/einja/warn-index-ts.sh +0 -0
  230. /package/presets/{minimal → default}/.claude/hooks/einja/warn-relative-import.sh +0 -0
  231. /package/presets/{minimal → default}/.claude/settings.json +0 -0
  232. /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/naming-conventions.md +0 -0
  233. /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/prohibited-patterns.md +0 -0
  234. /package/presets/{minimal/.claude/skills/einja/coding-standards → default/.claude/skills/einja-coding-standards}/reference/typescript-rules.md +0 -0
  235. /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/directory-structure.md +0 -0
  236. /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/props-patterns.md +0 -0
  237. /package/presets/{minimal/.claude/skills/einja/component-design → default/.claude/skills/einja-component-design}/reference/styling-guide.md +0 -0
  238. /package/presets/{minimal/.claude/skills/einja/conflict-resolver → default/.claude/skills/einja-conflict-resolver}/SKILL.md +0 -0
  239. /package/presets/{minimal/.claude/skills/einja/general-context-loader → default/.claude/skills/einja-general-context-loader}/SKILL.md +0 -0
  240. /package/presets/{minimal/.claude/skills/einja/spec-context-loader → default/.claude/skills/einja-spec-context-loader}/SKILL.md +0 -0
  241. /package/presets/{minimal/.claude/skills/einja/task-commit → default/.claude/skills/einja-task-commit}/SKILL.md +0 -0
  242. /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/SKILL.md +0 -0
  243. /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/failure-patterns.md +0 -0
  244. /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/troubleshooting.md +0 -0
  245. /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/reference/usage-patterns.md +0 -0
  246. /package/presets/{minimal/.claude/skills/einja/task-qa → default/.claude/skills/einja-task-qa}/templates/qa-test-template.md +0 -0
  247. /package/{templates → scaffolds/templates}/README.md +0 -0
  248. /package/{templates → scaffolds/templates}/design-simple.md.template +0 -0
  249. /package/{templates → scaffolds/templates}/design.md.template +0 -0
  250. /package/{templates → scaffolds/templates}/qa-test.md.template +0 -0
  251. /package/{templates → scaffolds/templates}/requirements.md.template +0 -0
@@ -0,0 +1,854 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { findTaskGroupById, getAllTaskGroups, getCompletedTaskGroupIds, getPendingTaskGroups, parseIssueBody, } from "./issue-parser.js";
3
+ import { SAMPLE_ISSUE_SIMPLE, SAMPLE_ISSUE_WITH_COMPLETED_TASKS, } from "./__mocks__/sample-issues.js";
4
+ describe("issue-parser", () => {
5
+ describe("parseIssueBody", () => {
6
+ // 正常系
7
+ it("PhaseとタスクグループとタスクのMarkdownをパースすると構造化オブジェクトを返す", () => {
8
+ // Given: Phase、タスクグループ、タスクを含むMarkdown (既存フィクスチャを使用)
9
+ const issue = {
10
+ number: 123,
11
+ title: "基本機能",
12
+ state: "open",
13
+ body: SAMPLE_ISSUE_SIMPLE,
14
+ };
15
+ // When: parseIssueBodyを呼び出す
16
+ const result = parseIssueBody(issue);
17
+ // Then: 構造化されたデータが返る
18
+ expect(result.issueNumber).toBe(123);
19
+ expect(result.title).toBe("基本機能");
20
+ expect(result.phases).toHaveLength(2);
21
+ const phase1 = result.phases[0];
22
+ expect(phase1.number).toBe(1);
23
+ expect(phase1.name).toBe("基本機能");
24
+ expect(phase1.taskGroups).toHaveLength(3);
25
+ const taskGroup1 = phase1.taskGroups[0];
26
+ expect(taskGroup1.id).toBe("1.1");
27
+ expect(taskGroup1.name).toBe("認証機能");
28
+ expect(taskGroup1.status).toBe("pending");
29
+ expect(taskGroup1.dependencies).toEqual([]);
30
+ expect(taskGroup1.completionCriteria).toBe("ログイン・ログアウトが動作すること");
31
+ const taskGroup2 = phase1.taskGroups[1];
32
+ expect(taskGroup2.id).toBe("1.2");
33
+ expect(taskGroup2.name).toBe("ユーザー管理");
34
+ expect(taskGroup2.status).toBe("pending");
35
+ expect(taskGroup2.dependencies).toEqual(["1.1"]);
36
+ });
37
+ it("チェック済みタスクグループ[x]の場合、statusがcompletedになる", () => {
38
+ // Given: チェック済みタスクグループを含むMarkdown (既存フィクスチャを使用)
39
+ const issue = {
40
+ number: 1,
41
+ title: "Test",
42
+ state: "open",
43
+ body: SAMPLE_ISSUE_WITH_COMPLETED_TASKS,
44
+ };
45
+ // When: parseIssueBodyを呼び出す
46
+ const result = parseIssueBody(issue);
47
+ // Then: ステータスが正しく設定される
48
+ const phase1 = result.phases[0];
49
+ expect(phase1.taskGroups[0].status).toBe("completed");
50
+ expect(phase1.taskGroups[1].status).toBe("completed");
51
+ expect(phase1.taskGroups[2].status).toBe("pending");
52
+ });
53
+ it("依存関係にタスクグループIDが指定されている場合、dependencies配列に含まれる", () => {
54
+ // Given: 依存関係を持つタスクグループを含むMarkdown
55
+ const issue = {
56
+ number: 1,
57
+ title: "Test",
58
+ state: "open",
59
+ body: `### Phase 1: テスト
60
+
61
+ - [ ] **1.1 基礎タスク**
62
+ **依存関係**: なし
63
+ **完了条件**: 完了
64
+
65
+ - [ ] **1.2 依存タスク**
66
+ **依存関係**: 1.1
67
+ **完了条件**: 完了
68
+
69
+ - [ ] **1.3 複数依存タスク**
70
+ **依存関係**: 1.1, 1.2
71
+ **完了条件**: 完了
72
+ `,
73
+ };
74
+ // When: parseIssueBodyを呼び出す
75
+ const result = parseIssueBody(issue);
76
+ // Then: 依存関係が正しく抽出される
77
+ const phase1 = result.phases[0];
78
+ expect(phase1.taskGroups[0].dependencies).toEqual([]);
79
+ expect(phase1.taskGroups[1].dependencies).toEqual(["1.1"]);
80
+ expect(phase1.taskGroups[2].dependencies).toEqual(["1.1", "1.2"]);
81
+ });
82
+ // 異常系
83
+ it("Phaseヘッダーがない場合、空のPhase配列を返す", () => {
84
+ // Given: Phaseヘッダーがない本文
85
+ const issue = {
86
+ number: 1,
87
+ title: "Test",
88
+ state: "open",
89
+ body: `これはPhaseヘッダーがない本文です。
90
+
91
+ - タスク1
92
+ - タスク2
93
+ `,
94
+ };
95
+ // When: parseIssueBodyを呼び出す
96
+ const result = parseIssueBody(issue);
97
+ // Then: 空のPhase配列が返る
98
+ expect(result.phases).toHaveLength(0);
99
+ });
100
+ it("依存関係セクションがない場合、dependenciesは空配列になる", () => {
101
+ // Given: 依存関係セクションがないタスクグループ
102
+ const issue = {
103
+ number: 1,
104
+ title: "Test",
105
+ state: "open",
106
+ body: `### Phase 1: テスト
107
+
108
+ - [ ] **1.1 タスク名**
109
+ **完了条件**: 完了
110
+ `,
111
+ };
112
+ // When: parseIssueBodyを呼び出す
113
+ const result = parseIssueBody(issue);
114
+ // Then: dependenciesは空配列
115
+ const phase1 = result.phases[0];
116
+ expect(phase1.taskGroups[0].dependencies).toEqual([]);
117
+ });
118
+ it("ボールドなしチェックボックスも認識される", () => {
119
+ // Given: ボールドなしのタスクグループ
120
+ const issue = {
121
+ number: 1,
122
+ title: "Test",
123
+ state: "open",
124
+ body: `### Phase 1: テスト
125
+
126
+ - [ ] 1.1 ボールドなしタスク
127
+ **依存関係**: なし
128
+ **完了条件**: 完了
129
+ `,
130
+ };
131
+ // When: parseIssueBodyを呼び出す
132
+ const result = parseIssueBody(issue);
133
+ // Then: タスクグループが認識される
134
+ const phase1 = result.phases[0];
135
+ expect(phase1.taskGroups).toHaveLength(1);
136
+ expect(phase1.taskGroups[0].id).toBe("1.1");
137
+ expect(phase1.taskGroups[0].name).toBe("ボールドなしタスク");
138
+ });
139
+ it("空のPhaseセクションの場合、taskGroupsは空配列として処理される", () => {
140
+ // Given: タスクグループがないPhase
141
+ const issue = {
142
+ number: 1,
143
+ title: "Test",
144
+ state: "open",
145
+ body: `### Phase 1: 空のフェーズ
146
+
147
+ ### Phase 2: タスクありフェーズ
148
+
149
+ - [ ] **2.1 タスク名**
150
+ **依存関係**: なし
151
+ **完了条件**: 完了
152
+ `,
153
+ };
154
+ // When: parseIssueBodyを呼び出す
155
+ const result = parseIssueBody(issue);
156
+ // Then: 空のPhaseも正しく処理される
157
+ expect(result.phases).toHaveLength(2);
158
+ expect(result.phases[0].taskGroups).toHaveLength(0);
159
+ expect(result.phases[1].taskGroups).toHaveLength(1);
160
+ });
161
+ });
162
+ describe("依存関係パターン", () => {
163
+ it("Phase依存パターン'Phase X完了'が認識される", () => {
164
+ // Given: Phase依存を持つタスクグループ
165
+ const issue = {
166
+ number: 1,
167
+ title: "Test",
168
+ state: "open",
169
+ body: `### Phase 1: フェーズ1
170
+
171
+ - [ ] **1.1 タスク1**
172
+ **依存関係**: なし
173
+ **完了条件**: 完了
174
+
175
+ ### Phase 2: フェーズ2
176
+
177
+ - [ ] **2.1 タスク2**
178
+ **依存関係**: Phase 1完了
179
+ **完了条件**: 完了
180
+ `,
181
+ };
182
+ // When: parseIssueBodyを呼び出す
183
+ const result = parseIssueBody(issue);
184
+ // Then: Phase依存が正しく認識される
185
+ const phase2 = result.phases[1];
186
+ expect(phase2.taskGroups[0].dependencies).toEqual(["Phase 1完了"]);
187
+ });
188
+ it("Issue依存パターン'#123'が認識される", () => {
189
+ // Given: Issue依存を持つタスクグループ
190
+ const issue = {
191
+ number: 1,
192
+ title: "Test",
193
+ state: "open",
194
+ body: `### Phase 1: テスト
195
+
196
+ - [ ] **1.1 タスク名**
197
+ **依存関係**: #123
198
+ **完了条件**: 完了
199
+ `,
200
+ };
201
+ // When: parseIssueBodyを呼び出す
202
+ const result = parseIssueBody(issue);
203
+ // Then: Issue依存が正しく認識される
204
+ const phase1 = result.phases[0];
205
+ expect(phase1.taskGroups[0].dependencies).toEqual(["#123"]);
206
+ });
207
+ it("サブタスクID'1.2.1'はタスクグループID'1.2'に変換される", () => {
208
+ // Given: サブタスクIDを含む依存関係
209
+ const issue = {
210
+ number: 1,
211
+ title: "Test",
212
+ state: "open",
213
+ body: `### Phase 1: テスト
214
+
215
+ - [ ] **1.1 タスク1**
216
+ **依存関係**: なし
217
+ **完了条件**: 完了
218
+
219
+ - [ ] **1.2 タスク2**
220
+ **依存関係**: 1.1.1
221
+ **完了条件**: 完了
222
+ `,
223
+ };
224
+ // When: parseIssueBodyを呼び出す
225
+ const result = parseIssueBody(issue);
226
+ // Then: サブタスクIDがタスクグループIDに変換される
227
+ const phase1 = result.phases[0];
228
+ expect(phase1.taskGroups[1].dependencies).toEqual(["1.1"]);
229
+ });
230
+ it("自己参照の依存関係は除外される", () => {
231
+ // Given: 自己参照する依存関係
232
+ const issue = {
233
+ number: 1,
234
+ title: "Test",
235
+ state: "open",
236
+ body: `### Phase 1: テスト
237
+
238
+ - [ ] **1.1 タスク1**
239
+ **依存関係**: 1.1
240
+ **完了条件**: 完了
241
+ `,
242
+ };
243
+ // When: parseIssueBodyを呼び出す
244
+ const result = parseIssueBody(issue);
245
+ // Then: 自己参照は除外される
246
+ const phase1 = result.phases[0];
247
+ expect(phase1.taskGroups[0].dependencies).toEqual([]);
248
+ });
249
+ it("重複する依存関係は一意化される", () => {
250
+ // Given: 重複する依存関係
251
+ const issue = {
252
+ number: 1,
253
+ title: "Test",
254
+ state: "open",
255
+ body: `### Phase 1: テスト
256
+
257
+ - [ ] **1.1 タスク1**
258
+ **依存関係**: なし
259
+ **完了条件**: 完了
260
+
261
+ - [ ] **1.2 タスク2**
262
+ **依存関係**: 1.1, 1.1.1, 1.1.2
263
+ **完了条件**: 完了
264
+ `,
265
+ };
266
+ // When: parseIssueBodyを呼び出す
267
+ const result = parseIssueBody(issue);
268
+ // Then: 重複が除去される(1.1.1と1.1.2は1.1に変換され、1.1として一意化)
269
+ const phase1 = result.phases[0];
270
+ expect(phase1.taskGroups[1].dependencies).toEqual(["1.1"]);
271
+ });
272
+ it("依存関係が'なし'または'-'の場合、空配列になる", () => {
273
+ // Given: 依存関係が'なし'または'-'
274
+ const issue = {
275
+ number: 1,
276
+ title: "Test",
277
+ state: "open",
278
+ body: `### Phase 1: テスト
279
+
280
+ - [ ] **1.1 タスク1**
281
+ **依存関係**: なし
282
+ **完了条件**: 完了
283
+
284
+ - [ ] **1.2 タスク2**
285
+ **依存関係**: -
286
+ **完了条件**: 完了
287
+ `,
288
+ };
289
+ // When: parseIssueBodyを呼び出す
290
+ const result = parseIssueBody(issue);
291
+ // Then: 両方とも空配列
292
+ const phase1 = result.phases[0];
293
+ expect(phase1.taskGroups[0].dependencies).toEqual([]);
294
+ expect(phase1.taskGroups[1].dependencies).toEqual([]);
295
+ });
296
+ });
297
+ describe("findTaskGroupById", () => {
298
+ it("存在するタスクグループIDを渡すと、該当タスクグループを返す", () => {
299
+ // Given: 複数のタスクグループを持つParsedIssue
300
+ const parsedIssue = {
301
+ issueNumber: 1,
302
+ title: "Test",
303
+ body: "",
304
+ phases: [
305
+ {
306
+ number: 1,
307
+ name: "Phase 1",
308
+ taskGroups: [
309
+ {
310
+ id: "1.1",
311
+ name: "Task 1.1",
312
+ phaseNumber: 1,
313
+ status: "pending",
314
+ dependencies: [],
315
+ completionCriteria: "",
316
+ tasks: [],
317
+ },
318
+ {
319
+ id: "1.2",
320
+ name: "Task 1.2",
321
+ phaseNumber: 1,
322
+ status: "completed",
323
+ dependencies: [],
324
+ completionCriteria: "",
325
+ tasks: [],
326
+ },
327
+ ],
328
+ },
329
+ ],
330
+ };
331
+ // When: findTaskGroupByIdを呼び出す
332
+ const result = findTaskGroupById(parsedIssue, "1.2");
333
+ // Then: 該当タスクグループが返る
334
+ expect(result).not.toBeNull();
335
+ expect(result === null || result === void 0 ? void 0 : result.id).toBe("1.2");
336
+ expect(result === null || result === void 0 ? void 0 : result.name).toBe("Task 1.2");
337
+ });
338
+ it("存在しないタスクグループIDを渡すと、nullを返す", () => {
339
+ // Given: タスクグループを持つParsedIssue
340
+ const parsedIssue = {
341
+ issueNumber: 1,
342
+ title: "Test",
343
+ body: "",
344
+ phases: [
345
+ {
346
+ number: 1,
347
+ name: "Phase 1",
348
+ taskGroups: [
349
+ {
350
+ id: "1.1",
351
+ name: "Task 1.1",
352
+ phaseNumber: 1,
353
+ status: "pending",
354
+ dependencies: [],
355
+ completionCriteria: "",
356
+ tasks: [],
357
+ },
358
+ ],
359
+ },
360
+ ],
361
+ };
362
+ // When: 存在しないIDで検索
363
+ const result = findTaskGroupById(parsedIssue, "2.1");
364
+ // Then: nullが返る
365
+ expect(result).toBeNull();
366
+ });
367
+ });
368
+ describe("getAllTaskGroups", () => {
369
+ it("全Phaseのタスクグループをフラットな配列で返す", () => {
370
+ // Given: 複数Phaseと複数タスクグループを持つParsedIssue
371
+ const parsedIssue = {
372
+ issueNumber: 1,
373
+ title: "Test",
374
+ body: "",
375
+ phases: [
376
+ {
377
+ number: 1,
378
+ name: "Phase 1",
379
+ taskGroups: [
380
+ {
381
+ id: "1.1",
382
+ name: "Task 1.1",
383
+ phaseNumber: 1,
384
+ status: "pending",
385
+ dependencies: [],
386
+ completionCriteria: "",
387
+ tasks: [],
388
+ },
389
+ {
390
+ id: "1.2",
391
+ name: "Task 1.2",
392
+ phaseNumber: 1,
393
+ status: "completed",
394
+ dependencies: [],
395
+ completionCriteria: "",
396
+ tasks: [],
397
+ },
398
+ ],
399
+ },
400
+ {
401
+ number: 2,
402
+ name: "Phase 2",
403
+ taskGroups: [
404
+ {
405
+ id: "2.1",
406
+ name: "Task 2.1",
407
+ phaseNumber: 2,
408
+ status: "pending",
409
+ dependencies: [],
410
+ completionCriteria: "",
411
+ tasks: [],
412
+ },
413
+ ],
414
+ },
415
+ ],
416
+ };
417
+ // When: getAllTaskGroupsを呼び出す
418
+ const result = getAllTaskGroups(parsedIssue);
419
+ // Then: 全タスクグループがフラットな配列で返る
420
+ expect(result).toHaveLength(3);
421
+ expect(result[0].id).toBe("1.1");
422
+ expect(result[1].id).toBe("1.2");
423
+ expect(result[2].id).toBe("2.1");
424
+ });
425
+ it("タスクグループが存在しない場合、空配列を返す", () => {
426
+ // Given: タスクグループが空のParsedIssue
427
+ const parsedIssue = {
428
+ issueNumber: 1,
429
+ title: "Test",
430
+ body: "",
431
+ phases: [
432
+ {
433
+ number: 1,
434
+ name: "Phase 1",
435
+ taskGroups: [],
436
+ },
437
+ ],
438
+ };
439
+ // When: getAllTaskGroupsを呼び出す
440
+ const result = getAllTaskGroups(parsedIssue);
441
+ // Then: 空配列が返る
442
+ expect(result).toHaveLength(0);
443
+ });
444
+ });
445
+ describe("getCompletedTaskGroupIds", () => {
446
+ it("完了済みタスクグループのIDのSetを返す", () => {
447
+ // Given: 完了済みと未完了のタスクグループを持つParsedIssue
448
+ const parsedIssue = {
449
+ issueNumber: 1,
450
+ title: "Test",
451
+ body: "",
452
+ phases: [
453
+ {
454
+ number: 1,
455
+ name: "Phase 1",
456
+ taskGroups: [
457
+ {
458
+ id: "1.1",
459
+ name: "Task 1.1",
460
+ phaseNumber: 1,
461
+ status: "completed",
462
+ dependencies: [],
463
+ completionCriteria: "",
464
+ tasks: [],
465
+ },
466
+ {
467
+ id: "1.2",
468
+ name: "Task 1.2",
469
+ phaseNumber: 1,
470
+ status: "pending",
471
+ dependencies: [],
472
+ completionCriteria: "",
473
+ tasks: [],
474
+ },
475
+ {
476
+ id: "1.3",
477
+ name: "Task 1.3",
478
+ phaseNumber: 1,
479
+ status: "completed",
480
+ dependencies: [],
481
+ completionCriteria: "",
482
+ tasks: [],
483
+ },
484
+ ],
485
+ },
486
+ ],
487
+ };
488
+ // When: getCompletedTaskGroupIdsを呼び出す
489
+ const result = getCompletedTaskGroupIds(parsedIssue);
490
+ // Then: 完了済みタスクグループのIDのSetが返る
491
+ expect(result.size).toBe(2);
492
+ expect(result.has("1.1")).toBe(true);
493
+ expect(result.has("1.3")).toBe(true);
494
+ expect(result.has("1.2")).toBe(false);
495
+ });
496
+ it("完了済みタスクグループが存在しない場合、空のSetを返す", () => {
497
+ // Given: 全て未完了のタスクグループ
498
+ const parsedIssue = {
499
+ issueNumber: 1,
500
+ title: "Test",
501
+ body: "",
502
+ phases: [
503
+ {
504
+ number: 1,
505
+ name: "Phase 1",
506
+ taskGroups: [
507
+ {
508
+ id: "1.1",
509
+ name: "Task 1.1",
510
+ phaseNumber: 1,
511
+ status: "pending",
512
+ dependencies: [],
513
+ completionCriteria: "",
514
+ tasks: [],
515
+ },
516
+ ],
517
+ },
518
+ ],
519
+ };
520
+ // When: getCompletedTaskGroupIdsを呼び出す
521
+ const result = getCompletedTaskGroupIds(parsedIssue);
522
+ // Then: 空のSetが返る
523
+ expect(result.size).toBe(0);
524
+ });
525
+ });
526
+ describe("getPendingTaskGroups", () => {
527
+ it("未着手タスクグループの配列を返す", () => {
528
+ // Given: 複数ステータスのタスクグループを持つParsedIssue
529
+ const parsedIssue = {
530
+ issueNumber: 1,
531
+ title: "Test",
532
+ body: "",
533
+ phases: [
534
+ {
535
+ number: 1,
536
+ name: "Phase 1",
537
+ taskGroups: [
538
+ {
539
+ id: "1.1",
540
+ name: "Task 1.1",
541
+ phaseNumber: 1,
542
+ status: "pending",
543
+ dependencies: [],
544
+ completionCriteria: "",
545
+ tasks: [],
546
+ },
547
+ {
548
+ id: "1.2",
549
+ name: "Task 1.2",
550
+ phaseNumber: 1,
551
+ status: "completed",
552
+ dependencies: [],
553
+ completionCriteria: "",
554
+ tasks: [],
555
+ },
556
+ {
557
+ id: "1.3",
558
+ name: "Task 1.3",
559
+ phaseNumber: 1,
560
+ status: "in-progress",
561
+ dependencies: [],
562
+ completionCriteria: "",
563
+ tasks: [],
564
+ },
565
+ {
566
+ id: "1.4",
567
+ name: "Task 1.4",
568
+ phaseNumber: 1,
569
+ status: "pending",
570
+ dependencies: [],
571
+ completionCriteria: "",
572
+ tasks: [],
573
+ },
574
+ ],
575
+ },
576
+ ],
577
+ };
578
+ // When: getPendingTaskGroupsを呼び出す
579
+ const result = getPendingTaskGroups(parsedIssue);
580
+ // Then: 未着手タスクグループのみ返る
581
+ expect(result).toHaveLength(2);
582
+ expect(result[0].id).toBe("1.1");
583
+ expect(result[1].id).toBe("1.4");
584
+ });
585
+ it("未着手タスクグループが存在しない場合、空配列を返す", () => {
586
+ // Given: 全て完了済みのタスクグループ
587
+ const parsedIssue = {
588
+ issueNumber: 1,
589
+ title: "Test",
590
+ body: "",
591
+ phases: [
592
+ {
593
+ number: 1,
594
+ name: "Phase 1",
595
+ taskGroups: [
596
+ {
597
+ id: "1.1",
598
+ name: "Task 1.1",
599
+ phaseNumber: 1,
600
+ status: "completed",
601
+ dependencies: [],
602
+ completionCriteria: "",
603
+ tasks: [],
604
+ },
605
+ ],
606
+ },
607
+ ],
608
+ };
609
+ // When: getPendingTaskGroupsを呼び出す
610
+ const result = getPendingTaskGroups(parsedIssue);
611
+ // Then: 空配列が返る
612
+ expect(result).toHaveLength(0);
613
+ });
614
+ });
615
+ describe("extractTasks", () => {
616
+ it("タスクグループ配下のサブタスク一覧が抽出される", () => {
617
+ // Given: サブタスクを含むタスクグループ
618
+ const issue = {
619
+ number: 1,
620
+ title: "Test",
621
+ state: "open",
622
+ body: `### Phase 1: テスト
623
+
624
+ - [ ] **1.1 タスクグループ名**
625
+ **依存関係**: なし
626
+ **完了条件**: 完了
627
+ - 1.1.1 サブタスク1
628
+ - 1.1.2 サブタスク2
629
+ - 1.1.3 サブタスク3
630
+ `,
631
+ };
632
+ // When: parseIssueBodyを呼び出す
633
+ const result = parseIssueBody(issue);
634
+ // Then: サブタスクが正しく抽出される
635
+ const phase1 = result.phases[0];
636
+ const taskGroup = phase1.taskGroups[0];
637
+ expect(taskGroup.tasks).toHaveLength(3);
638
+ expect(taskGroup.tasks[0].id).toBe("1.1.1");
639
+ expect(taskGroup.tasks[0].name).toBe("サブタスク1");
640
+ expect(taskGroup.tasks[1].id).toBe("1.1.2");
641
+ expect(taskGroup.tasks[1].name).toBe("サブタスク2");
642
+ expect(taskGroup.tasks[2].id).toBe("1.1.3");
643
+ expect(taskGroup.tasks[2].name).toBe("サブタスク3");
644
+ });
645
+ it("リストマーカーなしのサブタスクも抽出される", () => {
646
+ // Given: リストマーカーなしのサブタスク
647
+ const issue = {
648
+ number: 1,
649
+ title: "Test",
650
+ state: "open",
651
+ body: `### Phase 1: テスト
652
+
653
+ - [ ] **1.1 タスクグループ名**
654
+ **依存関係**: なし
655
+ **完了条件**: 完了
656
+ 1.1.1 リストなしサブタスク1
657
+ - 1.1.2 リストありサブタスク2
658
+ `,
659
+ };
660
+ // When: parseIssueBodyを呼び出す
661
+ const result = parseIssueBody(issue);
662
+ // Then: リストマーカーの有無に関わらずサブタスクが抽出される
663
+ const phase1 = result.phases[0];
664
+ const taskGroup = phase1.taskGroups[0];
665
+ expect(taskGroup.tasks).toHaveLength(2);
666
+ expect(taskGroup.tasks[0].id).toBe("1.1.1");
667
+ expect(taskGroup.tasks[1].id).toBe("1.1.2");
668
+ });
669
+ });
670
+ describe("HTMLコメント付きタスク", () => {
671
+ it("着手中コメント付きのタスクでもタスクグループIDが抽出される", () => {
672
+ // Given: 着手中コメント付きのタスク
673
+ const issue = {
674
+ number: 22,
675
+ title: "Test",
676
+ state: "open",
677
+ body: `### Phase 1: テスト
678
+
679
+ - [ ] **1.1 タスク名** <!-- 着手中: Issue22 - Vibe-Kanban Task: agent-123 -->
680
+ **依存関係**: なし
681
+ **完了条件**: 完了
682
+
683
+ - [x] **1.2 完了済みタスク** <!-- 着手中: Issue22 - Vibe-Kanban Task: def456 -->
684
+ **依存関係**: 1.1
685
+ **完了条件**: 完了
686
+ `,
687
+ };
688
+ // When: パース実行
689
+ const result = parseIssueBody(issue);
690
+ // Then: タスクグループが抽出される
691
+ expect(result.phases).toHaveLength(1);
692
+ const phase1 = result.phases[0];
693
+ expect(phase1.taskGroups).toHaveLength(2);
694
+ // 1つ目のタスクグループ
695
+ expect(phase1.taskGroups[0].id).toBe("1.1");
696
+ expect(phase1.taskGroups[0].name).toBe("タスク名");
697
+ expect(phase1.taskGroups[0].status).toBe("pending");
698
+ // 2つ目のタスクグループ
699
+ expect(phase1.taskGroups[1].id).toBe("1.2");
700
+ expect(phase1.taskGroups[1].name).toBe("完了済みタスク");
701
+ expect(phase1.taskGroups[1].status).toBe("completed");
702
+ expect(phase1.taskGroups[1].dependencies).toEqual(["1.1"]);
703
+ });
704
+ it("ボールドなしでHTMLコメント付きの場合もタスクグループが抽出される", () => {
705
+ // Given: ボールドなしでHTMLコメント付きのタスク
706
+ const issue = {
707
+ number: 1,
708
+ title: "Test",
709
+ state: "open",
710
+ body: `### Phase 1: テスト
711
+
712
+ - [ ] 1.1 ボールドなしタスク <!-- 着手中: agent-123 -->
713
+ **依存関係**: なし
714
+ **完了条件**: 完了
715
+ `,
716
+ };
717
+ // When: パース実行
718
+ const result = parseIssueBody(issue);
719
+ // Then: タスクグループが抽出される
720
+ expect(result.phases[0].taskGroups).toHaveLength(1);
721
+ expect(result.phases[0].taskGroups[0].id).toBe("1.1");
722
+ expect(result.phases[0].taskGroups[0].name).toBe("ボールドなしタスク");
723
+ });
724
+ });
725
+ describe("3階層タスクID", () => {
726
+ it("タスクグループとして3階層ID '1.2.1' を使った場合、正規表現で抽出されない", () => {
727
+ // Given: タスクグループとして3階層IDを記述
728
+ const issue = {
729
+ number: 1,
730
+ title: "Test",
731
+ state: "open",
732
+ body: `### Phase 1: テスト
733
+
734
+ - [ ] **1.2.1 3階層タスクグループ**
735
+ **依存関係**: なし
736
+ **完了条件**: これはタスクグループとして認識されない
737
+ `,
738
+ };
739
+ // When: パース実行
740
+ const result = parseIssueBody(issue);
741
+ // Then: タスクグループとして認識されない(正規表現が \d+\.\d+ のみ対応)
742
+ expect(result.phases[0].taskGroups).toHaveLength(0);
743
+ });
744
+ it("サブタスク(Task)として3階層ID '1.1.1' を使った場合、正しく抽出される", () => {
745
+ // Given: サブタスクとして3階層IDを記述
746
+ const issue = {
747
+ number: 1,
748
+ title: "Test",
749
+ state: "open",
750
+ body: `### Phase 1: テスト
751
+
752
+ - [ ] **1.1 親タスクグループ**
753
+ **依存関係**: なし
754
+ **完了条件**: 完了
755
+ - 1.1.1 サブタスク1
756
+ - 1.1.2 サブタスク2
757
+ - 1.1.3 サブタスク3
758
+ `,
759
+ };
760
+ // When: パース実行
761
+ const result = parseIssueBody(issue);
762
+ // Then: タスクグループは1つ、サブタスクは3つ抽出される
763
+ expect(result.phases[0].taskGroups).toHaveLength(1);
764
+ const taskGroup = result.phases[0].taskGroups[0];
765
+ expect(taskGroup.id).toBe("1.1");
766
+ expect(taskGroup.tasks).toHaveLength(3);
767
+ expect(taskGroup.tasks[0].id).toBe("1.1.1");
768
+ expect(taskGroup.tasks[1].id).toBe("1.1.2");
769
+ expect(taskGroup.tasks[2].id).toBe("1.1.3");
770
+ });
771
+ it("4階層ID '1.1.1.1' のようなより深いネストも認識されない", () => {
772
+ // Given: 4階層IDのタスクグループ
773
+ const issue = {
774
+ number: 1,
775
+ title: "Test",
776
+ state: "open",
777
+ body: `### Phase 1: テスト
778
+
779
+ - [ ] **1.1.1.1 4階層タスク**
780
+ **依存関係**: なし
781
+ **完了条件**: 認識されないはず
782
+ `,
783
+ };
784
+ // When: パース実行
785
+ const result = parseIssueBody(issue);
786
+ // Then: タスクグループとして認識されない
787
+ expect(result.phases[0].taskGroups).toHaveLength(0);
788
+ });
789
+ });
790
+ describe("複雑な実例", () => {
791
+ it("複数Phase、複数タスクグループ、複雑な依存関係を持つIssueを正しくパースする", () => {
792
+ // Given: 複雑な構造のIssue
793
+ const issue = {
794
+ number: 456,
795
+ title: "複雑なプロジェクト",
796
+ state: "open",
797
+ body: `## 概要
798
+ このプロジェクトは複雑な依存関係を持つタスク群です。
799
+
800
+ ### Phase 1: 基盤構築
801
+
802
+ - [x] **1.1 環境セットアップ**
803
+ **依存関係**: なし
804
+ **完了条件**: Docker環境が起動する
805
+ - 1.1.1 Dockerインストール
806
+ - 1.1.2 docker-compose.yml作成
807
+
808
+ - [ ] **1.2 データベース設計**
809
+ **依存関係**: 1.1
810
+ **完了条件**: スキーマが定義される
811
+ - 1.2.1 ER図作成
812
+ - 1.2.2 マイグレーションファイル作成
813
+
814
+ ### Phase 2: アプリケーション開発
815
+
816
+ - [ ] **2.1 API実装**
817
+ **依存関係**: Phase 1完了
818
+ **完了条件**: APIが動作する
819
+ - 2.1.1 ルーティング設定
820
+ - 2.1.2 コントローラー実装
821
+ - 2.1.3 テスト作成
822
+
823
+ - [ ] **2.2 フロントエンド実装**
824
+ **依存関係**: 2.1, #123
825
+ **完了条件**: 画面が表示される
826
+ - 2.2.1 コンポーネント作成
827
+ - 2.2.2 API連携
828
+ `,
829
+ };
830
+ // When: parseIssueBodyを呼び出す
831
+ const result = parseIssueBody(issue);
832
+ // Then: 全ての構造が正しくパースされる
833
+ expect(result.issueNumber).toBe(456);
834
+ expect(result.phases).toHaveLength(2);
835
+ // Phase 1
836
+ const phase1 = result.phases[0];
837
+ expect(phase1.number).toBe(1);
838
+ expect(phase1.name).toBe("基盤構築");
839
+ expect(phase1.taskGroups).toHaveLength(2);
840
+ expect(phase1.taskGroups[0].status).toBe("completed");
841
+ expect(phase1.taskGroups[1].dependencies).toEqual(["1.1"]);
842
+ expect(phase1.taskGroups[0].tasks).toHaveLength(2);
843
+ // Phase 2
844
+ const phase2 = result.phases[1];
845
+ expect(phase2.number).toBe(2);
846
+ expect(phase2.name).toBe("アプリケーション開発");
847
+ expect(phase2.taskGroups).toHaveLength(2);
848
+ expect(phase2.taskGroups[0].dependencies).toEqual(["Phase 1完了"]);
849
+ expect(phase2.taskGroups[1].dependencies).toEqual(["2.1", "#123"]);
850
+ expect(phase2.taskGroups[1].tasks).toHaveLength(2);
851
+ });
852
+ });
853
+ });
854
+ //# sourceMappingURL=issue-parser.test.js.map