@zat-design/sisyphus-scene 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +44 -0
  2. package/bin/sisyphus-scene.mjs +81 -0
  3. package/mcp/sisyphus-react.json +8 -0
  4. package/package.json +36 -0
  5. package/scripts/install-mcp.mjs +158 -0
  6. package/scripts/sync-platforms.mjs +87 -0
  7. package/skills/sisyphus-scene/SKILL.md +149 -0
  8. package/skills/sisyphus-scene/agents/openai.yaml +8 -0
  9. package/skills/sisyphus-scene/assets/templates/scene-registry.json +75 -0
  10. package/skills/sisyphus-scene/evals/fixtures/invalid-antd-fallback.tsx +5 -0
  11. package/skills/sisyphus-scene/evals/fixtures/invalid-decisions.json +11 -0
  12. package/skills/sisyphus-scene/evals/fixtures/valid-decisions.json +21 -0
  13. package/skills/sisyphus-scene/evals/fixtures/valid-page.tsx +25 -0
  14. package/skills/sisyphus-scene/evals/generation-cases.yaml +36 -0
  15. package/skills/sisyphus-scene/evals/trigger-cases.yaml +13 -0
  16. package/skills/sisyphus-scene/references/approved-examples.yaml +16 -0
  17. package/skills/sisyphus-scene/references/integration-contracts.md +112 -0
  18. package/skills/sisyphus-scene/references/platform-claude.md +10 -0
  19. package/skills/sisyphus-scene/references/platform-codex.md +10 -0
  20. package/skills/sisyphus-scene/references/platform-cursor.md +10 -0
  21. package/skills/sisyphus-scene/references/scene-model.md +51 -0
  22. package/skills/sisyphus-scene/references/validation-rules.md +45 -0
  23. package/skills/sisyphus-scene/references/workflow.md +94 -0
  24. package/skills/sisyphus-scene/scripts/inspect-project.mjs +229 -0
  25. package/skills/sisyphus-scene/scripts/install.mjs +100 -0
  26. package/skills/sisyphus-scene/scripts/resolve-versions.mjs +204 -0
  27. package/skills/sisyphus-scene/scripts/validate-scaffold.mjs +158 -0
  28. package/skills/sisyphus-scene/scripts/validate-skill.mjs +48 -0
  29. package/skills/sisyphus-scene-legacy/SKILL.md +614 -0
  30. package/skills/sisyphus-scene-legacy/agents/openai.yaml +7 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Sisyphus Scene
2
+
3
+ `sisyphus-agent` 下的保险业务场景 Agent 包,跨 Claude Code、Codex、Cursor 分发 Agent Skills 与 Sisyphus MCP 配置。
4
+
5
+ ## 安装
6
+
7
+ Node.js 18 及以上版本执行:
8
+
9
+ ```bash
10
+ npx -y @zat-design/sisyphus-scene@1.0.0 install
11
+ ```
12
+
13
+ 默认在当前项目为 Claude Code、Cursor、Codex 安装最新 Skill 与 MCP。常用参数:
14
+
15
+ | 参数 | 用途 |
16
+ | --- | --- |
17
+ | `--global` | 安装到用户级目录 |
18
+ | `--skill sisyphus-scene-legacy` | 安装 Sisyphus v3 + antd4 技能,不配置 4.x MCP |
19
+ | `--platform claude,cursor,codex` | 仅安装指定平台 |
20
+ | `--skip-mcp` | 只安装 Skill |
21
+ | `--check` | 只检查,不写文件 |
22
+
23
+ 后续可将 `@1.0.0` 改为 `@latest`。Skill 唯一事实来源仍为 `skills/`;MCP 唯一事实来源为 `mcp/sisyphus-react.json`。
24
+
25
+ ## 发布检查
26
+
27
+ ```bash
28
+ npm run build:plugin
29
+ npm test
30
+ npm run validate
31
+ npm run mcp:check
32
+ npm run release:check
33
+ npm run pack:check
34
+ ```
35
+
36
+ `mcp:check` 只检查发布所需的 Claude/Cursor 配置,不要求发布机安装 Codex CLI。需要验证本机 Codex MCP 时执行:
37
+
38
+ ```bash
39
+ npm run mcp:check:codex
40
+ ```
41
+
42
+ 在仓库根目录可直接执行 `npm run release:scene`;子包的 `prepublishOnly` 会自动重复执行上述校验,然后发布到公共 npm registry。
43
+
44
+ 全部通过后,使用具备 `@zat-design` 权限的账号执行 `npm publish --access public`。
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const scripts = path.join(root, 'scripts');
10
+ const argv = process.argv.slice(2);
11
+ const command = argv[0] && !argv[0].startsWith('--') ? argv.shift() : 'install';
12
+
13
+ if (command !== 'install') throw new Error(`不支持的命令:${command};仅支持 install。`);
14
+
15
+ const options = {
16
+ scope: 'project',
17
+ skill: 'sisyphus-scene',
18
+ platform: 'all',
19
+ project: process.cwd(),
20
+ check: false,
21
+ skipMcp: false,
22
+ };
23
+
24
+ for (let index = 0; index < argv.length; index += 1) {
25
+ const token = argv[index];
26
+ if (token === '--global') options.scope = 'user';
27
+ else if (token === '--check') options.check = true;
28
+ else if (token === '--skip-mcp') options.skipMcp = true;
29
+ else if (['--skill', '--platform', '--project'].includes(token)) {
30
+ const value = argv[index + 1];
31
+ if (!value || value.startsWith('--')) throw new Error(`${token} 缺少参数。`);
32
+ options[token.slice(2)] = value;
33
+ index += 1;
34
+ } else throw new Error(`不支持的参数:${token}`);
35
+ }
36
+
37
+ if (!['sisyphus-scene', 'sisyphus-scene-legacy'].includes(options.skill)) {
38
+ throw new Error(`不支持的 skill:${options.skill}`);
39
+ }
40
+
41
+ const platform = options.platform === 'all'
42
+ ? 'claude,cursor,codex'
43
+ : options.platform;
44
+ const selectedPlatforms = platform.split(',');
45
+ for (const item of selectedPlatforms) {
46
+ if (!['claude', 'cursor', 'codex'].includes(item)) throw new Error(`不支持的平台:${item}`);
47
+ }
48
+
49
+ const run = (script, args) => execFileSync(process.execPath, [path.join(scripts, script), ...args], {
50
+ encoding: 'utf8',
51
+ stdio: ['ignore', 'pipe', 'pipe'],
52
+ });
53
+
54
+ const skillArgs = [
55
+ '--platform', platform,
56
+ '--scope', options.scope,
57
+ '--skill', options.skill,
58
+ '--project', path.resolve(options.project),
59
+ ];
60
+ const configureMcp = options.skill === 'sisyphus-scene' && !options.skipMcp;
61
+ const mcpArgs = [
62
+ '--platform', platform,
63
+ '--scope', options.scope,
64
+ '--project', path.resolve(options.project),
65
+ ];
66
+
67
+ const preview = {
68
+ skill: JSON.parse(run('sync-platforms.mjs', [...skillArgs, '--check'])),
69
+ };
70
+ if (configureMcp) preview.mcp = JSON.parse(run('install-mcp.mjs', [...mcpArgs, '--check']));
71
+
72
+ if (options.check) {
73
+ process.stdout.write(`${JSON.stringify({ mode: 'check', ...preview }, null, 2)}\n`);
74
+ process.exit(0);
75
+ }
76
+
77
+ const result = {
78
+ skill: JSON.parse(run('sync-platforms.mjs', [...skillArgs, '--write'])),
79
+ };
80
+ if (configureMcp) result.mcp = JSON.parse(run('install-mcp.mjs', [...mcpArgs, '--write']));
81
+ process.stdout.write(`${JSON.stringify({ mode: 'write', ...result }, null, 2)}\n`);
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "sisyphus-react": {
4
+ "command": "npx",
5
+ "args": ["-y", "@zat-design/sisyphus-react-mcp@1.0.2"]
6
+ }
7
+ }
8
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@zat-design/sisyphus-scene",
3
+ "version": "1.0.0",
4
+ "description": "Sisyphus 业务场景 Agent 技能,跨 Claude Code、Codex、Cursor 分发 Sisyphus 4 + Ant Design 6 场景与 MCP 配置",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "sisyphus-scene": "bin/sisyphus-scene.mjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "scripts/install-mcp.mjs",
13
+ "scripts/sync-platforms.mjs",
14
+ "skills/",
15
+ "mcp/",
16
+ "README.md"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public",
20
+ "registry": "https://registry.npmjs.org/"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "scripts": {
26
+ "build:plugin": "node scripts/build-plugin-package.mjs",
27
+ "validate": "node scripts/validate-distribution.mjs",
28
+ "test": "node --test tests/*.test.mjs",
29
+ "pack:check": "npm pack --dry-run",
30
+ "mcp:check": "node scripts/install-mcp.mjs --platform claude,cursor --project . --check",
31
+ "mcp:check:codex": "node scripts/install-mcp.mjs --platform codex --project . --check",
32
+ "sync:check": "node scripts/sync-platforms.mjs --platform all --scope user --check",
33
+ "release:check": "node scripts/release.mjs --check",
34
+ "prepublishOnly": "npm run build:plugin && npm test && npm run validate && npm run mcp:check && npm run release:check && npm run pack:check"
35
+ }
36
+ }
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import os from 'node:os';
7
+ import { spawnSync } from 'node:child_process';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
11
+ const distributionRoot = path.resolve(scriptDirectory, '..');
12
+ const fragmentPath = path.join(distributionRoot, 'mcp', 'sisyphus-react.json');
13
+
14
+ const parseArgs = (argv) => {
15
+ const args = {};
16
+ for (let index = 0; index < argv.length; index += 1) {
17
+ const token = argv[index];
18
+ if (!token.startsWith('--')) continue;
19
+ const key = token.slice(2);
20
+ const value = argv[index + 1];
21
+ if (!value || value.startsWith('--')) args[key] = true;
22
+ else {
23
+ args[key] = value;
24
+ index += 1;
25
+ }
26
+ }
27
+ return args;
28
+ };
29
+
30
+ const args = parseArgs(process.argv.slice(2));
31
+ const platformInput = String(args.platform || 'all');
32
+ const platforms = platformInput === 'all' ? ['claude', 'cursor', 'codex'] : platformInput.split(',');
33
+ const projectRoot = path.resolve(String(args.project || process.cwd()));
34
+ const scope = String(args.scope || 'project');
35
+ const write = Boolean(args.write);
36
+ const check = Boolean(args.check);
37
+
38
+ if (write === check || (!write && !check)) {
39
+ throw new Error('必须且只能指定 --check 或 --write;默认不修改任何项目文件。');
40
+ }
41
+
42
+ const allowedPlatforms = new Set(['claude', 'cursor', 'codex']);
43
+ if (!['user', 'project'].includes(scope)) throw new Error(`不支持的 scope:${scope}`);
44
+ for (const platform of platforms) {
45
+ if (!allowedPlatforms.has(platform)) throw new Error(`不支持的平台:${platform}`);
46
+ }
47
+
48
+ const fragment = JSON.parse(fs.readFileSync(fragmentPath, 'utf8'));
49
+ const server = fragment?.mcpServers?.['sisyphus-react'];
50
+ if (!server || typeof server !== 'object') throw new Error(`MCP 片段无效:${fragmentPath}`);
51
+
52
+ const targets = scope === 'user'
53
+ ? {
54
+ claude: path.join(os.homedir(), '.claude.json'),
55
+ cursor: path.join(os.homedir(), '.cursor', 'mcp.json'),
56
+ }
57
+ : {
58
+ claude: path.join(projectRoot, '.mcp.json'),
59
+ cursor: path.join(projectRoot, '.cursor', 'mcp.json'),
60
+ };
61
+
62
+ const codexHome = scope === 'user'
63
+ ? path.resolve(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'))
64
+ : path.join(projectRoot, '.codex');
65
+
66
+ const codexEnv = { ...process.env, CODEX_HOME: codexHome };
67
+
68
+ const runCodex = (commandArgs) => spawnSync('codex', commandArgs, {
69
+ encoding: 'utf8',
70
+ env: codexEnv,
71
+ });
72
+
73
+ const inspectCodex = () => {
74
+ if (!fs.existsSync(codexHome)) {
75
+ const version = runCodex(['--version']);
76
+ if (version.error?.code === 'ENOENT') throw new Error('未找到 Codex CLI,无法配置 Codex MCP。');
77
+ if (version.status !== 0) throw new Error(`Codex CLI 不可用:${version.stderr || version.stdout}`);
78
+ return { status: 'missing' };
79
+ }
80
+
81
+ const result = runCodex(['mcp', 'get', 'sisyphus-react', '--json']);
82
+ if (result.error?.code === 'ENOENT') throw new Error('未找到 Codex CLI,无法配置 Codex MCP。');
83
+ if (result.status !== 0) {
84
+ const message = `${result.stderr || ''}${result.stdout || ''}`;
85
+ if (/not found|no mcp server/i.test(message)) return { status: 'missing' };
86
+ throw new Error(`读取 Codex MCP 配置失败:${message.trim()}`);
87
+ }
88
+
89
+ const value = JSON.parse(result.stdout);
90
+ const transport = value.transport || value;
91
+ const expectedArgs = server.args || [];
92
+ if (transport.command !== server.command || !sameJson(transport.args || [], expectedArgs)) {
93
+ throw new Error(`${path.join(codexHome, 'config.toml')} 已存在不同的 sisyphus-react 配置;请人工确认。`);
94
+ }
95
+ return { status: 'unchanged' };
96
+ };
97
+
98
+ const readConfig = (target) => {
99
+ if (!fs.existsSync(target)) return { mcpServers: {} };
100
+ const value = JSON.parse(fs.readFileSync(target, 'utf8'));
101
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
102
+ throw new Error(`${target} 必须是 JSON 对象。`);
103
+ }
104
+ if (value.mcpServers === undefined) value.mcpServers = {};
105
+ if (!value.mcpServers || typeof value.mcpServers !== 'object' || Array.isArray(value.mcpServers)) {
106
+ throw new Error(`${target}.mcpServers 必须是对象。`);
107
+ }
108
+ return value;
109
+ };
110
+
111
+ const sameJson = (left, right) => {
112
+ if (Object.is(left, right)) return true;
113
+ if (!left || !right || typeof left !== 'object' || typeof right !== 'object') return false;
114
+ if (Array.isArray(left) !== Array.isArray(right)) return false;
115
+ if (Array.isArray(left)) return left.length === right.length && left.every((value, index) => sameJson(value, right[index]));
116
+ const leftKeys = Object.keys(left).sort();
117
+ const rightKeys = Object.keys(right).sort();
118
+ return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && sameJson(left[key], right[key]));
119
+ };
120
+ const results = [];
121
+
122
+ for (const platform of platforms) {
123
+ if (platform === 'codex') {
124
+ const inspected = inspectCodex();
125
+ if (inspected.status === 'missing' && write) {
126
+ fs.mkdirSync(codexHome, { recursive: true });
127
+ const result = runCodex(['mcp', 'add', 'sisyphus-react', '--', server.command, ...(server.args || [])]);
128
+ if (result.status !== 0) throw new Error(`写入 Codex MCP 配置失败:${result.stderr || result.stdout}`);
129
+ }
130
+ results.push({
131
+ platform,
132
+ target: path.join(codexHome, 'config.toml'),
133
+ status: inspected.status === 'unchanged' ? 'unchanged' : write ? 'added' : 'would-add',
134
+ });
135
+ continue;
136
+ }
137
+
138
+ const target = targets[platform];
139
+ const config = readConfig(target);
140
+ const existing = config.mcpServers['sisyphus-react'];
141
+ if (existing && !sameJson(existing, server)) {
142
+ throw new Error(`${target} 已存在不同的 sisyphus-react 配置;为避免覆盖,请人工确认后再处理。`);
143
+ }
144
+
145
+ if (existing) {
146
+ results.push({ platform, target, status: 'unchanged' });
147
+ continue;
148
+ }
149
+
150
+ config.mcpServers['sisyphus-react'] = server;
151
+ if (write) {
152
+ fs.mkdirSync(path.dirname(target), { recursive: true });
153
+ fs.writeFileSync(target, `${JSON.stringify(config, null, 2)}\n`);
154
+ }
155
+ results.push({ platform, target, status: write ? 'added' : 'would-add' });
156
+ }
157
+
158
+ process.stdout.write(`${JSON.stringify({ projectRoot, scope, mode: write ? 'write' : 'check', results }, null, 2)}\n`);
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import process from 'node:process';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
11
+ const sourceRoot = path.join(root, 'skills');
12
+ const parseArgs = (argv) => {
13
+ const args = {};
14
+ for (let index = 0; index < argv.length; index += 1) {
15
+ const token = argv[index];
16
+ if (!token.startsWith('--')) continue;
17
+ const key = token.slice(2);
18
+ const value = argv[index + 1];
19
+ if (!value || value.startsWith('--')) args[key] = true;
20
+ else {
21
+ args[key] = value;
22
+ index += 1;
23
+ }
24
+ }
25
+ return args;
26
+ };
27
+
28
+ const args = parseArgs(process.argv.slice(2));
29
+ const platforms = String(args.platform || 'all') === 'all' ? ['claude', 'codex', 'cursor'] : String(args.platform).split(',');
30
+ const scope = String(args.scope || 'user');
31
+ const skills = String(args.skill || 'sisyphus-scene').split(',');
32
+ const write = Boolean(args.write);
33
+ const check = Boolean(args.check);
34
+ if (write === check || (!write && !check)) throw new Error('必须且只能指定 --check 或 --write。');
35
+ if (!['user', 'project'].includes(scope)) throw new Error(`不支持的 scope:${scope}`);
36
+ for (const platform of platforms) if (!['claude', 'codex', 'cursor'].includes(platform)) throw new Error(`不支持的平台:${platform}`);
37
+ for (const skill of skills) {
38
+ if (!['sisyphus-scene', 'sisyphus-scene-legacy'].includes(skill)) throw new Error(`不支持的 skill:${skill}`);
39
+ }
40
+
41
+ const projectRoot = path.resolve(String(args.project || process.cwd()));
42
+ const baseDirectories = {
43
+ claude: scope === 'user' ? path.join(os.homedir(), '.claude', 'skills') : path.join(projectRoot, '.claude', 'skills'),
44
+ codex: scope === 'user' ? path.join(os.homedir(), '.agents', 'skills') : path.join(projectRoot, '.agents', 'skills'),
45
+ cursor: scope === 'user' ? path.join(os.homedir(), '.agents', 'skills') : path.join(projectRoot, '.agents', 'skills'),
46
+ };
47
+
48
+ const hashDirectory = (directory) => {
49
+ if (!fs.existsSync(directory)) return null;
50
+ const hash = crypto.createHash('sha256');
51
+ const visit = (current, relative = '') => {
52
+ for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
53
+ const nextRelative = path.join(relative, entry.name);
54
+ const next = path.join(current, entry.name);
55
+ if (entry.isDirectory()) visit(next, nextRelative);
56
+ else if (entry.isFile()) {
57
+ hash.update(nextRelative);
58
+ hash.update(fs.readFileSync(next));
59
+ }
60
+ }
61
+ };
62
+ visit(directory);
63
+ return hash.digest('hex');
64
+ };
65
+
66
+ const results = [];
67
+ for (const platform of platforms) {
68
+ const targetBase = baseDirectories[platform];
69
+ for (const skillName of skills) {
70
+ const source = path.join(sourceRoot, skillName);
71
+ const target = path.join(targetBase, skillName);
72
+ const sourceHash = hashDirectory(source);
73
+ const targetHash = hashDirectory(target);
74
+ if (sourceHash === targetHash) {
75
+ results.push({ platform, skill: skillName, target, status: 'unchanged' });
76
+ continue;
77
+ }
78
+ if (write) {
79
+ fs.mkdirSync(targetBase, { recursive: true });
80
+ fs.rmSync(target, { recursive: true, force: true });
81
+ fs.cpSync(source, target, { recursive: true });
82
+ }
83
+ results.push({ platform, skill: skillName, target, status: write ? 'synced' : 'would-sync' });
84
+ }
85
+ }
86
+
87
+ process.stdout.write(JSON.stringify({ sourceRoot, scope, skills, mode: write ? 'write' : 'check', results }, null, 2) + '\n');
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: sisyphus-scene
3
+ description: 基于最新 @zat-design/sisyphus-react 4.x 与 Ant Design 6,从页面截图或需求描述识别保险业务场景并生成 React/TypeScript 脚手架。用于查询列表、Tab 分组列表、列表弹窗、多 Tab 详情、分区详情、列表跳转详情、步骤条分区表单、参考其他应用实现,以及无法命中既有模板时基于 MCP demo 组合新场景;支持 Claude Code、Codex、Cursor,不绑定 Umi Max。
4
+ ---
5
+
6
+ # Sisyphus 业务场景生成
7
+
8
+ 为保险核心系统生成可验证的页面骨架。场景模板只决定结构,组件 MCP 决定真实 API。禁止凭记忆、旧模板或 `node_modules` 推断 Sisyphus 用法。
9
+
10
+ ## 不可跳过的约束
11
+
12
+ - 仅支持 React + TypeScript、最新目标版本的 `@zat-design/sisyphus-react` 4.x 和 Ant Design 6。
13
+ - Umi Max、React Router、请求库、权限和状态库都属于目标项目适配,不是技能依赖。
14
+ - 无论场景是否命中模板,先查询 Sisyphus MCP 的组件列表、完整 API 和 demo。
15
+ - 只有 Sisyphus MCP 明确给出 `missing` 的能力才可进入 Ant Design 补全;`unverified` 必须先修复 MCP 连接或版本问题。
16
+ - Sisyphus、Ant Design 和项目公共组件都无法覆盖时停止生成,显式报告缺口。
17
+ - 不自动升级依赖、不修改全局布局、不创建新的基础组件库、不把临时场景自动写回本技能。
18
+ - 不声称“100% 合规”;只汇报实际证据和验证结果。
19
+
20
+ ## 执行工作流
21
+
22
+ ### 1. 识别平台并探测项目
23
+
24
+ 1. 读取与当前平台匹配的 reference:
25
+ - Claude Code:`references/platform-claude.md`
26
+ - Codex:`references/platform-codex.md`
27
+ - Cursor:`references/platform-cursor.md`
28
+ 2. 以当前技能目录的绝对路径运行:
29
+
30
+ ```bash
31
+ node scripts/inspect-project.mjs --root <项目根目录> --target <目标页面目录>
32
+ ```
33
+
34
+ 3. 读取最近层级的 `AGENTS.md`、`CLAUDE.md`、`.cursor/rules/*.mdc`、package.json、lockfile、相邻页面、路由、请求和权限实现。
35
+ 4. 同级规则冲突时停止并列出冲突,不静默选择。
36
+
37
+ 详细契约见 `references/integration-contracts.md`。
38
+
39
+ ### 2. 执行版本门禁
40
+
41
+ 运行:
42
+
43
+ ```bash
44
+ node scripts/resolve-versions.mjs --root <项目根目录> --channel stable
45
+ ```
46
+
47
+ - Ant Design 必须满足最新稳定 `>=6 <7`。
48
+ - Sisyphus 必须满足最新稳定 `>=4 <5`。
49
+ - beta 仅在用户明确要求时使用 `--channel beta`。
50
+ - 项目版本、registry 最新版本、MCP 报告版本不一致时停止,并输出独立升级建议。
51
+
52
+ ### 3. 从截图或需求建立 SceneSpec
53
+
54
+ 提取页面类型、字段、表格列、Tab、弹窗/抽屉、详情分区、步骤锚点、模式、操作、导航、接口、权限和未决项。
55
+
56
+ 读取:
57
+
58
+ - `references/scene-model.md`:场景识别与能力组合
59
+ - `references/integration-contracts.md`:`SceneSpec`、`ProjectProfile`、`ComponentDecision`
60
+ - `assets/templates/scene-registry.json`:注册场景与页面原语
61
+
62
+ 只对阻塞生成的业务歧义提问;非阻塞信息可以保留精准 TODO。
63
+
64
+ ### 4. 强制查询 Sisyphus MCP
65
+
66
+ 对本次候选组件依次执行:
67
+
68
+ 1. `sisyphus-list-components`
69
+ 2. `sisyphus-get-component-api(<组件名>)`
70
+ 3. `sisyphus-get-examples(<组件名>, <场景关键词>)`
71
+
72
+ 记录 MCP 报告版本、API 证据和 demo 证据。模板与 MCP 冲突时以匹配目标版本的 MCP 为准。
73
+
74
+ ### 5. 匹配或构造场景
75
+
76
+ - `exact`:加载注册场景,用 MCP API/demo 校准所有具体组件写法。
77
+ - `partial`:组合已有页面原语,用 Sisyphus demo 补齐新增能力。
78
+ - `none`:不强行归类;从 Sisyphus 组件和 demo 构造本次临时场景模板。
79
+
80
+ 完整决策流见 `references/workflow.md`。
81
+
82
+ ### 6. 逐项执行能力缺口检查
83
+
84
+ 为每条需求创建 `ComponentDecision`,状态仅允许:
85
+
86
+ - `covered`:组件和用法均有证据
87
+ - `partial`:组件存在,需要组合或项目适配
88
+ - `missing`:Sisyphus 明确无此能力
89
+ - `unverified`:MCP 无法确认
90
+
91
+ `unverified` 立即阻断。只有 `missing` 进入下一步。
92
+
93
+ ### 7. 按顺序补全剩余能力
94
+
95
+ 1. 明确回答:“Sisyphus 哪项能力不满足需求?”
96
+ 2. 通过平台可用的 Ant Design MCP 查询 antd 6 API 和官方 demo;平台无 Ant Design MCP 时使用 Context7 官方文档提供器。
97
+ 3. 只补齐缺口,记录 `fallbackReason`,不得重写 Sisyphus 已覆盖能力。
98
+ 4. 再次检查缺口;仍缺失时检索项目公共组件。
99
+ 5. 复用或修改项目组件前执行 GitNexus impact;无法安全复用则停止。
100
+
101
+ 禁止:
102
+
103
+ - 在 `ProForm` 中随意混入手写 `Form.Item`
104
+ - 用 antd Tabs 重写 `ProTable.tabs`
105
+ - 用 Modal/Drawer 重写 `ProDrawerForm`
106
+ - 无 fallback 证据的 Sisyphus/antd 混用
107
+
108
+ ### 8. 注入项目适配并生成最小文件集
109
+
110
+ - 只有确认项目使用 Umi Max 时才生成 `@umijs/max` import。
111
+ - 只有确认项目使用 React Router 时才生成对应 import。
112
+ - 无法确认导航实现时注入 `onView`、`onEdit`、`onBack` 等回调。
113
+ - 请求、权限、状态和类型文件遵循目标项目证据。
114
+ - 无需求时不创建空 `hooks`、`models`、`utils`、`assets`、样式或菜单文件。
115
+
116
+ ### 9. 验证并交付
117
+
118
+ 运行:
119
+
120
+ ```bash
121
+ node scripts/validate-scaffold.mjs \
122
+ --root <项目根目录> \
123
+ --files <逗号分隔的生成文件> \
124
+ --decisions <ComponentDecision JSON>
125
+ ```
126
+
127
+ 再运行目标项目的 typecheck、lint、相关测试;有截图时启动页面做视觉核对。完成后运行 GitNexus detect changes。
128
+
129
+ ## 输出格式
130
+
131
+ 按顺序输出:
132
+
133
+ 1. 中文场景名、匹配级别和能力组合
134
+ 2. 目标项目与版本门禁结果
135
+ 3. Sisyphus MCP 组件/API/demo 证据
136
+ 4. `ComponentDecision` 摘要和全部 Ant Design fallback
137
+ 5. 生成文件树与完整代码
138
+ 6. 实际执行的检查及结果
139
+ 7. 未解决项和按优先级排列的 TODO
140
+
141
+ ## 资源路由
142
+
143
+ - 决策工作流与停止条件:`references/workflow.md`
144
+ - 场景与能力模型:`references/scene-model.md`
145
+ - 数据和项目适配契约:`references/integration-contracts.md`
146
+ - 生成后校验规则:`references/validation-rules.md`
147
+ - 可引用的团队批准样例:`references/approved-examples.yaml`
148
+ - 结构模板注册表:`assets/templates/scene-registry.json`
149
+
@@ -0,0 +1,8 @@
1
+ interface:
2
+ display_name: "Sisyphus 业务场景生成"
3
+ short_description: "从截图或需求生成可验证的保险业务页面脚手架"
4
+ default_prompt: "Use $sisyphus-scene to match this requirement to a business scene and generate a verified React scaffold."
5
+
6
+ policy:
7
+ allow_implicit_invocation: true
8
+
@@ -0,0 +1,75 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "primitives": {
4
+ "search-form": {
5
+ "description": "查询条件、查询与重置",
6
+ "sisyphusCandidates": ["ProForm"]
7
+ },
8
+ "data-table": {
9
+ "description": "数据表格、分页、加载和数据源",
10
+ "sisyphusCandidates": ["ProTable"]
11
+ },
12
+ "table-tabs": {
13
+ "description": "同列表状态过滤或独立子页面切换",
14
+ "sisyphusCandidates": ["ProTable"]
15
+ },
16
+ "drawer-form": {
17
+ "description": "新增、编辑或查看弹窗/抽屉",
18
+ "sisyphusCandidates": ["ProDrawerForm", "ProForm"]
19
+ },
20
+ "detail-navigation": {
21
+ "description": "列表进入详情、返回和 URL 状态",
22
+ "sisyphusCandidates": ["ProHeader"]
23
+ },
24
+ "detail-tabs": {
25
+ "description": "横向 Tab 详情或编辑分区",
26
+ "sisyphusCandidates": ["ProStepTab", "ProHeader", "ProFooter"]
27
+ },
28
+ "detail-sections": {
29
+ "description": "纵向详情或折叠分区",
30
+ "sisyphusCandidates": ["ProCollapse", "ProHeader", "ProFooter"]
31
+ },
32
+ "step-anchor-form": {
33
+ "description": "纵向步骤锚点和跨分区统一校验",
34
+ "sisyphusCandidates": ["ProStep", "ProCollapse", "ProFooter"]
35
+ }
36
+ },
37
+ "scenes": [
38
+ {
39
+ "id": "query-list",
40
+ "name": "查询列表页",
41
+ "required": ["search-form", "data-table"]
42
+ },
43
+ {
44
+ "id": "tab-list",
45
+ "name": "Tab 分组列表页",
46
+ "required": ["search-form", "data-table", "table-tabs"]
47
+ },
48
+ {
49
+ "id": "list-drawer",
50
+ "name": "列表弹窗操作页",
51
+ "required": ["search-form", "data-table", "drawer-form"]
52
+ },
53
+ {
54
+ "id": "tab-detail",
55
+ "name": "多 Tab 详情编辑页",
56
+ "required": ["detail-tabs"]
57
+ },
58
+ {
59
+ "id": "section-detail",
60
+ "name": "分区块详情页",
61
+ "required": ["detail-sections"]
62
+ },
63
+ {
64
+ "id": "list-detail-navigation",
65
+ "name": "列表跳转详情页",
66
+ "required": ["data-table", "detail-navigation"],
67
+ "requiresOneOf": ["detail-tabs", "detail-sections"]
68
+ },
69
+ {
70
+ "id": "step-section-form",
71
+ "name": "步骤条分区表单页",
72
+ "required": ["detail-sections", "step-anchor-form"]
73
+ }
74
+ ]
75
+ }
@@ -0,0 +1,5 @@
1
+ import { Tabs } from 'antd';
2
+ import { ProTable } from '@zat-design/sisyphus-react';
3
+
4
+ export const InvalidFallback = () => <ProTable tableId="invalid" columns={[]} tabs={<Tabs items={[]} />} />;
5
+
@@ -0,0 +1,11 @@
1
+ [
2
+ {
3
+ "requirementId": "data-table",
4
+ "requirement": "数据表格",
5
+ "status": "covered",
6
+ "source": "sisyphus",
7
+ "component": "ProTable",
8
+ "apiEvidence": "sisyphus-get-component-api(ProTable)",
9
+ "demoEvidence": "sisyphus-get-examples(ProTable, 查询列表)"
10
+ }
11
+ ]