@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.
- package/CHANGELOG.md +33 -82
- package/LICENSE +1 -1
- package/README.md +28 -129
- package/dist/index.mjs +1165 -0
- package/package.json +33 -44
- package/.claude-plugin/marketplace.json +0 -48
- package/.claude-plugin/plugin.json +0 -52
- package/agent-preamble/preamble.md +0 -314
- package/agents/flow-adversary.md +0 -203
- package/agents/flow-architect.md +0 -198
- package/agents/flow-brownfield-analyst.md +0 -143
- package/agents/flow-debugger.md +0 -321
- package/agents/flow-edge-hunter.md +0 -289
- package/agents/flow-executor.md +0 -269
- package/agents/flow-orchestrator.md +0 -145
- package/agents/flow-planner.md +0 -247
- package/agents/flow-product-designer.md +0 -159
- package/agents/flow-qa-engineer.md +0 -282
- package/agents/flow-researcher.md +0 -166
- package/agents/flow-reviewer.md +0 -304
- package/agents/flow-security-auditor.md +0 -401
- package/agents/flow-triage-analyst.md +0 -272
- package/agents/flow-ui-researcher.md +0 -230
- package/agents/flow-ux-designer.md +0 -221
- package/agents/flow-verifier.md +0 -350
- package/bin/curdx-flow +0 -5
- package/bin/curdx-flow-state +0 -104
- package/bin/curdx-flow.js +0 -54
- package/cli/README.md +0 -104
- package/cli/doctor-workflow.js +0 -483
- package/cli/doctor.js +0 -73
- package/cli/help.js +0 -59
- package/cli/install-bundled-mcps.js +0 -37
- package/cli/install-companions.js +0 -19
- package/cli/install-context7-config.js +0 -80
- package/cli/install-curdx-plugin.js +0 -96
- package/cli/install-language.js +0 -35
- package/cli/install-next-steps.js +0 -29
- package/cli/install-options.js +0 -9
- package/cli/install-paths.js +0 -52
- package/cli/install-recommended-plugins.js +0 -104
- package/cli/install-required-plugins.js +0 -57
- package/cli/install-self-update.js +0 -62
- package/cli/install-workflow.js +0 -209
- package/cli/install.js +0 -101
- package/cli/lib/claude-commands.js +0 -41
- package/cli/lib/claude-ops.js +0 -47
- package/cli/lib/claude.js +0 -183
- package/cli/lib/config.js +0 -24
- package/cli/lib/doctor-claude-settings.js +0 -1186
- package/cli/lib/doctor-report.js +0 -978
- package/cli/lib/doctor-runtime-environment.js +0 -196
- package/cli/lib/frontmatter.js +0 -44
- package/cli/lib/json-schema.js +0 -57
- package/cli/lib/logging.js +0 -25
- package/cli/lib/process.js +0 -60
- package/cli/lib/prompts.js +0 -135
- package/cli/lib/runtime.js +0 -107
- package/cli/lib/semver.js +0 -109
- package/cli/lib/version.js +0 -12
- package/cli/protocols-body.md +0 -22
- package/cli/protocols.js +0 -162
- package/cli/registry.js +0 -123
- package/cli/router.js +0 -49
- package/cli/uninstall-actions.js +0 -360
- package/cli/uninstall-workflow.js +0 -146
- package/cli/uninstall.js +0 -42
- package/cli/upgrade-workflow.js +0 -80
- package/cli/upgrade.js +0 -91
- package/cli/utils.js +0 -40
- package/gates/adversarial-review-gate.md +0 -219
- package/gates/coverage-audit-gate.md +0 -182
- package/gates/devex-gate.md +0 -254
- package/gates/edge-case-gate.md +0 -194
- package/gates/karpathy-gate.md +0 -130
- package/gates/security-gate.md +0 -218
- package/gates/tdd-gate.md +0 -182
- package/gates/test-quality-gate.md +0 -59
- package/gates/verification-gate.md +0 -179
- package/hooks/hooks.json +0 -130
- package/hooks/scripts/common.sh +0 -237
- package/hooks/scripts/config-change-guard.sh +0 -94
- package/hooks/scripts/flow-context-watch.sh +0 -94
- package/hooks/scripts/inject-karpathy.sh +0 -53
- package/hooks/scripts/quick-mode-guard.sh +0 -69
- package/hooks/scripts/session-start.sh +0 -94
- package/hooks/scripts/session-title.sh +0 -87
- package/hooks/scripts/stop-watcher.sh +0 -231
- package/hooks/scripts/subagent-artifact-guard.sh +0 -92
- package/hooks/scripts/subagent-statusline.sh +0 -111
- package/hooks/scripts/task-lifecycle-guard.sh +0 -106
- package/hooks/scripts/teammate-idle-guard.sh +0 -83
- package/knowledge/artifact-output-discipline.md +0 -24
- package/knowledge/artifact-summary-contracts.md +0 -50
- package/knowledge/atomic-commits.md +0 -262
- package/knowledge/claude-code-runtime-contracts.md +0 -240
- package/knowledge/epic-decomposition.md +0 -307
- package/knowledge/execution-strategies.md +0 -303
- package/knowledge/karpathy-guidelines.md +0 -219
- package/knowledge/planning-reviews.md +0 -211
- package/knowledge/poc-first-workflow.md +0 -223
- package/knowledge/review-feedback-intake.md +0 -57
- package/knowledge/spec-driven-development.md +0 -180
- package/knowledge/systematic-debugging.md +0 -378
- package/knowledge/two-stage-review.md +0 -249
- package/knowledge/wave-execution.md +0 -403
- package/monitors/monitors.json +0 -8
- package/monitors/scripts/flow-state-monitor.sh +0 -102
- package/output-styles/curdx-evidence-first.md +0 -34
- package/output-styles/curdx-fast-mode.md +0 -42
- package/output-styles/curdx-spec-mode.md +0 -46
- package/schemas/agent-frontmatter.schema.json +0 -66
- package/schemas/config.schema.json +0 -134
- package/schemas/gate-frontmatter.schema.json +0 -30
- package/schemas/hooks.schema.json +0 -115
- package/schemas/output-style-frontmatter.schema.json +0 -22
- package/schemas/plugin-manifest.schema.json +0 -436
- package/schemas/plugin-settings.schema.json +0 -29
- package/schemas/skill-frontmatter.schema.json +0 -177
- package/schemas/spec-frontmatter.schema.json +0 -42
- package/schemas/spec-state.schema.json +0 -165
- package/settings.json +0 -8
- package/skills/brownfield-index/SKILL.md +0 -53
- package/skills/brownfield-index/references/applicability.md +0 -12
- package/skills/brownfield-index/references/handoff.md +0 -8
- package/skills/brownfield-index/references/index-contract.md +0 -10
- package/skills/browser-qa/SKILL.md +0 -39
- package/skills/browser-qa/references/handoff.md +0 -6
- package/skills/browser-qa/references/prerequisites.md +0 -10
- package/skills/browser-qa/references/qa-contract.md +0 -20
- package/skills/cancel/SKILL.md +0 -41
- package/skills/cancel/references/destructive-mode.md +0 -17
- package/skills/cancel/references/reporting.md +0 -18
- package/skills/cancel/references/state-recovery.md +0 -30
- package/skills/cancel/references/target-resolution.md +0 -7
- package/skills/debug/SKILL.md +0 -45
- package/skills/debug/references/context-gathering.md +0 -11
- package/skills/debug/references/failure-guard.md +0 -25
- package/skills/debug/references/intake.md +0 -12
- package/skills/debug/references/phase-workflow.md +0 -34
- package/skills/debug/references/reporting.md +0 -20
- package/skills/epic/SKILL.md +0 -39
- package/skills/epic/references/epic-artifacts.md +0 -20
- package/skills/epic/references/epic-intake.md +0 -9
- package/skills/epic/references/slice-handoff.md +0 -16
- package/skills/fast/SKILL.md +0 -62
- package/skills/fast/references/applicability.md +0 -25
- package/skills/fast/references/clarification.md +0 -20
- package/skills/fast/references/execution-contract.md +0 -56
- package/skills/help/SKILL.md +0 -55
- package/skills/help/references/dispatch.md +0 -20
- package/skills/help/references/overview.md +0 -39
- package/skills/help/references/troubleshoot.md +0 -47
- package/skills/help/references/workflow.md +0 -37
- package/skills/implement/SKILL.md +0 -104
- package/skills/implement/references/error-recovery.md +0 -36
- package/skills/implement/references/linear-execution.md +0 -43
- package/skills/implement/references/native-task-sync.md +0 -107
- package/skills/implement/references/preflight.md +0 -43
- package/skills/implement/references/progress-contract.md +0 -36
- package/skills/implement/references/state-init.md +0 -36
- package/skills/implement/references/stop-hook-execution.md +0 -50
- package/skills/implement/references/strategy-router.md +0 -38
- package/skills/implement/references/subagent-execution.md +0 -57
- package/skills/implement/references/wave-execution.md +0 -180
- package/skills/init/SKILL.md +0 -49
- package/skills/init/references/gitignore-and-health.md +0 -26
- package/skills/init/references/next-steps.md +0 -22
- package/skills/init/references/preflight.md +0 -15
- package/skills/init/references/scaffold-contract.md +0 -27
- package/skills/review/SKILL.md +0 -82
- package/skills/review/references/optional-passes.md +0 -48
- package/skills/review/references/preflight.md +0 -38
- package/skills/review/references/report-contract.md +0 -49
- package/skills/review/references/reporting.md +0 -20
- package/skills/review/references/stage-execution.md +0 -32
- package/skills/security-audit/SKILL.md +0 -47
- package/skills/security-audit/references/audit-contract.md +0 -21
- package/skills/security-audit/references/gate-handoff.md +0 -8
- package/skills/security-audit/references/scope-and-depth.md +0 -9
- package/skills/spec/SKILL.md +0 -100
- package/skills/spec/references/artifact-landing.md +0 -31
- package/skills/spec/references/phase-execution.md +0 -50
- package/skills/spec/references/planning-review.md +0 -31
- package/skills/spec/references/preflight-and-routing.md +0 -46
- package/skills/spec/references/reporting.md +0 -21
- package/skills/start/SKILL.md +0 -84
- package/skills/start/references/branch-routing.md +0 -51
- package/skills/start/references/mode-semantics.md +0 -12
- package/skills/start/references/preflight.md +0 -13
- package/skills/start/references/reporting.md +0 -20
- package/skills/start/references/state-seeding.md +0 -44
- package/skills/start/references/workflow-handoff.md +0 -26
- package/skills/status/SKILL.md +0 -41
- package/skills/status/references/gather-contract.md +0 -30
- package/skills/status/references/health-rules.md +0 -27
- package/skills/status/references/output-contract.md +0 -25
- package/skills/status/references/preflight.md +0 -10
- package/skills/status/references/recovery-hints.md +0 -18
- package/skills/ui-sketch/SKILL.md +0 -39
- package/skills/ui-sketch/references/brief-intake.md +0 -10
- package/skills/ui-sketch/references/iteration-handoff.md +0 -5
- package/skills/ui-sketch/references/variant-contract.md +0 -15
- package/skills/verify/SKILL.md +0 -56
- package/skills/verify/references/evidence-workflow.md +0 -39
- package/skills/verify/references/output-contract.md +0 -23
- package/skills/verify/references/preflight.md +0 -11
- package/skills/verify/references/report-handoff.md +0 -35
- package/skills/verify/references/strict-mode.md +0 -12
- package/templates/CONTEXT.md.tmpl +0 -53
- package/templates/PROJECT.md.tmpl +0 -59
- package/templates/ROADMAP.md.tmpl +0 -50
- package/templates/STATE.md.tmpl +0 -49
- package/templates/config.json.tmpl +0 -51
- package/templates/design.md.tmpl +0 -83
- package/templates/progress.md.tmpl +0 -77
- package/templates/requirements.md.tmpl +0 -76
- package/templates/research.md.tmpl +0 -83
- 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
|
+
}
|