From e9c4951a676d0bcf271afdf80d6041248a50d883 Mon Sep 17 00:00:00 2001 From: chufan Date: Wed, 24 Jun 2026 18:01:56 +0800 Subject: [PATCH 1/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=95=9C=E5=83=8F?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=BC=82=E5=B8=B8=EF=BC=8C=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=AD=A5=E9=AA=A4=E3=80=81=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 +-- package.json | 2 +- pnpm-lock.yaml | 98 ++++++------------------------------- scripts/core/build-image.js | 2 +- 4 files changed, 19 insertions(+), 89 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3d8fd3caa..ab061371d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ # CONTAINER_BUILD: 采用容器构建 # -FROM registry.cn-hangzhou.aliyuncs.com/142vip-infra/node:20.18.0-alpine AS build_base +FROM registry.cn-hangzhou.aliyuncs.com/142vip-infra/node:25.9.0-base AS build_base # 是否配置代理 -ARG NEED_PROXY_BUILD=false +ARG NEED_PROXY ENV NODE_OPTIONS="--max-old-space-size=200000" # 设置环境变量,支持容器构建时使用layer缓存,参考:https://pnpm.io/zh/docker @@ -25,7 +25,7 @@ COPY . . # 基于容器自动构建 RUN --mount=type=cache,id=pnpm,target=/pnpm/store sh ./scripts/ci --ignore-scripts && \ - if [ "$NEED_PROXY_BUILD" = "false" ]; \ + if [ "$NEED_PROXY" = "false" ]; \ then \ pnpm build; \ else \ diff --git a/package.json b/package.json index 1a0ea759e..ecbf7843f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@142vip/eslint-config": "0.0.1-alpha.5", "@142vip/fairy-cli": "0.0.3-alpha.30", "@142vip/open-source": "0.0.1-alpha.3", - "@142vip/utils": "0.0.1-alpha.50", + "@142vip/utils": "0.0.1-alpha.52", "@142vip/vuepress": "0.0.1-alpha.18", "only-allow": "1.2.1", "simple-git-hooks": "2.13.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 733f83843..630e97454 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: 0.0.1-alpha.3 version: 0.0.1-alpha.3 '@142vip/utils': - specifier: 0.0.1-alpha.50 - version: 0.0.1-alpha.50(@types/node@22.15.18)(typescript@5.8.3) + specifier: 0.0.1-alpha.52 + version: 0.0.1-alpha.52(@types/node@22.15.18)(typescript@5.8.3) '@142vip/vuepress': specifier: 0.0.1-alpha.18 version: 0.0.1-alpha.18(hnizoouq5pmea4sfd55bb4n4ra) @@ -143,8 +143,8 @@ packages: '@142vip/utils@0.0.1-alpha.42': resolution: {integrity: sha512-j/JoM/aoFkpw0K9IgRZJ1SDbWrIuqel5rrttnQdGWG9XspUq1tEmyW5K1mIeUK0p7iMLyBqnQYQyUFG9+hLthQ==} - '@142vip/utils@0.0.1-alpha.50': - resolution: {integrity: sha512-+Q8HRxYa40dA+SA110CRGDp42pjGn+EJLBofa6ohLcuGCQk6OE/lEXaEXClFgqPV9ei6rtWOne9+B753LHTayA==} + '@142vip/utils@0.0.1-alpha.52': + resolution: {integrity: sha512-RolSWxpvGBOAGvCKHdz1MGtFuxo4iRMCGq4S+JZO0QgnTr8gEDWVwmEbKGgqauU8r2eD7o+n/+LPMBjK2Ku8Ig==} '@142vip/vuepress@0.0.1-alpha.18': resolution: {integrity: sha512-3vjY1KGyM0qCPOHX7pZ/1rRvk/mp2Sa8mCLsSQCQn+5MEzpmxJYnqnjv0O7gv+7RVZ4boJfuLCBq++k5en3QKg==} @@ -994,67 +994,56 @@ packages: resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.41.0': resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.41.0': resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.41.0': resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.41.0': resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.41.0': resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.41.0': resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.41.0': resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.41.0': resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.41.0': resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.41.0': resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} @@ -1347,6 +1336,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@vitejs/plugin-vue@5.2.4': resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} @@ -1814,11 +1804,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.5: - resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1867,9 +1852,6 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001718: - resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} - caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} @@ -2407,9 +2389,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.155: - resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} - electron-to-chromium@1.5.356: resolution: {integrity: sha512-9NgFd7m5t5MCJ5rUSjJITUXAH9mEGlrlofnMf4YEr+pz6JlP7cWmTAH+JFmbPnaSW8koVTkuW7pacORWAnA5Yw==} @@ -2778,14 +2757,6 @@ packages: fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - fdir@6.4.4: - resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -3601,9 +3572,6 @@ packages: node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - node-releases@2.0.44: resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} @@ -4360,10 +4328,6 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} @@ -4501,12 +4465,6 @@ packages: resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} engines: {node: '>=4'} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -5019,7 +4977,7 @@ snapshots: - '@types/node' - typescript - '@142vip/utils@0.0.1-alpha.50(@types/node@22.15.18)(typescript@5.8.3)': + '@142vip/utils@0.0.1-alpha.52(@types/node@22.15.18)(typescript@5.8.3)': dependencies: '@142vip/open-source': 0.0.1-alpha.3 '@inquirer/prompts': 7.4.0(@types/node@22.15.18) @@ -5381,7 +5339,7 @@ snapshots: '@antfu/install-pkg': 1.1.0 '@antfu/utils': 8.1.1 '@iconify/types': 2.0.0 - debug: 4.4.1 + debug: 4.4.3 globals: 15.15.0 kolorist: 1.8.0 local-pkg: 1.1.1 @@ -6709,7 +6667,7 @@ snapshots: '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 '@vuepress/shared': 2.0.0-rc.22 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 11.3.0 globby: 14.1.0 hash-sum: 2.0.0 @@ -6803,8 +6761,8 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.3): dependencies: - browserslist: 4.24.5 - caniuse-lite: 1.0.30001718 + browserslist: 4.28.2 + caniuse-lite: 1.0.30001792 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -6905,13 +6863,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.5: - dependencies: - caniuse-lite: 1.0.30001718 - electron-to-chromium: 1.5.155 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.5) - browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.29 @@ -6962,8 +6913,6 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001718: {} - caniuse-lite@1.0.30001792: {} ccount@2.0.1: {} @@ -7489,8 +7438,6 @@ snapshots: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.155: {} - electron-to-chromium@1.5.356: {} emoji-regex@10.4.0: {} @@ -7965,10 +7912,6 @@ snapshots: dependencies: format: 0.2.2 - fdir@6.4.4(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -9003,8 +8946,6 @@ snapshots: node-fetch-native@1.6.6: {} - node-releases@2.0.19: {} - node-releases@2.0.44: {} normalize-path@3.0.0: {} @@ -9713,11 +9654,6 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 - tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -9840,17 +9776,11 @@ snapshots: unplugin@1.16.1: dependencies: - acorn: 8.14.1 + acorn: 8.16.0 webpack-virtual-modules: 0.6.2 upath@2.0.1: {} - update-browserslist-db@1.1.3(browserslist@4.24.5): - dependencies: - browserslist: 4.24.5 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -9902,11 +9832,11 @@ snapshots: vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(sass-embedded@1.87.0)(yaml@2.9.0): dependencies: esbuild: 0.25.4 - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.3 rollup: 4.41.0 - tinyglobby: 0.2.13 + tinyglobby: 0.2.16 optionalDependencies: '@types/node': 22.15.18 fsevents: 2.3.3 diff --git a/scripts/core/build-image.js b/scripts/core/build-image.js index 078e3308c..deddfae46 100755 --- a/scripts/core/build-image.js +++ b/scripts/core/build-image.js @@ -25,7 +25,7 @@ export async function buildImageMain() { imageName: realImageName, buildArgs: [ // 参数中是否包含 --proxy - ['NEED_PROXY_BUILD', VipNodeJS.getProcessArgv().includes('--proxy')], + ['NEED_PROXY', VipNodeJS.getProcessArgv().includes('--proxy')], ['APP_NAME', name], ['APP_VERSION', version], ['APP_DESCRIPTION', description], From 93b8f97638bac05ee239cd6bd7a44d097800906b Mon Sep 17 00:00:00 2001 From: chufan Date: Tue, 4 Aug 2026 00:09:58 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=A6=96=E9=A1=B5?= =?UTF-8?q?`git`=E4=BB=93=E5=BA=93=E6=8F=90=E4=BA=A4=E6=8A=98=E7=BA=BF?= =?UTF-8?q?=E5=9B=BE=E6=98=BE=E7=A4=BA=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 723c4bc87..ab0eff0fd 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,11 @@ Nuxt.js是在vue框架上进行封装的,主要是用来解决单体页面的 ## 趋势
- Star History Chart + Star History
## 联系作者 From a37eaeb557e6bd355462f1e5bd90a30f926a590f Mon Sep 17 00:00:00 2001 From: chufan Date: Tue, 22 Sep 2026 10:44:30 +0800 Subject: [PATCH 3/9] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=20Agent=20Skills=20=E4=B8=8E=20codegraph=20=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/agent-skills.json | 10 + .agents/skills/code-dev/SKILL.md | 492 +++++++++++++++++++++++++++++ .agents/skills/commit/SKILL.md | 211 +++++++++++++ .agents/skills/self-check/SKILL.md | 258 +++++++++++++++ .agents/skills/workflow/SKILL.md | 143 +++++++++ .codegraph/.gitignore | 5 + 6 files changed, 1119 insertions(+) create mode 100644 .agents/skills/agent-skills.json create mode 100644 .agents/skills/code-dev/SKILL.md create mode 100644 .agents/skills/commit/SKILL.md create mode 100644 .agents/skills/self-check/SKILL.md create mode 100644 .agents/skills/workflow/SKILL.md create mode 100644 .codegraph/.gitignore diff --git a/.agents/skills/agent-skills.json b/.agents/skills/agent-skills.json new file mode 100644 index 000000000..97c82c78a --- /dev/null +++ b/.agents/skills/agent-skills.json @@ -0,0 +1,10 @@ +{ + "package": "@142vip/agent-skills", + "version": "0.0.1-alpha.6", + "skills": [ + "workflow", + "code-dev", + "self-check", + "commit" + ] +} diff --git a/.agents/skills/code-dev/SKILL.md b/.agents/skills/code-dev/SKILL.md new file mode 100644 index 000000000..2ad68c450 --- /dev/null +++ b/.agents/skills/code-dev/SKILL.md @@ -0,0 +1,492 @@ +--- +name: agent-code-dev +description: 跨项目高质量代码生成规范(code-dev)。当用户要求实现功能、修复 Bug、重构代码、编写组件或接口、生成代码片段时使用。覆盖命名与常量、函数与 SOLID、类型纪律(禁止 any、慎用 unknown、尽最大努力声明类型)、Nest/HTTP 分层、数据库、注释与日志、最小改动、下游禁止手改通用 skill 镜像。项目栈与包名以仓库 AGENTS.md 为准。按意图触发。 +--- + +# 代码开发规范(通用核心) + +## 目标 + +生成正确、简洁、风格一致的高质量代码。类型准确、注释清晰,改完可通过局部 lint 与受影响 build。 + +**本 skill 不绑定任何业务仓库路径或私有 npm 包名。** 日期库、HTTP 工具、状态码枚举、应用目录等以下游项目根目录 `AGENTS.md`(及可选 `.agents/project/stack.md`)为准。若本仓依赖 `@142vip/utils` 等共享工具,**优先复用**,禁止重复造轮子。 + +## 触发场景 + +- 实现新功能、新接口、新页面、新组件 +- 修复 Bug / 重构 / 编写 TypeScript · Vue · NestJS(或本仓等价栈) +- 生成工具函数、类型定义、常量与枚举 + +## 前置:确认改动落点 + +1. 读本仓 `AGENTS.md` 的应用 / 包边界 +2. 不确定目录时:打开应用 README + 同模块 3~5 个现有文件对齐风格 +3. 若本仓提供「业务落点」类本地 skill,可先加载;没有也不阻塞 +4. 工具侧怎么搜/改文件:见该工具薄入口;本 skill 不写 IDE 专属命令 + +## 核心原则 + +1. **正确优先**:逻辑清晰、类型准确、边界合理 +2. **最小改动**:每行变更须能追溯到用户需求;不顺手「优化」相邻无关代码 +3. **风格一致**:匹配周边命名、结构、注释语言 +4. **注释清晰**:解释「为什么」与模块职责;禁止无信息量废话,也禁止关键逻辑零注释 +5. **改完自检**:同一轮完成局部 lint + 受影响 build(见 `self-check`) +6. **KISS**:避免过早优化;简单可维护优先 + +## 进程与端口管理(强制,对所有 agent 生效) + +> 大模型在执行 dev / docs / preview / task-runner 等需要启动本地服务(vite、nest、jest、playwright 等)的任务时,**必须**遵守以下端口与进程纪律,避免工作区被占满 / 端口冲突 / 残留进程污染后续调试。 + +1. **首选应用指定的端口号**:从项目约定(`package.json` 的 `dev` / `start` 脚本、`vite.config`、`.env*`、根 `scripts/` 等)读取**已声明**的目标端口(如脚本 `vite --port 9527` 或 `vite.config.ts` 的 `server.port`)。 +2. **端口被占 → 复用现有实例**: + - 启动前用 `lsof -nP -iTCP: -sTCP:LISTEN` 探测;若已有进程在 LISTEN(且 PID 仍存活),**直接复用,不另起新进程**。 + - `lsof` 仅返回 LISTEN 记录不代表进程真活着(kernel 可能已回收 socket 元信息)——必须用 `curl -m 3 -o /dev/null -w "%{http_code}" http://127.0.0.1:/` 或 `nc -zv` 真实探活。 +3. **不擅自偏移端口**:不要在 `Vite` / `next dev` 等框架自动"尝试下一个端口"时盲目接受新端口。如果目标端口被占且无响应,先停掉占用进程(或让用户确认),再启动;不要在原工作区同时存在 9527 / 9528 两个 dev 实例。 +4. **后台进程必须收尾**: + - 使用 `Bash` `run_in_background: true` 启动的 dev / build watcher / 长连接服务,**任务回复前必须**调用 `TaskStop` 或 `pkill -f ` 关闭;保留进程会让下一次任务的端口探测 / 日志查看 / 资源占用都受影响。 + - 对于非后台的一次性命令(`pnpm build`、`pnpm typecheck` 等),执行完毕即结束,不留进程。 +5. **禁止跨任务占着不释放**:一个任务链中已开的后台 dev 服务,下一个无关任务**不能继续依赖**;必要时先 `TaskStop` 旧实例再起新实例,避免孤儿进程累积。 +6. **改动涉及 dev / preview / docker-compose 端口**:在 `package.json`、`vite.config.ts`、`nest-cli.json` 等调整端口前,**先与本仓 README / AGENTS.md 核对**,避免改完无人更新文档导致团队成员启动失败。 + +**反例**(被叫作"瞎搞"): + +- ❌ 端口 9527 被占就开心地"那我用 9528 吧"——工作区出现两个 vite 实例 +- ❌ `pnpm dev` 跑后台 5 个小时后才发现,每次重启电脑都报"port in use" +- ❌ 改了 `vite.config.ts` 的 `server.port` 没同步 `README.md` 启动说明 +- ❌ 探测端口用 `lsof` 一眼就过,不做 `curl`/`nc` 真实探活,结果 socket 在但服务已挂 + +**正例**: + +- ✅ 启动前 `lsof + curl` 双向探活;端口可用 → 启动;端口被占且真实在用 → 复用,不另起 +- ✅ 后台 dev 用 `TaskStop` 或保存 `task_id`,任务结束主动关 +- ✅ 改端口前看 `README.md` / `AGENTS.md`,改完同步文档 + +## 业务命名同步(强制) + +业务职责变了,**同一任务内**改齐标识,禁止只改 UI 标题: + +| 对象 | 要求 | +|------|------| +| 目录 / 文件 | 落在真实业务域目录 | +| 路由 name/path、组件 name | 与菜单 / 导航职责一致 | +| 变量 / 类型 / 注释 | 勿混用无关域的旧名 | + +--- + +## 变量与命名 + +1. **可发音、有意义**的名字;禁止单字母心理映射(循环用 `location` 而非 `l`) +2. **同一概念用同一词汇**(User / Account 勿混用) +3. **命名贴业务,禁止空壳词**:慎用 / 默认不要用 `result`、`data`、`info`、`item`、`temp`、`obj`、`val`、`ret`、`res`(HTTP Response 等业界固定缩写除外)。 + 名称须能回答「这是什么业务对象」: + `syncOutcome` / `driftPaths` / `userProfile` / `orderList`,而不是 `result` / `data` / `list` +4. **禁止魔法值**:常量进模块 `constants.ts` 或类顶部;模版字符串可封装为函数 + `export const formatUserLabel = (name: string) => \`name is ${name}\`` +5. **常量数组**:直接字面量 + `as const`,**不要** `Object.freeze([...])`(啰嗦无收益) + `export const CORE_SKILL_NAMES = ['code-dev', 'self-check', 'commit'] as const` +6. **包名 / 版本**:从本包 `package.json` 读取(如 `VipPackageJSON.getPackageJSON` / `getCurrentVersion`),禁止源码里散落 `'@scope/name'` 硬编码 +7. **枚举**:用 `enum`,**显式字面量**(`= 0` / `= 1` 或字符串),禁止「裸常量 + 注释」冒充枚举 +8. **命名约定** + +| 种类 | 约定 | +|------|------| +| 简单常量 | `SCREAMING_SNAKE_CASE` | +| 枚举名 | `PascalCase`;枚举成员 `SCREAMING_SNAKE_CASE` | +| 类 / 接口 / 类型 | `PascalCase` | +| 方法 / 函数 / 变量 | `camelCase` | +| 未使用的局部 | 前缀 `_`(如 `_unused`) | + +9. **解释变量**:复杂表达式先拆**带业务含义**的中间量再使用(`matchedUser`、`isQuotaExceeded`),不要拆成 `tmp` / `result` +10. **默认参数**优于 `x \|\| default` / 三元短路赋默认 +11. **配置优于环境变量散落读取**:业务默认值、开关优先配置模块;`process.env` 仅边界注入并集中映射(见本仓配置约定) +12. **Node 文件/路径**:优先本仓共享工具包,**直接调用**(如 `@142vip/utils` 的 `VipNodeJS.pathJoin` / `pathResolve` / `pathDirname` / `existPath` / `writeFileByUTF8`);工具未覆盖的再使用 `node:fs`(如 `recursive` mkdir、`copyFileSync`、二进制读) +13. **禁止业务路径 `new Date()`**:时间戳 / 格式化 / 过期判断统一用本仓时间工具(如 `vipDayjs`:`getCurrentTimestamp`、`formatToISOStr`、`formatDateToStr`、`isBeforeByTtl`);无共享工具时集中封装,勿散落 +14. **布尔不要中途 `Boolean(x)` 洗一遍**:在类型、默认参数、解构默认、CLI option 默认值、入参校验等**边界前置收口**;函数体内直接用已确定的 `boolean` + +```ts +// ❌ 心理映射 + 空壳命名 + 短路默认 + Boolean 洗类型 + new Date +locations.forEach(l => dispatch(l)) +const result = await load() +const dryRun = Boolean(options.dryRun) +function create(name) { const n = name || 'default' } +const syncedAt = new Date().toISOString() + +// ✅ 业务语义清晰 + 默认参数 + vipDayjs +locations.forEach(location => dispatch(location)) +const userProfile = await loadUserProfile() +const { dryRun = false } = options +function create(name = 'default') { /* ... */ } +const syncedAt = vipDayjs.formatToISOStr() +``` + +--- + +## 类型纪律(强制) + +| 规则 | 要求 | +|------|------| +| **禁止 `any`** | 触及与新增代码一律不用;无「临时 any / 先写完再改」例外 | +| **慎用 `unknown`** | 仅外部边界(HTTP / JSON / Storage 入参)且**同一函数内**立刻用守卫收窄;禁止领域层、props、返回值长期停在 `unknown` | +| **尽最大努力声明类型** | 优先 `interface` / 联合 / `enum` / 泛型;不确定则读同模块既有类型并对齐,禁止用 `any`/`unknown` 偷懒 | +| **少用 `as`** | 仅边界单点并注释原因;领域层用 `instanceof` / 库守卫 / 具名 type guard | +| **风格统一** | 类型命名、文件放置、导出粒度与同模块现有代码一致 | + +- 运行时校验只在边界:HTTP DTO、JSON normalize、Storage +- **禁止无效数组展开**:已是数组禁止 `[...arr]` 仅为传参或再 `.filter`/`.map`;防 sort 突变用 `toSorted`/`toReversed`;Iterable→数组用 `Array.from`;必要隔离拷贝用 `.slice()` 并注释 +- **禁止无意义 `String(` / `Number(` / `Boolean(`**:已知标量类型直接用;边界一次 normalize / DTO Transform +- `interface`:按作用域放模块 `*.interface.ts` 或文件顶部;≥2 处引用再独立文件并 export +- **导出命名空间克制**:域内类型不要泄漏到无关上层(如 User 表的 `UserType` 不要挂到 Database 根导出) +- 禁止零信息量 `export type A = B` 完全等价别名 + +```ts +// ✅ 边界 unknown + 守卫(优先本仓已有守卫);领域层勿长期停在 unknown +function parseExternalData(raw: unknown): ParsedData { + if (!isJsonRecord(raw)) + throw new Error('Invalid data') + const name = typeof raw.name === 'string' && raw.name.trim() ? raw.name.trim() : undefined + return { name } +} + +// ❌ 无效展开 +const sorted = [...items].sort(cmp) +items.filter(fn) // 已是数组却先 [...items].filter + +// ✅ +const sorted = items.toSorted(cmp) +Array.from(someSet).toSorted() +``` + +--- + +## 函数 + +1. **只做一件事**;名称完整说明作用(动词 + 业务对象,如 `collectSkillDrifts`,而非 `doWork` / `handle`) +2. **参数宜 ≤2**;再多则收拢为 options 对象 +3. 类方法显式 `public` / `private` / `protected` +4. **避免否定条件**主导分支;复杂/重复判断抽成具名谓词函数 +5. 调用者写在被调用者**上方**(报纸顺序:自上而下读) +6. 能纯则纯;副作用边界集中 +7. **禁止有害内联 await**(实参/返回值/对象字面量里套 `(await …)`) +8. **禁止无意义一层包装**:函数体若只是 `return lib.fn(...args)` 或等价透传,**直接调 lib**,不要再套 `pathJoin` / `pathResolve` 这类本地别名。 + 封装门槛:至少具备**其一**——组合多步、补全 utils 缺口(如 recursive mkdir)、统一错误/日志边界、跨多处复用的真实业务规则。注释写清「为何存在」。 +9. **可直接 `return { ... }` 就不要再造 `buildXxxResult` 薄工厂**(字段组装不复杂时) + +```ts +// ❌ 无意义包装 +export function pathJoin(...parts: string[]) { + return VipNodeJS.pathJoin(...parts) +} + +// ✅ 直接调用 +VipNodeJS.pathJoin(root, 'skills') + +// ✅ 有真实组合逻辑才封装 +export function copyFile(fromPath: string, toPath: string): void { + ensureDir(VipNodeJS.pathDirname(toPath)) + fs.copyFileSync(fromPath, toPath) +} + +// ✅ 可选布尔:默认参数 / 解构默认收口 +function run(options: { dryRun?: boolean, check?: boolean }) { + const { dryRun = false, check = false } = options + if (check && dryRun) + throw new Error('mutually exclusive') +} + +// ❌ 中途 Boolean 洗类型(无信息量、易掩盖 undefined) +const dryRun = Boolean(options.dryRun) +const force = Boolean(cliOptions.force) + +// ✅ 时间:vipDayjs(禁止 new Date()) +syncedAt: vipDayjs.formatToISOStr() +const nowMs = vipDayjs.getCurrentTimestamp() + +// ❌ +syncedAt: new Date().toISOString() + +// ✅ await 先赋业务名;return 直接对象字面量 +const accessToken = await this.getAccessToken() +const orderDetail = await this.fetchOrderDetail(accessToken) +return { orderDetail } + +// ❌ 内联 await + 空壳 result + 无意义 buildResult +return buildSyncResult({ result: await this.fetchData(await this.getToken()) }) +``` + +--- + +## 对象与数据结构 + +- 需要字段子集时用 `pick` / `omit`(若本仓有 `vipLodash` 等),避免多重解构撞名 +- 默认**拷贝**再改;大对象且明确只读共享时才传引用 +- 优先对象展开 / `Object.assign` 浅拷贝,避免就地乱改共享引用 +- 同一逻辑路径避免对同一可变对象建多个别名引用后分头修改 + +--- + +## 类与 SOLID + +- **类名 = 文件名**,模块/文件夹名一致 +- 需要链式 API 时,变异方法末尾 `return this` +- **S** 单一职责 · **O** 对扩展开放、对修改关闭 · **L** 子类可替换父类 + **I** 多小接口优于胖接口 · **D** 依赖抽象,高层不绑死低层细节 +- 避免过早抽象与过度设计 + +--- + +## 后端(Nest 类等) + +- Controller:HTTP / DTO·VO / 状态码;**不**把胖业务塞进 Controller +- Service:领域逻辑;**避免**直接碰 Req/Res;**避免**直接抛 HTTP 专用异常(交给 Controller 映射),以利复用 +- 构造函数 DI 参数分行;装饰器单独一行 +- 返回 `Promise` 的方法须 `async` 并 `await`;无意义 `return await` 服从本仓 ESLint +- 接口方法名与路由语义一致;淘汰接口标 `@Deprecated()`(或本仓等价) +- 入参/出参用 DTO/VO,便于文档;HTTP 状态码用本仓枚举(如 `HttpStatus`),禁止裸 `401` +- 缓存 Key、配置目录等:**读 `AGENTS.md`**,本 skill 不写死前缀 + +```ts +class ExampleService { + constructor( + private readonly repo: ExampleRepository, + ) {} + + public async getDetail(id: string): Promise { + return await this.repo.findOneById(id) + } + + private buildQuery(params: QueryParams): FindOptions { + return {} + } +} +``` + +--- + +## 前端(Vue / Nuxt 类) + +- 优先 `