@curdx/flow 2.3.11 → 3.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 (210) hide show
  1. package/CHANGELOG.md +21 -34
  2. package/LICENSE +1 -1
  3. package/README.md +28 -79
  4. package/dist/index.mjs +995 -0
  5. package/package.json +33 -42
  6. package/.claude-plugin/marketplace.json +0 -48
  7. package/.claude-plugin/plugin.json +0 -70
  8. package/agent-preamble/preamble.md +0 -314
  9. package/agents/flow-adversary.md +0 -202
  10. package/agents/flow-architect.md +0 -197
  11. package/agents/flow-brownfield-analyst.md +0 -142
  12. package/agents/flow-debugger.md +0 -321
  13. package/agents/flow-edge-hunter.md +0 -288
  14. package/agents/flow-executor.md +0 -269
  15. package/agents/flow-orchestrator.md +0 -145
  16. package/agents/flow-planner.md +0 -246
  17. package/agents/flow-product-designer.md +0 -159
  18. package/agents/flow-qa-engineer.md +0 -282
  19. package/agents/flow-researcher.md +0 -165
  20. package/agents/flow-reviewer.md +0 -303
  21. package/agents/flow-security-auditor.md +0 -401
  22. package/agents/flow-triage-analyst.md +0 -272
  23. package/agents/flow-ui-researcher.md +0 -229
  24. package/agents/flow-ux-designer.md +0 -221
  25. package/agents/flow-verifier.md +0 -349
  26. package/bin/curdx-flow +0 -5
  27. package/bin/curdx-flow.js +0 -54
  28. package/cli/README.md +0 -104
  29. package/cli/doctor-workflow.js +0 -483
  30. package/cli/doctor.js +0 -73
  31. package/cli/help.js +0 -59
  32. package/cli/install-bundled-mcps.js +0 -37
  33. package/cli/install-companions.js +0 -19
  34. package/cli/install-context7-config.js +0 -80
  35. package/cli/install-curdx-plugin.js +0 -96
  36. package/cli/install-language.js +0 -35
  37. package/cli/install-next-steps.js +0 -29
  38. package/cli/install-options.js +0 -9
  39. package/cli/install-paths.js +0 -52
  40. package/cli/install-recommended-plugins.js +0 -104
  41. package/cli/install-required-plugins.js +0 -57
  42. package/cli/install-self-update.js +0 -62
  43. package/cli/install-workflow.js +0 -209
  44. package/cli/install.js +0 -101
  45. package/cli/lib/claude-commands.js +0 -41
  46. package/cli/lib/claude-ops.js +0 -47
  47. package/cli/lib/claude.js +0 -183
  48. package/cli/lib/config.js +0 -24
  49. package/cli/lib/doctor-claude-settings.js +0 -1186
  50. package/cli/lib/doctor-report.js +0 -978
  51. package/cli/lib/doctor-runtime-environment.js +0 -196
  52. package/cli/lib/frontmatter.js +0 -44
  53. package/cli/lib/json-schema.js +0 -57
  54. package/cli/lib/logging.js +0 -25
  55. package/cli/lib/process.js +0 -60
  56. package/cli/lib/prompts.js +0 -135
  57. package/cli/lib/runtime.js +0 -107
  58. package/cli/lib/semver.js +0 -109
  59. package/cli/lib/version.js +0 -12
  60. package/cli/protocols-body.md +0 -22
  61. package/cli/protocols.js +0 -162
  62. package/cli/registry.js +0 -123
  63. package/cli/router.js +0 -49
  64. package/cli/uninstall-actions.js +0 -360
  65. package/cli/uninstall-workflow.js +0 -146
  66. package/cli/uninstall.js +0 -42
  67. package/cli/upgrade-workflow.js +0 -80
  68. package/cli/upgrade.js +0 -91
  69. package/cli/utils.js +0 -40
  70. package/gates/adversarial-review-gate.md +0 -219
  71. package/gates/coverage-audit-gate.md +0 -182
  72. package/gates/devex-gate.md +0 -254
  73. package/gates/edge-case-gate.md +0 -194
  74. package/gates/karpathy-gate.md +0 -130
  75. package/gates/security-gate.md +0 -218
  76. package/gates/tdd-gate.md +0 -182
  77. package/gates/test-quality-gate.md +0 -59
  78. package/gates/verification-gate.md +0 -179
  79. package/hooks/hooks.json +0 -58
  80. package/hooks/scripts/common.sh +0 -46
  81. package/hooks/scripts/inject-karpathy.sh +0 -53
  82. package/hooks/scripts/quick-mode-guard.sh +0 -68
  83. package/hooks/scripts/session-start.sh +0 -90
  84. package/hooks/scripts/stop-watcher.sh +0 -230
  85. package/hooks/scripts/subagent-artifact-guard.sh +0 -159
  86. package/hooks/scripts/subagent-statusline.sh +0 -105
  87. package/knowledge/artifact-output-discipline.md +0 -24
  88. package/knowledge/artifact-summary-contracts.md +0 -50
  89. package/knowledge/atomic-commits.md +0 -262
  90. package/knowledge/claude-code-runtime-contracts.md +0 -219
  91. package/knowledge/epic-decomposition.md +0 -307
  92. package/knowledge/execution-strategies.md +0 -303
  93. package/knowledge/karpathy-guidelines.md +0 -219
  94. package/knowledge/planning-reviews.md +0 -211
  95. package/knowledge/poc-first-workflow.md +0 -223
  96. package/knowledge/review-feedback-intake.md +0 -57
  97. package/knowledge/spec-driven-development.md +0 -180
  98. package/knowledge/systematic-debugging.md +0 -378
  99. package/knowledge/two-stage-review.md +0 -249
  100. package/knowledge/wave-execution.md +0 -403
  101. package/monitors/monitors.json +0 -8
  102. package/monitors/scripts/flow-state-monitor.sh +0 -99
  103. package/output-styles/curdx-evidence-first.md +0 -34
  104. package/schemas/agent-frontmatter.schema.json +0 -63
  105. package/schemas/config.schema.json +0 -134
  106. package/schemas/gate-frontmatter.schema.json +0 -30
  107. package/schemas/hooks.schema.json +0 -115
  108. package/schemas/output-style-frontmatter.schema.json +0 -22
  109. package/schemas/plugin-manifest.schema.json +0 -436
  110. package/schemas/plugin-settings.schema.json +0 -29
  111. package/schemas/skill-frontmatter.schema.json +0 -177
  112. package/schemas/spec-frontmatter.schema.json +0 -42
  113. package/schemas/spec-state.schema.json +0 -147
  114. package/settings.json +0 -7
  115. package/skills/brownfield-index/SKILL.md +0 -53
  116. package/skills/brownfield-index/references/applicability.md +0 -12
  117. package/skills/brownfield-index/references/handoff.md +0 -8
  118. package/skills/brownfield-index/references/index-contract.md +0 -10
  119. package/skills/browser-qa/SKILL.md +0 -39
  120. package/skills/browser-qa/references/handoff.md +0 -6
  121. package/skills/browser-qa/references/prerequisites.md +0 -10
  122. package/skills/browser-qa/references/qa-contract.md +0 -20
  123. package/skills/cancel/SKILL.md +0 -41
  124. package/skills/cancel/references/destructive-mode.md +0 -17
  125. package/skills/cancel/references/reporting.md +0 -18
  126. package/skills/cancel/references/state-recovery.md +0 -30
  127. package/skills/cancel/references/target-resolution.md +0 -7
  128. package/skills/debug/SKILL.md +0 -45
  129. package/skills/debug/references/context-gathering.md +0 -11
  130. package/skills/debug/references/failure-guard.md +0 -25
  131. package/skills/debug/references/intake.md +0 -12
  132. package/skills/debug/references/phase-workflow.md +0 -34
  133. package/skills/debug/references/reporting.md +0 -20
  134. package/skills/epic/SKILL.md +0 -39
  135. package/skills/epic/references/epic-artifacts.md +0 -20
  136. package/skills/epic/references/epic-intake.md +0 -9
  137. package/skills/epic/references/slice-handoff.md +0 -16
  138. package/skills/fast/SKILL.md +0 -62
  139. package/skills/fast/references/applicability.md +0 -25
  140. package/skills/fast/references/clarification.md +0 -20
  141. package/skills/fast/references/execution-contract.md +0 -56
  142. package/skills/help/SKILL.md +0 -55
  143. package/skills/help/references/dispatch.md +0 -20
  144. package/skills/help/references/overview.md +0 -39
  145. package/skills/help/references/troubleshoot.md +0 -47
  146. package/skills/help/references/workflow.md +0 -37
  147. package/skills/implement/SKILL.md +0 -96
  148. package/skills/implement/references/error-recovery.md +0 -36
  149. package/skills/implement/references/linear-execution.md +0 -32
  150. package/skills/implement/references/preflight.md +0 -43
  151. package/skills/implement/references/progress-contract.md +0 -32
  152. package/skills/implement/references/state-init.md +0 -33
  153. package/skills/implement/references/stop-hook-execution.md +0 -36
  154. package/skills/implement/references/strategy-router.md +0 -38
  155. package/skills/implement/references/subagent-execution.md +0 -43
  156. package/skills/implement/references/wave-execution.md +0 -162
  157. package/skills/init/SKILL.md +0 -49
  158. package/skills/init/references/gitignore-and-health.md +0 -26
  159. package/skills/init/references/next-steps.md +0 -22
  160. package/skills/init/references/preflight.md +0 -15
  161. package/skills/init/references/scaffold-contract.md +0 -27
  162. package/skills/review/SKILL.md +0 -82
  163. package/skills/review/references/optional-passes.md +0 -48
  164. package/skills/review/references/preflight.md +0 -38
  165. package/skills/review/references/report-contract.md +0 -49
  166. package/skills/review/references/reporting.md +0 -20
  167. package/skills/review/references/stage-execution.md +0 -32
  168. package/skills/security-audit/SKILL.md +0 -47
  169. package/skills/security-audit/references/audit-contract.md +0 -21
  170. package/skills/security-audit/references/gate-handoff.md +0 -8
  171. package/skills/security-audit/references/scope-and-depth.md +0 -9
  172. package/skills/spec/SKILL.md +0 -100
  173. package/skills/spec/references/artifact-landing.md +0 -31
  174. package/skills/spec/references/phase-execution.md +0 -50
  175. package/skills/spec/references/planning-review.md +0 -31
  176. package/skills/spec/references/preflight-and-routing.md +0 -46
  177. package/skills/spec/references/reporting.md +0 -21
  178. package/skills/start/SKILL.md +0 -84
  179. package/skills/start/references/branch-routing.md +0 -51
  180. package/skills/start/references/mode-semantics.md +0 -12
  181. package/skills/start/references/preflight.md +0 -13
  182. package/skills/start/references/reporting.md +0 -20
  183. package/skills/start/references/state-seeding.md +0 -44
  184. package/skills/start/references/workflow-handoff.md +0 -26
  185. package/skills/status/SKILL.md +0 -41
  186. package/skills/status/references/gather-contract.md +0 -27
  187. package/skills/status/references/health-rules.md +0 -27
  188. package/skills/status/references/output-contract.md +0 -24
  189. package/skills/status/references/preflight.md +0 -10
  190. package/skills/status/references/recovery-hints.md +0 -18
  191. package/skills/ui-sketch/SKILL.md +0 -39
  192. package/skills/ui-sketch/references/brief-intake.md +0 -10
  193. package/skills/ui-sketch/references/iteration-handoff.md +0 -5
  194. package/skills/ui-sketch/references/variant-contract.md +0 -15
  195. package/skills/verify/SKILL.md +0 -56
  196. package/skills/verify/references/evidence-workflow.md +0 -39
  197. package/skills/verify/references/output-contract.md +0 -23
  198. package/skills/verify/references/preflight.md +0 -11
  199. package/skills/verify/references/report-handoff.md +0 -35
  200. package/skills/verify/references/strict-mode.md +0 -12
  201. package/templates/CONTEXT.md.tmpl +0 -53
  202. package/templates/PROJECT.md.tmpl +0 -59
  203. package/templates/ROADMAP.md.tmpl +0 -50
  204. package/templates/STATE.md.tmpl +0 -49
  205. package/templates/config.json.tmpl +0 -51
  206. package/templates/design.md.tmpl +0 -83
  207. package/templates/progress.md.tmpl +0 -77
  208. package/templates/requirements.md.tmpl +0 -76
  209. package/templates/research.md.tmpl +0 -83
  210. package/templates/tasks.md.tmpl +0 -107
package/dist/index.mjs ADDED
@@ -0,0 +1,995 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import * as p8 from "@clack/prompts";
5
+ import { defineCommand, runMain } from "citty";
6
+
7
+ // src/ui/language.ts
8
+ import * as p from "@clack/prompts";
9
+
10
+ // src/i18n/zh.ts
11
+ var messages = {
12
+ "app.intro": "@curdx/flow \u2014 Claude Code \u63D2\u4EF6 / MCP \u4E00\u952E\u5B89\u88C5",
13
+ "app.outro": "\u5B8C\u6210\u3002",
14
+ "app.cancelled": "\u5DF2\u53D6\u6D88\u3002",
15
+ "lang.prompt": "\u8BF7\u9009\u62E9\u754C\u9762\u8BED\u8A00 / Please choose your language",
16
+ "lang.zh": "\u4E2D\u6587",
17
+ "lang.en": "English",
18
+ "menu.title": "\u60F3\u505A\u4EC0\u4E48\uFF1F",
19
+ "menu.install": "\u5B89\u88C5 / \u91CD\u88C5\u63D2\u4EF6\u548C MCP",
20
+ "menu.update": "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
21
+ "menu.uninstall": "\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6\u548C MCP",
22
+ "menu.status": "\u67E5\u770B\u5F53\u524D\u5B89\u88C5\u72B6\u6001",
23
+ "menu.exit": "\u9000\u51FA",
24
+ "pkg.installed": "\u5DF2\u5B89\u88C5",
25
+ "pkg.notInstalled": "\u672A\u5B89\u88C5",
26
+ "pkg.unknown": "\u672A\u77E5",
27
+ "install.selectPrompt": "\u52FE\u9009\u8981\u5B89\u88C5 / \u91CD\u88C5\u7684\u6761\u76EE\uFF08\u9ED8\u8BA4\u52FE\u9009\u672A\u5B89\u88C5\u7684\uFF09",
28
+ "install.nothingSelected": "\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6761\u76EE\uFF0C\u5DF2\u9000\u51FA\u3002",
29
+ "install.confirmReinstall": '"{name}" \u5DF2\u5B89\u88C5\uFF0C\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u5148\u5378\u8F7D\u518D\u5B89\u88C5\uFF09\uFF1F',
30
+ "install.skippedReinstall": '\u5DF2\u8DF3\u8FC7 "{name}"\uFF08\u5DF2\u5B89\u88C5\uFF09\u3002',
31
+ "install.prereqFail": '"{name}" \u524D\u7F6E\u68C0\u67E5\u672A\u901A\u8FC7\uFF1A{reason}',
32
+ "install.starting": '\u5F00\u59CB\u5B89\u88C5 "{name}"',
33
+ "install.success": '"{name}" \u5B89\u88C5\u6210\u529F',
34
+ "install.failed": '"{name}" \u5B89\u88C5\u5931\u8D25',
35
+ "install.summaryTitle": "\u5B89\u88C5\u7ED3\u679C",
36
+ "install.summaryOk": "\u6210\u529F {count}",
37
+ "install.summaryFail": "\u5931\u8D25 {count}",
38
+ "install.summarySkip": "\u8DF3\u8FC7 {count}",
39
+ "uninstall.selectPrompt": "\u52FE\u9009\u8981\u5378\u8F7D\u7684\u6761\u76EE\uFF08\u4EC5\u663E\u793A\u5F53\u524D\u5DF2\u5B89\u88C5\u7684\uFF09",
40
+ "uninstall.noneInstalled": "\u5F53\u524D\u6CA1\u6709\u7531\u672C\u5DE5\u5177\u7BA1\u7406\u7684\u6761\u76EE\u5904\u4E8E\u5DF2\u5B89\u88C5\u72B6\u6001\u3002",
41
+ "uninstall.confirm": "\u5C06\u5378\u8F7D {count} \u9879\uFF0C\u786E\u5B9A\u5417\uFF1F",
42
+ "uninstall.starting": '\u5F00\u59CB\u5378\u8F7D "{name}"',
43
+ "uninstall.success": '"{name}" \u5378\u8F7D\u6210\u529F',
44
+ "uninstall.failed": '"{name}" \u5378\u8F7D\u5931\u8D25',
45
+ "update.selectPrompt": "\u52FE\u9009\u8981\u66F4\u65B0\u7684\u6761\u76EE",
46
+ "update.noneInstalled": "\u5F53\u524D\u6CA1\u6709\u53EF\u66F4\u65B0\u7684\u5DF2\u5B89\u88C5\u6761\u76EE\u3002",
47
+ "update.starting": '\u5F00\u59CB\u66F4\u65B0 "{name}"',
48
+ "update.success": '"{name}" \u66F4\u65B0\u6210\u529F',
49
+ "update.failed": '"{name}" \u66F4\u65B0\u5931\u8D25',
50
+ "update.mcpAutoNote": '"{name}" \u901A\u8FC7 npx -y \u542F\u52A8\uFF0C\u6BCF\u6B21\u8FD0\u884C\u81EA\u52A8\u62C9\u53D6\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u624B\u52A8\u66F4\u65B0\u3002',
51
+ "update.context7Note": "context7 \u662F\u8FDC\u7AEF HTTP \u670D\u52A1\uFF0C\u7531\u670D\u52A1\u7AEF\u81EA\u52A8\u66F4\u65B0\uFF0C\u672C\u5730\u65E0\u9700\u64CD\u4F5C\u3002",
52
+ "status.title": "\u5F53\u524D\u72B6\u6001",
53
+ "status.headerName": "\u540D\u79F0",
54
+ "status.headerType": "\u7C7B\u578B",
55
+ "status.headerState": "\u72B6\u6001",
56
+ "context7.askKey": "\u662F\u5426\u586B\u5199 context7 API Key\uFF1F(\u56DE\u8F66\u8DF3\u8FC7\u5373\u7528\u514D\u8D39\u5C42)",
57
+ "context7.keyPlaceholder": "\u7C98\u8D34 API Key\uFF0C\u7559\u7A7A\u8DF3\u8FC7",
58
+ "context7.keyWarning": "\u6CE8\u610F\uFF1AAPI Key \u4F1A\u4EE5\u660E\u6587\u5199\u5165 ~/.claude.json\uFF0C\u8BF7\u59A5\u5584\u7BA1\u7406\u3002",
59
+ "context7.dashboardHint": "\u53EF\u5728 https://context7.com/dashboard \u521B\u5EFA API Key",
60
+ "chrome.prereqNode": "\u9700\u8981 Node.js >= 20.19\uFF0C\u5F53\u524D\u7248\u672C {current}",
61
+ "chrome.prereqChrome": "\u9700\u8981\u672C\u673A\u5DF2\u5B89\u88C5 Chrome\uFF08chrome-devtools-mcp \u4F1A\u8C03\u7528\u672C\u5730\u6D4F\u89C8\u5668\uFF09",
62
+ "reinstall.uninstalling": "\u5148\u5378\u8F7D\u65E7\u7248\u672C\u2026",
63
+ "reinstall.installing": "\u5B89\u88C5\u65B0\u7248\u672C\u2026"
64
+ };
65
+ var zh_default = messages;
66
+
67
+ // src/i18n/en.ts
68
+ var messages2 = {
69
+ "app.intro": "@curdx/flow \u2014 Claude Code plugin & MCP installer",
70
+ "app.outro": "Done.",
71
+ "app.cancelled": "Cancelled.",
72
+ "lang.prompt": "Please choose your language / \u8BF7\u9009\u62E9\u754C\u9762\u8BED\u8A00",
73
+ "lang.zh": "\u4E2D\u6587",
74
+ "lang.en": "English",
75
+ "menu.title": "What would you like to do?",
76
+ "menu.install": "Install / reinstall plugins & MCP servers",
77
+ "menu.update": "Update installed plugins",
78
+ "menu.uninstall": "Uninstall installed plugins & MCP servers",
79
+ "menu.status": "Show current install status",
80
+ "menu.exit": "Exit",
81
+ "pkg.installed": "installed",
82
+ "pkg.notInstalled": "not installed",
83
+ "pkg.unknown": "unknown",
84
+ "install.selectPrompt": "Select items to install / reinstall (not-installed are pre-selected)",
85
+ "install.nothingSelected": "Nothing selected. Exiting.",
86
+ "install.confirmReinstall": '"{name}" is already installed. Reinstall (uninstall then install)?',
87
+ "install.skippedReinstall": 'Skipped "{name}" (already installed).',
88
+ "install.prereqFail": 'Prerequisite failed for "{name}": {reason}',
89
+ "install.starting": 'Installing "{name}"',
90
+ "install.success": '"{name}" installed',
91
+ "install.failed": '"{name}" failed',
92
+ "install.summaryTitle": "Install summary",
93
+ "install.summaryOk": "{count} succeeded",
94
+ "install.summaryFail": "{count} failed",
95
+ "install.summarySkip": "{count} skipped",
96
+ "uninstall.selectPrompt": "Select items to uninstall (only currently installed shown)",
97
+ "uninstall.noneInstalled": "None of the managed items are currently installed.",
98
+ "uninstall.confirm": "About to uninstall {count} item(s). Proceed?",
99
+ "uninstall.starting": 'Uninstalling "{name}"',
100
+ "uninstall.success": '"{name}" uninstalled',
101
+ "uninstall.failed": '"{name}" failed to uninstall',
102
+ "update.selectPrompt": "Select items to update",
103
+ "update.noneInstalled": "No installed items available to update.",
104
+ "update.starting": 'Updating "{name}"',
105
+ "update.success": '"{name}" updated',
106
+ "update.failed": '"{name}" failed to update',
107
+ "update.mcpAutoNote": '"{name}" runs via npx -y and auto-pulls latest on every launch. No manual update needed.',
108
+ "update.context7Note": "context7 is a remote HTTP service, updated server-side. No local action needed.",
109
+ "status.title": "Current status",
110
+ "status.headerName": "Name",
111
+ "status.headerType": "Type",
112
+ "status.headerState": "State",
113
+ "context7.askKey": "Provide a context7 API key? (Enter to skip and use the free tier)",
114
+ "context7.keyPlaceholder": "Paste API key, or leave blank to skip",
115
+ "context7.keyWarning": "Note: the API key is written to ~/.claude.json in plaintext.",
116
+ "context7.dashboardHint": "Get a key at https://context7.com/dashboard",
117
+ "chrome.prereqNode": "Requires Node.js >= 20.19 (current: {current})",
118
+ "chrome.prereqChrome": "Requires Chrome installed locally (chrome-devtools-mcp drives the local browser)",
119
+ "reinstall.uninstalling": "Uninstalling old version\u2026",
120
+ "reinstall.installing": "Installing new version\u2026"
121
+ };
122
+ var en_default = messages2;
123
+
124
+ // src/i18n/index.ts
125
+ var tables = { zh: zh_default, en: en_default };
126
+ var currentLang = "zh";
127
+ function setLang(lang) {
128
+ currentLang = lang;
129
+ }
130
+ function t(key, vars) {
131
+ const raw = tables[currentLang][key] ?? tables.en[key] ?? key;
132
+ if (!vars) return raw;
133
+ return raw.replace(/\{(\w+)\}/g, (_, name) => {
134
+ const v = vars[name];
135
+ return v === void 0 ? `{${name}}` : String(v);
136
+ });
137
+ }
138
+
139
+ // src/ui/language.ts
140
+ function detectLang() {
141
+ const env = process.env["LANG"] ?? process.env["LC_ALL"] ?? process.env["LC_MESSAGES"] ?? "";
142
+ return /^zh/i.test(env) ? "zh" : "en";
143
+ }
144
+ async function initLanguage(override) {
145
+ if (override) {
146
+ setLang(override);
147
+ return;
148
+ }
149
+ if (!process.stdin.isTTY) {
150
+ setLang(detectLang());
151
+ return;
152
+ }
153
+ const picked = await p.select({
154
+ message: t("lang.prompt"),
155
+ options: [
156
+ { value: "zh", label: "\u4E2D\u6587" },
157
+ { value: "en", label: "English" }
158
+ ],
159
+ initialValue: detectLang()
160
+ });
161
+ if (p.isCancel(picked)) {
162
+ setLang(detectLang());
163
+ process.exit(0);
164
+ }
165
+ setLang(picked);
166
+ }
167
+
168
+ // src/ui/menu.ts
169
+ import * as p7 from "@clack/prompts";
170
+
171
+ // src/flows/install.ts
172
+ import * as p3 from "@clack/prompts";
173
+ import pc from "picocolors";
174
+
175
+ // src/runner/exec.ts
176
+ import { x } from "tinyexec";
177
+ async function run(cmd, args) {
178
+ const proc = x(cmd, args, { throwOnError: false });
179
+ const result = await proc;
180
+ return {
181
+ exitCode: result.exitCode ?? 0,
182
+ stdout: result.stdout,
183
+ stderr: result.stderr
184
+ };
185
+ }
186
+ async function runStreaming(cmd, args, log4) {
187
+ log4.message(`$ ${cmd} ${args.join(" ")}`);
188
+ const proc = x(cmd, args, { throwOnError: false });
189
+ let stdout = "";
190
+ for await (const line of proc) {
191
+ const trimmed = line.replace(/\r?\n$/, "");
192
+ if (trimmed.length > 0) {
193
+ stdout += trimmed + "\n";
194
+ log4.message(trimmed);
195
+ }
196
+ }
197
+ const finished = await proc;
198
+ return {
199
+ exitCode: finished.exitCode ?? 0,
200
+ stdout,
201
+ stderr: finished.stderr
202
+ };
203
+ }
204
+ var CmdError = class extends Error {
205
+ constructor(message, result) {
206
+ super(message);
207
+ this.result = result;
208
+ }
209
+ result;
210
+ };
211
+ function ensureOk(result, label) {
212
+ if (result.exitCode !== 0) {
213
+ const detail = (result.stderr || result.stdout || "").trim().slice(0, 500);
214
+ throw new CmdError(`${label} (exit ${result.exitCode})${detail ? `
215
+ ${detail}` : ""}`, result);
216
+ }
217
+ }
218
+
219
+ // src/runner/state.ts
220
+ var pluginCache = null;
221
+ var marketplaceCache = null;
222
+ var mcpCache = null;
223
+ function clearStateCache() {
224
+ pluginCache = null;
225
+ marketplaceCache = null;
226
+ mcpCache = null;
227
+ }
228
+ async function listPlugins(force = false) {
229
+ if (!force && pluginCache) return pluginCache;
230
+ const res = await run("claude", ["plugin", "list", "--json"]);
231
+ if (res.exitCode !== 0) {
232
+ pluginCache = [];
233
+ return pluginCache;
234
+ }
235
+ try {
236
+ const arr = JSON.parse(res.stdout);
237
+ pluginCache = arr.map((p9) => {
238
+ const [name = p9.id, marketplace = ""] = p9.id.split("@");
239
+ return {
240
+ id: p9.id,
241
+ name,
242
+ marketplace,
243
+ version: p9.version,
244
+ scope: p9.scope,
245
+ enabled: p9.enabled
246
+ };
247
+ });
248
+ } catch {
249
+ pluginCache = [];
250
+ }
251
+ return pluginCache;
252
+ }
253
+ async function listMarketplaces(force = false) {
254
+ if (!force && marketplaceCache) return marketplaceCache;
255
+ const res = await run("claude", ["plugin", "marketplace", "list", "--json"]);
256
+ if (res.exitCode !== 0) {
257
+ marketplaceCache = [];
258
+ return marketplaceCache;
259
+ }
260
+ try {
261
+ marketplaceCache = JSON.parse(res.stdout);
262
+ } catch {
263
+ marketplaceCache = [];
264
+ }
265
+ return marketplaceCache;
266
+ }
267
+ async function listMcp(force = false) {
268
+ if (!force && mcpCache) return mcpCache;
269
+ const res = await run("claude", ["mcp", "list"]);
270
+ if (res.exitCode !== 0) {
271
+ mcpCache = [];
272
+ return mcpCache;
273
+ }
274
+ const entries = [];
275
+ for (const rawLine of res.stdout.split(/\r?\n/)) {
276
+ const line = rawLine.trim();
277
+ if (!line) continue;
278
+ if (line.startsWith("Checking ")) continue;
279
+ if (!line.includes(":")) continue;
280
+ if (line.startsWith("plugin:")) continue;
281
+ const colonIdx = line.indexOf(":");
282
+ const name = line.slice(0, colonIdx).trim();
283
+ if (!name || name.includes(" ")) continue;
284
+ entries.push({ name, raw: line });
285
+ }
286
+ mcpCache = entries;
287
+ return mcpCache;
288
+ }
289
+ async function isPluginInstalled(id) {
290
+ const list = await listPlugins();
291
+ return list.some((p9) => p9.id === id);
292
+ }
293
+ async function isMarketplaceAdded(name) {
294
+ const list = await listMarketplaces();
295
+ return list.some((m) => m.name === name);
296
+ }
297
+ async function isMcpInstalled(name) {
298
+ const list = await listMcp();
299
+ return list.some((m) => m.name === name);
300
+ }
301
+
302
+ // src/registry/plugins/_helpers.ts
303
+ async function ensureMarketplace(marketplaceName, marketplaceSource, ctx) {
304
+ if (await isMarketplaceAdded(marketplaceName)) return;
305
+ const r = await runStreaming("claude", ["plugin", "marketplace", "add", marketplaceSource], ctx.log);
306
+ ensureOk(r, `marketplace add ${marketplaceSource}`);
307
+ clearStateCache();
308
+ }
309
+ async function installPluginById(pluginId, ctx) {
310
+ const r = await runStreaming("claude", ["plugin", "install", pluginId], ctx.log);
311
+ ensureOk(r, `plugin install ${pluginId}`);
312
+ clearStateCache();
313
+ }
314
+ async function uninstallPluginById(pluginId, ctx) {
315
+ if (!await isPluginInstalled(pluginId)) return;
316
+ const r = await runStreaming("claude", ["plugin", "uninstall", pluginId], ctx.log);
317
+ ensureOk(r, `plugin uninstall ${pluginId}`);
318
+ clearStateCache();
319
+ }
320
+ async function updatePluginById(pluginId, ctx) {
321
+ const r = await runStreaming("claude", ["plugin", "update", pluginId], ctx.log);
322
+ ensureOk(r, `plugin update ${pluginId}`);
323
+ clearStateCache();
324
+ }
325
+
326
+ // src/registry/plugins/pua.ts
327
+ var PLUGIN_ID = "pua@pua-skills";
328
+ var MARKETPLACE_NAME = "pua-skills";
329
+ var MARKETPLACE_SOURCE = "tanweai/pua";
330
+ var pua = {
331
+ id: "pua",
332
+ name: "pua",
333
+ description: "tanweai/pua \u2014 Chinese Claude Code skills bundle",
334
+ type: "plugin",
335
+ isInstalled: () => isPluginInstalled(PLUGIN_ID),
336
+ install: async (ctx) => {
337
+ await ensureMarketplace(MARKETPLACE_NAME, MARKETPLACE_SOURCE, ctx);
338
+ await installPluginById(PLUGIN_ID, ctx);
339
+ },
340
+ uninstall: (ctx) => uninstallPluginById(PLUGIN_ID, ctx),
341
+ update: (ctx) => updatePluginById(PLUGIN_ID, ctx)
342
+ };
343
+ var pua_default = pua;
344
+
345
+ // src/registry/plugins/claude-mem.ts
346
+ var PLUGIN_ID2 = "claude-mem@thedotmack";
347
+ var MARKETPLACE_NAME2 = "thedotmack";
348
+ var MARKETPLACE_SOURCE2 = "thedotmack/claude-mem";
349
+ var claudeMem = {
350
+ id: "claude-mem",
351
+ name: "claude-mem",
352
+ description: "thedotmack/claude-mem \u2014 persistent cross-session memory for Claude Code",
353
+ type: "plugin",
354
+ isInstalled: () => isPluginInstalled(PLUGIN_ID2),
355
+ install: async (ctx) => {
356
+ await ensureMarketplace(MARKETPLACE_NAME2, MARKETPLACE_SOURCE2, ctx);
357
+ await installPluginById(PLUGIN_ID2, ctx);
358
+ },
359
+ uninstall: (ctx) => uninstallPluginById(PLUGIN_ID2, ctx),
360
+ update: (ctx) => updatePluginById(PLUGIN_ID2, ctx)
361
+ };
362
+ var claude_mem_default = claudeMem;
363
+
364
+ // src/registry/plugins/chrome-devtools-mcp.ts
365
+ var PLUGIN_ID3 = "chrome-devtools-mcp@chrome-devtools-plugins";
366
+ var MARKETPLACE_NAME3 = "chrome-devtools-plugins";
367
+ var MARKETPLACE_SOURCE3 = "ChromeDevTools/chrome-devtools-mcp";
368
+ async function checkChrome() {
369
+ const macPath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
370
+ const [stat, viaPath, viaPathChromium] = await Promise.all([
371
+ run("test", ["-x", macPath]),
372
+ run("which", ["google-chrome"]),
373
+ run("which", ["chromium"])
374
+ ]);
375
+ return stat.exitCode === 0 || viaPath.exitCode === 0 || viaPathChromium.exitCode === 0;
376
+ }
377
+ var chromeDevtoolsMcp = {
378
+ id: "chrome-devtools-mcp",
379
+ name: "chrome-devtools-mcp",
380
+ description: "ChromeDevTools/chrome-devtools-mcp \u2014 drive a real Chrome from Claude Code",
381
+ type: "plugin",
382
+ prereqCheck: async (t2) => {
383
+ const major = Number(process.versions.node.split(".")[0] ?? "0");
384
+ const minor = Number(process.versions.node.split(".")[1] ?? "0");
385
+ if (major < 20 || major === 20 && minor < 19) {
386
+ return { ok: false, reason: t2("chrome.prereqNode", { current: process.versions.node }) };
387
+ }
388
+ if (!await checkChrome()) {
389
+ return { ok: false, reason: t2("chrome.prereqChrome") };
390
+ }
391
+ return { ok: true };
392
+ },
393
+ isInstalled: () => isPluginInstalled(PLUGIN_ID3),
394
+ install: async (ctx) => {
395
+ await ensureMarketplace(MARKETPLACE_NAME3, MARKETPLACE_SOURCE3, ctx);
396
+ await installPluginById(PLUGIN_ID3, ctx);
397
+ },
398
+ uninstall: (ctx) => uninstallPluginById(PLUGIN_ID3, ctx),
399
+ update: (ctx) => updatePluginById(PLUGIN_ID3, ctx)
400
+ };
401
+ var chrome_devtools_mcp_default = chromeDevtoolsMcp;
402
+
403
+ // src/registry/plugins/frontend-design.ts
404
+ var PLUGIN_ID4 = "frontend-design@claude-plugins-official";
405
+ var frontendDesign = {
406
+ id: "frontend-design",
407
+ name: "frontend-design",
408
+ description: "Anthropic official \u2014 UI/frontend design helpers",
409
+ type: "plugin",
410
+ isInstalled: () => isPluginInstalled(PLUGIN_ID4),
411
+ install: (ctx) => installPluginById(PLUGIN_ID4, ctx),
412
+ uninstall: (ctx) => uninstallPluginById(PLUGIN_ID4, ctx),
413
+ update: (ctx) => updatePluginById(PLUGIN_ID4, ctx)
414
+ };
415
+ var frontend_design_default = frontendDesign;
416
+
417
+ // src/registry/mcps/sequential-thinking.ts
418
+ var MCP_NAME = "sequential-thinking";
419
+ var sequentialThinking = {
420
+ id: "sequential-thinking",
421
+ name: "sequential-thinking",
422
+ description: "modelcontextprotocol/server-sequential-thinking \u2014 structured reasoning helper",
423
+ type: "mcp",
424
+ isInstalled: () => isMcpInstalled(MCP_NAME),
425
+ install: async (ctx) => {
426
+ const r = await runStreaming(
427
+ "claude",
428
+ [
429
+ "mcp",
430
+ "add",
431
+ "--scope",
432
+ "user",
433
+ MCP_NAME,
434
+ "--",
435
+ "npx",
436
+ "-y",
437
+ "@modelcontextprotocol/server-sequential-thinking"
438
+ ],
439
+ ctx.log
440
+ );
441
+ ensureOk(r, `mcp add ${MCP_NAME}`);
442
+ clearStateCache();
443
+ },
444
+ uninstall: async (ctx) => {
445
+ if (!await isMcpInstalled(MCP_NAME)) return;
446
+ const r = await runStreaming("claude", ["mcp", "remove", MCP_NAME], ctx.log);
447
+ ensureOk(r, `mcp remove ${MCP_NAME}`);
448
+ clearStateCache();
449
+ }
450
+ // For npx-based MCPs the latest version is fetched at every launch — nothing to do.
451
+ // Flow layer detects update?===noop and shows the i18n note instead.
452
+ };
453
+ var sequential_thinking_default = sequentialThinking;
454
+
455
+ // src/registry/mcps/context7.ts
456
+ import * as p2 from "@clack/prompts";
457
+ var MCP_NAME2 = "context7";
458
+ var URL = "https://mcp.context7.com/mcp";
459
+ var context7 = {
460
+ id: "context7",
461
+ name: "context7",
462
+ description: "upstash/context7 \u2014 up-to-date docs from any library (HTTP MCP, optional API key)",
463
+ type: "mcp",
464
+ isInstalled: () => isMcpInstalled(MCP_NAME2),
465
+ configPrompts: async ({ t: t2 }) => {
466
+ p2.note(`${t2("context7.dashboardHint")}
467
+ ${t2("context7.keyWarning")}`, "context7");
468
+ const key = await p2.text({
469
+ message: t2("context7.askKey"),
470
+ placeholder: t2("context7.keyPlaceholder"),
471
+ defaultValue: ""
472
+ });
473
+ if (p2.isCancel(key)) return null;
474
+ const trimmed = String(key ?? "").trim();
475
+ const out = {};
476
+ if (trimmed) out["CONTEXT7_API_KEY"] = trimmed;
477
+ return out;
478
+ },
479
+ install: async (ctx) => {
480
+ const args = [
481
+ "mcp",
482
+ "add",
483
+ "--scope",
484
+ "user",
485
+ "--transport",
486
+ "http"
487
+ ];
488
+ const apiKey = ctx.config["CONTEXT7_API_KEY"];
489
+ if (apiKey) {
490
+ args.push("--header", `CONTEXT7_API_KEY: ${apiKey}`);
491
+ }
492
+ args.push(MCP_NAME2, URL);
493
+ const r = await runStreaming("claude", args, ctx.log);
494
+ ensureOk(r, `mcp add ${MCP_NAME2}`);
495
+ clearStateCache();
496
+ },
497
+ uninstall: async (ctx) => {
498
+ if (!await isMcpInstalled(MCP_NAME2)) return;
499
+ const r = await runStreaming("claude", ["mcp", "remove", MCP_NAME2], ctx.log);
500
+ ensureOk(r, `mcp remove ${MCP_NAME2}`);
501
+ clearStateCache();
502
+ }
503
+ };
504
+ var context7_default = context7;
505
+
506
+ // src/registry/index.ts
507
+ var PKGS = [
508
+ pua_default,
509
+ claude_mem_default,
510
+ chrome_devtools_mcp_default,
511
+ frontend_design_default,
512
+ sequential_thinking_default,
513
+ context7_default
514
+ ];
515
+ function findPkg(id) {
516
+ return PKGS.find((p9) => p9.id === id);
517
+ }
518
+
519
+ // src/flows/install.ts
520
+ async function selectInteractive() {
521
+ const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
522
+ const options = states.map(({ pkg, installed }) => ({
523
+ value: pkg.id,
524
+ label: `${pkg.name} ${pc.dim(`(${pkg.type})`)} ${installed ? pc.green(`\u2713 ${t("pkg.installed")}`) : pc.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`,
525
+ hint: pkg.description
526
+ }));
527
+ const initialValues = states.filter((s) => !s.installed).map((s) => s.pkg.id);
528
+ const picked = await p3.multiselect({
529
+ message: t("install.selectPrompt"),
530
+ options,
531
+ initialValues,
532
+ required: false
533
+ });
534
+ if (p3.isCancel(picked)) return null;
535
+ return picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
536
+ }
537
+ function selectFromIds(opts) {
538
+ if (opts.all) return [...PKGS];
539
+ if (!opts.ids || opts.ids.length === 0) return [];
540
+ const found = [];
541
+ for (const id of opts.ids) {
542
+ const pkg = findPkg(id);
543
+ if (pkg) found.push(pkg);
544
+ else p3.log.warn(`Unknown id: ${id}`);
545
+ }
546
+ return found;
547
+ }
548
+ async function runOne(pkg, opts, alreadyInstalled) {
549
+ let reinstall = false;
550
+ if (alreadyInstalled) {
551
+ if (opts.yes) {
552
+ reinstall = true;
553
+ } else {
554
+ const ans = await p3.confirm({
555
+ message: t("install.confirmReinstall", { name: pkg.name }),
556
+ initialValue: false
557
+ });
558
+ if (p3.isCancel(ans) || ans === false) {
559
+ return { id: pkg.id, status: "skip", message: t("install.skippedReinstall", { name: pkg.name }) };
560
+ }
561
+ reinstall = true;
562
+ }
563
+ }
564
+ if (pkg.prereqCheck) {
565
+ const r = await pkg.prereqCheck(t);
566
+ if (!r.ok) {
567
+ p3.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
568
+ return { id: pkg.id, status: "skip", message: r.reason };
569
+ }
570
+ }
571
+ let config = {};
572
+ if (pkg.configPrompts) {
573
+ const cfg = await pkg.configPrompts({ t });
574
+ if (cfg === null) return { id: pkg.id, status: "skip", message: t("app.cancelled") };
575
+ config = cfg;
576
+ }
577
+ const log4 = p3.taskLog({ title: t("install.starting", { name: pkg.name }) });
578
+ try {
579
+ if (reinstall) {
580
+ log4.message(t("reinstall.uninstalling"));
581
+ await pkg.uninstall({ log: log4, config, t });
582
+ log4.message(t("reinstall.installing"));
583
+ }
584
+ await pkg.install({ log: log4, config, t });
585
+ log4.success(t("install.success", { name: pkg.name }));
586
+ return { id: pkg.id, status: "ok" };
587
+ } catch (err) {
588
+ const msg = err instanceof Error ? err.message : String(err);
589
+ log4.error(`${t("install.failed", { name: pkg.name })}
590
+ ${msg}`);
591
+ return { id: pkg.id, status: "fail", message: msg };
592
+ }
593
+ }
594
+ function summarize(results) {
595
+ const ok = results.filter((r) => r.status === "ok");
596
+ const fail = results.filter((r) => r.status === "fail");
597
+ const skip = results.filter((r) => r.status === "skip");
598
+ const lines = [
599
+ pc.green(t("install.summaryOk", { count: ok.length })),
600
+ pc.red(t("install.summaryFail", { count: fail.length })),
601
+ pc.yellow(t("install.summarySkip", { count: skip.length })),
602
+ "",
603
+ ...ok.map((r) => ` ${pc.green("\u2713")} ${r.id}`),
604
+ ...skip.map((r) => ` ${pc.yellow("-")} ${r.id}${r.message ? pc.dim(` (${r.message})`) : ""}`),
605
+ ...fail.map((r) => ` ${pc.red("\u2717")} ${r.id}${r.message ? pc.dim(` (${r.message.split("\n")[0]})`) : ""}`)
606
+ ];
607
+ p3.note(lines.join("\n"), t("install.summaryTitle"));
608
+ }
609
+ async function installFlow(opts = {}) {
610
+ const explicit = opts.all || opts.ids && opts.ids.length > 0;
611
+ const targets = explicit ? selectFromIds(opts) : await selectInteractive();
612
+ if (targets === null) {
613
+ p3.cancel(t("app.cancelled"));
614
+ return;
615
+ }
616
+ if (targets.length === 0) {
617
+ p3.log.info(t("install.nothingSelected"));
618
+ return;
619
+ }
620
+ const installedMap = /* @__PURE__ */ new Map();
621
+ await Promise.all(
622
+ targets.map(async (pkg) => {
623
+ installedMap.set(pkg.id, await pkg.isInstalled());
624
+ })
625
+ );
626
+ const results = [];
627
+ for (const pkg of targets) {
628
+ const installed = installedMap.get(pkg.id) ?? false;
629
+ results.push(await runOne(pkg, opts, installed));
630
+ }
631
+ summarize(results);
632
+ }
633
+
634
+ // src/flows/uninstall.ts
635
+ import * as p4 from "@clack/prompts";
636
+ import pc2 from "picocolors";
637
+ async function getInstalled() {
638
+ const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
639
+ return states.filter((s) => s.installed).map((s) => s.pkg);
640
+ }
641
+ async function uninstallFlow(opts = {}) {
642
+ const installed = await getInstalled();
643
+ let targets;
644
+ if (opts.ids && opts.ids.length > 0) {
645
+ targets = [];
646
+ for (const id of opts.ids) {
647
+ const pkg = findPkg(id);
648
+ if (!pkg) {
649
+ p4.log.warn(`Unknown id: ${id}`);
650
+ continue;
651
+ }
652
+ if (!installed.some((x2) => x2.id === pkg.id)) {
653
+ p4.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
654
+ continue;
655
+ }
656
+ targets.push(pkg);
657
+ }
658
+ } else {
659
+ if (installed.length === 0) {
660
+ p4.log.info(t("uninstall.noneInstalled"));
661
+ return;
662
+ }
663
+ const picked = await p4.multiselect({
664
+ message: t("uninstall.selectPrompt"),
665
+ options: installed.map((pkg) => ({
666
+ value: pkg.id,
667
+ label: `${pkg.name} ${pc2.dim(`(${pkg.type})`)}`,
668
+ hint: pkg.description
669
+ })),
670
+ required: false
671
+ });
672
+ if (p4.isCancel(picked)) {
673
+ p4.cancel(t("app.cancelled"));
674
+ return;
675
+ }
676
+ targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
677
+ }
678
+ if (targets.length === 0) {
679
+ p4.log.info(t("install.nothingSelected"));
680
+ return;
681
+ }
682
+ if (!opts.yes) {
683
+ const ok2 = await p4.confirm({
684
+ message: t("uninstall.confirm", { count: targets.length }),
685
+ initialValue: false
686
+ });
687
+ if (p4.isCancel(ok2) || ok2 === false) {
688
+ p4.cancel(t("app.cancelled"));
689
+ return;
690
+ }
691
+ }
692
+ const results = [];
693
+ for (const pkg of targets) {
694
+ const log4 = p4.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
695
+ try {
696
+ await pkg.uninstall({ log: log4, config: {}, t });
697
+ log4.success(t("uninstall.success", { name: pkg.name }));
698
+ results.push({ id: pkg.id, status: "ok" });
699
+ } catch (err) {
700
+ const msg = err instanceof Error ? err.message : String(err);
701
+ log4.error(`${t("uninstall.failed", { name: pkg.name })}
702
+ ${msg}`);
703
+ results.push({ id: pkg.id, status: "fail", message: msg });
704
+ }
705
+ }
706
+ const ok = results.filter((r) => r.status === "ok").length;
707
+ const fail = results.filter((r) => r.status === "fail").length;
708
+ p4.note(
709
+ [
710
+ pc2.green(t("install.summaryOk", { count: ok })),
711
+ pc2.red(t("install.summaryFail", { count: fail }))
712
+ ].join("\n"),
713
+ t("install.summaryTitle")
714
+ );
715
+ }
716
+
717
+ // src/flows/update.ts
718
+ import * as p5 from "@clack/prompts";
719
+ import pc3 from "picocolors";
720
+ async function getInstalled2() {
721
+ const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
722
+ return states.filter((s) => s.installed).map((s) => s.pkg);
723
+ }
724
+ async function updateFlow(opts = {}) {
725
+ const installed = await getInstalled2();
726
+ if (installed.length === 0) {
727
+ p5.log.info(t("update.noneInstalled"));
728
+ return;
729
+ }
730
+ let targets;
731
+ if (opts.all) {
732
+ targets = installed;
733
+ } else if (opts.ids && opts.ids.length > 0) {
734
+ targets = [];
735
+ for (const id of opts.ids) {
736
+ const pkg = findPkg(id);
737
+ if (!pkg) {
738
+ p5.log.warn(`Unknown id: ${id}`);
739
+ continue;
740
+ }
741
+ if (!installed.some((x2) => x2.id === pkg.id)) {
742
+ p5.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
743
+ continue;
744
+ }
745
+ targets.push(pkg);
746
+ }
747
+ } else {
748
+ const picked = await p5.multiselect({
749
+ message: t("update.selectPrompt"),
750
+ options: installed.map((pkg) => ({
751
+ value: pkg.id,
752
+ label: `${pkg.name} ${pc3.dim(`(${pkg.type})`)}`,
753
+ hint: pkg.description
754
+ })),
755
+ required: false
756
+ });
757
+ if (p5.isCancel(picked)) {
758
+ p5.cancel(t("app.cancelled"));
759
+ return;
760
+ }
761
+ targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
762
+ }
763
+ if (targets.length === 0) {
764
+ p5.log.info(t("install.nothingSelected"));
765
+ return;
766
+ }
767
+ const results = [];
768
+ for (const pkg of targets) {
769
+ if (pkg.id === "sequential-thinking") {
770
+ p5.log.info(t("update.mcpAutoNote", { name: pkg.name }));
771
+ results.push({ id: pkg.id, status: "noop" });
772
+ continue;
773
+ }
774
+ if (pkg.id === "context7") {
775
+ p5.log.info(t("update.context7Note"));
776
+ results.push({ id: pkg.id, status: "noop" });
777
+ continue;
778
+ }
779
+ const log4 = p5.taskLog({ title: t("update.starting", { name: pkg.name }) });
780
+ try {
781
+ if (pkg.update) {
782
+ await pkg.update({ log: log4, config: {}, t });
783
+ } else {
784
+ await pkg.uninstall({ log: log4, config: {}, t });
785
+ await pkg.install({ log: log4, config: {}, t });
786
+ }
787
+ log4.success(t("update.success", { name: pkg.name }));
788
+ results.push({ id: pkg.id, status: "ok" });
789
+ } catch (err) {
790
+ const msg = err instanceof Error ? err.message : String(err);
791
+ log4.error(`${t("update.failed", { name: pkg.name })}
792
+ ${msg}`);
793
+ results.push({ id: pkg.id, status: "fail", message: msg });
794
+ }
795
+ }
796
+ const ok = results.filter((r) => r.status === "ok").length;
797
+ const fail = results.filter((r) => r.status === "fail").length;
798
+ const noop = results.filter((r) => r.status === "noop").length;
799
+ p5.note(
800
+ [
801
+ pc3.green(t("install.summaryOk", { count: ok })),
802
+ pc3.red(t("install.summaryFail", { count: fail })),
803
+ pc3.dim(`noop: ${noop}`)
804
+ ].join("\n"),
805
+ t("install.summaryTitle")
806
+ );
807
+ }
808
+
809
+ // src/flows/status.ts
810
+ import * as p6 from "@clack/prompts";
811
+ import pc4 from "picocolors";
812
+ async function statusFlow(opts = {}) {
813
+ const states = await Promise.all(
814
+ PKGS.map(async (pkg) => ({
815
+ id: pkg.id,
816
+ name: pkg.name,
817
+ type: pkg.type,
818
+ installed: await pkg.isInstalled()
819
+ }))
820
+ );
821
+ if (opts.json) {
822
+ process.stdout.write(JSON.stringify(states, null, 2) + "\n");
823
+ return;
824
+ }
825
+ const nameW = Math.max(t("status.headerName").length, ...states.map((s) => s.name.length));
826
+ const typeW = Math.max(t("status.headerType").length, ...states.map((s) => s.type.length));
827
+ const header = `${t("status.headerName").padEnd(nameW)} ${t("status.headerType").padEnd(typeW)} ${t("status.headerState")}`;
828
+ const sep = `${"-".repeat(nameW)} ${"-".repeat(typeW)} ${"-".repeat(15)}`;
829
+ const rows = states.map(
830
+ (s) => `${s.name.padEnd(nameW)} ${s.type.padEnd(typeW)} ${s.installed ? pc4.green(`\u2713 ${t("pkg.installed")}`) : pc4.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`
831
+ );
832
+ p6.note([header, sep, ...rows].join("\n"), t("status.title"));
833
+ }
834
+
835
+ // src/ui/menu.ts
836
+ async function mainMenu() {
837
+ const action = await p7.select({
838
+ message: t("menu.title"),
839
+ options: [
840
+ { value: "install", label: t("menu.install") },
841
+ { value: "update", label: t("menu.update") },
842
+ { value: "uninstall", label: t("menu.uninstall") },
843
+ { value: "status", label: t("menu.status") },
844
+ { value: "exit", label: t("menu.exit") }
845
+ ]
846
+ });
847
+ if (p7.isCancel(action) || action === "exit") {
848
+ p7.cancel(t("app.cancelled"));
849
+ return;
850
+ }
851
+ switch (action) {
852
+ case "install":
853
+ await installFlow();
854
+ break;
855
+ case "update":
856
+ await updateFlow();
857
+ break;
858
+ case "uninstall":
859
+ await uninstallFlow();
860
+ break;
861
+ case "status":
862
+ await statusFlow();
863
+ break;
864
+ }
865
+ }
866
+
867
+ // src/index.ts
868
+ function parseLang(v) {
869
+ return v === "zh" || v === "en" ? v : void 0;
870
+ }
871
+ var sharedArgs = {
872
+ lang: { type: "string", description: "Override language: zh or en" }
873
+ };
874
+ var installCmd = defineCommand({
875
+ meta: { name: "install", description: "Install or reinstall plugins / MCP servers" },
876
+ args: {
877
+ ...sharedArgs,
878
+ all: { type: "boolean", description: "Install all known items" },
879
+ yes: { type: "boolean", description: "Skip reinstall confirmation (assume yes)" },
880
+ ids: { type: "positional", required: false, description: "Item ids", default: "" }
881
+ },
882
+ async run({ args }) {
883
+ await initLanguage(parseLang(args.lang));
884
+ p8.intro(t("app.intro"));
885
+ const ids = collectPositional(args);
886
+ await installFlow({ ids, all: Boolean(args.all), yes: Boolean(args.yes) });
887
+ p8.outro(t("app.outro"));
888
+ }
889
+ });
890
+ var uninstallCmd = defineCommand({
891
+ meta: { name: "uninstall", description: "Uninstall installed plugins / MCP servers" },
892
+ args: {
893
+ ...sharedArgs,
894
+ yes: { type: "boolean", description: "Skip confirmation" },
895
+ ids: { type: "positional", required: false, description: "Item ids", default: "" }
896
+ },
897
+ async run({ args }) {
898
+ await initLanguage(parseLang(args.lang));
899
+ p8.intro(t("app.intro"));
900
+ const ids = collectPositional(args);
901
+ await uninstallFlow({ ids, yes: Boolean(args.yes) });
902
+ p8.outro(t("app.outro"));
903
+ }
904
+ });
905
+ var updateCmd = defineCommand({
906
+ meta: { name: "update", description: "Update installed plugins" },
907
+ args: {
908
+ ...sharedArgs,
909
+ all: { type: "boolean", description: "Update all installed" },
910
+ ids: { type: "positional", required: false, description: "Item ids", default: "" }
911
+ },
912
+ async run({ args }) {
913
+ await initLanguage(parseLang(args.lang));
914
+ p8.intro(t("app.intro"));
915
+ const ids = collectPositional(args);
916
+ await updateFlow({ ids, all: Boolean(args.all) });
917
+ p8.outro(t("app.outro"));
918
+ }
919
+ });
920
+ var statusCmd = defineCommand({
921
+ meta: { name: "status", description: "Show install status" },
922
+ args: {
923
+ ...sharedArgs,
924
+ json: { type: "boolean", description: "Output JSON (machine-readable)" }
925
+ },
926
+ async run({ args }) {
927
+ await initLanguage(parseLang(args.lang));
928
+ if (!args.json) p8.intro(t("app.intro"));
929
+ await statusFlow({ json: Boolean(args.json) });
930
+ if (!args.json) p8.outro(t("app.outro"));
931
+ }
932
+ });
933
+ var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "status"]);
934
+ var root = defineCommand({
935
+ meta: {
936
+ name: "@curdx/flow",
937
+ version: "3.1.0",
938
+ description: "Interactive installer for Claude Code plugins and MCP servers"
939
+ },
940
+ args: sharedArgs,
941
+ subCommands: {
942
+ install: installCmd,
943
+ uninstall: uninstallCmd,
944
+ update: updateCmd,
945
+ status: statusCmd
946
+ }
947
+ // No root run() — citty 0.1.6 calls parent.run AFTER a matching subcommand,
948
+ // which would render the menu after a subcommand finishes. We dispatch the
949
+ // interactive menu ourselves below for the no-subcommand case.
950
+ });
951
+ function collectPositional(args) {
952
+ const ids = [];
953
+ const rest = args._;
954
+ if (Array.isArray(rest)) ids.push(...rest);
955
+ const single = args["ids"];
956
+ if (typeof single === "string" && single.length > 0) ids.unshift(single);
957
+ return ids;
958
+ }
959
+ function firstNonFlag(argv2) {
960
+ for (const a of argv2) {
961
+ if (!a.startsWith("-")) return a;
962
+ }
963
+ return void 0;
964
+ }
965
+ async function runInteractive(argv2) {
966
+ let lang;
967
+ for (let i = 0; i < argv2.length; i++) {
968
+ if (argv2[i] === "--lang" && argv2[i + 1]) lang = parseLang(argv2[i + 1]);
969
+ else if (argv2[i]?.startsWith("--lang=")) lang = parseLang(argv2[i].slice("--lang=".length));
970
+ }
971
+ await initLanguage(lang);
972
+ p8.intro(t("app.intro"));
973
+ await mainMenu();
974
+ p8.outro(t("app.outro"));
975
+ }
976
+ var argv = process.argv.slice(2);
977
+ var first = firstNonFlag(argv);
978
+ if (first === void 0 || first !== void 0 && !SUBCOMMANDS.has(first) && first !== "--help" && first !== "-h") {
979
+ if (first === void 0) {
980
+ runInteractive(argv).catch((err) => {
981
+ console.error(err);
982
+ process.exit(1);
983
+ });
984
+ } else {
985
+ runMain(root).catch((err) => {
986
+ console.error(err);
987
+ process.exit(1);
988
+ });
989
+ }
990
+ } else {
991
+ runMain(root).catch((err) => {
992
+ console.error(err);
993
+ process.exit(1);
994
+ });
995
+ }