@curdx/flow 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -87
- package/LICENSE +1 -1
- package/README.md +28 -129
- package/dist/index.mjs +995 -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/cli/doctor.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* doctor command — external health check (no need to enter Claude Code).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
color,
|
|
7
|
-
log,
|
|
8
|
-
} from "./utils.js";
|
|
9
|
-
import { buildDoctorReport } from "./lib/doctor-report.js";
|
|
10
|
-
import {
|
|
11
|
-
applyDoctorFixes,
|
|
12
|
-
buildDoctorJsonPayload,
|
|
13
|
-
collectDoctorData,
|
|
14
|
-
createDoctorContext,
|
|
15
|
-
printDoctorSummary,
|
|
16
|
-
printVerboseDoctorDetails,
|
|
17
|
-
renderReportLines,
|
|
18
|
-
} from "./doctor-workflow.js";
|
|
19
|
-
|
|
20
|
-
export async function doctor(
|
|
21
|
-
args = [],
|
|
22
|
-
{
|
|
23
|
-
applyDoctorFixesImpl = applyDoctorFixes,
|
|
24
|
-
buildDoctorJsonPayloadImpl = buildDoctorJsonPayload,
|
|
25
|
-
buildDoctorReportImpl = buildDoctorReport,
|
|
26
|
-
collectDoctorDataImpl = collectDoctorData,
|
|
27
|
-
createDoctorContextImpl = createDoctorContext,
|
|
28
|
-
printDoctorSummaryImpl = printDoctorSummary,
|
|
29
|
-
printVerboseDoctorDetailsImpl = printVerboseDoctorDetails,
|
|
30
|
-
renderReportLinesImpl = renderReportLines,
|
|
31
|
-
logImpl = log,
|
|
32
|
-
colorImpl = color,
|
|
33
|
-
consoleImpl = console,
|
|
34
|
-
processImpl = process,
|
|
35
|
-
} = {}
|
|
36
|
-
) {
|
|
37
|
-
const context = createDoctorContextImpl(args);
|
|
38
|
-
let fixes = [];
|
|
39
|
-
|
|
40
|
-
if (!context.json) {
|
|
41
|
-
logImpl.title("🏥 CurdX-Flow Health Check");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const doctorData = await collectDoctorDataImpl();
|
|
45
|
-
if (context.fix) {
|
|
46
|
-
if (!context.json) {
|
|
47
|
-
logImpl.info("Applying safe fixes...");
|
|
48
|
-
}
|
|
49
|
-
fixes = await applyDoctorFixesImpl(doctorData);
|
|
50
|
-
if (fixes.length === 0 && !context.json) {
|
|
51
|
-
logImpl.info("No automatic fixes available for the current environment");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const report = buildDoctorReportImpl(doctorData);
|
|
55
|
-
|
|
56
|
-
if (context.json) {
|
|
57
|
-
const payload = buildDoctorJsonPayloadImpl({ context, doctorData, fixes, report });
|
|
58
|
-
consoleImpl.log(JSON.stringify(payload, null, 2));
|
|
59
|
-
processImpl.exitCode = report.errors > 0 ? 1 : 0;
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
renderReportLinesImpl(report.lines, { logImpl });
|
|
64
|
-
for (const section of report.sections) {
|
|
65
|
-
consoleImpl.log(`\n${colorImpl.bold(section.title)}`);
|
|
66
|
-
renderReportLinesImpl(section.lines, { logImpl });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
printDoctorSummaryImpl(report, { logImpl });
|
|
70
|
-
if (context.verbose && doctorData.claudeVersionValue) {
|
|
71
|
-
printVerboseDoctorDetailsImpl();
|
|
72
|
-
}
|
|
73
|
-
}
|
package/cli/help.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { VERSION, color } from "./utils.js";
|
|
2
|
-
|
|
3
|
-
export function printHelp() {
|
|
4
|
-
console.log(`
|
|
5
|
-
${color.bold("curdx-flow")} ${color.dim(`v${VERSION}`)}
|
|
6
|
-
CurdX-Flow installer & helper for Claude Code
|
|
7
|
-
|
|
8
|
-
${color.bold("USAGE")}
|
|
9
|
-
npx @curdx/flow <command> [options]
|
|
10
|
-
|
|
11
|
-
${color.bold("COMMANDS")}
|
|
12
|
-
${color.cyan("install")} Install curdx-flow plugin + optional recommended plugins
|
|
13
|
-
--all Install all recommended (skip prompt)
|
|
14
|
-
--no-deps Only install curdx-flow, skip recommendations
|
|
15
|
-
--online Fetch plugin from GitHub instead of using the
|
|
16
|
-
local npm package (slower; default is offline
|
|
17
|
-
when the plugin body is bundled)
|
|
18
|
-
|
|
19
|
-
${color.cyan("doctor")} Check health (claude CLI, plugin, MCPs, recommended)
|
|
20
|
-
--fix Apply safe runtime fixes (bun/uv PATH symlinks)
|
|
21
|
-
--json Emit machine-readable health report JSON
|
|
22
|
-
--verbose Show raw plugin list details
|
|
23
|
-
|
|
24
|
-
${color.cyan("upgrade")} Update curdx-flow and recommended plugins to latest
|
|
25
|
-
|
|
26
|
-
${color.cyan("uninstall")} Remove curdx-flow plugin (and optionally recommended / artifacts)
|
|
27
|
-
-y, --yes Skip confirmation, keep recommended + .flow/
|
|
28
|
-
--keep-recommended Don't ask about pua/claude-mem/frontend-design
|
|
29
|
-
--purge Also remove ~/.local/bin/bun, ~/.local/bin/uv symlinks
|
|
30
|
-
|
|
31
|
-
${color.bold("OPTIONS")}
|
|
32
|
-
-v, --version Print version
|
|
33
|
-
-h, --help Show this CLI usage summary
|
|
34
|
-
|
|
35
|
-
${color.dim("For the full command / workflow reference (including all slash")}
|
|
36
|
-
${color.dim("commands like /curdx-flow:start, /curdx-flow:spec, …) run:")}
|
|
37
|
-
${color.cyan("/curdx-flow:help")} ${color.dim("(inside Claude Code)")}
|
|
38
|
-
|
|
39
|
-
${color.bold("EXAMPLES")}
|
|
40
|
-
${color.dim("# First-time install with recommended plugins")}
|
|
41
|
-
npx @curdx/flow install --all
|
|
42
|
-
|
|
43
|
-
${color.dim("# Check what's installed")}
|
|
44
|
-
npx @curdx/flow doctor
|
|
45
|
-
|
|
46
|
-
${color.dim("# Update everything")}
|
|
47
|
-
npx @curdx/flow upgrade
|
|
48
|
-
|
|
49
|
-
${color.bold("INITIALIZING A PROJECT")}
|
|
50
|
-
Once curdx-flow is installed, initialize your project inside Claude Code:
|
|
51
|
-
|
|
52
|
-
${color.cyan("claude")}
|
|
53
|
-
${color.cyan("/curdx-flow:init")}
|
|
54
|
-
${color.cyan("/curdx-flow:start my-feature \"<description>\"")}
|
|
55
|
-
|
|
56
|
-
${color.bold("LEARN MORE")}
|
|
57
|
-
https://github.com/curdx/curdx-flow
|
|
58
|
-
`);
|
|
59
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Register MCP servers that curdx-flow depends on at user level via
|
|
3
|
-
* `claude mcp add`. Source-of-truth list: cli/registry.js BUNDLED_MCPS.
|
|
4
|
-
* Preserves existing user-level entries when mcp.preserveExisting is set.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { addMcp } from "./lib/claude-ops.js";
|
|
8
|
-
import { BUNDLED_MCPS } from "./registry.js";
|
|
9
|
-
import { color, log, readUserMcpConfig, resultLastLine } from "./utils.js";
|
|
10
|
-
|
|
11
|
-
export async function registerBundledMcps() {
|
|
12
|
-
log.blank();
|
|
13
|
-
log.info("Registering required MCP servers (user-level)...");
|
|
14
|
-
const existingUserMcps = readUserMcpConfig();
|
|
15
|
-
for (const mcp of BUNDLED_MCPS) {
|
|
16
|
-
if (mcp.preserveExisting && existingUserMcps.has(mcp.name)) {
|
|
17
|
-
const existing = existingUserMcps.get(mcp.name);
|
|
18
|
-
log.info(
|
|
19
|
-
` ${mcp.name.padEnd(22)} ${color.dim(`already registered (${(existing.args || []).join(" ")}) — preserving`)}`
|
|
20
|
-
);
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const r = await addMcp(mcp);
|
|
24
|
-
if (r.code === 0) {
|
|
25
|
-
log.ok(` ${mcp.name.padEnd(22)} ${color.dim("registered")}`);
|
|
26
|
-
} else if (r.stderr.includes("already exists")) {
|
|
27
|
-
log.info(` ${mcp.name.padEnd(22)} ${color.dim("already exists — skipped")}`);
|
|
28
|
-
} else {
|
|
29
|
-
log.warn(
|
|
30
|
-
` ${mcp.name.padEnd(22)} registration failed: ${resultLastLine(r)}`
|
|
31
|
-
);
|
|
32
|
-
log.info(
|
|
33
|
-
` Run manually: claude mcp add --scope user ${mcp.name} -- ${mcp.command} ${mcp.args.join(" ")}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export barrel preserving the stable import surface. New callers should
|
|
3
|
-
* import directly from the concern-specific modules below instead of going
|
|
4
|
-
* through this barrel. Kept in place so existing imports in cli/install.js
|
|
5
|
-
* and cli/install-workflow.js keep working unchanged.
|
|
6
|
-
*
|
|
7
|
-
* Concern split:
|
|
8
|
-
* install-required-plugins.js — required Claude Code companion plugins
|
|
9
|
-
* install-bundled-mcps.js — user-level MCP registration
|
|
10
|
-
* install-recommended-plugins.js — optional recommended plugins
|
|
11
|
-
* install-context7-config.js — legacy Context7 state reconciliation (private to required)
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
addRequiredPluginMarketplaces,
|
|
16
|
-
installRequiredPlugins,
|
|
17
|
-
} from "./install-required-plugins.js";
|
|
18
|
-
export { registerBundledMcps } from "./install-bundled-mcps.js";
|
|
19
|
-
export { installRecommendedPlugins } from "./install-recommended-plugins.js";
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reconcile legacy Context7 install state after the official plugin is
|
|
3
|
-
* installed or refreshed. Private to the required-plugins concern; not
|
|
4
|
-
* re-exported from install-companions.js.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { removeMcp } from "./lib/claude-ops.js";
|
|
8
|
-
import {
|
|
9
|
-
color,
|
|
10
|
-
log,
|
|
11
|
-
readUserMcpConfig,
|
|
12
|
-
resultLastLine,
|
|
13
|
-
writeConfig,
|
|
14
|
-
} from "./utils.js";
|
|
15
|
-
|
|
16
|
-
export async function reconcileLegacyContext7InstallState(
|
|
17
|
-
plugin,
|
|
18
|
-
language,
|
|
19
|
-
config,
|
|
20
|
-
{
|
|
21
|
-
removeMcpImpl = removeMcp,
|
|
22
|
-
readUserMcpConfigImpl = readUserMcpConfig,
|
|
23
|
-
writeConfigImpl = writeConfig,
|
|
24
|
-
logImpl = log,
|
|
25
|
-
} = {}
|
|
26
|
-
) {
|
|
27
|
-
if (plugin.name !== "context7-plugin") return;
|
|
28
|
-
|
|
29
|
-
let removedStoredApiKey = false;
|
|
30
|
-
if (config && Object.hasOwn(config, "context7ApiKey")) {
|
|
31
|
-
delete config.context7ApiKey;
|
|
32
|
-
writeConfigImpl(config);
|
|
33
|
-
removedStoredApiKey = true;
|
|
34
|
-
logImpl.info(
|
|
35
|
-
language === "zh"
|
|
36
|
-
? " 已移除旧的 Context7 API key 本地配置,当前官方插件不再使用该安装方式"
|
|
37
|
-
: " Removed legacy local Context7 API key config; the official plugin no longer uses that install path"
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const userMcps = readUserMcpConfigImpl();
|
|
42
|
-
if (!userMcps.has("context7")) {
|
|
43
|
-
return {
|
|
44
|
-
removedLegacyMcp: false,
|
|
45
|
-
removedStoredApiKey,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const result = await removeMcpImpl({ name: "context7" });
|
|
50
|
-
if (result.code === 0) {
|
|
51
|
-
logImpl.ok(
|
|
52
|
-
language === "zh"
|
|
53
|
-
? " 已移除遗留的用户级 context7 MCP,避免与官方 Context7 插件重复注册"
|
|
54
|
-
: " Removed legacy user-level context7 MCP so the official Context7 plugin is the only owner"
|
|
55
|
-
);
|
|
56
|
-
return {
|
|
57
|
-
removedLegacyMcp: true,
|
|
58
|
-
removedStoredApiKey,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
logImpl.warn(
|
|
63
|
-
language === "zh"
|
|
64
|
-
? ` 清理旧的 context7 MCP 失败: ${resultLastLine(result)}`
|
|
65
|
-
: ` Failed to remove legacy context7 MCP: ${resultLastLine(result)}`
|
|
66
|
-
);
|
|
67
|
-
logImpl.info(
|
|
68
|
-
color.dim(
|
|
69
|
-
language === "zh"
|
|
70
|
-
? " 手动运行: claude mcp remove --scope user context7"
|
|
71
|
-
: " Run manually: claude mcp remove --scope user context7"
|
|
72
|
-
)
|
|
73
|
-
);
|
|
74
|
-
return {
|
|
75
|
-
removedLegacyMcp: false,
|
|
76
|
-
removedStoredApiKey,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const installContext7Config = reconcileLegacyContext7InstallState;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { color, log, resultOutput } from "./utils.js";
|
|
2
|
-
import {
|
|
3
|
-
addPluginMarketplace,
|
|
4
|
-
installPlugin,
|
|
5
|
-
removePluginMarketplace,
|
|
6
|
-
} from "./lib/claude-ops.js";
|
|
7
|
-
|
|
8
|
-
const CURDX_FLOW_INSTALL_ENTRY = {
|
|
9
|
-
scope: "user",
|
|
10
|
-
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function resolveCurdxFlowInstallPlan({ prevCurdxFlow, shippedVersion }) {
|
|
14
|
-
if (prevCurdxFlow && shippedVersion && prevCurdxFlow.version === shippedVersion) {
|
|
15
|
-
return {
|
|
16
|
-
intro: `curdx-flow already at v${prevCurdxFlow.version}, re-registering...`,
|
|
17
|
-
success: `curdx-flow re-registered at v${shippedVersion}`,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (prevCurdxFlow && shippedVersion) {
|
|
22
|
-
return {
|
|
23
|
-
intro: `curdx-flow v${prevCurdxFlow.version} → v${shippedVersion}, installing...`,
|
|
24
|
-
success: `curdx-flow upgraded to v${shippedVersion}`,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (prevCurdxFlow) {
|
|
29
|
-
return {
|
|
30
|
-
intro: `curdx-flow v${prevCurdxFlow.version} detected, re-registering...`,
|
|
31
|
-
success: "curdx-flow re-registered",
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (shippedVersion) {
|
|
36
|
-
return {
|
|
37
|
-
intro: null,
|
|
38
|
-
success: `curdx-flow v${shippedVersion} installed`,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
intro: null,
|
|
44
|
-
success: "curdx-flow installed",
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function addCurdxMarketplace({ marketplaceSource, marketplaceLabel, useOffline }) {
|
|
49
|
-
log.blank();
|
|
50
|
-
log.step(2, 5, `Adding curdx-flow marketplace from ${marketplaceLabel}...`);
|
|
51
|
-
|
|
52
|
-
// Remove any existing marketplace with the same name so we get a clean
|
|
53
|
-
// rebind to the chosen source. Errors are non-fatal (marketplace may
|
|
54
|
-
// simply not exist yet).
|
|
55
|
-
await removePluginMarketplace("curdx-flow-marketplace");
|
|
56
|
-
|
|
57
|
-
const addRes = await addPluginMarketplace({ scope: "user", marketplaceSource });
|
|
58
|
-
if (addRes.code !== 0 && !addRes.stderr.includes("already")) {
|
|
59
|
-
log.warn(`marketplace add output: ${resultOutput(addRes)}`);
|
|
60
|
-
} else {
|
|
61
|
-
log.ok(
|
|
62
|
-
`curdx-flow-marketplace added ${color.dim(useOffline ? "(offline, no GitHub fetch)" : "(from GitHub)")}`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function installCurdxFlowPlugin(
|
|
68
|
-
{ prevCurdxFlow, shippedVersion },
|
|
69
|
-
{
|
|
70
|
-
installPluginImpl = installPlugin,
|
|
71
|
-
exitImpl = process.exit,
|
|
72
|
-
logImpl = log,
|
|
73
|
-
} = {}
|
|
74
|
-
) {
|
|
75
|
-
logImpl.blank();
|
|
76
|
-
logImpl.step(3, 5, "Installing curdx-flow plugin...");
|
|
77
|
-
|
|
78
|
-
// Use the pre-Step-2 snapshot — by this point `claude plugin marketplace
|
|
79
|
-
// remove` has already evicted the plugin, so listPlugins() here would
|
|
80
|
-
// always return undefined for curdx-flow and we'd mis-report "installed"
|
|
81
|
-
// when we actually upgraded (the bug reported by @wdx's beta.14 log).
|
|
82
|
-
const plan = resolveCurdxFlowInstallPlan({ prevCurdxFlow, shippedVersion });
|
|
83
|
-
|
|
84
|
-
if (plan.intro) {
|
|
85
|
-
logImpl.info(plan.intro);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const result = await installPluginImpl(CURDX_FLOW_INSTALL_ENTRY);
|
|
89
|
-
if (result.code !== 0) {
|
|
90
|
-
logImpl.err(`Install failed: ${resultOutput(result)}`);
|
|
91
|
-
exitImpl(1);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
logImpl.ok(plan.success);
|
|
96
|
-
}
|
package/cli/install-language.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { log, readConfig, select, writeConfig } from "./utils.js";
|
|
2
|
-
|
|
3
|
-
export async function resolveInstallLanguage({ yes }) {
|
|
4
|
-
const config = readConfig();
|
|
5
|
-
let language = config.language;
|
|
6
|
-
|
|
7
|
-
if (!language && !yes) {
|
|
8
|
-
log.blank();
|
|
9
|
-
language = await select({
|
|
10
|
-
message: "Choose your preferred language / 选择语言",
|
|
11
|
-
options: [
|
|
12
|
-
{
|
|
13
|
-
value: "en",
|
|
14
|
-
label: "English",
|
|
15
|
-
hint: "CLI output and documentation in English",
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
value: "zh",
|
|
19
|
-
label: "简体中文",
|
|
20
|
-
hint: "CLI 输出和文档使用简体中文",
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
initialValue: "en",
|
|
24
|
-
});
|
|
25
|
-
config.language = language;
|
|
26
|
-
writeConfig(config);
|
|
27
|
-
log.ok(`Language set to ${language === "zh" ? "简体中文" : "English"}`);
|
|
28
|
-
} else if (!language) {
|
|
29
|
-
language = "en";
|
|
30
|
-
config.language = language;
|
|
31
|
-
writeConfig(config);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return { language, config };
|
|
35
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { color, has } from "./utils.js";
|
|
2
|
-
|
|
3
|
-
export function printNextSteps() {
|
|
4
|
-
const cliOnPath = has("curdx-flow");
|
|
5
|
-
const cliCmd = cliOnPath ? "curdx-flow" : "npx @curdx/flow";
|
|
6
|
-
|
|
7
|
-
console.log(`\n${color.bold(`${color.green("✓")} Install complete`)}\n`);
|
|
8
|
-
console.log(
|
|
9
|
-
`${color.bold("Restart Claude Code")} so the plugin registers its commands, hooks, default main-thread agent, and background monitor.\n`
|
|
10
|
-
);
|
|
11
|
-
console.log(`${color.bold("Next steps")}:\n`);
|
|
12
|
-
console.log(` ${color.dim("# Verify health")}`);
|
|
13
|
-
console.log(` ${cliCmd} doctor\n`);
|
|
14
|
-
console.log(` ${color.dim("# Review the bundled main-agent / monitor defaults")}`);
|
|
15
|
-
console.log(` ${color.dim("# (doctor now prints CurDX-Flow runtime defaults and plugin option keys)")}\n`);
|
|
16
|
-
console.log(` ${color.dim("# Inside any project, initialize and start a feature spec")}`);
|
|
17
|
-
console.log(` ${color.cyan("cd ~/your-project")}`);
|
|
18
|
-
console.log(` ${color.cyan("claude")}`);
|
|
19
|
-
console.log(` ${color.cyan("/curdx-flow:init")}`);
|
|
20
|
-
console.log(` ${color.cyan("/curdx-flow:start my-feature \"<one-line goal>\"")}\n`);
|
|
21
|
-
if (!cliOnPath) {
|
|
22
|
-
console.log(
|
|
23
|
-
`${color.dim("Tip: install the CLI globally for shorter commands —")} ${color.cyan("npm i -g @curdx/flow")}\n`
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
console.log(
|
|
27
|
-
`${color.bold("Learn more")}: https://github.com/curdx/curdx-flow/blob/main/docs/getting-started.md\n`
|
|
28
|
-
);
|
|
29
|
-
}
|
package/cli/install-options.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export function parseInstallOptions(args = []) {
|
|
2
|
-
return {
|
|
3
|
-
all: args.includes("--all"),
|
|
4
|
-
noDeps: args.includes("--no-deps"),
|
|
5
|
-
forceOnline: args.includes("--online") || args.includes("--from-github"),
|
|
6
|
-
yes: args.includes("--yes") || args.includes("-y"),
|
|
7
|
-
skipSelfUpdate: args.includes("--skip-self-update"),
|
|
8
|
-
};
|
|
9
|
-
}
|
package/cli/install-paths.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
// When installed via npm, this CLI file lives at <pkg-root>/cli/*.js.
|
|
6
|
-
// The npm package bundles the full plugin body (.claude-plugin/, agents/,
|
|
7
|
-
// commands/, etc.) so install can register <pkg-root> as a local marketplace
|
|
8
|
-
// and avoid fetching anything from GitHub.
|
|
9
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
export const PKG_ROOT = dirname(__dirname);
|
|
11
|
-
export const LOCAL_MARKETPLACE_MANIFEST = join(
|
|
12
|
-
PKG_ROOT,
|
|
13
|
-
".claude-plugin",
|
|
14
|
-
"marketplace.json"
|
|
15
|
-
);
|
|
16
|
-
export const LOCAL_PLUGIN_MANIFEST = join(
|
|
17
|
-
PKG_ROOT,
|
|
18
|
-
".claude-plugin",
|
|
19
|
-
"plugin.json"
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export function shouldUseOfflineInstall({ forceOnline }) {
|
|
23
|
-
return !forceOnline && existsSync(LOCAL_MARKETPLACE_MANIFEST);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getMarketplaceSource(useOffline) {
|
|
27
|
-
return useOffline ? PKG_ROOT : "curdx/curdx-flow";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getMarketplaceLabel(useOffline) {
|
|
31
|
-
return useOffline
|
|
32
|
-
? `local npm package (${PKG_ROOT})`
|
|
33
|
-
: "GitHub curdx/curdx-flow";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function readShippedVersion() {
|
|
37
|
-
try {
|
|
38
|
-
const plugin = JSON.parse(readFileSync(LOCAL_PLUGIN_MANIFEST, "utf-8"));
|
|
39
|
-
if (plugin?.version) return plugin.version;
|
|
40
|
-
} catch {
|
|
41
|
-
// Fall back to the marketplace manifest below. Online installs or old
|
|
42
|
-
// package layouts may not have a local plugin manifest available.
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const marketplace = JSON.parse(readFileSync(LOCAL_MARKETPLACE_MANIFEST, "utf-8"));
|
|
47
|
-
return marketplace?.metadata?.version || null;
|
|
48
|
-
} catch {
|
|
49
|
-
// marketplace not local (online install) or unreadable
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Install optional recommended companion plugins. List source-of-truth:
|
|
3
|
-
* cli/registry.js RECOMMENDED_PLUGINS. Handles --all / --yes / interactive
|
|
4
|
-
* multiselect and the claude-mem bun/uv PATH symlink post-install step.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { addPluginMarketplace, installPlugin } from "./lib/claude-ops.js";
|
|
8
|
-
import { RECOMMENDED_PLUGINS } from "./registry.js";
|
|
9
|
-
import {
|
|
10
|
-
color,
|
|
11
|
-
ensureClaudeMemRuntimes,
|
|
12
|
-
listPlugins,
|
|
13
|
-
log,
|
|
14
|
-
multiselectClack,
|
|
15
|
-
resultLastLine,
|
|
16
|
-
} from "./utils.js";
|
|
17
|
-
|
|
18
|
-
const RECOMMENDED = RECOMMENDED_PLUGINS;
|
|
19
|
-
|
|
20
|
-
export async function installRecommendedPlugins({ all, yes, language }) {
|
|
21
|
-
log.blank();
|
|
22
|
-
log.step(4, 5, "Recommended plugins");
|
|
23
|
-
|
|
24
|
-
let toInstall;
|
|
25
|
-
if (all) {
|
|
26
|
-
toInstall = RECOMMENDED.map((r) => r.name);
|
|
27
|
-
log.info("--all mode: installing all recommended");
|
|
28
|
-
} else if (yes) {
|
|
29
|
-
const currentlyInstalled = new Set(listPlugins().map((p) => p.name));
|
|
30
|
-
toInstall = RECOMMENDED
|
|
31
|
-
.filter((r) => !currentlyInstalled.has(r.name))
|
|
32
|
-
.map((r) => r.name);
|
|
33
|
-
log.info(`--yes mode: installing ${toInstall.length} recommended plugins`);
|
|
34
|
-
} else {
|
|
35
|
-
const currentlyInstalled = new Set(listPlugins().map((p) => p.name));
|
|
36
|
-
const options = RECOMMENDED.map((r) => ({
|
|
37
|
-
value: r.name,
|
|
38
|
-
label: `${r.name}${currentlyInstalled.has(r.name) ? " (installed)" : ""}`,
|
|
39
|
-
hint: r.hint,
|
|
40
|
-
}));
|
|
41
|
-
const initialValues = RECOMMENDED.map((r) => r.name);
|
|
42
|
-
|
|
43
|
-
toInstall = await multiselectClack({
|
|
44
|
-
message: language === "zh"
|
|
45
|
-
? "选择要安装的推荐插件(空格切换,回车确认)"
|
|
46
|
-
: "Select recommended plugins to install (space to toggle, enter to confirm)",
|
|
47
|
-
options,
|
|
48
|
-
initialValues,
|
|
49
|
-
required: false,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!toInstall || toInstall.length === 0) {
|
|
54
|
-
log.info("No recommended plugins selected, skipping");
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
for (const pluginName of toInstall) {
|
|
59
|
-
const rec = RECOMMENDED.find((r) => r.name === pluginName);
|
|
60
|
-
log.blank();
|
|
61
|
-
console.log(` ${color.cyan("▸")} Installing ${color.bold(rec.name)}...`);
|
|
62
|
-
|
|
63
|
-
if (rec.marketplaceSource) {
|
|
64
|
-
const ma = await addPluginMarketplace(rec);
|
|
65
|
-
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
66
|
-
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const ir = await installPlugin(rec);
|
|
71
|
-
if (ir.code === 0) {
|
|
72
|
-
console.log(` ${color.green("✓")} ${rec.name} installed`);
|
|
73
|
-
|
|
74
|
-
if (rec.postInstall === "claude-mem-runtimes") {
|
|
75
|
-
const r = ensureClaudeMemRuntimes();
|
|
76
|
-
for (const [name, res] of Object.entries(r)) {
|
|
77
|
-
if (res.status === "linked") {
|
|
78
|
-
console.log(
|
|
79
|
-
` ${color.green("✓")} ${name} → PATH symlink created ${color.dim(`(${res.link} → ${res.path})`)}`
|
|
80
|
-
);
|
|
81
|
-
} else if (res.status === "missing") {
|
|
82
|
-
console.log(
|
|
83
|
-
` ${color.yellow("⚠")} ${name} not installed ${color.dim("(claude-mem will auto-install on first run; or run: curdx-flow doctor)")}`
|
|
84
|
-
);
|
|
85
|
-
} else if (res.status === "path-unwritable") {
|
|
86
|
-
console.log(
|
|
87
|
-
` ${color.yellow("⚠")} ${name} installed ${color.dim(`(${res.path}) but no writable PATH location — add export PATH="${res.path.split("/").slice(0,-1).join("/")}:$PATH" to your shell rc`)}`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
console.log(
|
|
94
|
-
` ${color.red("✗")} ${rec.name} install failed: ${resultLastLine(ir)}`
|
|
95
|
-
);
|
|
96
|
-
console.log(
|
|
97
|
-
color.dim(
|
|
98
|
-
` Run manually: claude plugin install --scope ${rec.scope} ${rec.installSpec}`
|
|
99
|
-
)
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Install required Claude Code companion plugins (today: context7-plugin).
|
|
3
|
-
* Post-install, reconcile any legacy Context7 user-level MCP/config state.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { addPluginMarketplace, installPlugin } from "./lib/claude-ops.js";
|
|
7
|
-
import { REQUIRED_PLUGINS } from "./registry.js";
|
|
8
|
-
import { color, log, resultLastLine } from "./utils.js";
|
|
9
|
-
import { reconcileLegacyContext7InstallState } from "./install-context7-config.js";
|
|
10
|
-
|
|
11
|
-
export async function addRequiredPluginMarketplaces({ logWarnings = true } = {}) {
|
|
12
|
-
for (const plugin of REQUIRED_PLUGINS) {
|
|
13
|
-
const ma = await addPluginMarketplace(plugin);
|
|
14
|
-
if (ma.code !== 0 && !ma.stderr.includes("already") && logWarnings) {
|
|
15
|
-
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function installRequiredPlugins({
|
|
21
|
-
yes,
|
|
22
|
-
language,
|
|
23
|
-
config,
|
|
24
|
-
skipMarketplaceAdd = false,
|
|
25
|
-
}) {
|
|
26
|
-
log.blank();
|
|
27
|
-
log.info("Installing required Claude Code plugins...");
|
|
28
|
-
if (!skipMarketplaceAdd) {
|
|
29
|
-
await addRequiredPluginMarketplaces();
|
|
30
|
-
}
|
|
31
|
-
for (const plugin of REQUIRED_PLUGINS) {
|
|
32
|
-
console.log(` ${color.cyan("▸")} Installing ${color.bold(plugin.name)}...`);
|
|
33
|
-
|
|
34
|
-
const ir = await installPlugin(plugin);
|
|
35
|
-
if (ir.code === 0) {
|
|
36
|
-
console.log(` ${color.green("✓")} ${plugin.name} installed`);
|
|
37
|
-
|
|
38
|
-
if (plugin.name === "context7-plugin") {
|
|
39
|
-
await reconcileLegacyContext7InstallState(plugin, language, config);
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
console.log(
|
|
43
|
-
` ${color.red("✗")} ${plugin.name} install failed: ${resultLastLine(ir)}`
|
|
44
|
-
);
|
|
45
|
-
console.log(
|
|
46
|
-
color.dim(
|
|
47
|
-
` Run manually: claude plugin marketplace add --scope ${plugin.scope} ${plugin.marketplaceSource}`
|
|
48
|
-
)
|
|
49
|
-
);
|
|
50
|
-
console.log(
|
|
51
|
-
color.dim(
|
|
52
|
-
` Then: claude plugin install --scope ${plugin.scope} ${plugin.installSpec}`
|
|
53
|
-
)
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|