@wdyy/skills 0.1.23 → 0.1.24
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/.well-known/skills/index.json +3 -3
- package/.well-known/skills/wdyy-database-standard/SKILL.md +12 -8
- package/.well-known/skills/wdyy-database-standard/reference/database-rules.md +7 -0
- package/.well-known/skills/wdyy-database-standard/scripts/validate-table-design.mjs +7 -0
- package/.well-known/skills/wdyy-database-standard/scripts/validate-table-design.test.mjs +24 -0
- package/.well-known/skills/wdyy-database-standard/templates/table-design.template.md +11 -0
- package/.well-known/skills/wdyy-logging-standard/SKILL.md +33 -45
- package/.well-known/skills/wdyy-logging-standard/agents/openai.yaml +2 -2
- package/.well-known/skills/wdyy-logging-standard/reference/logging-rules.md +51 -14
- package/.well-known/skills/wdyy-logging-standard/scripts/validate-log-entry.mjs +66 -11
- package/.well-known/skills/wdyy-logging-standard/scripts/validate-log-entry.test.mjs +395 -119
- package/.well-known/skills/wdyy-logging-standard/templates/frontend-error-report.template.ts +42 -12
- package/.well-known/skills/wdyy-logging-standard/templates/logger.template.ts +437 -22
- package/.well-known/skills/wdyy-logging-standard/templates/nestjs-http-logging.middleware.template.ts +68 -0
- package/.well-known/skills/wdyy-logging-standard/templates/security-audit-logger.template.ts +61 -0
- package/README.md +20 -7
- package/lib/wdyy-cli.js +231 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,22 +16,34 @@ OpenSpec 单项变更的需求、设计、任务、实
|
|
|
16
16
|
|---|---|
|
|
17
17
|
| `wdyy-database-standard` | PostgreSQL 18 数据库设计、版本化迁移、数据兼容性与 DDL 校验。 |
|
|
18
18
|
| `wdyy-bug-record` | 手动汇总当前任务已解决的问题,并按模板追加至 `docs/bug_record.md`。 |
|
|
19
|
-
| `wdyy-logging-standard` |
|
|
19
|
+
| `wdyy-logging-standard` | 访问、应用、安全与审计日志,递归敏感字段净化、W3C trace、前端异常上报、轮转与哈希链校验。 |
|
|
20
20
|
| `wdyy-deployment-standard` | 在 Linux、macOS Docker Desktop 或 Windows Docker Desktop + WSL2/Linux 容器模式中,使用本地基础镜像构建前后端独立镜像,分别导出并统一压缩交付,远程数据库直连并一键部署整个项目栈。 |
|
|
21
21
|
| `wdyy-ui` | 统一医院信息系统页面的布局、清新生命绿配色、医院 Logo、常用组件和响应式行为,不定义业务流程、接口或数据模型。 |
|
|
22
22
|
|
|
23
23
|
## 使用方式
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
安装全局包后,先由工程师手工安装或更新 OpenSpec、公共 Skill 与 CodeGraph,再在目标项目根目录执行初始化:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
npm install -g @wdyy/skills
|
|
29
|
+
npm install -g @fission-ai/openspec@latest
|
|
30
|
+
npx skills@latest add mattpocock/skills -g --skill grill-me grill-with-docs prototype frontend-design research tdd codebase-design
|
|
31
|
+
npx skills@latest add tt-a1i/archify -g
|
|
32
|
+
npm i -g @colbymchenry/codegraph
|
|
29
33
|
wdyy init
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
`wdyy init`
|
|
36
|
+
`wdyy init` 启动时会再次显示上述外部依赖安装命令,但不会自动执行 npm 或 npx。命令在写入前集中检查 Node.js 24、pnpm 12.1.0、Git、OpenSpec、CodeGraph,以及规则强制使用的公共 Skill;任一项缺失或版本不符时一次性报告全部问题并退出。
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
预检通过后,`wdyy init` 会在系统用户目录 `.agents/skills/` 安装或更新 5 个受管理 Skill,并删除其中旧的 `wdyy-api-standard`。它还会初始化 Git、默认目录、环境模板、BUG 模板、OpenSpec 与 CodeGraph。已有环境文件和 BUG 记录保持不变;已有 `AGENTS.md` 时生成 `AGENTS_new.md`。全部步骤成功后生成 `.wdyy/toolchain.json`,记录初始化器与工具版本、实际规则文件以及公共和企业 Skill 的 SHA-256;清单不含用户名、凭据或绝对主目录。
|
|
39
|
+
|
|
40
|
+
生成规则负责项目边界与 Skill 路由:`grill-me` 澄清需求和验收标准,`grill-with-docs` 记录 PRD、术语与 ADR;文档沉淀完成后先使用 `openspec-explore` 探索方案、约束与风险,再使用 `openspec-propose` 形成可评审计划并进入实施流程。数据库、日志、部署和 UI 的执行细节由对应 Skill 唯一维护。自建接口由 Agent 根据已确认需求生成 OpenAPI 3.1 YAML 并交工程师验收;第三方接口只依据工程师提供的契约或官方文档。
|
|
41
|
+
|
|
42
|
+
初始化后在项目根目录执行以下只读验收;命令集中检查初始化产物、工具版本、Skill 和工具链清单漂移,不会自动修复文件:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
wdyy validate
|
|
46
|
+
```
|
|
35
47
|
|
|
36
48
|
生成的项目规则还要求:每个经确认的变更完成并通过全部测试与验证后创建对应 Git commit,不得提交未完成或验证失败的状态;每个变更依据对应 OpenSpec 的需求、场景和任务编写或更新相关测试,交付前必须解决全部测试与验证失败,不得跳过或隐瞒。既有项目重新执行 `wdyy init` 后,可从新生成的 `AGENTS_new.md` 审阅并合并该规则。
|
|
37
49
|
|
|
@@ -50,7 +62,7 @@ wdyy update-skill --all
|
|
|
50
62
|
|
|
51
63
|
部署 Skill 生成的标准流程为:在 Linux、macOS Docker Desktop,或 Windows Docker Desktop + WSL2/Linux 文件系统的项目根目录执行 `./deploy.sh build`,只使用本地已有的 Node、Nginx 基础镜像构建 `<项目名>_frontend:latest` 与 `<项目名>_backend:latest`,分别导出两个镜像 tar,并统一封装为 `deploy/<项目名>-docker.tar.gz`。工程师人工复制并解压该压缩包,在受支持的 Linux engine 上无参数执行 `./deploy.sh` 即校验、加载并整体部署前后端;macOS/arm64 Docker Desktop 可通过禁止拉取的实际目标镜像探针验证并运行 `linux/amd64` 包,而不是仅按 Server 原生架构拒绝。另可执行 `./deploy.sh stop` 和 `./deploy.sh status`。数据库通过 `.env` 直连远程 PostgreSQL,不生成数据库容器;前后端的宿主机与容器端口都来自 `.env`,容器名与镜像 repository 相同,使用 `unless-stopped`,并验证 24 小时制 CST(UTC+8);日志直接保存在部署根 `logs/`。不支持原生 PowerShell 或 Windows 容器模式。
|
|
52
64
|
|
|
53
|
-
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive`。涉及数据库、日志、部署或医院信息系统 UI 时,按 `AGENTS.md` 的路由加载相应企业 Skill
|
|
65
|
+
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive`。涉及数据库、日志、部署或医院信息系统 UI 时,按 `AGENTS.md` 的路由加载相应企业 Skill。自建接口由 Agent 生成 OpenAPI 3.1 YAML 并交工程师验收,第三方接口依据工程师提供的契约或官方文档。所有 URL、端口和 IP 地址从 `.env` 读取,不得硬编码;不得提交秘密或生产环境 `.env`。项目完成后创建 `README.md`,说明技术栈、目录结构和安装部署。
|
|
54
66
|
|
|
55
67
|
## 固定技术栈
|
|
56
68
|
|
|
@@ -71,7 +83,7 @@ wdyy update-skill --all
|
|
|
71
83
|
|
|
72
84
|
### 使用发布脚本
|
|
73
85
|
|
|
74
|
-
推荐从项目根目录手动运行 `release_npm.sh
|
|
86
|
+
推荐从项目根目录手动运行 `release_npm.sh`。脚本会检查工作区、组织权限、完整测试、lint 与打包内容,自动执行 `npm version <patch|minor|major>`、`npm publish --access public`;发布成功后等待 15 秒,再一次查询刚发布的确切版本进行 registry 核验。它不会执行登录、浏览器认证或处理认证凭据。
|
|
75
87
|
|
|
76
88
|
首次运行或登录失效时,先手工登录;脚本检测到未登录也会提示并退出:
|
|
77
89
|
|
|
@@ -116,10 +128,11 @@ npm version patch # 修复或小幅调整
|
|
|
116
128
|
```bash
|
|
117
129
|
npm test
|
|
118
130
|
npm run lint
|
|
119
|
-
node --test $(find .well-known/skills -path '*/scripts/*.test.mjs' -print)
|
|
120
131
|
npm pack --dry-run
|
|
121
132
|
```
|
|
122
133
|
|
|
134
|
+
`npm test` 会通过索引聚合器执行全部受管理 Skill 声明的测试,新增 Skill 测试无需维护 shell 文件发现命令。
|
|
135
|
+
|
|
123
136
|
其中 `npm pack --dry-run` 用于确认包内容;应包含 `bin`、`lib`、`.well-known/skills` 与 `README.md`,且不应包含本地环境文件或其他无关文件。
|
|
124
137
|
|
|
125
138
|
### 3. 登录并核对组织权限
|
package/lib/wdyy-cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { access, appendFile, cp, lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
5
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
7
8
|
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
@@ -14,6 +15,26 @@ const managedSkillNames = [
|
|
|
14
15
|
];
|
|
15
16
|
|
|
16
17
|
const removedManagedSkillNames = ['wdyy-api-standard'];
|
|
18
|
+
const packageMetadata = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'));
|
|
19
|
+
const requiredNodeMajor = Number(packageMetadata.engines.node.match(/\d+/)?.[0]);
|
|
20
|
+
const requiredPnpmVersion = packageMetadata.engines.pnpm;
|
|
21
|
+
const requiredPublicSkillNames = [
|
|
22
|
+
'grill-me',
|
|
23
|
+
'grill-with-docs',
|
|
24
|
+
'prototype',
|
|
25
|
+
'frontend-design',
|
|
26
|
+
'research',
|
|
27
|
+
'tdd',
|
|
28
|
+
'codebase-design',
|
|
29
|
+
'archify'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const manualPrerequisiteCommands = [
|
|
33
|
+
'npm install -g @fission-ai/openspec@latest',
|
|
34
|
+
'npx skills@latest add mattpocock/skills -g --skill grill-me grill-with-docs prototype frontend-design research tdd codebase-design',
|
|
35
|
+
'npx skills@latest add tt-a1i/archify -g',
|
|
36
|
+
'npm i -g @colbymchenry/codegraph'
|
|
37
|
+
];
|
|
17
38
|
|
|
18
39
|
const defaultProjectDirectories = [
|
|
19
40
|
'src/frontend',
|
|
@@ -59,9 +80,13 @@ DB_SCHEMA=api_platform
|
|
|
59
80
|
|
|
60
81
|
const defaultProjectLayout = [...defaultProjectDirectories, '.env.example'];
|
|
61
82
|
|
|
62
|
-
const generatedAgentsContent = `#
|
|
83
|
+
const generatedAgentsContent = `# 项目开发规则
|
|
84
|
+
|
|
85
|
+
本项目使用 OpenSpec 及企业 Skill 统一开发规则与流程。所有规则面向 agent 执行;规则未覆盖或不明确时,先询问工程师,不得猜测。
|
|
86
|
+
|
|
87
|
+
## 固定时区
|
|
63
88
|
|
|
64
|
-
|
|
89
|
+
- 全链路统一使用 \`TZ=Asia/Shanghai\` 作为时间基准:日志时间戳、数据库时间、前端时间展示、部署脚本时间验证都必须基于 \`Asia/Shanghai\`,禁止混用其他时区。
|
|
65
90
|
|
|
66
91
|
## 固定技术栈
|
|
67
92
|
|
|
@@ -76,39 +101,127 @@ const generatedAgentsContent = `# 企业开发规则
|
|
|
76
101
|
|
|
77
102
|
\`.env.example\` 是配置模板。需要本地配置时,必须先手工复制 \`.env.example\` 为 \`.env\`;后续修改配置文件时,必须先修改 \`.env.example\`,再同步修改 \`.env\`。不得将真实密码、密钥或生产配置写入 \`.env.example\`。
|
|
78
103
|
|
|
79
|
-
项目开发完成后必须创建 \`README.md\`,内容包括项目技术栈、目录结构和安装部署说明。
|
|
80
|
-
|
|
81
104
|
## Skill 路由
|
|
82
105
|
|
|
83
|
-
| 场景 | 必须使用的 Skill |
|
|
106
|
+
| 阶段 / 场景 | 必须使用的 Skill |
|
|
84
107
|
|---|---|
|
|
108
|
+
| 需求澄清 | 使用 \`grill-me\` 消除需求、边界与验收标准中的歧义 |
|
|
109
|
+
| PRD 与决策记录 | 使用 \`grill-with-docs\` 记录 PRD、术语和架构决策 |
|
|
110
|
+
| 方案探索 | \`grill-with-docs\` 完成后,使用 \`openspec-explore\` 探索方案、约束与风险 |
|
|
111
|
+
| 变更规划与实施 | 使用 \`openspec-propose\` 生成可评审计划,确认后使用对应 OpenSpec 实施与归档流程 |
|
|
112
|
+
| 原型设计 | \`prototype\` |
|
|
113
|
+
| 通用前端视觉与交互设计 | \`frontend-design\` |
|
|
114
|
+
| 医院信息系统页面的新建、改造或审查 | \`wdyy-ui\`;不负责定义业务流程、接口或数据模型 |
|
|
115
|
+
| 开发过程 | 按需使用 \`research\`、\`tdd\`、\`codebase-design\` |
|
|
116
|
+
| 架构图、时序图、流程图 | \`archify\`,产出放入 \`docs/\` |
|
|
85
117
|
| PostgreSQL 表、DDL、迁移、数据兼容性 | \`wdyy-database-standard\` |
|
|
86
|
-
|
|
|
87
|
-
| 结构化日志、traceId、完整原始入参、前端异常上报 | \`wdyy-logging-standard\` |
|
|
118
|
+
| 访问、应用、安全与审计日志、W3C trace、净化后的完整业务参数、前端异常上报 | \`wdyy-logging-standard\` |
|
|
88
119
|
| 统一 Docker 交付、Linux/Mac/Windows WSL2 双镜像、远程数据库与整栈一键部署 | \`wdyy-deployment-standard\` |
|
|
89
|
-
|
|
|
120
|
+
| 汇总当前任务已解决的 BUG、漏洞或运行异常 | 手动调用 \`wdyy-bug-record\`,追加写入 \`docs/bug_record.md\` |
|
|
121
|
+
|
|
122
|
+
## 文档与目录约定
|
|
123
|
+
|
|
124
|
+
- 项目开发完成后必须创建 \`README.md\`,内容至少包括:项目简介、目录结构、技术栈、本地测试流程、部署流程、使用的数据库。
|
|
125
|
+
- 所有数据库文档统一放置在 \`database/\` 目录。
|
|
126
|
+
- 架构图、时序图、流程图等其它文档统一放置在 \`docs/\` 目录。
|
|
90
127
|
|
|
91
128
|
## 通用边界
|
|
92
129
|
|
|
130
|
+
- 所有需求必须先完成需求澄清和验收标准确认,再形成 PRD 与必要决策记录;\`grill-with-docs\` 完成后必须先使用 \`openspec-explore\` 探索方案,再使用 \`openspec-propose\` 形成可评审规划。涉及业务、接口、数据库、部署或不可逆决定时,实施前必须由工程师确认规划产物。
|
|
93
131
|
- 项目根目录应提供真实可执行的 \`pnpm dev\`、\`pnpm build\`、\`pnpm test\`、\`pnpm lint\` 命令;\`pnpm dev\` 只负责前后端的启动,不得启动或管理数据库。
|
|
94
132
|
- 生成的接口文档必须符合 OpenAPI 3.1 规范,并声明 \`openapi: 3.1.0\`。
|
|
133
|
+
- 自建接口由 Agent 根据已确认需求生成 OpenAPI 3.1 YAML 契约,经工程师验收后实施。第三方接口仅依据工程师提供的契约或官方接口文档;来源缺失或契约不完整时必须询问工程师,不得推测。
|
|
95
134
|
- 在下载或安装任何项目依赖前,必须先从官方发布渠道确认当前最新稳定版本,并检查该版本及其传递依赖的公开漏洞修复状态。原则上必须使用最新稳定版本;不得使用已过时的依赖,也不得使用存在未修复高危(High/Critical)漏洞的依赖。若最新稳定版本不兼容,或不存在满足安全要求的版本,必须停止安装并报告依赖名称、候选版本、兼容性及漏洞证据,等待工程师确认;不得擅自降级、忽略审计结果或添加豁免。
|
|
96
135
|
- 代码、配置、迁移、测试与部署文件必须以实际实现为准;不得用模板虚构已实现能力。
|
|
97
|
-
- 所有需求(如数据库变更、流程变更、功能变更、从零开发项目等),必须先使用\`openspec-explore\`技能确认需求,再根据实际情况,使用\`openspec\`的其它技能。
|
|
98
136
|
- 每个经确认的变更完成并通过全部测试与验证后,必须创建一个对应的 Git commit,以支持追踪和回滚;不得提交未完成或验证失败的状态。
|
|
99
137
|
- 每个变更都必须依据对应 OpenSpec 的需求、场景和任务编写或更新相关测试;交付前必须解决所有测试与验证失败,不得跳过或隐瞒失败。
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
138
|
+
- 日志实现必须使用 \`wdyy-logging-standard\`,并保持日志直接写入项目根目录 \`logs/\` 的项目边界。
|
|
139
|
+
- 生产交付必须使用 \`wdyy-deployment-standard\`,并保持前后端双镜像、远程数据库和整栈一键部署的项目边界。
|
|
140
|
+
- 不得静默吞错、跳过验证或以兜底逻辑掩盖未决问题。
|
|
103
141
|
`;
|
|
104
142
|
|
|
105
143
|
const bugRecordTemplate = '- 日期:yyyy-mm-dd\n- 问题描述:\n- 解决方法:\n';
|
|
106
144
|
|
|
107
145
|
function printHelp() {
|
|
108
146
|
console.log('用法: wdyy init');
|
|
109
|
-
console.log('
|
|
147
|
+
console.log('请先按提示手工安装外部依赖;命令将在当前项目生成 AGENTS 规则、安装 wdyy Skill,并执行 openspec init --tools agents 与 codegraph init。');
|
|
148
|
+
console.log('用法: wdyy validate');
|
|
149
|
+
console.log('只读验证当前项目的初始化产物、工具链和 Skill 完整性。');
|
|
110
150
|
console.log('用法: wdyy update-skill <skill-name> | --all');
|
|
111
|
-
console.log('
|
|
151
|
+
console.log('仅更新系统用户目录中的一个或全部受管理 wdyy Skill,不执行项目初始化。');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function printManualPrerequisites() {
|
|
155
|
+
console.log('请先手工执行以下安装命令:');
|
|
156
|
+
for (const command of manualPrerequisiteCommands) console.log(command);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function inspectCommand(command, displayName) {
|
|
160
|
+
const result = spawnSync(command, ['--version'], { encoding: 'utf8', timeout: 10_000 });
|
|
161
|
+
if (result.error || result.status !== 0) {
|
|
162
|
+
const detail = result.error?.message ?? `退出码 ${result.status ?? 1}`;
|
|
163
|
+
return { problem: `${displayName} 命令不可用: ${detail}` };
|
|
164
|
+
}
|
|
165
|
+
const version = `${result.stdout ?? ''}`.split(/\r?\n/).find((line) => line.trim())?.trim();
|
|
166
|
+
if (!version) return { problem: `${displayName} 未返回版本信息` };
|
|
167
|
+
return { version };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function inspectPrerequisites(skillsRoot = getGlobalSkillsRoot()) {
|
|
171
|
+
const problems = [];
|
|
172
|
+
const tools = { node: process.version };
|
|
173
|
+
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
174
|
+
if (!Number.isInteger(requiredNodeMajor) || nodeMajor < requiredNodeMajor) {
|
|
175
|
+
problems.push(`Node.js 版本不符合要求: 当前 ${process.version},要求 >=${requiredNodeMajor}`);
|
|
176
|
+
}
|
|
177
|
+
for (const [command, displayName] of [['pnpm', 'pnpm'], ['git', 'Git'], ['openspec', 'OpenSpec'], ['codegraph', 'CodeGraph']]) {
|
|
178
|
+
const inspection = inspectCommand(command, displayName);
|
|
179
|
+
if (inspection.problem) problems.push(inspection.problem);
|
|
180
|
+
else tools[command] = inspection.version;
|
|
181
|
+
}
|
|
182
|
+
if (tools.pnpm !== requiredPnpmVersion) {
|
|
183
|
+
problems.push(`pnpm 版本不符合要求: 当前 ${tools.pnpm ?? '不可用'},要求 ${requiredPnpmVersion}`);
|
|
184
|
+
}
|
|
185
|
+
for (const skillName of requiredPublicSkillNames) {
|
|
186
|
+
try {
|
|
187
|
+
await access(join(skillsRoot, skillName, 'SKILL.md'));
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (error.code !== 'ENOENT') throw error;
|
|
190
|
+
problems.push(`缺少公共 Skill: ${skillName}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return { problems, tools };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function reportProblems(problems) {
|
|
197
|
+
for (const problem of problems) console.error(`- ${problem}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function hashFile(path) {
|
|
201
|
+
return createHash('sha256').update(await readFile(path)).digest('hex');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function createToolchainManifest(targetRoot, agentsFile, tools, skillsRoot = getGlobalSkillsRoot()) {
|
|
205
|
+
const publicSkills = {};
|
|
206
|
+
for (const name of requiredPublicSkillNames) publicSkills[name] = await hashFile(join(skillsRoot, name, 'SKILL.md'));
|
|
207
|
+
const managedSkills = {};
|
|
208
|
+
for (const name of managedSkillNames) managedSkills[name] = await hashFile(join(skillsRoot, name, 'SKILL.md'));
|
|
209
|
+
return {
|
|
210
|
+
initializer: { name: packageMetadata.name, version: packageMetadata.version },
|
|
211
|
+
tools,
|
|
212
|
+
agentsFile,
|
|
213
|
+
skills: { public: publicSkills, managed: managedSkills }
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function writeToolchainManifest(targetRoot, agentsFile, tools, writtenPaths) {
|
|
218
|
+
const toolchainRoot = join(targetRoot, '.wdyy');
|
|
219
|
+
const manifestPath = join(toolchainRoot, 'toolchain.json');
|
|
220
|
+
await mkdir(toolchainRoot, { recursive: true });
|
|
221
|
+
const manifest = await createToolchainManifest(targetRoot, agentsFile, tools);
|
|
222
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
223
|
+
writtenPaths.push(manifestPath);
|
|
224
|
+
console.log(`已写入工具链清单: ${manifestPath}`);
|
|
112
225
|
}
|
|
113
226
|
|
|
114
227
|
async function installManagedSkills(skillsRoot, writtenPaths, skillNames = managedSkillNames) {
|
|
@@ -231,28 +344,27 @@ async function writeAgentsFile(targetRoot, writtenPaths) {
|
|
|
231
344
|
await writeFile(destination, generatedAgentsContent, 'utf8');
|
|
232
345
|
writtenPaths.push(destination);
|
|
233
346
|
console.log(`已写入项目规则: ${destination}`);
|
|
347
|
+
return basename(destination);
|
|
234
348
|
}
|
|
235
349
|
|
|
236
|
-
async function
|
|
350
|
+
async function createBugRecordTemplate(targetRoot, writtenPaths) {
|
|
237
351
|
const docsRoot = join(targetRoot, 'docs');
|
|
238
352
|
const bugRecordPath = join(docsRoot, 'bug_record.md');
|
|
239
353
|
await mkdir(docsRoot, { recursive: true });
|
|
240
|
-
|
|
241
|
-
let prefix = '';
|
|
242
354
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
355
|
+
await access(bugRecordPath);
|
|
356
|
+
console.log(`已保留 BUG 记录: ${bugRecordPath}`);
|
|
357
|
+
return;
|
|
245
358
|
} catch (error) {
|
|
246
359
|
if (error.code !== 'ENOENT') throw error;
|
|
247
360
|
}
|
|
248
|
-
|
|
249
|
-
await appendFile(bugRecordPath, `${prefix}${bugRecordTemplate}`, 'utf8');
|
|
361
|
+
await writeFile(bugRecordPath, bugRecordTemplate, 'utf8');
|
|
250
362
|
writtenPaths.push(bugRecordPath);
|
|
251
|
-
console.log(
|
|
363
|
+
console.log(`已创建 BUG 记录模板: ${bugRecordPath}`);
|
|
252
364
|
}
|
|
253
365
|
|
|
254
366
|
function initializeOpenSpec(targetRoot, writtenPaths) {
|
|
255
|
-
const result = spawnSync('openspec', ['init'], { cwd: targetRoot, stdio: 'inherit' });
|
|
367
|
+
const result = spawnSync('openspec', ['init', '--tools', 'agents'], { cwd: targetRoot, stdio: 'inherit' });
|
|
256
368
|
if (result.error || result.status !== 0) {
|
|
257
369
|
const detail = result.error?.message ?? `退出码 ${result.status ?? 1}`;
|
|
258
370
|
console.error(`OpenSpec 初始化失败: ${detail}`);
|
|
@@ -264,21 +376,105 @@ function initializeOpenSpec(targetRoot, writtenPaths) {
|
|
|
264
376
|
return 0;
|
|
265
377
|
}
|
|
266
378
|
|
|
379
|
+
function initializeCodeGraph(targetRoot, writtenPaths) {
|
|
380
|
+
const result = spawnSync('codegraph', ['init'], { cwd: targetRoot, stdio: 'inherit' });
|
|
381
|
+
if (result.error || result.status !== 0) {
|
|
382
|
+
const detail = result.error?.message ?? `退出码 ${result.status ?? 1}`;
|
|
383
|
+
console.error(`CodeGraph 初始化失败: ${detail}`);
|
|
384
|
+
printWrittenPaths(writtenPaths);
|
|
385
|
+
return result.status ?? 1;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log('CodeGraph 初始化完成。');
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function pathExists(path) {
|
|
393
|
+
try {
|
|
394
|
+
await access(path);
|
|
395
|
+
return true;
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (error.code === 'ENOENT') return false;
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function validateProject(targetRoot) {
|
|
403
|
+
const problems = [];
|
|
404
|
+
const skillsRoot = getGlobalSkillsRoot();
|
|
405
|
+
const prerequisites = await inspectPrerequisites(skillsRoot);
|
|
406
|
+
problems.push(...prerequisites.problems);
|
|
407
|
+
|
|
408
|
+
for (const path of ['.git', ...defaultProjectDirectories, '.env.example', 'docs/bug_record.md', 'openspec', '.codegraph']) {
|
|
409
|
+
if (!await pathExists(join(targetRoot, path))) problems.push(`缺少初始化产物: ${path}`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const gitIgnorePath = join(targetRoot, '.gitignore');
|
|
413
|
+
if (!await pathExists(gitIgnorePath)) {
|
|
414
|
+
problems.push('缺少初始化产物: .gitignore');
|
|
415
|
+
} else {
|
|
416
|
+
const entries = new Set((await readFile(gitIgnorePath, 'utf8')).split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean));
|
|
417
|
+
for (const entry of requiredGitIgnoreEntries) if (!entries.has(entry)) problems.push(`.gitignore 缺少规则: ${entry}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const manifestPath = join(targetRoot, '.wdyy', 'toolchain.json');
|
|
421
|
+
if (!await pathExists(manifestPath)) {
|
|
422
|
+
problems.push('缺少初始化产物: .wdyy/toolchain.json');
|
|
423
|
+
} else {
|
|
424
|
+
try {
|
|
425
|
+
const actualText = await readFile(manifestPath, 'utf8');
|
|
426
|
+
const actual = JSON.parse(actualText);
|
|
427
|
+
const agentsFile = actual.agentsFile;
|
|
428
|
+
if (!['AGENTS.md', 'AGENTS_new.md'].includes(agentsFile)) {
|
|
429
|
+
problems.push('工具链清单中的规则文件路径无效');
|
|
430
|
+
} else if (!await pathExists(join(targetRoot, agentsFile))) {
|
|
431
|
+
problems.push(`缺少工具链清单声明的规则文件: ${agentsFile}`);
|
|
432
|
+
} else if (await readFile(join(targetRoot, agentsFile), 'utf8') !== generatedAgentsContent) {
|
|
433
|
+
problems.push(`项目规则已漂移: ${agentsFile}`);
|
|
434
|
+
}
|
|
435
|
+
if (prerequisites.problems.length === 0 && ['AGENTS.md', 'AGENTS_new.md'].includes(agentsFile)) {
|
|
436
|
+
const expected = `${JSON.stringify(await createToolchainManifest(targetRoot, agentsFile, prerequisites.tools, skillsRoot), null, 2)}\n`;
|
|
437
|
+
if (actualText !== expected) problems.push('工具链清单与当前工具或 Skill 不一致');
|
|
438
|
+
}
|
|
439
|
+
} catch (error) {
|
|
440
|
+
problems.push(`工具链清单不可读: ${error.message}`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (problems.length > 0) {
|
|
445
|
+
console.error('wdyy 验证失败:');
|
|
446
|
+
reportProblems(problems);
|
|
447
|
+
return 1;
|
|
448
|
+
}
|
|
449
|
+
console.log('wdyy 验证通过。');
|
|
450
|
+
return 0;
|
|
451
|
+
}
|
|
452
|
+
|
|
267
453
|
export async function run(argumentsList) {
|
|
268
454
|
if (argumentsList.length === 1 && argumentsList[0] === 'init') {
|
|
269
455
|
const writtenPaths = [];
|
|
270
456
|
try {
|
|
457
|
+
printManualPrerequisites();
|
|
458
|
+
const prerequisites = await inspectPrerequisites();
|
|
459
|
+
if (prerequisites.problems.length > 0) {
|
|
460
|
+
console.error('初始化前置条件不满足:');
|
|
461
|
+
reportProblems(prerequisites.problems);
|
|
462
|
+
return 1;
|
|
463
|
+
}
|
|
271
464
|
const gitExitCode = initializeGit(process.cwd(), writtenPaths);
|
|
272
465
|
if (gitExitCode !== 0) return gitExitCode;
|
|
273
|
-
await writeAgentsFile(process.cwd(), writtenPaths);
|
|
466
|
+
const agentsFile = await writeAgentsFile(process.cwd(), writtenPaths);
|
|
274
467
|
const globalSkillsRoot = getGlobalSkillsRoot();
|
|
275
468
|
await installManagedSkills(globalSkillsRoot, writtenPaths);
|
|
276
469
|
await removeDeprecatedManagedSkills(globalSkillsRoot, writtenPaths);
|
|
277
470
|
await appendGitIgnoreEntries(process.cwd(), writtenPaths);
|
|
278
471
|
await createDefaultProjectLayout(process.cwd(), writtenPaths);
|
|
279
|
-
await
|
|
472
|
+
await createBugRecordTemplate(process.cwd(), writtenPaths);
|
|
280
473
|
const openSpecExitCode = initializeOpenSpec(process.cwd(), writtenPaths);
|
|
281
474
|
if (openSpecExitCode !== 0) return openSpecExitCode;
|
|
475
|
+
const codeGraphExitCode = initializeCodeGraph(process.cwd(), writtenPaths);
|
|
476
|
+
if (codeGraphExitCode !== 0) return codeGraphExitCode;
|
|
477
|
+
await writeToolchainManifest(process.cwd(), agentsFile, prerequisites.tools, writtenPaths);
|
|
282
478
|
console.log('wdyy 初始化完成。');
|
|
283
479
|
return 0;
|
|
284
480
|
} catch (error) {
|
|
@@ -288,6 +484,15 @@ export async function run(argumentsList) {
|
|
|
288
484
|
}
|
|
289
485
|
}
|
|
290
486
|
|
|
487
|
+
if (argumentsList.length === 1 && argumentsList[0] === 'validate') {
|
|
488
|
+
try {
|
|
489
|
+
return await validateProject(process.cwd());
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error(`wdyy 验证失败: ${error.message}`);
|
|
492
|
+
return 1;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
291
496
|
if (argumentsList[0] === 'update-skill') {
|
|
292
497
|
const skillArguments = argumentsList.slice(1);
|
|
293
498
|
const updateAll = skillArguments.length === 1 && skillArguments[0] === '--all';
|