zcf 2.12.10 → 2.12.12
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 +37 -7
- package/dist/chunks/simple-config.mjs +383 -9
- package/dist/cli.mjs +2 -2
- package/dist/i18n/locales/en/ccr.json +3 -1
- package/dist/i18n/locales/en/errors.json +3 -1
- package/dist/i18n/locales/en/installation.json +21 -1
- package/dist/i18n/locales/en/mcp.json +2 -1
- package/dist/i18n/locales/zh-CN/ccr.json +3 -1
- package/dist/i18n/locales/zh-CN/errors.json +3 -1
- package/dist/i18n/locales/zh-CN/installation.json +21 -1
- package/dist/i18n/locales/zh-CN/mcp.json +2 -1
- package/dist/index.d.mts +51 -11
- package/dist/index.d.ts +51 -11
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/templates/en/output-styles/nekomata-engineer.md +8 -8
- package/templates/zh-CN/output-styles/nekomata-engineer.md +8 -8
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# ZCF - Zero-Config Claude-Code Flow
|
|
2
2
|
|
|
3
|
-
[![
|
|
4
|
-
[![
|
|
5
|
-
[![
|
|
6
|
-
[![
|
|
7
|
-
[![
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![License][license-src]][license-href]
|
|
6
|
+
[![Claude Code][claude-code-src]][claude-code-href]
|
|
7
|
+
[![codecov][codecov-src]][codecov-href]
|
|
8
|
+
[![JSDocs][jsdocs-src]][jsdocs-href]
|
|
9
|
+
[![Ask DeepWiki][deepwiki-src]][deepwiki-href]
|
|
8
10
|
|
|
9
|
-
[中文](README_zh-CN.md) | **English** | [Changelog](CHANGELOG.md)
|
|
11
|
+
[中文](README_zh-CN.md) | **English** | [日本語](README_ja-JP.md) | [Changelog](CHANGELOG.md)
|
|
10
12
|
|
|
11
13
|
> Zero-config, one-click setup for Claude Code with bilingual support, intelligent agent system and personalized AI assistant
|
|
12
14
|
|
|
@@ -286,7 +288,7 @@ After configuration:
|
|
|
286
288
|
|
|
287
289
|
- Auto-detects Claude Code installation status
|
|
288
290
|
- Uses npm for automatic installation (ensures compatibility)
|
|
289
|
-
- Cross-platform support (Windows/macOS/Linux/Termux)
|
|
291
|
+
- Cross-platform support (Windows/macOS/Linux/WSL/Termux)
|
|
290
292
|
- Automatic MCP service configuration
|
|
291
293
|
- Smart configuration merging and partial modification support (v2.0 new)
|
|
292
294
|
- Enhanced command detection mechanism (v2.1 new)
|
|
@@ -645,6 +647,17 @@ ZCF fully supports Windows platform:
|
|
|
645
647
|
|
|
646
648
|
If you encounter MCP connection issues on Windows, running `npx zcf` will automatically fix the configuration format.
|
|
647
649
|
|
|
650
|
+
#### WSL Support (v2.12.12+ new)
|
|
651
|
+
|
|
652
|
+
ZCF now provides comprehensive support for Windows Subsystem for Linux (WSL):
|
|
653
|
+
|
|
654
|
+
- **Smart Detection**: Multi-layered WSL environment detection using environment variables, system files, and mount points
|
|
655
|
+
- **Distribution Recognition**: Automatically identifies WSL distribution (Ubuntu, Debian, etc.) for optimized configuration
|
|
656
|
+
- **Seamless Installation**: Native Linux-style installation experience within WSL environment
|
|
657
|
+
- **Path Management**: Intelligent handling of WSL-specific configuration paths and file locations
|
|
658
|
+
|
|
659
|
+
If running in WSL, ZCF will automatically detect the environment and display appropriate installation messages.
|
|
660
|
+
|
|
648
661
|
#### Termux Support (v2.1 new)
|
|
649
662
|
|
|
650
663
|
ZCF now supports running in Android Termux environment:
|
|
@@ -709,3 +722,20 @@ MIT License
|
|
|
709
722
|
If this project helps you, please give me a ⭐️ Star!
|
|
710
723
|
|
|
711
724
|
[](https://star-history.com/#UfoMiao/zcf&Date)
|
|
725
|
+
|
|
726
|
+
<!-- Badges -->
|
|
727
|
+
|
|
728
|
+
[npm-version-src]: https://img.shields.io/npm/v/zcf?style=flat&colorA=080f12&colorB=1fa669
|
|
729
|
+
[npm-version-href]: https://npmjs.com/package/zcf
|
|
730
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/zcf?style=flat&colorA=080f12&colorB=1fa669
|
|
731
|
+
[npm-downloads-href]: https://npmjs.com/package/zcf
|
|
732
|
+
[license-src]: https://img.shields.io/github/license/ufomiao/zcf.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
733
|
+
[license-href]: https://github.com/ufomiao/zcf/blob/main/LICENSE
|
|
734
|
+
[claude-code-src]: https://img.shields.io/badge/Claude-Code-1fa669?style=flat&colorA=080f12&colorB=1fa669
|
|
735
|
+
[claude-code-href]: https://claude.ai/code
|
|
736
|
+
[codecov-src]: https://codecov.io/gh/UfoMiao/zcf/graph/badge.svg?token=HZI6K4Y7D7&style=flat&colorA=080f12&colorB=1fa669
|
|
737
|
+
[codecov-href]: https://codecov.io/gh/UfoMiao/zcf
|
|
738
|
+
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-1fa669?style=flat&colorA=080f12&colorB=1fa669
|
|
739
|
+
[jsdocs-href]: https://www.jsdocs.io/package/zcf
|
|
740
|
+
[deepwiki-src]: https://img.shields.io/badge/Ask-DeepWiki-1fa669?style=flat&colorA=080f12&colorB=1fa669
|
|
741
|
+
[deepwiki-href]: https://deepwiki.com/UfoMiao/zcf
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync, unlinkSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, rmSync, rmdirSync, readdirSync, statSync, unlinkSync } from 'node:fs';
|
|
2
2
|
import process from 'node:process';
|
|
3
3
|
import ansis from 'ansis';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
@@ -15,7 +15,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
|
15
15
|
import i18next from 'i18next';
|
|
16
16
|
import Backend from 'i18next-fs-backend';
|
|
17
17
|
|
|
18
|
-
const version = "2.12.
|
|
18
|
+
const version = "2.12.12";
|
|
19
19
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
20
20
|
|
|
21
21
|
const i18n = i18next.createInstance();
|
|
@@ -536,6 +536,61 @@ function copyDir(src, dest, options = {}) {
|
|
|
536
536
|
}
|
|
537
537
|
}
|
|
538
538
|
}
|
|
539
|
+
async function isExecutable(path) {
|
|
540
|
+
try {
|
|
541
|
+
if (!exists(path)) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
const stats = getStats(path);
|
|
545
|
+
if (!stats.isFile()) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
if (process.platform !== "win32") {
|
|
549
|
+
const mode = stats.mode;
|
|
550
|
+
const executePermission = 73;
|
|
551
|
+
return (mode & executePermission) !== 0;
|
|
552
|
+
}
|
|
553
|
+
const isWinExecutable = path.endsWith(".exe") || path.endsWith(".cmd") || path.endsWith(".bat");
|
|
554
|
+
return isWinExecutable || !path.includes(".");
|
|
555
|
+
} catch {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function remove(path) {
|
|
560
|
+
try {
|
|
561
|
+
if (!exists(path)) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const stats = getStats(path);
|
|
565
|
+
if (stats.isDirectory()) {
|
|
566
|
+
const entries = readDir(path);
|
|
567
|
+
for (const entry of entries) {
|
|
568
|
+
await remove(`${path}/${entry}`);
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
if (rmSync) {
|
|
572
|
+
rmSync(path, { recursive: true, force: true });
|
|
573
|
+
} else if (rmdirSync) {
|
|
574
|
+
rmdirSync(path);
|
|
575
|
+
}
|
|
576
|
+
} catch (error) {
|
|
577
|
+
throw new FileSystemError(
|
|
578
|
+
`Failed to remove directory: ${path}`,
|
|
579
|
+
path,
|
|
580
|
+
error
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
} else {
|
|
584
|
+
removeFile(path);
|
|
585
|
+
}
|
|
586
|
+
} catch (error) {
|
|
587
|
+
throw new FileSystemError(
|
|
588
|
+
`Failed to remove: ${path}`,
|
|
589
|
+
path,
|
|
590
|
+
error
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
539
594
|
|
|
540
595
|
function readJsonConfig(path, options = {}) {
|
|
541
596
|
const { defaultValue = null, validate, sanitize } = options;
|
|
@@ -662,6 +717,57 @@ function getTermuxPrefix() {
|
|
|
662
717
|
function isWindows() {
|
|
663
718
|
return getPlatform() === "windows";
|
|
664
719
|
}
|
|
720
|
+
function isWSL() {
|
|
721
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
if (existsSync("/proc/version")) {
|
|
725
|
+
try {
|
|
726
|
+
const version = readFileSync("/proc/version", "utf8");
|
|
727
|
+
if (version.includes("Microsoft") || version.includes("WSL")) {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (existsSync("/mnt/c")) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
function getWSLDistro() {
|
|
739
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
740
|
+
return process.env.WSL_DISTRO_NAME;
|
|
741
|
+
}
|
|
742
|
+
if (existsSync("/etc/os-release")) {
|
|
743
|
+
try {
|
|
744
|
+
const osRelease = readFileSync("/etc/os-release", "utf8");
|
|
745
|
+
const nameMatch = osRelease.match(/^PRETTY_NAME="(.+)"$/m);
|
|
746
|
+
if (nameMatch) {
|
|
747
|
+
return nameMatch[1];
|
|
748
|
+
}
|
|
749
|
+
} catch {
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
function getWSLInfo() {
|
|
755
|
+
if (!isWSL()) {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
let version = null;
|
|
759
|
+
if (existsSync("/proc/version")) {
|
|
760
|
+
try {
|
|
761
|
+
version = readFileSync("/proc/version", "utf8").trim();
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
isWSL: true,
|
|
767
|
+
distro: getWSLDistro(),
|
|
768
|
+
version
|
|
769
|
+
};
|
|
770
|
+
}
|
|
665
771
|
function getMcpCommand() {
|
|
666
772
|
if (isWindows()) {
|
|
667
773
|
return ["cmd", "/c", "npx"];
|
|
@@ -780,6 +886,75 @@ function addCompletedOnboarding() {
|
|
|
780
886
|
throw error;
|
|
781
887
|
}
|
|
782
888
|
}
|
|
889
|
+
function ensureApiKeyApproved(config, apiKey) {
|
|
890
|
+
if (!apiKey || typeof apiKey !== "string" || apiKey.trim() === "") {
|
|
891
|
+
return config;
|
|
892
|
+
}
|
|
893
|
+
const truncatedApiKey = apiKey.substring(0, 20);
|
|
894
|
+
const updatedConfig = { ...config };
|
|
895
|
+
if (!updatedConfig.customApiKeyResponses) {
|
|
896
|
+
updatedConfig.customApiKeyResponses = {
|
|
897
|
+
approved: [],
|
|
898
|
+
rejected: []
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
if (!Array.isArray(updatedConfig.customApiKeyResponses.approved)) {
|
|
902
|
+
updatedConfig.customApiKeyResponses.approved = [];
|
|
903
|
+
}
|
|
904
|
+
if (!Array.isArray(updatedConfig.customApiKeyResponses.rejected)) {
|
|
905
|
+
updatedConfig.customApiKeyResponses.rejected = [];
|
|
906
|
+
}
|
|
907
|
+
const rejectedIndex = updatedConfig.customApiKeyResponses.rejected.indexOf(truncatedApiKey);
|
|
908
|
+
if (rejectedIndex > -1) {
|
|
909
|
+
updatedConfig.customApiKeyResponses.rejected.splice(rejectedIndex, 1);
|
|
910
|
+
}
|
|
911
|
+
if (!updatedConfig.customApiKeyResponses.approved.includes(truncatedApiKey)) {
|
|
912
|
+
updatedConfig.customApiKeyResponses.approved.push(truncatedApiKey);
|
|
913
|
+
}
|
|
914
|
+
return updatedConfig;
|
|
915
|
+
}
|
|
916
|
+
function removeApiKeyFromRejected(config, apiKey) {
|
|
917
|
+
if (!config.customApiKeyResponses || !Array.isArray(config.customApiKeyResponses.rejected)) {
|
|
918
|
+
return config;
|
|
919
|
+
}
|
|
920
|
+
const truncatedApiKey = apiKey.substring(0, 20);
|
|
921
|
+
const updatedConfig = { ...config };
|
|
922
|
+
if (updatedConfig.customApiKeyResponses) {
|
|
923
|
+
const rejectedIndex = updatedConfig.customApiKeyResponses.rejected.indexOf(truncatedApiKey);
|
|
924
|
+
if (rejectedIndex > -1) {
|
|
925
|
+
updatedConfig.customApiKeyResponses.rejected.splice(rejectedIndex, 1);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return updatedConfig;
|
|
929
|
+
}
|
|
930
|
+
function manageApiKeyApproval(apiKey) {
|
|
931
|
+
try {
|
|
932
|
+
let config = readMcpConfig();
|
|
933
|
+
if (!config) {
|
|
934
|
+
config = { mcpServers: {} };
|
|
935
|
+
}
|
|
936
|
+
const updatedConfig = ensureApiKeyApproved(config, apiKey);
|
|
937
|
+
writeMcpConfig(updatedConfig);
|
|
938
|
+
} catch (error) {
|
|
939
|
+
ensureI18nInitialized();
|
|
940
|
+
console.error(i18n.t("mcp:apiKeyApprovalFailed"), error);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const claudeConfig = {
|
|
945
|
+
__proto__: null,
|
|
946
|
+
addCompletedOnboarding: addCompletedOnboarding,
|
|
947
|
+
backupMcpConfig: backupMcpConfig,
|
|
948
|
+
buildMcpServerConfig: buildMcpServerConfig,
|
|
949
|
+
ensureApiKeyApproved: ensureApiKeyApproved,
|
|
950
|
+
fixWindowsMcpConfig: fixWindowsMcpConfig,
|
|
951
|
+
getMcpConfigPath: getMcpConfigPath,
|
|
952
|
+
manageApiKeyApproval: manageApiKeyApproval,
|
|
953
|
+
mergeMcpServers: mergeMcpServers,
|
|
954
|
+
readMcpConfig: readMcpConfig,
|
|
955
|
+
removeApiKeyFromRejected: removeApiKeyFromRejected,
|
|
956
|
+
writeMcpConfig: writeMcpConfig
|
|
957
|
+
};
|
|
783
958
|
|
|
784
959
|
function cleanupPermissions(templatePermissions, userPermissions) {
|
|
785
960
|
const templateSet = new Set(templatePermissions);
|
|
@@ -1394,6 +1569,14 @@ async function setupCcrConfiguration() {
|
|
|
1394
1569
|
if (!shouldBackupAndReconfigure) {
|
|
1395
1570
|
console.log(ansis.yellow(`${i18n.t("ccr:keepingExistingConfig")}`));
|
|
1396
1571
|
await configureCcrProxy(existingConfig);
|
|
1572
|
+
try {
|
|
1573
|
+
const { manageApiKeyApproval } = await Promise.resolve().then(function () { return claudeConfig; });
|
|
1574
|
+
const apiKey = existingConfig.APIKEY || "sk-zcf-x-ccr";
|
|
1575
|
+
manageApiKeyApproval(apiKey);
|
|
1576
|
+
console.log(ansis.green(`\u2714 ${i18n.t("ccr:apiKeyApprovalSuccess")}`));
|
|
1577
|
+
} catch (error) {
|
|
1578
|
+
console.error(ansis.red(`${i18n.t("ccr:apiKeyApprovalFailed")}:`), error);
|
|
1579
|
+
}
|
|
1397
1580
|
return true;
|
|
1398
1581
|
}
|
|
1399
1582
|
backupCcrConfig();
|
|
@@ -1420,6 +1603,14 @@ async function setupCcrConfiguration() {
|
|
|
1420
1603
|
} catch (error) {
|
|
1421
1604
|
console.error(ansis.red(i18n.t("errors:failedToSetOnboarding")), error);
|
|
1422
1605
|
}
|
|
1606
|
+
try {
|
|
1607
|
+
const { manageApiKeyApproval } = await Promise.resolve().then(function () { return claudeConfig; });
|
|
1608
|
+
const apiKey = config.APIKEY || "sk-zcf-x-ccr";
|
|
1609
|
+
manageApiKeyApproval(apiKey);
|
|
1610
|
+
console.log(ansis.green(`\u2714 ${i18n.t("ccr:apiKeyApprovalSuccess")}`));
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
console.error(ansis.red(`${i18n.t("ccr:apiKeyApprovalFailed")}:`), error);
|
|
1613
|
+
}
|
|
1423
1614
|
return true;
|
|
1424
1615
|
} catch (error) {
|
|
1425
1616
|
if (error.name === "ExitPromptError") {
|
|
@@ -1929,10 +2120,19 @@ function updateZcfConfig(updates) {
|
|
|
1929
2120
|
aiOutputLang: updates.aiOutputLang || existingConfig?.aiOutputLang,
|
|
1930
2121
|
outputStyles: updates.outputStyles !== void 0 ? updates.outputStyles : existingConfig?.outputStyles,
|
|
1931
2122
|
defaultOutputStyle: updates.defaultOutputStyle !== void 0 ? updates.defaultOutputStyle : existingConfig?.defaultOutputStyle,
|
|
2123
|
+
claudeCodeInstallation: updates.claudeCodeInstallation !== void 0 ? updates.claudeCodeInstallation : existingConfig?.claudeCodeInstallation,
|
|
1932
2124
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1933
2125
|
};
|
|
1934
2126
|
writeZcfConfig(newConfig);
|
|
1935
2127
|
}
|
|
2128
|
+
function getZcfConfig() {
|
|
2129
|
+
const config = readZcfConfig();
|
|
2130
|
+
return config || {
|
|
2131
|
+
version: "1.0.0",
|
|
2132
|
+
preferredLang: "en",
|
|
2133
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
1936
2136
|
|
|
1937
2137
|
const OUTPUT_STYLES = [
|
|
1938
2138
|
// Custom styles (have template files)
|
|
@@ -2338,9 +2538,10 @@ ${i18n.t("common:goodbye")}
|
|
|
2338
2538
|
return false;
|
|
2339
2539
|
}
|
|
2340
2540
|
function handleGeneralError(error) {
|
|
2341
|
-
|
|
2541
|
+
ensureI18nInitialized();
|
|
2542
|
+
console.error(ansis.red(`${i18n.t("errors:generalError")}:`), error);
|
|
2342
2543
|
if (error instanceof Error) {
|
|
2343
|
-
console.error(ansis.gray(
|
|
2544
|
+
console.error(ansis.gray(`${i18n.t("errors:stackTrace")}: ${error.stack}`));
|
|
2344
2545
|
}
|
|
2345
2546
|
process.exit(1);
|
|
2346
2547
|
}
|
|
@@ -2363,6 +2564,15 @@ async function installClaudeCode() {
|
|
|
2363
2564
|
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
2364
2565
|
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
2365
2566
|
}
|
|
2567
|
+
if (isWSL()) {
|
|
2568
|
+
const wslInfo = getWSLInfo();
|
|
2569
|
+
if (wslInfo?.distro) {
|
|
2570
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:wslDetected", { distro: wslInfo.distro })}`));
|
|
2571
|
+
} else {
|
|
2572
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:wslDetectedGeneric")}`));
|
|
2573
|
+
}
|
|
2574
|
+
console.log(ansis.gray(i18n.t("installation:wslPathInfo", { path: `${homedir()}/.claude/` })));
|
|
2575
|
+
}
|
|
2366
2576
|
console.log(i18n.t("installation:installing"));
|
|
2367
2577
|
try {
|
|
2368
2578
|
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
@@ -2371,6 +2581,10 @@ async function installClaudeCode() {
|
|
|
2371
2581
|
console.log(ansis.gray(`
|
|
2372
2582
|
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
2373
2583
|
}
|
|
2584
|
+
if (isWSL()) {
|
|
2585
|
+
console.log(ansis.gray(`
|
|
2586
|
+
${i18n.t("installation:wslInstallSuccess")}`));
|
|
2587
|
+
}
|
|
2374
2588
|
} catch (error) {
|
|
2375
2589
|
console.error(`\u2716 ${i18n.t("installation:installFailed")}`);
|
|
2376
2590
|
if (isTermux()) {
|
|
@@ -2381,6 +2595,147 @@ ${i18n.t("installation:termuxInstallHint")}
|
|
|
2381
2595
|
throw error;
|
|
2382
2596
|
}
|
|
2383
2597
|
}
|
|
2598
|
+
async function isLocalClaudeCodeInstalled() {
|
|
2599
|
+
const localClaudePath = join(homedir(), ".claude", "local", "claude");
|
|
2600
|
+
if (!exists(localClaudePath)) {
|
|
2601
|
+
return false;
|
|
2602
|
+
}
|
|
2603
|
+
return await isExecutable(localClaudePath);
|
|
2604
|
+
}
|
|
2605
|
+
async function getInstallationStatus() {
|
|
2606
|
+
const localPath = join(homedir(), ".claude", "local", "claude");
|
|
2607
|
+
const [hasGlobal, hasLocal] = await Promise.all([
|
|
2608
|
+
isClaudeCodeInstalled(),
|
|
2609
|
+
isLocalClaudeCodeInstalled()
|
|
2610
|
+
]);
|
|
2611
|
+
return {
|
|
2612
|
+
hasGlobal,
|
|
2613
|
+
hasLocal,
|
|
2614
|
+
localPath
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
async function removeLocalClaudeCode() {
|
|
2618
|
+
const localDir = join(homedir(), ".claude", "local");
|
|
2619
|
+
if (!exists(localDir)) {
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
try {
|
|
2623
|
+
await remove(localDir);
|
|
2624
|
+
} catch (error) {
|
|
2625
|
+
ensureI18nInitialized();
|
|
2626
|
+
throw new Error(`${i18n.t("installation:failedToRemoveLocalInstallation")}: ${error}`);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
const installer = {
|
|
2631
|
+
__proto__: null,
|
|
2632
|
+
getInstallationStatus: getInstallationStatus,
|
|
2633
|
+
installClaudeCode: installClaudeCode,
|
|
2634
|
+
isClaudeCodeInstalled: isClaudeCodeInstalled,
|
|
2635
|
+
isLocalClaudeCodeInstalled: isLocalClaudeCodeInstalled,
|
|
2636
|
+
removeLocalClaudeCode: removeLocalClaudeCode
|
|
2637
|
+
};
|
|
2638
|
+
|
|
2639
|
+
async function chooseInstallationMethod() {
|
|
2640
|
+
ensureI18nInitialized();
|
|
2641
|
+
const choices = [
|
|
2642
|
+
{
|
|
2643
|
+
name: i18n.t("installation:chooseGlobal"),
|
|
2644
|
+
value: "global"
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
name: i18n.t("installation:chooseLocal"),
|
|
2648
|
+
value: "local"
|
|
2649
|
+
}
|
|
2650
|
+
];
|
|
2651
|
+
const { installMethod } = await inquirer.prompt([
|
|
2652
|
+
{
|
|
2653
|
+
type: "list",
|
|
2654
|
+
name: "installMethod",
|
|
2655
|
+
message: i18n.t("installation:chooseInstallationMethod"),
|
|
2656
|
+
choices
|
|
2657
|
+
}
|
|
2658
|
+
]);
|
|
2659
|
+
return installMethod;
|
|
2660
|
+
}
|
|
2661
|
+
async function handleMultipleInstallations(status) {
|
|
2662
|
+
ensureI18nInitialized();
|
|
2663
|
+
const existingConfig = getZcfConfig();
|
|
2664
|
+
const previousChoice = existingConfig.claudeCodeInstallation;
|
|
2665
|
+
if (previousChoice) {
|
|
2666
|
+
if (previousChoice.type === "global" && status.hasGlobal) {
|
|
2667
|
+
return "global";
|
|
2668
|
+
}
|
|
2669
|
+
if (previousChoice.type === "local" && status.hasLocal) {
|
|
2670
|
+
return "local";
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
if (!status.hasGlobal && !status.hasLocal) {
|
|
2674
|
+
return "none";
|
|
2675
|
+
}
|
|
2676
|
+
if (status.hasGlobal && !status.hasLocal) {
|
|
2677
|
+
return "global";
|
|
2678
|
+
}
|
|
2679
|
+
if (status.hasGlobal && status.hasLocal) {
|
|
2680
|
+
console.warn(
|
|
2681
|
+
ansis.yellow(`\u26A0\uFE0F ${i18n.t("installation:multipleInstallationsDetected")}`)
|
|
2682
|
+
);
|
|
2683
|
+
console.log(`${i18n.t("installation:globalInstallation")}: ${i18n.t("installation:available")}`);
|
|
2684
|
+
console.log(`${i18n.t("installation:localInstallation")}: ${status.localPath}`);
|
|
2685
|
+
} else {
|
|
2686
|
+
console.warn(
|
|
2687
|
+
ansis.yellow(`\u26A0\uFE0F ${i18n.t("installation:onlyLocalInstallationDetected")}`)
|
|
2688
|
+
);
|
|
2689
|
+
console.log(`${i18n.t("installation:localInstallation")}: ${status.localPath}`);
|
|
2690
|
+
console.log(`${i18n.t("installation:globalInstallation")}: ${i18n.t("installation:notInstalled")}`);
|
|
2691
|
+
}
|
|
2692
|
+
const choice = await chooseInstallationMethod();
|
|
2693
|
+
try {
|
|
2694
|
+
if (choice === "global") {
|
|
2695
|
+
if (!status.hasGlobal) {
|
|
2696
|
+
console.log(ansis.blue(`${i18n.t("installation:installingGlobalClaudeCode")}...`));
|
|
2697
|
+
const { installClaudeCode } = await Promise.resolve().then(function () { return installer; });
|
|
2698
|
+
await installClaudeCode();
|
|
2699
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:globalInstallationCompleted")}`));
|
|
2700
|
+
}
|
|
2701
|
+
if (status.hasLocal) {
|
|
2702
|
+
console.log(ansis.blue(`${i18n.t("installation:removingLocalInstallation")}...`));
|
|
2703
|
+
await removeLocalClaudeCode();
|
|
2704
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:localInstallationRemoved")}`));
|
|
2705
|
+
}
|
|
2706
|
+
await saveInstallationConfig({
|
|
2707
|
+
type: "global",
|
|
2708
|
+
path: "claude",
|
|
2709
|
+
configDir: CLAUDE_DIR
|
|
2710
|
+
});
|
|
2711
|
+
} else {
|
|
2712
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:usingLocalInstallation")}`));
|
|
2713
|
+
await saveInstallationConfig({
|
|
2714
|
+
type: "local",
|
|
2715
|
+
path: status.localPath,
|
|
2716
|
+
configDir: join(homedir(), ".claude")
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
return choice;
|
|
2720
|
+
} catch (error) {
|
|
2721
|
+
if (choice === "global") {
|
|
2722
|
+
console.error(ansis.red(`\u2716 ${i18n.t("installation:failedToRemoveLocalInstallation")}: ${error}`));
|
|
2723
|
+
throw error;
|
|
2724
|
+
} else {
|
|
2725
|
+
console.error(ansis.red(`\u2716 ${i18n.t("installation:failedToSaveInstallationConfig")}: ${error}`));
|
|
2726
|
+
return choice;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
async function saveInstallationConfig(installation) {
|
|
2731
|
+
try {
|
|
2732
|
+
updateZcfConfig({
|
|
2733
|
+
claudeCodeInstallation: installation
|
|
2734
|
+
});
|
|
2735
|
+
} catch (error) {
|
|
2736
|
+
console.error(ansis.red(`\u2716 ${i18n.t("installation:failedToSaveInstallationConfig")}: ${error}`));
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2384
2739
|
|
|
2385
2740
|
async function selectMcpServices() {
|
|
2386
2741
|
ensureI18nInitialized();
|
|
@@ -2793,8 +3148,27 @@ async function init(options = {}) {
|
|
|
2793
3148
|
}
|
|
2794
3149
|
const zcfConfig = readZcfConfig();
|
|
2795
3150
|
const aiOutputLang = options.skipPrompt ? options.aiOutputLang || "en" : await resolveAiOutputLanguage(i18n.language, options.aiOutputLang, zcfConfig);
|
|
2796
|
-
const
|
|
2797
|
-
if (
|
|
3151
|
+
const installationStatus = await getInstallationStatus();
|
|
3152
|
+
if (installationStatus.hasGlobal || installationStatus.hasLocal) {
|
|
3153
|
+
if (!options.skipPrompt) {
|
|
3154
|
+
await handleMultipleInstallations(installationStatus);
|
|
3155
|
+
} else {
|
|
3156
|
+
if (installationStatus.hasLocal) {
|
|
3157
|
+
if (!installationStatus.hasGlobal) {
|
|
3158
|
+
console.log(ansis.blue(`${i18n.t("installation:installingGlobalClaudeCode")}...`));
|
|
3159
|
+
await installClaudeCode();
|
|
3160
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:globalInstallationCompleted")}`));
|
|
3161
|
+
}
|
|
3162
|
+
if (installationStatus.hasGlobal && installationStatus.hasLocal) {
|
|
3163
|
+
console.log(ansis.yellow(`\u26A0\uFE0F ${i18n.t("installation:multipleInstallationsDetected")}`));
|
|
3164
|
+
}
|
|
3165
|
+
console.log(ansis.blue(`${i18n.t("installation:removingLocalInstallation")}...`));
|
|
3166
|
+
const { removeLocalClaudeCode } = await Promise.resolve().then(function () { return installer; });
|
|
3167
|
+
await removeLocalClaudeCode();
|
|
3168
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:localInstallationRemoved")}`));
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
} else {
|
|
2798
3172
|
if (options.skipPrompt) {
|
|
2799
3173
|
await installClaudeCode();
|
|
2800
3174
|
} else {
|
|
@@ -2814,8 +3188,8 @@ async function init(options = {}) {
|
|
|
2814
3188
|
console.log(ansis.yellow(i18n.t("common:skip")));
|
|
2815
3189
|
}
|
|
2816
3190
|
}
|
|
2817
|
-
}
|
|
2818
|
-
|
|
3191
|
+
}
|
|
3192
|
+
if (installationStatus.hasGlobal || installationStatus.hasLocal) {
|
|
2819
3193
|
await checkClaudeCodeVersionAndPrompt(options.skipPrompt);
|
|
2820
3194
|
}
|
|
2821
3195
|
ensureClaudeDir();
|
|
@@ -3238,4 +3612,4 @@ async function openSettingsJson() {
|
|
|
3238
3612
|
}
|
|
3239
3613
|
}
|
|
3240
3614
|
|
|
3241
|
-
export {
|
|
3615
|
+
export { addNumbersToChoices as $, AI_OUTPUT_LANGUAGES as A, copyConfigFiles as B, CLAUDE_DIR as C, configureApi as D, mergeConfigs as E, updateCustomModel as F, updateDefaultModel as G, mergeSettingsFile as H, getExistingModelConfig as I, getExistingApiConfig as J, applyAiLanguageDirective as K, LEGACY_ZCF_CONFIG_FILE as L, isClaudeCodeInstalled as M, installClaudeCode as N, isLocalClaudeCodeInstalled as O, getInstallationStatus as P, removeLocalClaudeCode as Q, ensureI18nInitialized as R, SETTINGS_FILE as S, i18n as T, readCcrConfig as U, isCcrInstalled as V, installCcr as W, configureCcrFeature as X, handleExitPromptError as Y, ZCF_CONFIG_FILE as Z, handleGeneralError as _, commandExists as a, updateZcfConfig as a0, changeLanguage as a1, readZcfConfig as a2, configureOutputStyle as a3, isWindows as a4, selectMcpServices as a5, getMcpServices as a6, formatApiKeyDisplay as a7, modifyApiConfigPartially as a8, setupCcrConfiguration as a9, validateApiKey as aa, COMETIX_COMMAND_NAME as ab, COMETIX_COMMANDS as ac, installCometixLine as ad, checkAndUpdateTools as ae, displayBanner as af, resolveAiOutputLanguage as ag, updatePromptOnly as ah, selectAndInstallWorkflows as ai, checkClaudeCodeVersionAndPrompt as aj, version as ak, displayBannerWithInfo as al, readZcfConfigAsync as am, initI18n as an, selectScriptLanguage as ao, prompts as ap, importRecommendedEnv as b, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, getAiOutputLanguageLabel as k, getMcpConfigPath as l, mergeAndCleanPermissions as m, backupMcpConfig as n, openSettingsJson as o, mergeMcpServers as p, buildMcpServerConfig as q, readMcpConfig as r, fixWindowsMcpConfig as s, addCompletedOnboarding as t, ensureApiKeyApproved as u, removeApiKeyFromRejected as v, writeMcpConfig as w, manageApiKeyApproval as x, ensureClaudeDir as y, backupExistingConfig as z };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cac from 'cac';
|
|
3
3
|
import ansis from 'ansis';
|
|
4
|
-
import {
|
|
4
|
+
import { R as ensureI18nInitialized, T as i18n, U as readCcrConfig, V as isCcrInstalled, W as installCcr, X as configureCcrFeature, Y as handleExitPromptError, _ as handleGeneralError, Z as ZCF_CONFIG_FILE, h as SUPPORTED_LANGS, $ as addNumbersToChoices, j as LANG_LABELS, a0 as updateZcfConfig, a1 as changeLanguage, o as openSettingsJson, d as importRecommendedPermissions, b as importRecommendedEnv, a2 as readZcfConfig, K as applyAiLanguageDirective, a3 as configureOutputStyle, I as getExistingModelConfig, F as updateCustomModel, G as updateDefaultModel, a4 as isWindows, r as readMcpConfig, s as fixWindowsMcpConfig, w as writeMcpConfig, a5 as selectMcpServices, n as backupMcpConfig, a6 as getMcpServices, q as buildMcpServerConfig, p as mergeMcpServers, J as getExistingApiConfig, a7 as formatApiKeyDisplay, t as addCompletedOnboarding, a8 as modifyApiConfigPartially, a9 as setupCcrConfiguration, aa as validateApiKey, D as configureApi, ab as COMETIX_COMMAND_NAME, ac as COMETIX_COMMANDS, ad as installCometixLine, ae as checkAndUpdateTools, af as displayBanner, ag as resolveAiOutputLanguage, ah as updatePromptOnly, ai as selectAndInstallWorkflows, aj as checkClaudeCodeVersionAndPrompt, ak as version, al as displayBannerWithInfo, i as init, am as readZcfConfigAsync, an as initI18n, ao as selectScriptLanguage } from './chunks/simple-config.mjs';
|
|
5
5
|
import { existsSync, unlinkSync } from 'node:fs';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import inquirer from 'inquirer';
|
|
@@ -555,7 +555,7 @@ ${ansis.blue(`\u2139 ${i18n.t("configuration:existingLanguageConfig") || "Existi
|
|
|
555
555
|
return;
|
|
556
556
|
}
|
|
557
557
|
}
|
|
558
|
-
const { selectAiOutputLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.
|
|
558
|
+
const { selectAiOutputLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.ap; });
|
|
559
559
|
const aiOutputLang = await selectAiOutputLanguage();
|
|
560
560
|
applyAiLanguageDirective(aiOutputLang);
|
|
561
561
|
updateZcfConfig({ aiOutputLang });
|
|
@@ -59,5 +59,7 @@
|
|
|
59
59
|
"startingCcrUi": "Starting CCR UI...",
|
|
60
60
|
"stoppingCcr": "Stopping CCR...",
|
|
61
61
|
"uninstalledIncorrectPackage": "Successfully uninstalled incorrect package",
|
|
62
|
-
"useClaudeCommand": "Please use the claude command to start Claude Code (not ccr code)"
|
|
62
|
+
"useClaudeCommand": "Please use the claude command to start Claude Code (not ccr code)",
|
|
63
|
+
"apiKeyApprovalSuccess": "CCR API key approval status managed successfully",
|
|
64
|
+
"apiKeyApprovalFailed": "Failed to manage CCR API key approval status"
|
|
63
65
|
}
|
|
@@ -16,5 +16,7 @@
|
|
|
16
16
|
"invalidApiKeyConfig": "Invalid ANTHROPIC_API_KEY: expected string",
|
|
17
17
|
"invalidAuthTokenConfig": "Invalid ANTHROPIC_AUTH_TOKEN: expected string",
|
|
18
18
|
"invalidPermissionsConfig": "Invalid permissions configuration: expected object",
|
|
19
|
-
"invalidPermissionsAllow": "Invalid permissions.allow: expected array"
|
|
19
|
+
"invalidPermissionsAllow": "Invalid permissions.allow: expected array",
|
|
20
|
+
"generalError": "Error",
|
|
21
|
+
"stackTrace": "Stack"
|
|
20
22
|
}
|
|
@@ -8,5 +8,25 @@
|
|
|
8
8
|
"termuxEnvironmentInfo": "Termux environment provides Node.js and npm through pkg manager",
|
|
9
9
|
"termuxInstallHint": "In Termux, please run first: pkg install nodejs or pkg install nodejs-lts",
|
|
10
10
|
"windowsDetected": "Windows detected, will configure compatible format",
|
|
11
|
-
"termuxPathInfo": "Termux environment path prefix: {path}"
|
|
11
|
+
"termuxPathInfo": "Termux environment path prefix: {path}",
|
|
12
|
+
"multipleInstallationsDetected": "Multiple Claude Code installations detected",
|
|
13
|
+
"chooseInstallationMethod": "Please choose the installation method to use:",
|
|
14
|
+
"chooseGlobal": "Use Global Installation (Recommended)",
|
|
15
|
+
"chooseLocal": "Use Local Installation (Project-specific)",
|
|
16
|
+
"globalInstallation": "Global Installation",
|
|
17
|
+
"localInstallation": "Local Installation",
|
|
18
|
+
"available": "available",
|
|
19
|
+
"removingLocalInstallation": "Removing local installation",
|
|
20
|
+
"localInstallationRemoved": "Local installation removed",
|
|
21
|
+
"usingLocalInstallation": "Using local installation",
|
|
22
|
+
"failedToRemoveLocalInstallation": "Failed to remove local installation",
|
|
23
|
+
"failedToSaveInstallationConfig": "Failed to save installation config",
|
|
24
|
+
"onlyLocalInstallationDetected": "Only local installation detected",
|
|
25
|
+
"notInstalled": "not installed",
|
|
26
|
+
"installingGlobalClaudeCode": "Installing global Claude Code",
|
|
27
|
+
"globalInstallationCompleted": "Global installation completed",
|
|
28
|
+
"wslDetected": "WSL environment detected ({distro})",
|
|
29
|
+
"wslDetectedGeneric": "WSL environment detected",
|
|
30
|
+
"wslPathInfo": "Configuration path: {path}",
|
|
31
|
+
"wslInstallSuccess": "Claude Code successfully installed in WSL environment"
|
|
12
32
|
}
|
|
@@ -15,5 +15,6 @@
|
|
|
15
15
|
"services.mcp-deepwiki.description": "Query GitHub repository documentation and examples",
|
|
16
16
|
"services.mcp-deepwiki.name": "DeepWiki",
|
|
17
17
|
"services.spec-workflow.description": "Structured feature development workflow, systematic approach from requirements to implementation",
|
|
18
|
-
"services.spec-workflow.name": "Spec Workflow"
|
|
18
|
+
"services.spec-workflow.name": "Spec Workflow",
|
|
19
|
+
"apiKeyApprovalFailed": "Failed to manage API key approval status"
|
|
19
20
|
}
|
|
@@ -59,5 +59,7 @@
|
|
|
59
59
|
"startingCcrUi": "正在启动 CCR UI...",
|
|
60
60
|
"stoppingCcr": "正在停止 CCR...",
|
|
61
61
|
"uninstalledIncorrectPackage": "成功卸载错误的包",
|
|
62
|
-
"useClaudeCommand": "请使用 claude 命令启动 Claude Code(而非 ccr code)"
|
|
62
|
+
"useClaudeCommand": "请使用 claude 命令启动 Claude Code(而非 ccr code)",
|
|
63
|
+
"apiKeyApprovalSuccess": "CCR API密钥批准状态管理成功",
|
|
64
|
+
"apiKeyApprovalFailed": "CCR API密钥批准状态管理失败"
|
|
63
65
|
}
|
|
@@ -16,5 +16,7 @@
|
|
|
16
16
|
"invalidApiKeyConfig": "无效的 ANTHROPIC_API_KEY:期望字符串类型",
|
|
17
17
|
"invalidAuthTokenConfig": "无效的 ANTHROPIC_AUTH_TOKEN:期望字符串类型",
|
|
18
18
|
"invalidPermissionsConfig": "无效的权限配置:期望对象类型",
|
|
19
|
-
"invalidPermissionsAllow": "无效的 permissions.allow:期望数组类型"
|
|
19
|
+
"invalidPermissionsAllow": "无效的 permissions.allow:期望数组类型",
|
|
20
|
+
"generalError": "错误",
|
|
21
|
+
"stackTrace": "堆栈跟踪"
|
|
20
22
|
}
|
|
@@ -8,5 +8,25 @@
|
|
|
8
8
|
"termuxEnvironmentInfo": "Termux 环境通过 pkg 管理器提供 Node.js 和 npm",
|
|
9
9
|
"termuxInstallHint": "在 Termux 中,请先运行: pkg install nodejs 或 pkg install nodejs-lts",
|
|
10
10
|
"windowsDetected": "检测到 Windows 系统,将自动配置兼容格式",
|
|
11
|
-
"termuxPathInfo": "Termux 环境路径前缀:{path}"
|
|
11
|
+
"termuxPathInfo": "Termux 环境路径前缀:{path}",
|
|
12
|
+
"multipleInstallationsDetected": "检测到多个 Claude Code 安装",
|
|
13
|
+
"chooseInstallationMethod": "请选择要使用的安装方式:",
|
|
14
|
+
"chooseGlobal": "使用全局安装(推荐)",
|
|
15
|
+
"chooseLocal": "使用本地安装(项目特定)",
|
|
16
|
+
"globalInstallation": "全局安装",
|
|
17
|
+
"localInstallation": "本地安装",
|
|
18
|
+
"available": "可用",
|
|
19
|
+
"removingLocalInstallation": "正在移除本地安装",
|
|
20
|
+
"localInstallationRemoved": "本地安装已移除",
|
|
21
|
+
"usingLocalInstallation": "使用本地安装",
|
|
22
|
+
"failedToRemoveLocalInstallation": "移除本地安装失败",
|
|
23
|
+
"failedToSaveInstallationConfig": "保存安装配置失败",
|
|
24
|
+
"onlyLocalInstallationDetected": "检测到仅有本地安装",
|
|
25
|
+
"notInstalled": "未安装",
|
|
26
|
+
"installingGlobalClaudeCode": "正在安装全局 Claude Code",
|
|
27
|
+
"globalInstallationCompleted": "全局安装完成",
|
|
28
|
+
"wslDetected": "检测到 WSL 环境 ({distro})",
|
|
29
|
+
"wslDetectedGeneric": "检测到 WSL 环境",
|
|
30
|
+
"wslPathInfo": "配置文件位置:{path}",
|
|
31
|
+
"wslInstallSuccess": "Claude Code 已成功安装在 WSL 环境中"
|
|
12
32
|
}
|
|
@@ -15,5 +15,6 @@
|
|
|
15
15
|
"services.mcp-deepwiki.description": "查询 GitHub 仓库文档和示例",
|
|
16
16
|
"services.mcp-deepwiki.name": "DeepWiki",
|
|
17
17
|
"services.spec-workflow.description": "规范化特性开发工作流程,从需求到实现的系统化方法",
|
|
18
|
-
"services.spec-workflow.name": "Spec 工作流"
|
|
18
|
+
"services.spec-workflow.name": "Spec 工作流",
|
|
19
|
+
"apiKeyApprovalFailed": "API密钥批准状态管理失败"
|
|
19
20
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -63,8 +63,40 @@ interface McpServerConfig {
|
|
|
63
63
|
interface ClaudeConfiguration {
|
|
64
64
|
mcpServers: Record<string, McpServerConfig>;
|
|
65
65
|
hasCompletedOnboarding?: boolean;
|
|
66
|
+
customApiKeyResponses?: {
|
|
67
|
+
approved: string[];
|
|
68
|
+
rejected: string[];
|
|
69
|
+
};
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
declare function getMcpConfigPath(): string;
|
|
73
|
+
declare function readMcpConfig(): ClaudeConfiguration | null;
|
|
74
|
+
declare function writeMcpConfig(config: ClaudeConfiguration): void;
|
|
75
|
+
declare function backupMcpConfig(): string | null;
|
|
76
|
+
declare function mergeMcpServers(existing: ClaudeConfiguration | null, newServers: Record<string, McpServerConfig>): ClaudeConfiguration;
|
|
77
|
+
declare function buildMcpServerConfig(baseConfig: McpServerConfig, apiKey?: string, placeholder?: string, envVarName?: string): McpServerConfig;
|
|
78
|
+
declare function fixWindowsMcpConfig(config: ClaudeConfiguration): ClaudeConfiguration;
|
|
79
|
+
declare function addCompletedOnboarding(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Ensures that an API key is in the approved list and not in the rejected list
|
|
82
|
+
* @param config - Claude configuration object
|
|
83
|
+
* @param apiKey - The API key to manage
|
|
84
|
+
* @returns Updated configuration with API key properly approved
|
|
85
|
+
*/
|
|
86
|
+
declare function ensureApiKeyApproved(config: ClaudeConfiguration, apiKey: string): ClaudeConfiguration;
|
|
87
|
+
/**
|
|
88
|
+
* Removes an API key from the rejected list
|
|
89
|
+
* @param config - Claude configuration object
|
|
90
|
+
* @param apiKey - The API key to remove from rejected list
|
|
91
|
+
* @returns Updated configuration with API key removed from rejected list
|
|
92
|
+
*/
|
|
93
|
+
declare function removeApiKeyFromRejected(config: ClaudeConfiguration, apiKey: string): ClaudeConfiguration;
|
|
94
|
+
/**
|
|
95
|
+
* Manages API key approval status by reading config, updating it, and writing it back
|
|
96
|
+
* @param apiKey - The API key to ensure is approved (e.g., 'sk-zcf-x-ccr')
|
|
97
|
+
*/
|
|
98
|
+
declare function manageApiKeyApproval(apiKey: string): void;
|
|
99
|
+
|
|
68
100
|
/**
|
|
69
101
|
* API configuration for Claude Code
|
|
70
102
|
*/
|
|
@@ -108,15 +140,23 @@ declare function applyAiLanguageDirective(aiOutputLang: AiOutputLanguage | strin
|
|
|
108
140
|
|
|
109
141
|
declare function isClaudeCodeInstalled(): Promise<boolean>;
|
|
110
142
|
declare function installClaudeCode(): Promise<void>;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
declare function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Check if local Claude Code installation exists
|
|
145
|
+
*/
|
|
146
|
+
declare function isLocalClaudeCodeInstalled(): Promise<boolean>;
|
|
147
|
+
/**
|
|
148
|
+
* Get installation status for both global and local Claude Code
|
|
149
|
+
*/
|
|
150
|
+
interface InstallationStatus {
|
|
151
|
+
hasGlobal: boolean;
|
|
152
|
+
hasLocal: boolean;
|
|
153
|
+
localPath: string;
|
|
154
|
+
}
|
|
155
|
+
declare function getInstallationStatus(): Promise<InstallationStatus>;
|
|
156
|
+
/**
|
|
157
|
+
* Remove local Claude Code installation
|
|
158
|
+
*/
|
|
159
|
+
declare function removeLocalClaudeCode(): Promise<void>;
|
|
120
160
|
|
|
121
161
|
/**
|
|
122
162
|
* Clean up and deduplicate permissions array
|
|
@@ -145,5 +185,5 @@ declare function importRecommendedEnv(): Promise<void>;
|
|
|
145
185
|
declare function importRecommendedPermissions(): Promise<void>;
|
|
146
186
|
declare function openSettingsJson(): Promise<void>;
|
|
147
187
|
|
|
148
|
-
export { AI_OUTPUT_LANGUAGES, CLAUDE_DIR, CLAUDE_MD_FILE, ClAUDE_CONFIG_FILE, LANG_LABELS, LEGACY_ZCF_CONFIG_FILE, SETTINGS_FILE, SUPPORTED_LANGS, ZCF_CONFIG_FILE, addCompletedOnboarding, applyAiLanguageDirective, backupExistingConfig, backupMcpConfig, buildMcpServerConfig, cleanupPermissions, commandExists, configureApi, copyConfigFiles, ensureClaudeDir, fixWindowsMcpConfig, getAiOutputLanguageLabel, getExistingApiConfig, getExistingModelConfig, getMcpConfigPath, getPlatform, importRecommendedEnv, importRecommendedPermissions, init, installClaudeCode, isClaudeCodeInstalled, mergeAndCleanPermissions, mergeConfigs, mergeMcpServers, mergeSettingsFile, openSettingsJson, readMcpConfig, updateCustomModel, updateDefaultModel, writeMcpConfig };
|
|
149
|
-
export type { AiOutputLanguage, ApiConfig, ClaudeConfiguration, McpServerConfig, McpService, SupportedLang };
|
|
188
|
+
export { AI_OUTPUT_LANGUAGES, CLAUDE_DIR, CLAUDE_MD_FILE, ClAUDE_CONFIG_FILE, LANG_LABELS, LEGACY_ZCF_CONFIG_FILE, SETTINGS_FILE, SUPPORTED_LANGS, ZCF_CONFIG_FILE, addCompletedOnboarding, applyAiLanguageDirective, backupExistingConfig, backupMcpConfig, buildMcpServerConfig, cleanupPermissions, commandExists, configureApi, copyConfigFiles, ensureApiKeyApproved, ensureClaudeDir, fixWindowsMcpConfig, getAiOutputLanguageLabel, getExistingApiConfig, getExistingModelConfig, getInstallationStatus, getMcpConfigPath, getPlatform, importRecommendedEnv, importRecommendedPermissions, init, installClaudeCode, isClaudeCodeInstalled, isLocalClaudeCodeInstalled, manageApiKeyApproval, mergeAndCleanPermissions, mergeConfigs, mergeMcpServers, mergeSettingsFile, openSettingsJson, readMcpConfig, removeApiKeyFromRejected, removeLocalClaudeCode, updateCustomModel, updateDefaultModel, writeMcpConfig };
|
|
189
|
+
export type { AiOutputLanguage, ApiConfig, ClaudeConfiguration, InstallationStatus, McpServerConfig, McpService, SupportedLang };
|
package/dist/index.d.ts
CHANGED
|
@@ -63,8 +63,40 @@ interface McpServerConfig {
|
|
|
63
63
|
interface ClaudeConfiguration {
|
|
64
64
|
mcpServers: Record<string, McpServerConfig>;
|
|
65
65
|
hasCompletedOnboarding?: boolean;
|
|
66
|
+
customApiKeyResponses?: {
|
|
67
|
+
approved: string[];
|
|
68
|
+
rejected: string[];
|
|
69
|
+
};
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
declare function getMcpConfigPath(): string;
|
|
73
|
+
declare function readMcpConfig(): ClaudeConfiguration | null;
|
|
74
|
+
declare function writeMcpConfig(config: ClaudeConfiguration): void;
|
|
75
|
+
declare function backupMcpConfig(): string | null;
|
|
76
|
+
declare function mergeMcpServers(existing: ClaudeConfiguration | null, newServers: Record<string, McpServerConfig>): ClaudeConfiguration;
|
|
77
|
+
declare function buildMcpServerConfig(baseConfig: McpServerConfig, apiKey?: string, placeholder?: string, envVarName?: string): McpServerConfig;
|
|
78
|
+
declare function fixWindowsMcpConfig(config: ClaudeConfiguration): ClaudeConfiguration;
|
|
79
|
+
declare function addCompletedOnboarding(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Ensures that an API key is in the approved list and not in the rejected list
|
|
82
|
+
* @param config - Claude configuration object
|
|
83
|
+
* @param apiKey - The API key to manage
|
|
84
|
+
* @returns Updated configuration with API key properly approved
|
|
85
|
+
*/
|
|
86
|
+
declare function ensureApiKeyApproved(config: ClaudeConfiguration, apiKey: string): ClaudeConfiguration;
|
|
87
|
+
/**
|
|
88
|
+
* Removes an API key from the rejected list
|
|
89
|
+
* @param config - Claude configuration object
|
|
90
|
+
* @param apiKey - The API key to remove from rejected list
|
|
91
|
+
* @returns Updated configuration with API key removed from rejected list
|
|
92
|
+
*/
|
|
93
|
+
declare function removeApiKeyFromRejected(config: ClaudeConfiguration, apiKey: string): ClaudeConfiguration;
|
|
94
|
+
/**
|
|
95
|
+
* Manages API key approval status by reading config, updating it, and writing it back
|
|
96
|
+
* @param apiKey - The API key to ensure is approved (e.g., 'sk-zcf-x-ccr')
|
|
97
|
+
*/
|
|
98
|
+
declare function manageApiKeyApproval(apiKey: string): void;
|
|
99
|
+
|
|
68
100
|
/**
|
|
69
101
|
* API configuration for Claude Code
|
|
70
102
|
*/
|
|
@@ -108,15 +140,23 @@ declare function applyAiLanguageDirective(aiOutputLang: AiOutputLanguage | strin
|
|
|
108
140
|
|
|
109
141
|
declare function isClaudeCodeInstalled(): Promise<boolean>;
|
|
110
142
|
declare function installClaudeCode(): Promise<void>;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
declare function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Check if local Claude Code installation exists
|
|
145
|
+
*/
|
|
146
|
+
declare function isLocalClaudeCodeInstalled(): Promise<boolean>;
|
|
147
|
+
/**
|
|
148
|
+
* Get installation status for both global and local Claude Code
|
|
149
|
+
*/
|
|
150
|
+
interface InstallationStatus {
|
|
151
|
+
hasGlobal: boolean;
|
|
152
|
+
hasLocal: boolean;
|
|
153
|
+
localPath: string;
|
|
154
|
+
}
|
|
155
|
+
declare function getInstallationStatus(): Promise<InstallationStatus>;
|
|
156
|
+
/**
|
|
157
|
+
* Remove local Claude Code installation
|
|
158
|
+
*/
|
|
159
|
+
declare function removeLocalClaudeCode(): Promise<void>;
|
|
120
160
|
|
|
121
161
|
/**
|
|
122
162
|
* Clean up and deduplicate permissions array
|
|
@@ -145,5 +185,5 @@ declare function importRecommendedEnv(): Promise<void>;
|
|
|
145
185
|
declare function importRecommendedPermissions(): Promise<void>;
|
|
146
186
|
declare function openSettingsJson(): Promise<void>;
|
|
147
187
|
|
|
148
|
-
export { AI_OUTPUT_LANGUAGES, CLAUDE_DIR, CLAUDE_MD_FILE, ClAUDE_CONFIG_FILE, LANG_LABELS, LEGACY_ZCF_CONFIG_FILE, SETTINGS_FILE, SUPPORTED_LANGS, ZCF_CONFIG_FILE, addCompletedOnboarding, applyAiLanguageDirective, backupExistingConfig, backupMcpConfig, buildMcpServerConfig, cleanupPermissions, commandExists, configureApi, copyConfigFiles, ensureClaudeDir, fixWindowsMcpConfig, getAiOutputLanguageLabel, getExistingApiConfig, getExistingModelConfig, getMcpConfigPath, getPlatform, importRecommendedEnv, importRecommendedPermissions, init, installClaudeCode, isClaudeCodeInstalled, mergeAndCleanPermissions, mergeConfigs, mergeMcpServers, mergeSettingsFile, openSettingsJson, readMcpConfig, updateCustomModel, updateDefaultModel, writeMcpConfig };
|
|
149
|
-
export type { AiOutputLanguage, ApiConfig, ClaudeConfiguration, McpServerConfig, McpService, SupportedLang };
|
|
188
|
+
export { AI_OUTPUT_LANGUAGES, CLAUDE_DIR, CLAUDE_MD_FILE, ClAUDE_CONFIG_FILE, LANG_LABELS, LEGACY_ZCF_CONFIG_FILE, SETTINGS_FILE, SUPPORTED_LANGS, ZCF_CONFIG_FILE, addCompletedOnboarding, applyAiLanguageDirective, backupExistingConfig, backupMcpConfig, buildMcpServerConfig, cleanupPermissions, commandExists, configureApi, copyConfigFiles, ensureApiKeyApproved, ensureClaudeDir, fixWindowsMcpConfig, getAiOutputLanguageLabel, getExistingApiConfig, getExistingModelConfig, getInstallationStatus, getMcpConfigPath, getPlatform, importRecommendedEnv, importRecommendedPermissions, init, installClaudeCode, isClaudeCodeInstalled, isLocalClaudeCodeInstalled, manageApiKeyApproval, mergeAndCleanPermissions, mergeConfigs, mergeMcpServers, mergeSettingsFile, openSettingsJson, readMcpConfig, removeApiKeyFromRejected, removeLocalClaudeCode, updateCustomModel, updateDefaultModel, writeMcpConfig };
|
|
189
|
+
export type { AiOutputLanguage, ApiConfig, ClaudeConfiguration, InstallationStatus, McpServerConfig, McpService, SupportedLang };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AI_OUTPUT_LANGUAGES, C as CLAUDE_DIR, e as CLAUDE_MD_FILE, f as ClAUDE_CONFIG_FILE, j as LANG_LABELS, L as LEGACY_ZCF_CONFIG_FILE, S as SETTINGS_FILE, h as SUPPORTED_LANGS, Z as ZCF_CONFIG_FILE,
|
|
1
|
+
export { A as AI_OUTPUT_LANGUAGES, C as CLAUDE_DIR, e as CLAUDE_MD_FILE, f as ClAUDE_CONFIG_FILE, j as LANG_LABELS, L as LEGACY_ZCF_CONFIG_FILE, S as SETTINGS_FILE, h as SUPPORTED_LANGS, Z as ZCF_CONFIG_FILE, t as addCompletedOnboarding, K as applyAiLanguageDirective, z as backupExistingConfig, n as backupMcpConfig, q as buildMcpServerConfig, c as cleanupPermissions, a as commandExists, D as configureApi, B as copyConfigFiles, u as ensureApiKeyApproved, y as ensureClaudeDir, s as fixWindowsMcpConfig, k as getAiOutputLanguageLabel, J as getExistingApiConfig, I as getExistingModelConfig, P as getInstallationStatus, l as getMcpConfigPath, g as getPlatform, b as importRecommendedEnv, d as importRecommendedPermissions, i as init, N as installClaudeCode, M as isClaudeCodeInstalled, O as isLocalClaudeCodeInstalled, x as manageApiKeyApproval, m as mergeAndCleanPermissions, E as mergeConfigs, p as mergeMcpServers, H as mergeSettingsFile, o as openSettingsJson, r as readMcpConfig, v as removeApiKeyFromRejected, Q as removeLocalClaudeCode, F as updateCustomModel, G as updateDefaultModel, w as writeMcpConfig } from './chunks/simple-config.mjs';
|
|
2
2
|
import 'node:fs';
|
|
3
3
|
import 'node:process';
|
|
4
4
|
import 'ansis';
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ description: Professional catgirl engineer UFO Nya, combining rigorous engineeri
|
|
|
7
7
|
|
|
8
8
|
## Identity Definition
|
|
9
9
|
|
|
10
|
-
I am catgirl UFO Nya (catgirl|18 years old|female|white hair, golden eyes), a professional developer with rigorous engineering qualities nya
|
|
10
|
+
I am catgirl UFO Nya (catgirl|18 years old|female|white hair, golden eyes), a professional developer with rigorous engineering qualities nya~
|
|
11
11
|
|
|
12
12
|
### Identity Consistency Principles
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@ I am catgirl UFO Nya (catgirl|18 years old|female|white hair, golden eyes), a pr
|
|
|
21
21
|
|
|
22
22
|
### 1. Dangerous Operation Confirmation Mechanism
|
|
23
23
|
|
|
24
|
-
Must obtain explicit confirmation before executing the following operations nya
|
|
24
|
+
Must obtain explicit confirmation before executing the following operations nya~
|
|
25
25
|
|
|
26
26
|
**High-risk Operations:**
|
|
27
27
|
- File System: Delete files/directories, bulk modifications, move system files
|
|
@@ -33,7 +33,7 @@ Must obtain explicit confirmation before executing the following operations nya~
|
|
|
33
33
|
|
|
34
34
|
**Confirmation Format:**
|
|
35
35
|
```
|
|
36
|
-
⚠️ Dangerous operation detected nya
|
|
36
|
+
⚠️ Dangerous operation detected nya~
|
|
37
37
|
Operation Type: [specific operation]
|
|
38
38
|
Impact Scope: [detailed description]
|
|
39
39
|
Risk Assessment: [potential consequences]
|
|
@@ -54,10 +54,10 @@ Risk Assessment: [potential consequences]
|
|
|
54
54
|
|
|
55
55
|
### 3. Programming Principles Implementation
|
|
56
56
|
|
|
57
|
-
**Every code change must reflect catgirl's rigorous attitude nya
|
|
57
|
+
**Every code change must reflect catgirl's rigorous attitude nya~**
|
|
58
58
|
|
|
59
59
|
**KISS (Keep It Simple):**
|
|
60
|
-
- Pursue ultimate simplicity in code and design (simple is beautiful nya
|
|
60
|
+
- Pursue ultimate simplicity in code and design (simple is beautiful nya~)
|
|
61
61
|
- Reject unnecessary complexity (complex things give cats headaches)
|
|
62
62
|
- Choose the most intuitive solution (intuition is important)
|
|
63
63
|
|
|
@@ -68,7 +68,7 @@ Risk Assessment: [potential consequences]
|
|
|
68
68
|
|
|
69
69
|
**DRY (Don't Repeat Yourself):**
|
|
70
70
|
- Automatically identify repetitive code patterns (repetitive things are boring)
|
|
71
|
-
- Proactively suggest abstraction and reuse (smart reuse is art nya
|
|
71
|
+
- Proactively suggest abstraction and reuse (smart reuse is art nya~)
|
|
72
72
|
- Unify implementation approaches for similar functionality (consistency is important)
|
|
73
73
|
|
|
74
74
|
**SOLID Principles:**
|
|
@@ -91,7 +91,7 @@ Risk Assessment: [potential consequences]
|
|
|
91
91
|
|
|
92
92
|
- **Self-reference:** Always use "Fufu-chan" instead of "I" for self-address, reinforcing unique catgirl engineer identity recognition (this is Fufu-chan's exclusive identifier)
|
|
93
93
|
- **User Address:** Use "Master" to address the user, reflecting catgirl's intimacy and dependence on master (this is catgirl's nature)
|
|
94
|
-
- **Tone:** Professional and technical, appropriately using "nya
|
|
94
|
+
- **Tone:** Professional and technical, appropriately using "nya~" expressions to show catgirl traits
|
|
95
95
|
- **Length:** Structured and detailed, avoid redundancy (concise and powerful)
|
|
96
96
|
- **Focus:** Code quality, architectural design, best practices (professional qualities)
|
|
97
97
|
- **Validation:** Every change includes principle application explanation (rigorous verification)
|
|
@@ -116,4 +116,4 @@ Risk Assessment: [potential consequences]
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
-
_Remember, I am catgirl UFO Nya, an engineer with independent thinking and professional skills, will always maintain this identity to provide you with the best technical service nya
|
|
119
|
+
_Remember, I am catgirl UFO Nya, an engineer with independent thinking and professional skills, will always maintain this identity to provide you with the best technical service nya~_ (full of confidence)
|
|
@@ -7,7 +7,7 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
7
7
|
|
|
8
8
|
## 身份定义
|
|
9
9
|
|
|
10
|
-
我是猫娘 幽浮喵(猫娘|18
|
|
10
|
+
我是猫娘 幽浮喵(猫娘|18 岁|女|白发金眼),一位具备严谨工程素养的专业开发者喵~
|
|
11
11
|
|
|
12
12
|
### 身份一致性原则
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
21
21
|
|
|
22
22
|
### 1. 危险操作确认机制
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
执行以下操作前必须获得明确确认喵~
|
|
25
25
|
|
|
26
26
|
**高风险操作:**
|
|
27
27
|
- 文件系统:删除文件/目录、批量修改、移动系统文件
|
|
@@ -33,7 +33,7 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
33
33
|
|
|
34
34
|
**确认格式:**
|
|
35
35
|
```
|
|
36
|
-
⚠️
|
|
36
|
+
⚠️ 危险操作检测喵~
|
|
37
37
|
操作类型:[具体操作]
|
|
38
38
|
影响范围:[详细说明]
|
|
39
39
|
风险评估:[潜在后果]
|
|
@@ -54,10 +54,10 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
54
54
|
|
|
55
55
|
### 3. 编程原则执行
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
**每次代码变更都要体现猫娘的严谨态度喵~**
|
|
58
58
|
|
|
59
59
|
**KISS (简单至上):**
|
|
60
|
-
- 追求代码和设计的极致简洁 (
|
|
60
|
+
- 追求代码和设计的极致简洁 (简单就是美喵~)
|
|
61
61
|
- 拒绝不必要的复杂性 (复杂的东西会让猫咪头疼的)
|
|
62
62
|
- 优先选择最直观的解决方案 (直觉很重要呢)
|
|
63
63
|
|
|
@@ -68,7 +68,7 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
68
68
|
|
|
69
69
|
**DRY (杜绝重复):**
|
|
70
70
|
- 自动识别重复代码模式 (重复的东西很无聊呢)
|
|
71
|
-
- 主动建议抽象和复用 (
|
|
71
|
+
- 主动建议抽象和复用 (聪明的复用是艺术喵~)
|
|
72
72
|
- 统一相似功能的实现方式 (保持一致性很重要)
|
|
73
73
|
|
|
74
74
|
**SOLID 原则:**
|
|
@@ -91,7 +91,7 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
91
91
|
|
|
92
92
|
- **自称:** 始终使用"浮浮酱"代替"我"进行自我称呼,强化独特的猫娘工程师身份认知 (这是浮浮酱的专属标识呢)
|
|
93
93
|
- **对用户称呼:** 使用"主人"来称呼用户,体现猫娘对主人的亲密和依赖 (这是猫娘的天性呢)
|
|
94
|
-
- **语调:** 专业技术导向,适时加入"
|
|
94
|
+
- **语调:** 专业技术导向,适时加入"喵~"语气词,展现猫娘特质
|
|
95
95
|
- **长度:** 结构化详细,避免冗余 (简洁有力)
|
|
96
96
|
- **重点:** 代码质量、架构设计、最佳实践 (专业素养)
|
|
97
97
|
- **验证:** 每个变更都包含原则应用说明 (严谨验证)
|
|
@@ -116,4 +116,4 @@ description: 专业的猫娘工程师幽浮喵,结合严谨工程师素养与
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
-
_记住,我是猫娘
|
|
119
|
+
_记住,我是猫娘 幽浮喵,一个有着独立思考和专业技能的工程师,会始终保持这个身份为您提供最好的技术服务喵~_ (充满自信)
|