@benbenwu/zcf 3.6.4 → 3.6.6

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/README.md CHANGED
@@ -82,6 +82,37 @@ node bin/zcf.mjs i -s -T codex
82
82
 
83
83
  Bare `npx @benbenwu/zcf` resolves the published npm package, so it will not automatically use your local repository changes.
84
84
 
85
+ ## 📦 Release to npm
86
+
87
+ If you want `npx @benbenwu/zcf` to pick up your local changes, you must publish a new npm version. Republishing the same version is not allowed.
88
+
89
+ Recommended release flow for this repository:
90
+
91
+ ```bash
92
+ # 1. Create a changeset and choose version bump type
93
+ pnpm changeset
94
+
95
+ # 2. Apply the version bump
96
+ pnpm changeset version
97
+
98
+ # 3. Run validation
99
+ pnpm lint
100
+ pnpm typecheck
101
+ pnpm test:run
102
+ pnpm build
103
+
104
+ # 4. Login to npm if needed
105
+ npm login
106
+
107
+ # 5. Publish using the repo release script
108
+ pnpm release
109
+ ```
110
+
111
+ Notes:
112
+
113
+ - `package.json` already sets `"publishConfig": { "access": "public" }`, so `pnpm release` / `changeset publish` will publish as a public package.
114
+ - `pnpm publish --access public` can still be used as a manual fallback after you run `pnpm changeset version` and `pnpm build`, but it is not the primary workflow in this repo.
115
+
85
116
  More usage, options, and workflows: see documentation.
86
117
 
87
118
  ## 📖 Full Documentation
@@ -18,8 +18,8 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
18
18
  import i18next from 'i18next';
19
19
  import Backend from 'i18next-fs-backend';
20
20
 
21
- const version = "3.6.4";
22
- const homepage = "https://github.com/UfoMiao/zcf";
21
+ const version = "3.6.6";
22
+ const homepage = "https://github.com/benbenlijie/zcf";
23
23
 
24
24
  const i18n = i18next.createInstance();
25
25
  const NAMESPACES = [
@@ -208,8 +208,8 @@ const MCP_SERVICE_CONFIGS = [
208
208
  requiresApiKey: false,
209
209
  config: {
210
210
  type: "stdio",
211
- command: "uvx",
212
- args: ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server", "--context", "ide-assistant", "--enable-web-dashboard", "false"],
211
+ command: "serena",
212
+ args: ["start-mcp-server", "--context", "ide-assistant", "--enable-web-dashboard", "false"],
213
213
  env: {}
214
214
  }
215
215
  }
@@ -3540,6 +3540,79 @@ async function selectMcpServices() {
3540
3540
  return services;
3541
3541
  }
3542
3542
 
3543
+ const UV_INSTALL_ARGS = ["tool", "install", "-p", "3.13", "serena-agent@latest", "--prerelease=allow"];
3544
+ function getExecutableCandidates(command) {
3545
+ const home = homedir();
3546
+ const extension = getPlatform() === "windows" ? ".exe" : "";
3547
+ return [
3548
+ join(home, ".local", "bin", `${command}${extension}`),
3549
+ join(home, ".cargo", "bin", `${command}${extension}`)
3550
+ ];
3551
+ }
3552
+ async function resolveExecutablePath(command) {
3553
+ const discoveredPath = await findCommandPath(command);
3554
+ if (discoveredPath) {
3555
+ return discoveredPath;
3556
+ }
3557
+ for (const candidate of getExecutableCandidates(command)) {
3558
+ if (existsSync(candidate)) {
3559
+ return candidate;
3560
+ }
3561
+ }
3562
+ return null;
3563
+ }
3564
+ async function installUv() {
3565
+ if (getPlatform() === "windows") {
3566
+ await exec("powershell", ["-ExecutionPolicy", "ByPass", "-c", "irm https://astral.sh/uv/install.ps1 | iex"]);
3567
+ return;
3568
+ }
3569
+ await exec("sh", ["-c", "curl -LsSf https://astral.sh/uv/install.sh | env UV_NO_MODIFY_PATH=1 sh"]);
3570
+ }
3571
+ async function ensureUvPath() {
3572
+ const existingPath = await resolveExecutablePath("uv");
3573
+ if (existingPath) {
3574
+ return existingPath;
3575
+ }
3576
+ await installUv();
3577
+ const installedPath = await resolveExecutablePath("uv");
3578
+ if (!installedPath) {
3579
+ throw new Error("uv installation completed but the uv executable was not found");
3580
+ }
3581
+ return installedPath;
3582
+ }
3583
+ async function installSerenaAgent(uvPath) {
3584
+ await exec(uvPath, UV_INSTALL_ARGS);
3585
+ }
3586
+ async function initializeSerena(serenaPath) {
3587
+ const serenaConfigFile = join(homedir(), ".serena", "serena_config.yml");
3588
+ if (existsSync(serenaConfigFile)) {
3589
+ return;
3590
+ }
3591
+ await exec(serenaPath, ["init"]);
3592
+ }
3593
+ async function ensureSerenaForCodex() {
3594
+ let serenaPath = await resolveExecutablePath("serena");
3595
+ if (!serenaPath) {
3596
+ const uvPath = await ensureUvPath();
3597
+ await installSerenaAgent(uvPath);
3598
+ serenaPath = await resolveExecutablePath("serena");
3599
+ }
3600
+ if (!serenaPath) {
3601
+ throw new Error("Serena installation completed but the serena executable was not found");
3602
+ }
3603
+ await initializeSerena(serenaPath);
3604
+ return serenaPath;
3605
+ }
3606
+ async function resolveCodexMcpCommandOverrides(serviceIds) {
3607
+ const normalizedIds = serviceIds.map((id) => id.toLowerCase());
3608
+ if (!normalizedIds.includes("serena")) {
3609
+ return {};
3610
+ }
3611
+ return {
3612
+ serena: await ensureSerenaForCodex()
3613
+ };
3614
+ }
3615
+
3543
3616
  function applyCodexPlatformCommand(config) {
3544
3617
  if (isWindows() && config.command) {
3545
3618
  const mcpCmd = getMcpCommand(config.command);
@@ -3691,6 +3764,27 @@ const codexTomlUpdater = {
3691
3764
  upsertCodexProvider: upsertCodexProvider
3692
3765
  };
3693
3766
 
3767
+ function buildSerenaArgs() {
3768
+ return ["start-mcp-server", "--context=codex", "--project-from-cwd", "--enable-web-dashboard", "false"];
3769
+ }
3770
+ function applyCodexSpecificMcpOverrides(id, command, args, env) {
3771
+ const nextCommand = command;
3772
+ let nextArgs = args;
3773
+ let startupTimeoutSec = 30;
3774
+ if (id === "serena") {
3775
+ nextArgs = buildSerenaArgs();
3776
+ }
3777
+ if (id === "spec-workflow") {
3778
+ env.SPEC_WORKFLOW_HOME = join(homedir(), ".codex", "memories", "spec-workflow");
3779
+ startupTimeoutSec = 90;
3780
+ }
3781
+ return {
3782
+ command: nextCommand,
3783
+ args: nextArgs,
3784
+ env,
3785
+ startupTimeoutSec
3786
+ };
3787
+ }
3694
3788
  async function configureCodexMcp(options) {
3695
3789
  ensureI18nInitialized();
3696
3790
  const { skipPrompt = false } = options ?? {};
@@ -3707,37 +3801,34 @@ async function configureCodexMcp(options) {
3707
3801
  return;
3708
3802
  }
3709
3803
  const defaultServiceIds = Array.isArray(options?.mcpServices) ? options.mcpServices : MCP_SERVICE_CONFIGS.filter((service) => !service.requiresApiKey).map((service) => service.id);
3804
+ const commandOverrides2 = await resolveCodexMcpCommandOverrides(defaultServiceIds);
3710
3805
  const existingServices2 = existingConfig?.mcpServices || [];
3711
3806
  const selection2 = [];
3712
3807
  for (const id of defaultServiceIds) {
3713
3808
  const configInfo = MCP_SERVICE_CONFIGS.find((service) => service.id === id);
3714
3809
  if (!configInfo)
3715
3810
  continue;
3716
- let command = configInfo.config.command || id;
3811
+ let command = commandOverrides2[id.toLowerCase()] || configInfo.config.command || id;
3717
3812
  let args = (configInfo.config.args || []).map((arg) => String(arg));
3718
- if (id === "serena") {
3719
- const idx = args.indexOf("--context");
3720
- if (idx >= 0 && idx + 1 < args.length)
3721
- args[idx + 1] = "codex";
3722
- else
3723
- args.push("--context", "codex");
3724
- }
3813
+ const env = { ...configInfo.config.env || {} };
3814
+ const serviceConfigOverride = applyCodexSpecificMcpOverrides(id, command, args, env);
3815
+ command = serviceConfigOverride.command;
3816
+ args = serviceConfigOverride.args;
3725
3817
  const serviceConfig = { id: id.toLowerCase(), command, args };
3726
3818
  applyCodexPlatformCommand(serviceConfig);
3727
3819
  command = serviceConfig.command;
3728
3820
  args = serviceConfig.args || [];
3729
- const env = { ...configInfo.config.env || {} };
3730
3821
  if (isWindows()) {
3731
3822
  const systemRoot = getSystemRoot();
3732
3823
  if (systemRoot)
3733
- env.SYSTEMROOT = systemRoot;
3824
+ serviceConfigOverride.env.SYSTEMROOT = systemRoot;
3734
3825
  }
3735
3826
  selection2.push({
3736
3827
  id: id.toLowerCase(),
3737
3828
  command,
3738
3829
  args,
3739
- env: Object.keys(env).length > 0 ? env : void 0,
3740
- startup_timeout_sec: 30
3830
+ env: Object.keys(serviceConfigOverride.env).length > 0 ? serviceConfigOverride.env : void 0,
3831
+ startup_timeout_sec: serviceConfigOverride.startupTimeoutSec
3741
3832
  });
3742
3833
  }
3743
3834
  const mergedMap2 = /* @__PURE__ */ new Map();
@@ -3769,6 +3860,7 @@ async function configureCodexMcp(options) {
3769
3860
  if (!selectedIds)
3770
3861
  return;
3771
3862
  const servicesMeta = await getMcpServices();
3863
+ const commandOverrides = await resolveCodexMcpCommandOverrides(selectedIds);
3772
3864
  const selection = [];
3773
3865
  const existingServices = existingConfig?.mcpServices || [];
3774
3866
  if (selectedIds.length === 0) {
@@ -3797,25 +3889,20 @@ async function configureCodexMcp(options) {
3797
3889
  if (!configInfo)
3798
3890
  continue;
3799
3891
  const serviceMeta = servicesMeta.find((service) => service.id === id);
3800
- let command = configInfo.config.command || id;
3892
+ let command = commandOverrides[id.toLowerCase()] || configInfo.config.command || id;
3801
3893
  let args = (configInfo.config.args || []).map((arg) => String(arg));
3802
- if (id === "serena") {
3803
- const idx = args.indexOf("--context");
3804
- if (idx >= 0 && idx + 1 < args.length) {
3805
- args[idx + 1] = "codex";
3806
- } else {
3807
- args.push("--context", "codex");
3808
- }
3809
- }
3894
+ const env = { ...configInfo.config.env || {} };
3895
+ const serviceConfigOverride = applyCodexSpecificMcpOverrides(id, command, args, env);
3896
+ command = serviceConfigOverride.command;
3897
+ args = serviceConfigOverride.args;
3810
3898
  const serviceConfig = { id: id.toLowerCase(), command, args };
3811
3899
  applyCodexPlatformCommand(serviceConfig);
3812
3900
  command = serviceConfig.command;
3813
3901
  args = serviceConfig.args || [];
3814
- const env = { ...configInfo.config.env || {} };
3815
3902
  if (isWindows()) {
3816
3903
  const systemRoot = getSystemRoot();
3817
3904
  if (systemRoot)
3818
- env.SYSTEMROOT = systemRoot;
3905
+ serviceConfigOverride.env.SYSTEMROOT = systemRoot;
3819
3906
  }
3820
3907
  if (configInfo.requiresApiKey && configInfo.apiKeyEnvVar) {
3821
3908
  const promptMessage = serviceMeta?.apiKeyPrompt || i18n.t("mcp:apiKeyPrompt");
@@ -3827,14 +3914,14 @@ async function configureCodexMcp(options) {
3827
3914
  }]);
3828
3915
  if (!apiKey)
3829
3916
  continue;
3830
- env[configInfo.apiKeyEnvVar] = apiKey;
3917
+ serviceConfigOverride.env[configInfo.apiKeyEnvVar] = apiKey;
3831
3918
  }
3832
3919
  selection.push({
3833
3920
  id: id.toLowerCase(),
3834
3921
  command: serviceConfig.command,
3835
3922
  args: serviceConfig.args,
3836
- env: Object.keys(env).length > 0 ? env : void 0,
3837
- startup_timeout_sec: 30
3923
+ env: Object.keys(serviceConfigOverride.env).length > 0 ? serviceConfigOverride.env : void 0,
3924
+ startup_timeout_sec: serviceConfigOverride.startupTimeoutSec
3838
3925
  });
3839
3926
  }
3840
3927
  const mergedMap = /* @__PURE__ */ new Map();
package/dist/cli.mjs CHANGED
File without changes
package/package.json CHANGED
@@ -1,24 +1,23 @@
1
1
  {
2
2
  "name": "@benbenwu/zcf",
3
3
  "type": "module",
4
- "version": "3.6.4",
5
- "packageManager": "pnpm@10.17.1",
4
+ "version": "3.6.6",
6
5
  "description": "Zero-Config Code Flow - One-click configuration tool for Code Cli",
7
6
  "author": {
8
- "name": "Miao Da",
9
- "email": "ufo025174@gmail.com",
10
- "url": "https://github.com/WitMiao"
7
+ "name": "Wu Yubin",
8
+ "email": "benbenwyb@gmail.com",
9
+ "url": "https://github.com/benbenlijie"
11
10
  },
12
11
  "license": "MIT",
13
12
  "publishConfig": {
14
13
  "access": "public"
15
14
  },
16
- "homepage": "https://github.com/UfoMiao/zcf",
15
+ "homepage": "https://github.com/benbenlijie/zcf",
17
16
  "repository": {
18
17
  "type": "git",
19
- "url": "git+https://github.com/UfoMiao/zcf.git"
18
+ "url": "git+https://github.com/benbenlijie/zcf.git"
20
19
  },
21
- "bugs": "https://github.com/UfoMiao/zcf/issues",
20
+ "bugs": "https://github.com/benbenlijie/zcf/issues",
22
21
  "keywords": [
23
22
  "claude",
24
23
  "claude-code",
@@ -46,12 +45,55 @@
46
45
  "dist",
47
46
  "templates"
48
47
  ],
48
+ "dependencies": {
49
+ "@rainbowatcher/toml-edit-js": "^0.6.4",
50
+ "@types/semver": "^7.7.1",
51
+ "ansis": "^4.1.0",
52
+ "cac": "^6.7.14",
53
+ "dayjs": "^1.11.18",
54
+ "find-up-simple": "^1.0.1",
55
+ "fs-extra": "^11.3.2",
56
+ "i18next": "^25.5.2",
57
+ "i18next-fs-backend": "^2.6.0",
58
+ "inquirer": "^12.9.6",
59
+ "inquirer-toggle": "^1.0.1",
60
+ "ora": "^9.0.0",
61
+ "pathe": "^2.0.3",
62
+ "semver": "^7.7.2",
63
+ "tinyexec": "^1.0.1",
64
+ "trash": "^10.0.0"
65
+ },
66
+ "devDependencies": {
67
+ "@antfu/eslint-config": "^5.4.1",
68
+ "@changesets/cli": "^2.30.0",
69
+ "@commitlint/cli": "^19.8.1",
70
+ "@commitlint/config-conventional": "^19.8.1",
71
+ "@commitlint/types": "^19.8.1",
72
+ "@types/fs-extra": "^11.0.4",
73
+ "@types/inquirer": "^9.0.9",
74
+ "@types/node": "^22.18.6",
75
+ "@vitest/coverage-v8": "^3.2.4",
76
+ "@vitest/ui": "^3.2.4",
77
+ "eslint": "^9.36.0",
78
+ "eslint-plugin-format": "^1.0.2",
79
+ "glob": "^11.0.3",
80
+ "husky": "^9.1.7",
81
+ "lint-staged": "^16.2.0",
82
+ "tsx": "^4.20.5",
83
+ "typescript": "^5.9.2",
84
+ "unbuild": "^3.6.1",
85
+ "vitest": "^3.2.4"
86
+ },
87
+ "lint-staged": {
88
+ "*": [
89
+ "pnpm lint"
90
+ ]
91
+ },
49
92
  "scripts": {
50
93
  "dev": "tsx ./src/cli.ts",
51
94
  "build": "unbuild",
52
95
  "start": "node bin/zcf.mjs",
53
96
  "typecheck": "tsc --noEmit",
54
- "prepublishOnly": "pnpm build",
55
97
  "lint": "eslint",
56
98
  "lint:fix": "eslint --fix",
57
99
  "test": "vitest",
@@ -63,55 +105,10 @@
63
105
  "version": "changeset version",
64
106
  "update:deps": "pnpx taze major -r -w",
65
107
  "release": "pnpm build && changeset publish",
66
- "prepare": "husky",
67
108
  "commitlint": "commitlint",
68
109
  "commitlint:check": "commitlint --from HEAD~1 --to HEAD --verbose",
69
110
  "docs:dev": "pnpm -F @zcf/docs dev",
70
111
  "docs:build": "pnpm -F @zcf/docs build",
71
112
  "docs:preview": "pnpm -F @zcf/docs preview"
72
- },
73
- "dependencies": {
74
- "@rainbowatcher/toml-edit-js": "catalog:runtime",
75
- "@types/semver": "catalog:types",
76
- "ansis": "catalog:cli",
77
- "cac": "catalog:cli",
78
- "dayjs": "catalog:runtime",
79
- "find-up-simple": "catalog:runtime",
80
- "fs-extra": "catalog:runtime",
81
- "i18next": "catalog:runtime",
82
- "i18next-fs-backend": "catalog:runtime",
83
- "inquirer": "catalog:cli",
84
- "inquirer-toggle": "catalog:cli",
85
- "ora": "catalog:cli",
86
- "pathe": "catalog:runtime",
87
- "semver": "catalog:runtime",
88
- "tinyexec": "catalog:runtime",
89
- "trash": "catalog:runtime"
90
- },
91
- "devDependencies": {
92
- "@antfu/eslint-config": "catalog:build",
93
- "@changesets/cli": "catalog:tooling",
94
- "@commitlint/cli": "catalog:tooling",
95
- "@commitlint/config-conventional": "catalog:tooling",
96
- "@commitlint/types": "catalog:tooling",
97
- "@types/fs-extra": "catalog:types",
98
- "@types/inquirer": "catalog:types",
99
- "@types/node": "catalog:types",
100
- "@vitest/coverage-v8": "catalog:testing",
101
- "@vitest/ui": "catalog:testing",
102
- "eslint": "catalog:build",
103
- "eslint-plugin-format": "catalog:build",
104
- "glob": "catalog:testing",
105
- "husky": "catalog:tooling",
106
- "lint-staged": "catalog:tooling",
107
- "tsx": "catalog:build",
108
- "typescript": "catalog:build",
109
- "unbuild": "catalog:build",
110
- "vitest": "catalog:testing"
111
- },
112
- "lint-staged": {
113
- "*": [
114
- "pnpm lint"
115
- ]
116
113
  }
117
- }
114
+ }