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