@deepstorm/cli 0.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 (148) hide show
  1. package/README.md +72 -0
  2. package/dist/agents/reef-inspect-figma.md +77 -0
  3. package/dist/agents/reef-review-backend.md.tmpl +112 -0
  4. package/dist/agents/reef-review-frontend.md.tmpl +78 -0
  5. package/dist/agents/reef-review-infra.md +47 -0
  6. package/dist/agents/reef-review-security.md.tmpl +80 -0
  7. package/dist/agents/reef-scope-analysis.md +64 -0
  8. package/dist/build-registry.js +375 -0
  9. package/dist/cli.js +8581 -0
  10. package/dist/config-schema.json +133 -0
  11. package/dist/env-examples/context7.env-example +19 -0
  12. package/dist/env-examples/feishu-wiki.env-example +16 -0
  13. package/dist/env-examples/figma.env-example +16 -0
  14. package/dist/env-examples/github.env-example +20 -0
  15. package/dist/env-examples/jira.env-example +20 -0
  16. package/dist/hooks/mcp-hook.sh +77 -0
  17. package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
  18. package/dist/hooks/reef-block-dangerous.sh +70 -0
  19. package/dist/hooks/reef-hooks.json +72 -0
  20. package/dist/hooks/reef-intent-detect.sh +129 -0
  21. package/dist/hooks/reef-protect-files.sh +55 -0
  22. package/dist/hooks/reef-run-tests.sh +84 -0
  23. package/dist/hooks/reef-scope-check.sh +386 -0
  24. package/dist/hooks/reef-scope-ci.sh +28 -0
  25. package/dist/hooks/reef-scope-gate.sh +115 -0
  26. package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
  27. package/dist/hooks/reef-scope-setup.sh +204 -0
  28. package/dist/hooks/reef-scope-split.sh +203 -0
  29. package/dist/hooks/sweep-hooks.json +14 -0
  30. package/dist/hooks/sweep-mcp-hook.sh +77 -0
  31. package/dist/hooks/tide-hooks.json +14 -0
  32. package/dist/hooks/tide-session-preload.sh +17 -0
  33. package/dist/mcp/code-hosting/github.json +20 -0
  34. package/dist/mcp/design-tools/figma.json +19 -0
  35. package/dist/mcp/docs-reference/context7.json +28 -0
  36. package/dist/mcp/e2e-testing/playwright.json +13 -0
  37. package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
  38. package/dist/mcp/project-management/jira.json +27 -0
  39. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
  40. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
  41. package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
  42. package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
  43. package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
  44. package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
  45. package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
  46. package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
  47. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
  48. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
  49. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
  50. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
  51. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
  52. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
  53. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
  54. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
  55. package/dist/registry.json +818 -0
  56. package/dist/skills/atoll-ops/SKILL.md +46 -0
  57. package/dist/skills/reef-commit/SKILL.md +127 -0
  58. package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
  59. package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
  60. package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
  61. package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
  62. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
  63. package/dist/skills/reef-harden/EXAMPLES.md +89 -0
  64. package/dist/skills/reef-harden/SKILL.md +136 -0
  65. package/dist/skills/reef-pr/SKILL.md +97 -0
  66. package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
  67. package/dist/skills/reef-scope/SKILL.md +134 -0
  68. package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
  69. package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
  70. package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
  71. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
  72. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
  73. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
  74. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
  75. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
  76. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
  77. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
  78. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
  79. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
  80. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
  81. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
  82. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
  83. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
  84. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
  85. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
  86. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
  87. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
  88. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
  89. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
  90. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
  91. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
  92. package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
  93. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
  94. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
  95. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
  96. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
  97. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
  98. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
  99. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
  100. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
  101. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
  102. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
  103. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
  104. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
  105. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
  106. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
  107. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
  108. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
  109. package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
  110. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
  111. package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
  112. package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
  113. package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
  114. package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
  115. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
  116. package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
  117. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
  118. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
  119. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
  120. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
  121. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
  122. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
  123. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
  124. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
  125. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
  126. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
  127. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
  128. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
  129. package/dist/skills/reef-testcase/SKILL.md +138 -0
  130. package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
  131. package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
  132. package/dist/skills/sweep-init/SKILL.md +286 -0
  133. package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
  134. package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
  135. package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
  136. package/dist/skills/sweep-run/SKILL.md +437 -0
  137. package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
  138. package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
  139. package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
  140. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
  141. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
  142. package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
  143. package/dist/skills/tide-discuss/references/checklists.md +88 -0
  144. package/dist/skills/tide-discuss/references/data-format.md +237 -0
  145. package/dist/skills/tide-discuss/references/prd-template.md +134 -0
  146. package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
  147. package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
  148. package/package.json +38 -0
@@ -0,0 +1,63 @@
1
+ # ๆต‹่ฏ•็”จไพ‹ๆจกๆฟ
2
+
3
+ ## ๅญ—ๆฎตๅฎšไน‰
4
+
5
+ ๆฏไธชๆต‹่ฏ•็”จไพ‹ๅŒ…ๅซไปฅไธ‹ๅญ—ๆฎต๏ผš
6
+
7
+ | ๅญ—ๆฎต | ๅฟ…ๅกซ | ่ฏดๆ˜Ž |
8
+ |------|------|------|
9
+ | `ID` | ๆ˜ฏ | ๅ”ฏไธ€ๆ ‡่ฏ†๏ผŒๆ ผๅผ `TC-{feature}-{seq}` |
10
+ | `็ฑปๅž‹` | ๆ˜ฏ | ๆญฃๅธธๆต็จ‹ / ่พน็•Œๆกไปถ / ๅผ‚ๅธธๅœบๆ™ฏ / ้ชŒๆ”ถๆ ‡ๅ‡† |
11
+ | `ๅ‰็ฝฎๆกไปถ` | ๆ˜ฏ | ๆต‹่ฏ•ๆ‰€้œ€็š„ๅ‰็ฝฎ็Šถๆ€ๆˆ–ๆ•ฐๆฎ |
12
+ | `ๆต‹่ฏ•ๆญฅ้ชค` | ๆ˜ฏ | ๆธ…ๆ™ฐ็š„ๆ“ไฝœๆญฅ้ชคๆ่ฟฐ |
13
+ | `้ข„ๆœŸ็ป“ๆžœ` | ๆ˜ฏ | ๆœŸๆœ›็š„่กŒไธบๆˆ–่พ“ๅ‡บ |
14
+ | `ๅ…ณ่”้ชŒๆ”ถๆ ‡ๅ‡†` | ๅฆ | ๅฏนๅบ” PRD ๆˆ– Jira ไธญ็š„้ชŒๆ”ถๆ ‡ๅ‡†็ผ–ๅท |
15
+
16
+ ## ๆจกๆฟ็คบไพ‹
17
+
18
+ ```markdown
19
+ ### TC-{feature}-001
20
+
21
+ | ๅญ—ๆฎต | ๅ†…ๅฎน |
22
+ |------|------|
23
+ | **ID** | TC-{feature}-001 |
24
+ | **็ฑปๅž‹** | ๆญฃๅธธๆต็จ‹ |
25
+ | **ๅ‰็ฝฎๆกไปถ** | {ๆต‹่ฏ•ๆ‰€้œ€็š„ๅ‰็ฝฎ็Šถๆ€ๆˆ–ๆ•ฐๆฎ} |
26
+ | **ๆต‹่ฏ•ๆญฅ้ชค** | 1. {ๆญฅ้ชคไธ€}<br>2. {ๆญฅ้ชคไบŒ}<br>3. {ๆญฅ้ชคไธ‰} |
27
+ | **้ข„ๆœŸ็ป“ๆžœ** | {ๆœŸๆœ›็š„่กŒไธบๆˆ–่พ“ๅ‡บ} |
28
+ | **ๅ…ณ่”้ชŒๆ”ถๆ ‡ๅ‡†** | AC-{id}๏ผˆๅฏ้€‰๏ผ‰ |
29
+ ```
30
+
31
+ ## ่พ“ๅ‡บ็ป“ๆž„
32
+
33
+ ็”Ÿๆˆ็š„ๆต‹่ฏ•็”จไพ‹ๆธ…ๅ•ๆ•ดไฝ“็ป“ๆž„๏ผš
34
+
35
+ ```markdown
36
+ ## ๆต‹่ฏ•็”จไพ‹ๆธ…ๅ•
37
+
38
+ **ๅŠŸ่ƒฝ๏ผš** {feature name}
39
+ **ๆฅๆบ๏ผš** {Jira Issue ID}
40
+ **PRD ไธŠไธ‹ๆ–‡๏ผš** {PRD link / "ๆ— "}
41
+
42
+ ---
43
+
44
+ ### ๆญฃๅธธๆต็จ‹
45
+
46
+ {ๆญฃๅธธๆต็จ‹ๆต‹่ฏ•็”จไพ‹ๅˆ—่กจ}
47
+
48
+ ### ่พน็•Œๆกไปถ
49
+
50
+ {่พน็•Œๆกไปถๆต‹่ฏ•็”จไพ‹ๅˆ—่กจ}
51
+
52
+ ### ๅผ‚ๅธธๅœบๆ™ฏ
53
+
54
+ {ๅผ‚ๅธธๅœบๆ™ฏๆต‹่ฏ•็”จไพ‹ๅˆ—่กจ}
55
+
56
+ ### ้ชŒๆ”ถๆ ‡ๅ‡†้ชŒ่ฏ
57
+
58
+ {้ชŒๆ”ถๆ ‡ๅ‡†ๆต‹่ฏ•็”จไพ‹ๅˆ—่กจ}
59
+
60
+ ---
61
+
62
+ > ่ฏฅๆต‹่ฏ•็”จไพ‹ๆธ…ๅ•ๅฏ่พ“ๅ…ฅ superpowers ่พ…ๅŠฉ็”Ÿๆˆๆ›ดๅฎŒๅค‡็š„ๅ•ๅ…ƒๆต‹่ฏ•ใ€‚
63
+ ```
@@ -0,0 +1,286 @@
1
+ ---
2
+ name: sweep-init
3
+ description: ๅˆๅง‹ๅŒ– E2E ๆต‹่ฏ•้กน็›ฎ้ชจๆžถใ€‚ๅˆ›ๅปบ็›ฎๅฝ•็ป“ๆž„ใ€ๆ นๆฎ setup ้…็ฝฎ็”Ÿๆˆๅฏนๅบ”ๆต‹่ฏ•ๆก†ๆžถ้…็ฝฎใ€่ฏปๅ– MCP ้…็ฝฎใ€่ฎพ็ฝฎๅคš็Žฏๅขƒๅ˜้‡๏ผŒๆ ‡ๅฟ—้กน็›ฎๅฏ่ขซ flow-create ๅ’Œ flow-run ไฝฟ็”จใ€‚
4
+ allowed-tools: Read, Write, Edit, Bash
5
+ deepstorm:
6
+ tool: sweep
7
+ ---
8
+
9
+ # Sweep Setup โ€” E2E ๆต‹่ฏ•้กน็›ฎๅˆๅง‹ๅŒ–
10
+
11
+ ๅˆๅง‹ๅŒ–ๅฝ“ๅ‰็›ฎๅฝ•ไธบ Sweep E2E ๆต‹่ฏ•้กน็›ฎใ€‚ๆ นๆฎ DeepStorm setup ้˜ถๆฎต้€‰ๆ‹ฉ็š„ E2E ๆก†ๆžถ๏ผˆๅฆ‚ Playwright๏ผ‰๏ผŒๅˆ›ๅปบๅฏนๅบ”็š„้กน็›ฎ้ชจๆžถใ€‚
12
+
13
+ > **ๆณจๆ„๏ผš** setup ๆ˜ฏ flow-create ๅ’Œ flow-run ็š„็กฌๆ€งๅ‰็ฝฎๆกไปถใ€‚ๅฆ‚ๆžœๅฝ“ๅ‰็›ฎๅฝ•ๆœชๅˆๅง‹ๅŒ–๏ผŒๅ…ถไป– skill ไผšๆŠฅ้”™ๅผ•ๅฏผๆ‰ง่กŒ `/sweep-init`ใ€‚
14
+
15
+ ---
16
+
17
+ ## ไฝฟ็”จๆ–นๅผ
18
+
19
+ ```bash
20
+ /sweep-init
21
+ ```
22
+
23
+ ๅœจ้œ€่ฆไฝœไธบ E2E ๆต‹่ฏ•้กน็›ฎ็š„็›ฎๅฝ•ไธญๆ‰ง่กŒใ€‚้€šๅธธๅœจไธ€ไธช็ฉบ็š„ๆˆ–ๅทฒๆœ‰ git ไป“ๅบ“็š„็›ฎๅฝ•ไธญ่ฟ่กŒใ€‚
24
+
25
+ ---
26
+
27
+ ## ๅทฅไฝœๆต
28
+
29
+ ### ๆญฅ้ชค 0๏ผš่ฏปๅ–ๆก†ๆžถ้…็ฝฎ
30
+
31
+ ๅœจๆ‰ง่กŒๅˆๅง‹ๅŒ–ๅ‰๏ผŒๅ…ˆ่ฏปๅ– `.claude/settings.json` ไธญ็š„ `deepstorm.sweep.e2eFramework` ้…็ฝฎ๏ผŒ็กฎๅฎšๅฝ“ๅ‰้กน็›ฎไฝฟ็”จ็š„ E2E ๆก†ๆžถใ€‚
32
+
33
+ ```bash
34
+ cat .claude/settings.json 2>/dev/null | grep -o '"e2eFramework"[^,]*' | head -1 | cut -d'"' -f4
35
+ ```
36
+
37
+ ๅฆ‚ๆžœ้…็ฝฎไธๅญ˜ๅœจๆˆ–ไธบ็ฉบ๏ผŒไฝฟ็”จ้ป˜่ฎคๅ€ผ `playwright` ๅนถ่พ“ๅ‡บๆ็คบใ€‚
38
+
39
+ ๅฝ“ๅ‰ๆ”ฏๆŒ็š„ๆก†ๆžถ๏ผš
40
+ - **playwright** โ€” Microsoft Playwright๏ผˆ้ป˜่ฎค๏ผ‰
41
+
42
+ ---
43
+
44
+ ### ๆญฅ้ชค 1๏ผšๆฃ€ๆŸฅๅˆๅง‹ๅŒ–็Šถๆ€
45
+
46
+ ๆฃ€ๆŸฅๅฝ“ๅ‰็›ฎๅฝ•ๆ˜ฏๅฆๅญ˜ๅœจ `.sweep-init` ๆ ‡่ฎฐๆ–‡ไปถใ€‚
47
+
48
+ #### 1.1 ๅทฒๅˆๅง‹ๅŒ– โ†’ ้€€ๅ‡บ
49
+
50
+ - **WHEN** `.sweep-init` ๆ–‡ไปถๅทฒๅญ˜ๅœจ
51
+ - **THEN** ๆ็คบ"ๅฝ“ๅ‰็›ฎๅฝ•ๅทฒๅˆๅง‹ๅŒ–ไธบ Sweep ๆต‹่ฏ•้กน็›ฎใ€‚ๅฆ‚้œ€้‡ๆ–ฐๅˆๅง‹ๅŒ–๏ผŒ่ฏทๅˆ ้™ค .sweep-init ๆ–‡ไปถๅŽ้‡่ฏ•ใ€‚"ๅนถ้€€ๅ‡บ
52
+
53
+ #### 1.2 ๆœชๅˆๅง‹ๅŒ– โ†’ ็ปง็ปญ
54
+
55
+ - **WHEN** `.sweep-init` ๆ–‡ไปถไธๅญ˜ๅœจ
56
+ - **THEN** ็ปง็ปญๆ‰ง่กŒๅˆๅง‹ๅŒ–ๆต็จ‹
57
+
58
+ ---
59
+
60
+ ### ๆญฅ้ชค 2๏ผšๅˆ›ๅปบ้กน็›ฎ็›ฎๅฝ•็ป“ๆž„
61
+
62
+ ๅˆ›ๅปบ E2E ๆต‹่ฏ•้กน็›ฎ็š„็›ฎๅฝ•้ชจๆžถใ€‚
63
+
64
+ #### ็›ฎๅฝ•็ป“ๆž„
65
+
66
+ ```
67
+ .
68
+ โ”œโ”€โ”€ flows/ # ๆต‹่ฏ•ๆ„ๅ›พๆ–‡ๆกฃ็›ฎๅฝ•
69
+ โ”‚ โ”œโ”€โ”€ topology.yaml # ๅŠŸ่ƒฝๆจกๅ—ๆ‹“ๆ‰‘ๅฎšไน‰๏ผˆๅพ…็”จๆˆท็ผ–่พ‘๏ผ‰
70
+ โ”‚ โ””โ”€โ”€ reports/ # ๆ‰ง่กŒๆŠฅๅ‘ŠๆŒไน…ๅŒ–็›ฎๅฝ•
71
+ โ”œโ”€โ”€ scripts/
72
+ โ”‚ โ””โ”€โ”€ flow-selector.mjs # ๅฑ‚็บง้€‰ๆ‹ฉๅทฅๅ…ท๏ผˆ้ข„็ฝฎ๏ผ‰
73
+ โ”œโ”€โ”€ .env # ็Žฏๅขƒๅ˜้‡๏ผˆgitignored๏ผŒๅณๆจกๆฟ๏ผ‰
74
+ โ”œโ”€โ”€ package.json # ไพ่ต–ๅฃฐๆ˜Ž
75
+ โ””โ”€โ”€ tsconfig.json # TypeScript ้…็ฝฎ
76
+ ```
77
+
78
+ > ๅฆ‚ๆžœๆก†ๆžถไธบ `playwright`๏ผŒ่ฟ˜ไผš้ขๅค–็”Ÿๆˆ `playwright.config.ts`ใ€‚
79
+
80
+ #### ๆ‰ง่กŒ
81
+
82
+ ```bash
83
+ mkdir -p flows flows/reports scripts
84
+ ```
85
+
86
+ ---
87
+
88
+ ### ๆญฅ้ชค 3๏ผš็”Ÿๆˆ้กน็›ฎ้…็ฝฎๆ–‡ไปถ๏ผˆๆก†ๆžถๆ„Ÿ็Ÿฅ๏ผ‰
89
+
90
+ ๆ นๆฎๆญฅ้ชค 0 ่ฏปๅ–็š„ๆก†ๆžถ้…็ฝฎ๏ผŒ็”Ÿๆˆๅฏนๅบ”็š„้…็ฝฎๆ–‡ไปถใ€‚
91
+
92
+ #### 3.1 package.json
93
+
94
+ ๅ†™ๅ…ฅๅŸบ็ก€ `package.json`ใ€‚ๅฆ‚ๆžœๆก†ๆžถไธบ `playwright`๏ผŒๅŒ…ๅซ Playwright ไพ่ต–๏ผš
95
+
96
+ ```json
97
+ {
98
+ "name": "sweep-e2e",
99
+ "version": "1.0.0",
100
+ "private": true,
101
+ "type": "module",
102
+ "scripts": {
103
+ "test": "playwright test",
104
+ "report": "playwright show-report"
105
+ },
106
+ "devDependencies": {
107
+ "@playwright/test": "^1.50.0",
108
+ "@inquirer/checkbox": "^4.0.0",
109
+ "@types/node": "^22.0.0"
110
+ }
111
+ }
112
+ ```
113
+
114
+ > ๅฝ“ E2E ๆก†ๆžถๆœช็Ÿฅๆˆ–ๆœช้…็ฝฎๆ—ถ๏ผŒ`package.json` ไธญไป…ๅŒ…ๅซ `@inquirer/checkbox` ๅ’Œ `@types/node` ไพ่ต–๏ผŒ**ไธๅŒ…ๅซ** `@playwright/test`ใ€‚
115
+
116
+ #### 3.2 ๆก†ๆžถ้…็ฝฎๆ–‡ไปถ
117
+
118
+ **ๅฝ“ๆก†ๆžถไธบ `playwright` ๆ—ถ**๏ผŒๅ†™ๅ…ฅ `playwright.config.ts`๏ผŒๅŒ…ๅซๅคš็Žฏๅขƒ baseURL ่ฎพ็ฝฎ๏ผš
119
+
120
+ ```ts
121
+ import { defineConfig } from '@playwright/test';
122
+
123
+ export default defineConfig({
124
+ use: {
125
+ baseURL: process.env.BASE_URL || 'http://localhost:3000',
126
+ },
127
+ timeout: 30000,
128
+ retries: 0,
129
+ reporter: [['line'], ['html', { outputFolder: 'flows/reports' }]],
130
+ projects: [
131
+ { name: 'chromium', use: { browserName: 'chromium' } },
132
+ ],
133
+ });
134
+ ```
135
+
136
+ **ๅฝ“ๆก†ๆžถๆœช็Ÿฅๆˆ–ๆœช้…็ฝฎๆ—ถ**๏ผŒ่ทณ่ฟ‡ๆก†ๆžถ้…็ฝฎๆ–‡ไปถ็š„็”Ÿๆˆ๏ผŒ่พ“ๅ‡บๆ็คบ๏ผš"ๆก†ๆžถๆœช้…็ฝฎ๏ผŒ่ฏท่ฟ่กŒ deepstorm setup ๅนถ้€‰ๆ‹ฉ E2E ๆก†ๆžถใ€‚"
137
+
138
+ #### 3.3 tsconfig.json
139
+
140
+ ๅ†™ๅ…ฅ TypeScript ้…็ฝฎ๏ผˆๆก†ๆžถๆ— ๅ…ณ๏ผ‰๏ผš
141
+
142
+ ```json
143
+ {
144
+ "compilerOptions": {
145
+ "target": "ES2022",
146
+ "module": "ESNext",
147
+ "moduleResolution": "bundler",
148
+ "strict": true,
149
+ "esModuleInterop": true
150
+ }
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ### ๆญฅ้ชค 4๏ผšๆ”ถ้›†ๅคš็Žฏๅขƒ้…็ฝฎไฟกๆฏ
157
+
158
+ #### 4.1 ไบคไบ’ๅผ่ฏข้—ฎ็Žฏๅขƒๅœฐๅ€
159
+
160
+ ้€้กน่ฏข้—ฎ็”จๆˆท่พ“ๅ…ฅ่ขซๆต‹็ณป็ปŸ็š„็Žฏๅขƒๅœฐๅ€๏ผš
161
+
162
+ 1. ๆต‹่ฏ•็Žฏๅขƒ๏ผˆtest๏ผ‰baseURL
163
+ 2. ้ข„ๅ‘ๅธƒ็Žฏๅขƒ๏ผˆstaging๏ผ‰baseURL
164
+ 3. ็”Ÿไบง็Žฏๅขƒ๏ผˆprod๏ผ‰baseURL
165
+ 4. ้ป˜่ฎค็›ฎๆ ‡็Žฏๅขƒ๏ผˆtest / staging / prod๏ผ‰
166
+
167
+ #### 4.2 ๅ†™ๅ…ฅ .env
168
+
169
+ ๅชๅ†™ๅ…ฅ็Žฏๅขƒๅœฐๅ€้…็ฝฎ๏ผŒไธๅŒ…ๅซ MCP ่ฟžๆŽฅ่ฏดๆ˜Ž๏ผˆMCP ้…็ฝฎๅœจ `.mcp.json` ไธญ็ปŸไธ€็ฎก็†๏ผ‰๏ผš
170
+
171
+ ```bash
172
+ # .env๏ผˆgitignored๏ผŒไธๆไบค๏ผ‰
173
+ BASE_URL_TEST=http://localhost:3000
174
+ BASE_URL_STAGING=https://staging.example.com
175
+ BASE_URL_PROD=https://prod.example.com
176
+ DEFAULT_ENV=test
177
+ ```
178
+
179
+ ---
180
+
181
+ ### ๆญฅ้ชค 5๏ผšๅˆ›ๅปบ topology.yaml ๅˆๅง‹ๆจกๆฟ
182
+
183
+ ๅ†™ๅ…ฅๅŠŸ่ƒฝๆจกๅ—ๆ‹“ๆ‰‘ๆ–‡ไปถๅŸบ็ก€็ป“ๆž„๏ผš
184
+
185
+ ```yaml
186
+ # flows/topology.yaml
187
+ name: E2E ๆต‹่ฏ•ๆ‹“ๆ‰‘
188
+ version: 1
189
+
190
+ modules:
191
+ - name: example
192
+ description: ็คบไพ‹ๆจกๅ—
193
+ children:
194
+ - name: feature1
195
+ description: ๅŠŸ่ƒฝ 1
196
+ features: []
197
+ ```
198
+
199
+ **ๆ็คบ็”จๆˆท๏ผš** ่ฏทๆ นๆฎๅฎž้™…ไบงๅ“็ป“ๆž„็ผ–่พ‘ `flows/topology.yaml`๏ผŒๆทปๅŠ ๅฏนๅบ”็š„ๅŠŸ่ƒฝๆจกๅ—ใ€‚
200
+
201
+ ---
202
+
203
+ ### ๆญฅ้ชค 6๏ผšๅˆ›ๅปบ flow-selector.mjs
204
+
205
+ ไฝฟ็”จ Read ่ฏปๅ–ๆœฌ skill ็›ฎๅฝ•ไธ‹็š„ `scripts/flow-selector.mjs` ๆ–‡ไปถ๏ผŒ็”จ Write ๅ†™ๅ…ฅ็›ฎๆ ‡้กน็›ฎ็š„ `scripts/flow-selector.mjs`ใ€‚
206
+
207
+ > **่ฏดๆ˜Ž๏ผš** `flow-selector.mjs` ๆ˜ฏๅŸบไบŽ `@inquirer/checkbox` ็š„ๅฑ‚็บง้€‰ๆ‹ฉๅทฅๅ…ท่„šๆœฌ๏ผˆไพ› flow-run skill ่ฐƒ็”จ๏ผ‰ใ€‚
208
+ > ๅฎƒไธŽๆœฌ SKILL.md ๅŒๅฑžไธ€ไธช skill๏ผŒไฝœไธบ skill ็š„ๅญๆ–‡ไปถๅˆ†ๅผ€็ปดๆŠค๏ผŒ้ฟๅ…ๅ†…่”ไปฃ็ ๅ—ๅฏผ่‡ด็š„้‡ๅคใ€‚
209
+
210
+ ---
211
+
212
+ ### ๆญฅ้ชค 7๏ผš็กฎ่ฎค E2E ๆก†ๆžถ MCP ้…็ฝฎ๏ผˆไปŽ `.mcp.json` ่ฏปๅ–๏ผ‰
213
+
214
+ ๆฃ€ๆŸฅ `.mcp.json` ไธญๆ˜ฏๅฆๅญ˜ๅœจๅฏนๅบ”ๆก†ๆžถ็š„ MCP ๆœๅŠก้…็ฝฎใ€‚ๅฝ“ๆก†ๆžถไธบ `playwright` ๆ—ถ๏ผŒๆฃ€ๆŸฅ `deepstorm-playwright` ๆœๅŠกใ€‚
215
+
216
+ #### ๆฃ€ๆŸฅ MCP ้…็ฝฎ
217
+
218
+ ```bash
219
+ cat .mcp.json 2>/dev/null | grep -c "deepstorm-playwright"
220
+ ```
221
+
222
+ #### 7.1 MCP ๅทฒ้…็ฝฎ
223
+
224
+ - **WHEN** `.mcp.json` ไธญๅญ˜ๅœจ `deepstorm-playwright`
225
+ - **THEN** ่พ“ๅ‡บ "โœ… Playwright MCP ๅทฒๅฐฑ็ปช๏ผˆ็”ฑ deepstorm setup ็ฎก็†๏ผ‰"
226
+
227
+ #### 7.2 MCP ๆœช้…็ฝฎ
228
+
229
+ - **WHEN** `.mcp.json` ไธญไธๅญ˜ๅœจ `deepstorm-playwright`
230
+ - **THEN** ่พ“ๅ‡บๆ็คบ๏ผš"โš ๏ธ Playwright MCP ๆœช้…็ฝฎใ€‚ๅปบ่ฎฎ่ฟ่กŒ `deepstorm setup` ๅนถ้€‰ๆ‹ฉ Playwright MCP ๆœๅŠกไปฅๅฏ็”จๆต่งˆๅ™จ่‡ชๅŠจๅŒ–ใ€‚"
231
+ - **THEN** ไธ้˜ปๅกžๅˆๅง‹ๅŒ–ๆต็จ‹๏ผŒ็ปง็ปญๆ‰ง่กŒ
232
+
233
+ > **ๆณจๆ„๏ผš** Playwright MCP ้…็ฝฎ็”ฑ CLI setup wizard ็ปŸไธ€็ฎก็†๏ผŒไธๅ†็”ฑๆœฌ skill ๅœจ `.claude/settings.json` ไธญ็‹ฌ็ซ‹้…็ฝฎใ€‚
234
+
235
+ ---
236
+
237
+ ### ๆญฅ้ชค 8๏ผšๅฎŒๆˆๅˆๅง‹ๅŒ–
238
+
239
+ #### 8.1 ๅˆ›ๅปบๅˆๅง‹ๅŒ–ๆ ‡่ฎฐ
240
+
241
+ ```bash
242
+ echo "sweep-e2e-project" > .sweep-init
243
+ ```
244
+
245
+ #### 8.2 ๅฎ‰่ฃ…ไพ่ต–
246
+
247
+ ```bash
248
+ npm install
249
+ ```
250
+
251
+ #### 8.3 ่พ“ๅ‡บๅฎŒๆˆไฟกๆฏ
252
+
253
+ ```
254
+ โœ… Sweep E2E ๆต‹่ฏ•้กน็›ฎๅˆๅง‹ๅŒ–ๅฎŒๆˆ๏ผ
255
+
256
+ ๆก†ๆžถ๏ผš{ๆ‰€้€‰ๆก†ๆžถ}
257
+
258
+ ้กน็›ฎ็ป“ๆž„๏ผš
259
+ flows/ โ€” ๆต‹่ฏ•ๆ„ๅ›พๆ–‡ๆกฃ็›ฎๅฝ•
260
+ flows/reports/ โ€” ๆ‰ง่กŒๆŠฅๅ‘Š็›ฎๅฝ•
261
+ scripts/ โ€” ่พ…ๅŠฉ่„šๆœฌ
262
+ (ๆก†ๆžถ้…็ฝฎๆ–‡ไปถ) โ€” {ๆก†ๆžถๅ} ้…็ฝฎๆ–‡ไปถ
263
+
264
+ ไธ‹ไธ€ๆญฅ๏ผš
265
+ 1. ็ผ–่พ‘ flows/topology.yaml ๆทปๅŠ ไฝ ็š„ๅŠŸ่ƒฝๆจกๅ—
266
+ 2. ่ฟ่กŒ /sweep-plan ๅˆ›ๅปบๆต‹่ฏ•ๆ„ๅ›พๆ–‡ๆกฃ
267
+ 3. ่ฟ่กŒ /sweep-run ๆ‰ง่กŒๆต‹่ฏ•ๆต็จ‹
268
+ ```
269
+
270
+ ---
271
+
272
+ ## ๆฃ€ๆŸฅๆธ…ๅ•
273
+
274
+ - [ ] ๅทฒ่ฏปๅ–ๆก†ๆžถ้…็ฝฎ๏ผˆ`deepstorm.sweep.e2eFramework`๏ผ‰
275
+ - [ ] `.sweep-init` ไธๅญ˜ๅœจ โ†’ ็ปง็ปญ
276
+ - [ ] ้กน็›ฎ็›ฎๅฝ•็ป“ๆž„ๅทฒๅˆ›ๅปบ๏ผˆflows/ใ€flows/reports/ใ€scripts/๏ผ‰
277
+ - [ ] package.json ๅทฒๅ†™ๅ…ฅ๏ผˆๅซๅฏนๅบ”ๆก†ๆžถ็š„ไพ่ต–๏ผ‰
278
+ - [ ] ๆก†ๆžถ้…็ฝฎๆ–‡ไปถๅทฒ็”Ÿๆˆ๏ผˆๅฆ‚ playwright.config.ts๏ผ‰
279
+ - [ ] tsconfig.json ๅทฒๅ†™ๅ…ฅ
280
+ - [ ] ็”จๆˆทๅทฒ่พ“ๅ…ฅไธ‰ไธช็Žฏๅขƒ็š„ baseURL
281
+ - [ ] .env ๅทฒๅ†™ๅ…ฅ๏ผˆgitignored๏ผ‰
282
+ - [ ] topology.yaml ๅˆๅง‹ๆจกๆฟๅทฒๅˆ›ๅปบ
283
+ - [ ] flow-selector.mjs ๅทฒๅ†™ๅ…ฅ
284
+ - [ ] ๅทฒ็กฎ่ฎค MCP ้…็ฝฎ็Šถๆ€๏ผˆไปŽ `.mcp.json` ่ฏปๅ–๏ผ‰
285
+ - [ ] .sweep-init ๆ ‡่ฎฐๆ–‡ไปถๅทฒๅˆ›ๅปบ
286
+ - [ ] npm install ๅทฒๆ‰ง่กŒ
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Sweep Flow Selector โ€” ๅŸบไบŽ @inquirer/checkbox ็š„ๅฑ‚็บง้€‰ๆ‹ฉๅทฅๅ…ท
5
+ *
6
+ * ๆ”ฏๆŒ้€‰ๆ‹ฉ็ฒ’ๅบฆ๏ผš
7
+ * 1. ๆจกๅ—็บงๅˆซ๏ผˆeg. user-system๏ผ‰
8
+ * 2. ๆ–‡ไปถ็บงๅˆซ๏ผˆeg. user-system/login.flow.md๏ผ‰
9
+ * 3. ็”จไพ‹็บงๅˆซ๏ผˆeg. L01 - ๆญฃๅธธ็™ปๅฝ•๏ผ‰
10
+ *
11
+ * ่ฏปๅ– config๏ผˆtopology.yaml ๆˆ–ๆ–‡ไปถ็ณป็ปŸๆ‰ซๆ๏ผ‰๏ผŒๅฑ•็คบ้€็”จไพ‹ๅ‹พ้€‰็•Œ้ขใ€‚
12
+ * ๅฐ†้€‰ไธญ็ป“ๆžœๅ†™ๅ…ฅ .sweep-selection.json ๅนถ่พ“ๅ‡บๅˆฐ stdoutใ€‚
13
+ *
14
+ * ไฝฟ็”จๆ–นๅผ๏ผšnode scripts/flow-selector.mjs
15
+ */
16
+
17
+ import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs';
18
+ import { join, relative } from 'node:path';
19
+
20
+ const BASE_FLOWS_DIR = join(process.cwd(), 'flows');
21
+ const TOPOLOGY_PATH = join(BASE_FLOWS_DIR, 'topology.yaml');
22
+ const SELECTION_PATH = join(process.cwd(), '.sweep-selection.json');
23
+
24
+ /**
25
+ * Override BASE_FLOWS_DIR for testing.
26
+ * Only affects function calls that receive this parameter.
27
+ */
28
+ export const DEFAULT_FLOWS_DIR = BASE_FLOWS_DIR;
29
+
30
+ // โ”€โ”€ YAML ่งฃๆž๏ผˆ่ฝป้‡๏ผŒไธไพ่ต– yaml ๅŒ…๏ผ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
31
+
32
+ export function parseTopology(yaml) {
33
+ const modules = [];
34
+ const lines = yaml.split('\n');
35
+ let stack = [];
36
+
37
+ for (const raw of lines) {
38
+ const trimmed = raw.trimEnd();
39
+ if (!trimmed.trim() || trimmed.trimStart().startsWith('#')) continue;
40
+
41
+ const indent = trimmed.length - trimmed.trimStart().length;
42
+ while (stack.length > 0 && stack[stack.length - 1].indent >= indent) {
43
+ stack.pop();
44
+ }
45
+
46
+ const lineTrimmed = trimmed.trimStart();
47
+ const listMatch = lineTrimmed.match(/^-\s*name:\s*(.+)$/);
48
+ if (!listMatch) continue;
49
+
50
+ const name = listMatch[1];
51
+ const parentList = stack.length > 0 ? stack[stack.length - 1].list : modules;
52
+ const existing = parentList.find((m) => m.name === name);
53
+ if (!existing) {
54
+ parentList.push({ name, children: [] });
55
+ }
56
+ stack.push({ indent, list: parentList[parentList.length - 1].children });
57
+ }
58
+
59
+ return modules;
60
+ }
61
+
62
+ // โ”€โ”€ Flow ๆ–‡ไปถ่งฃๆž โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
63
+
64
+ /**
65
+ * ่งฃๆž .flow.md ๅ†…ๅฎน๏ผŒๆๅ–ๆ‰€ๆœ‰ Flow ๅฎšไน‰
66
+ * @returns {Array<{id: string, title: string}>}
67
+ */
68
+ export function parseFlows(content) {
69
+ const flows = [];
70
+ const regex = /^## Flow:\s*(\S+)\s*-\s*(.+)$/gm;
71
+ let match;
72
+ while ((match = regex.exec(content)) !== null) {
73
+ flows.push({ id: match[1], title: match[2].trim() });
74
+ }
75
+ return flows;
76
+ }
77
+
78
+ /**
79
+ * ๆ นๆฎๆจกๅ—ๅๆŸฅๆ‰พๅฏนๅบ”็š„ .flow.md ๆ–‡ไปถ
80
+ * ๆœ็ดข็ญ–็•ฅ๏ผš
81
+ * 1. ็›ดๆŽฅๅŒน้…๏ผšflows/{module.path}.flow.md
82
+ * 2. ็ˆถ็›ฎๅฝ•ๆ‰ซๆ๏ผšๅœจ็ˆถ็›ฎๅฝ•ไธญๆ‰พๅ็งฐๅŒ…ๅซๆจกๅ—ๅ็š„ .flow.md
83
+ * 3. ๅญ็›ฎๅฝ•ๆจกๅผ๏ผšflows/{module.path}/ ไธ‹็š„ .flow.md
84
+ */
85
+ export function resolveFlowFile(modulePath, flowsDir = BASE_FLOWS_DIR) {
86
+ const directPath = join(flowsDir, `${modulePath}.flow.md`);
87
+ if (existsSync(directPath)) return directPath;
88
+
89
+ const parts = modulePath.split('/');
90
+ const leafName = parts[parts.length - 1];
91
+ const parentDir = parts.length > 1
92
+ ? join(flowsDir, parts.slice(0, -1).join('/'))
93
+ : flowsDir;
94
+ if (existsSync(parentDir)) {
95
+ const files = readdirSync(parentDir);
96
+ const found = files.find(
97
+ (f) => f.endsWith('.flow.md') && f.toLowerCase().includes(leafName.toLowerCase())
98
+ );
99
+ if (found) return join(parentDir, found);
100
+ }
101
+
102
+ const dirPath = join(flowsDir, modulePath);
103
+ if (existsSync(dirPath)) {
104
+ const files = readdirSync(dirPath);
105
+ const found = files.find((f) => f.endsWith('.flow.md'));
106
+ if (found) return join(dirPath, found);
107
+ }
108
+
109
+ return null;
110
+ }
111
+
112
+ // โ”€โ”€ ๆ–‡ไปถ็ณป็ปŸๆ‰ซๆ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
113
+
114
+ export function scanFlowFiles(dir) {
115
+ const result = [];
116
+ const entries = readdirSync(dir, { withFileTypes: true });
117
+ for (const entry of entries) {
118
+ const fullPath = join(dir, entry.name);
119
+ if (entry.isDirectory()) {
120
+ if (entry.name.startsWith('.')) continue;
121
+ result.push(...scanFlowFiles(fullPath));
122
+ } else if (entry.name.endsWith('.flow.md')) {
123
+ result.push(fullPath);
124
+ }
125
+ }
126
+ return result;
127
+ }
128
+
129
+ // โ”€โ”€ ๆž„ๅปบ Choice ๆ ‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
130
+
131
+ export function buildChoicesWithFlows(modules, prefix = '', flowsDir = BASE_FLOWS_DIR) {
132
+ const choices = [];
133
+
134
+ for (const mod of modules) {
135
+ const path = prefix ? `${prefix}/${mod.name}` : mod.name;
136
+
137
+ if (mod.children && mod.children.length > 0) {
138
+ choices.push(...buildChoicesWithFlows(mod.children, path, flowsDir));
139
+ } else {
140
+ const flowFile = resolveFlowFile(path, flowsDir);
141
+ if (flowFile) {
142
+ const content = readFileSync(flowFile, 'utf-8');
143
+ const flows = parseFlows(content);
144
+
145
+ choices.push({
146
+ name: `๐Ÿ“ ${path}`,
147
+ value: `__file:${flowFile}`,
148
+ description: `${flows.length} ไธช็”จไพ‹`,
149
+ });
150
+
151
+ for (const f of flows) {
152
+ choices.push({
153
+ name: ` ${f.id} - ${f.title}`,
154
+ value: JSON.stringify({ file: flowFile, flowId: f.id }),
155
+ description: flowFile,
156
+ });
157
+ }
158
+ } else {
159
+ choices.push({
160
+ name: `๐Ÿ“ ${path}`,
161
+ value: `__module:${path}`,
162
+ description: 'ๆœชๆ‰พๅˆฐๅฏนๅบ” .flow.md ๆ–‡ไปถ',
163
+ });
164
+ }
165
+ }
166
+ }
167
+
168
+ return choices;
169
+ }
170
+
171
+ // โ”€โ”€ ๅค„็†้€‰ไธญ็ป“ๆžœ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
172
+
173
+ export function normalizeSelection(answer) {
174
+ if (answer.includes('__all__')) {
175
+ return { type: 'all' };
176
+ }
177
+
178
+ const fileMap = new Map();
179
+
180
+ for (const item of answer) {
181
+ if (item.startsWith('__file:')) {
182
+ const filePath = item.slice('__file:'.length);
183
+ if (!fileMap.has(filePath)) fileMap.set(filePath, []);
184
+ } else if (!item.startsWith('__module:')) {
185
+ try {
186
+ const { file, flowId } = JSON.parse(item);
187
+ if (!fileMap.has(file)) fileMap.set(file, []);
188
+ const flows = fileMap.get(file);
189
+ if (!flows.includes(flowId)) flows.push(flowId);
190
+ } catch { /* ignore */ }
191
+ }
192
+ }
193
+
194
+ const files = [];
195
+ for (const [filePath, selectedFlows] of fileMap) {
196
+ const content = readFileSync(filePath, 'utf-8');
197
+ const allFlows = parseFlows(content);
198
+ const allIds = allFlows.map((f) => f.id);
199
+
200
+ if (selectedFlows.length === 0 || selectedFlows.length >= allIds.length) {
201
+ files.push({ file: filePath, flows: [], all: true });
202
+ } else {
203
+ files.push({ file: filePath, flows: selectedFlows, all: false });
204
+ }
205
+ }
206
+
207
+ for (const item of answer) {
208
+ if (item.startsWith('__file:')) {
209
+ const filePath = item.slice('__file:'.length);
210
+ const entry = files.find((f) => f.file === filePath);
211
+ if (entry) entry.all = true;
212
+ }
213
+ }
214
+
215
+ return { type: 'selection', files };
216
+ }
217
+
218
+ export function writeSelection(data, selectionPath = SELECTION_PATH) {
219
+ const json = JSON.stringify(data, null, 2);
220
+ writeFileSync(selectionPath, json, 'utf-8');
221
+ console.log(json);
222
+ }
223
+
224
+ // โ”€โ”€ CLI entry point โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
225
+
226
+ if (process.argv[1] === import.meta.filename) {
227
+ main().catch((e) => {
228
+ if (e.name === 'ExitPromptError') process.exit(0);
229
+ const fallback = { type: 'all' };
230
+ writeSelection(fallback);
231
+ process.exit(0);
232
+ });
233
+ }
234
+
235
+ async function main() {
236
+ try {
237
+ let modules = [];
238
+ let isFileScan = false;
239
+
240
+ if (existsSync(TOPOLOGY_PATH)) {
241
+ const yaml = readFileSync(TOPOLOGY_PATH, 'utf-8');
242
+ modules = parseTopology(yaml);
243
+ }
244
+
245
+ if (!modules.length && !existsSync(BASE_FLOWS_DIR)) {
246
+ console.error('โš  flows/ ็›ฎๅฝ•ไธๅญ˜ๅœจ');
247
+ process.exit(1);
248
+ }
249
+
250
+ let choices;
251
+
252
+ if (modules.length > 0) {
253
+ choices = buildChoicesWithFlows(modules);
254
+ } else {
255
+ const files = scanFlowFiles(BASE_FLOWS_DIR);
256
+ if (files.length === 0) {
257
+ console.error('โš  flows/ ็›ฎๅฝ•ไธ‹ๆฒกๆœ‰ๆ‰พๅˆฐไปปไฝ• .flow.md ๆ–‡ไปถ');
258
+ process.exit(1);
259
+ }
260
+
261
+ choices = [{ name: '๐Ÿ“ ๅ…จ้ƒจๆ‰ง่กŒ', value: '__all__' }];
262
+ for (const file of files) {
263
+ const rel = relative(BASE_FLOWS_DIR, file);
264
+ const content = readFileSync(file, 'utf-8');
265
+ const flows = parseFlows(content);
266
+ choices.push({
267
+ name: `๐Ÿ“„ ${rel}`,
268
+ value: `__file:${file}`,
269
+ description: `${flows.length} ไธช็”จไพ‹`,
270
+ });
271
+ for (const f of flows) {
272
+ choices.push({
273
+ name: ` ${f.id} - ${f.title}`,
274
+ value: JSON.stringify({ file, flowId: f.id }),
275
+ description: rel,
276
+ });
277
+ }
278
+ }
279
+ }
280
+
281
+ choices = [
282
+ { name: '๐Ÿ“‹ ๅ…จ้ƒจๆ‰ง่กŒ', value: '__all__' },
283
+ ...choices,
284
+ ];
285
+
286
+ const { default: checkbox } = await import('@inquirer/checkbox');
287
+ const answer = await checkbox({
288
+ message: '้€‰ๆ‹ฉ่ฆๆ‰ง่กŒ็š„ๆต‹่ฏ•็”จไพ‹๏ผˆ็ฉบๆ ผๅ‹พ้€‰๏ผŒๅ›ž่ฝฆ็กฎ่ฎค๏ผŒa=ๅ…จ้€‰๏ผ‰๏ผš',
289
+ pageSize: 24,
290
+ loop: false,
291
+ choices,
292
+ shortcuts: { all: 'a', invert: 'i' },
293
+ });
294
+
295
+ const selection = normalizeSelection(answer);
296
+ writeSelection(selection);
297
+ } catch (e) {
298
+ if (e.name === 'ExitPromptError') {
299
+ process.exit(0);
300
+ }
301
+ const fallback = { type: 'all' };
302
+ writeSelection(fallback);
303
+ process.exit(0);
304
+ }
305
+ }