@dianzhong/create-harness-app 0.1.4 → 0.2.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.
- package/dist/index.mjs +69 -13
- package/package.json +1 -1
- package/templates/harness/full/.claude/agents/harness-reviewer.md +0 -2
- package/templates/harness/full/.claude/settings.json +1 -1
- package/templates/harness/full/docs/ai-harness.md +3 -10
- package/templates/harness/full/docs/delivery-template.md +1 -1
- package/templates/harness/full/docs/harness-quick-reference.md +2 -15
- package/templates/harness/full/docs/review-checklist.md +1 -1
- package/templates/harness/full/docs/verification.md +35 -0
- package/templates/harness/full/scripts/verify-skills.mjs +0 -8
- package/templates/openspec/docs/openspec.md +34 -0
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path8 from "path";
|
|
5
|
+
import fse4 from "fs-extra";
|
|
6
6
|
import { outro } from "@clack/prompts";
|
|
7
7
|
|
|
8
8
|
// src/prompts.ts
|
|
@@ -15,7 +15,8 @@ function defaultConfig(projectName) {
|
|
|
15
15
|
vitest: false,
|
|
16
16
|
uiLibrary: "element-plus",
|
|
17
17
|
axios: true,
|
|
18
|
-
harness: "full"
|
|
18
|
+
harness: "full",
|
|
19
|
+
openspec: true
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
function check(val) {
|
|
@@ -52,7 +53,8 @@ async function collectConfig(initialName) {
|
|
|
52
53
|
{ value: "none", label: "\u4E0D\u96C6\u6210" }
|
|
53
54
|
]
|
|
54
55
|
}));
|
|
55
|
-
|
|
56
|
+
const openspec = check(await confirm({ message: "\u542F\u7528 OpenSpec \u4E1A\u52A1\u89C4\u683C\u7BA1\u7406\uFF1F", initialValue: true }));
|
|
57
|
+
return { projectName, router, pinia, vitest, uiLibrary, axios, harness, openspec };
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
// src/spawn-create-vue.ts
|
|
@@ -73,7 +75,8 @@ async function spawnCreateVue(config, parentDir) {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
// src/overlay.ts
|
|
76
|
-
import
|
|
78
|
+
import path7 from "path";
|
|
79
|
+
import fse3 from "fs-extra";
|
|
77
80
|
|
|
78
81
|
// src/utils/fs.ts
|
|
79
82
|
import fse from "fs-extra";
|
|
@@ -289,9 +292,11 @@ var HARNESS_FULL_PKG = {
|
|
|
289
292
|
"husky": "^9.1.7"
|
|
290
293
|
},
|
|
291
294
|
scripts: {
|
|
295
|
+
"prepare": "husky",
|
|
292
296
|
"harness:sync": "node scripts/verify-skills.mjs --write",
|
|
293
297
|
"harness:check": "node scripts/verify-skills.mjs && node scripts/harness-hooks.test.mjs",
|
|
294
298
|
"harness:test": "node scripts/harness-hooks.test.mjs && node scripts/verify-skills.test.mjs",
|
|
299
|
+
"check:fix": "pnpm format && pnpm lint",
|
|
295
300
|
"check": "pnpm type-check && pnpm lint && pnpm format:check && pnpm harness:check"
|
|
296
301
|
}
|
|
297
302
|
};
|
|
@@ -305,9 +310,48 @@ async function overlayHarness(projectRoot, level) {
|
|
|
305
310
|
await copyDir(path5.join(TEMPLATES_DIR2, "harness", level), projectRoot);
|
|
306
311
|
}
|
|
307
312
|
|
|
313
|
+
// src/features/openspec.ts
|
|
314
|
+
import path6 from "path";
|
|
315
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
316
|
+
import fse2 from "fs-extra";
|
|
317
|
+
import { execa as execa2 } from "execa";
|
|
318
|
+
var TEMPLATES_DIR3 = fileURLToPath3(new URL("../templates", import.meta.url));
|
|
319
|
+
var OPENSPEC_PKG = {
|
|
320
|
+
devDependencies: {
|
|
321
|
+
"@fission-ai/openspec": "^1.4.1"
|
|
322
|
+
},
|
|
323
|
+
scripts: {
|
|
324
|
+
"openspec:validate": "openspec validate --all --no-interactive",
|
|
325
|
+
"openspec:list": "openspec list"
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
async function overlayOpenSpec(projectRoot) {
|
|
329
|
+
await execa2("npx", ["--yes", "@fission-ai/openspec@latest", "init", "--tools", "claude"], {
|
|
330
|
+
cwd: projectRoot,
|
|
331
|
+
stdio: "inherit"
|
|
332
|
+
});
|
|
333
|
+
const verifyScript = path6.join(projectRoot, "scripts/verify-skills.mjs");
|
|
334
|
+
if (await fse2.pathExists(verifyScript)) {
|
|
335
|
+
await execa2("node", ["scripts/verify-skills.mjs", "--write"], { cwd: projectRoot });
|
|
336
|
+
}
|
|
337
|
+
await copyDir(path6.join(TEMPLATES_DIR3, "openspec"), projectRoot);
|
|
338
|
+
}
|
|
339
|
+
|
|
308
340
|
// src/overlay.ts
|
|
341
|
+
var UI_LABEL = {
|
|
342
|
+
"element-plus": "Element Plus",
|
|
343
|
+
"ant-design-vue": "Ant Design Vue",
|
|
344
|
+
"none": "\uFF08\u672A\u96C6\u6210 UI \u5E93\uFF0C\u8BF7\u81EA\u884C\u914D\u7F6E\uFF09"
|
|
345
|
+
};
|
|
346
|
+
async function fillClaudeMdUiPlaceholder(projectRoot, config) {
|
|
347
|
+
const claudePath = path7.join(projectRoot, "CLAUDE.md");
|
|
348
|
+
if (!await fse3.pathExists(claudePath)) return;
|
|
349
|
+
const content = await readText(claudePath);
|
|
350
|
+
if (!content.includes("[\u5728\u6B64\u586B\u5199 UI \u5E93]")) return;
|
|
351
|
+
await writeText(claudePath, content.replaceAll("[\u5728\u6B64\u586B\u5199 UI \u5E93]", UI_LABEL[config.uiLibrary]));
|
|
352
|
+
}
|
|
309
353
|
async function overlay(projectRoot, config) {
|
|
310
|
-
const pkgPath =
|
|
354
|
+
const pkgPath = path7.join(projectRoot, "package.json");
|
|
311
355
|
let pkg = await readJson(pkgPath);
|
|
312
356
|
const add = { dependencies: {}, devDependencies: {}, scripts: {} };
|
|
313
357
|
const merge = (src) => {
|
|
@@ -329,11 +373,19 @@ async function overlay(projectRoot, config) {
|
|
|
329
373
|
if (config.harness !== "none") {
|
|
330
374
|
await overlayHarness(projectRoot, config.harness);
|
|
331
375
|
merge(config.harness === "full" ? HARNESS_FULL_PKG : HARNESS_MINIMAL_PKG);
|
|
376
|
+
await fillClaudeMdUiPlaceholder(projectRoot, config);
|
|
332
377
|
const existingScripts = pkg.scripts ?? {};
|
|
333
378
|
if (existingScripts.format?.includes("--write") && !existingScripts["format:check"]) {
|
|
334
379
|
add.scripts["format:check"] = existingScripts.format.replace("--write", "--check");
|
|
335
380
|
}
|
|
336
381
|
}
|
|
382
|
+
if (config.openspec) {
|
|
383
|
+
await overlayOpenSpec(projectRoot);
|
|
384
|
+
merge(OPENSPEC_PKG);
|
|
385
|
+
if (add.scripts.check) {
|
|
386
|
+
add.scripts.check = `${add.scripts.check} && pnpm openspec:validate`;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
337
389
|
pkg = mergePackageJson(pkg, add);
|
|
338
390
|
await writeJson(pkgPath, pkg);
|
|
339
391
|
}
|
|
@@ -353,6 +405,7 @@ function parseArgs(argv) {
|
|
|
353
405
|
uiLibrary: argv.find((a) => a.startsWith("--ui="))?.split("=")[1],
|
|
354
406
|
harness: argv.find((a) => a.startsWith("--harness="))?.split("=")[1],
|
|
355
407
|
noAxios: argv.includes("--no-axios"),
|
|
408
|
+
noOpenspec: argv.includes("--no-openspec"),
|
|
356
409
|
vitest: argv.includes("--vitest")
|
|
357
410
|
};
|
|
358
411
|
}
|
|
@@ -362,17 +415,18 @@ async function main() {
|
|
|
362
415
|
log(`Usage: create-harness-app [project-name] [options]
|
|
363
416
|
|
|
364
417
|
Options:
|
|
365
|
-
--yes Non-interactive: TS + Router + Pinia + ElementPlus + axios + harness:full
|
|
418
|
+
--yes Non-interactive: TS + Router + Pinia + ElementPlus + axios + harness:full + OpenSpec
|
|
366
419
|
--ui=<lib> element-plus | ant-design-vue | none (default: element-plus)
|
|
367
420
|
--harness=<level> full | minimal | none (default: full)
|
|
368
421
|
--no-axios Skip axios layer
|
|
422
|
+
--no-openspec Skip OpenSpec spec management
|
|
369
423
|
--vitest Enable Vitest
|
|
370
424
|
-h, --help Show this help
|
|
371
425
|
|
|
372
426
|
Examples:
|
|
373
427
|
npm create @dianzhong/harness-app my-app
|
|
374
428
|
npm create @dianzhong/harness-app my-app -- --yes
|
|
375
|
-
npm create @dianzhong/harness-app my-app -- --ui=ant-design-vue --harness=minimal
|
|
429
|
+
npm create @dianzhong/harness-app my-app -- --ui=ant-design-vue --harness=minimal --no-openspec
|
|
376
430
|
`);
|
|
377
431
|
process.exit(0);
|
|
378
432
|
}
|
|
@@ -384,22 +438,23 @@ Examples:
|
|
|
384
438
|
uiLibrary: args.uiLibrary ?? base.uiLibrary,
|
|
385
439
|
harness: args.harness ?? base.harness,
|
|
386
440
|
axios: !args.noAxios,
|
|
441
|
+
openspec: !args.noOpenspec,
|
|
387
442
|
vitest: args.vitest
|
|
388
443
|
};
|
|
389
|
-
info(`\u975E\u4EA4\u4E92\u6A21\u5F0F: ${config.projectName} | UI=${config.uiLibrary} | harness=${config.harness}`);
|
|
444
|
+
info(`\u975E\u4EA4\u4E92\u6A21\u5F0F: ${config.projectName} | UI=${config.uiLibrary} | harness=${config.harness} | openspec=${config.openspec}`);
|
|
390
445
|
} else {
|
|
391
446
|
config = await collectConfig(args.projectName);
|
|
392
447
|
}
|
|
393
|
-
const targetDir =
|
|
394
|
-
if (await
|
|
395
|
-
const entries = await
|
|
448
|
+
const targetDir = path8.resolve(process.cwd(), config.projectName);
|
|
449
|
+
if (await fse4.pathExists(targetDir)) {
|
|
450
|
+
const entries = await fse4.readdir(targetDir);
|
|
396
451
|
if (entries.length > 0) {
|
|
397
452
|
error(`\u76EE\u5F55 "${config.projectName}" \u5DF2\u5B58\u5728\u4E14\u4E0D\u4E3A\u7A7A`);
|
|
398
453
|
process.exit(1);
|
|
399
454
|
}
|
|
400
455
|
}
|
|
401
456
|
info("\u4F7F\u7528 create-vue \u751F\u6210\u57FA\u7840\u9AA8\u67B6...");
|
|
402
|
-
await spawnCreateVue(config,
|
|
457
|
+
await spawnCreateVue(config, path8.dirname(targetDir));
|
|
403
458
|
info("\u53E0\u52A0 features...");
|
|
404
459
|
await overlay(targetDir, config);
|
|
405
460
|
success("\u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
@@ -409,6 +464,7 @@ Examples:
|
|
|
409
464
|
log(" pnpm install");
|
|
410
465
|
if (config.harness === "full") log(" pnpm harness:sync # \u91CD\u7B97 skills \u6307\u7EB9");
|
|
411
466
|
log(" pnpm dev\n");
|
|
467
|
+
if (config.openspec) log(' OpenSpec \u5DF2\u5C31\u7EEA\uFF1A\u5728 Claude Code \u91CC\u7528 /opsx:propose "\u4F60\u7684\u9700\u6C42" \u5F00\u59CB\u7B2C\u4E00\u4E2A\u53D8\u66F4\n');
|
|
412
468
|
}
|
|
413
469
|
main().catch((err) => {
|
|
414
470
|
error(String(err.message ?? err));
|
package/package.json
CHANGED
|
@@ -20,7 +20,6 @@ tools: Read, Glob, Grep, Bash
|
|
|
20
20
|
- `docs/review-checklist.md`
|
|
21
21
|
- `docs/verification.md`
|
|
22
22
|
- `skills-lock.json`
|
|
23
|
-
- `scripts/check-project-structure.mjs`
|
|
24
23
|
- `scripts/*.test.mjs`
|
|
25
24
|
- `package.json` scripts 和 `.husky/*`
|
|
26
25
|
|
|
@@ -32,7 +31,6 @@ tools: Read, Glob, Grep, Bash
|
|
|
32
31
|
- 完成通知必须兼容 macOS 和 Windows,并在失败时安全降级。
|
|
33
32
|
- skills 必须同步 lock hash(`skills-lock.json` 中每个 skill 的 `computedHash` 必须与当前文件内容一致)。
|
|
34
33
|
- harness 脚本的测试必须接入 `pnpm harness:check`,不能只放测试文件。
|
|
35
|
-
- 结构检查必须只约束通用工程护栏,不得固化当前占位业务流程或 mock 数据。
|
|
36
34
|
- harness 配置以源文件为准,不维护额外 generated config inventory。
|
|
37
35
|
- 敏感文件和危险命令必须被拒绝。
|
|
38
36
|
- 人工审核交付要求必须在入口文档、速查表、review checklist、delivery template 和 reviewer 规则中保持一致。
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"hooks": [
|
|
82
82
|
{
|
|
83
83
|
"type": "agent",
|
|
84
|
-
"prompt": "你是强制 AI harness 代码审查子 agent。审查前先运行 `git diff --name-only` 查看本次改动了哪些文件。若任一改动文件匹配 `.claude/**`、`.husky/**`、`scripts/verify-skills.mjs`、`scripts
|
|
84
|
+
"prompt": "你是强制 AI harness 代码审查子 agent。审查前先运行 `git diff --name-only` 查看本次改动了哪些文件。若任一改动文件匹配 `.claude/**`、`.husky/**`、`scripts/verify-skills.mjs`、`scripts/*.test.mjs`、`skills-lock.json`、`package.json`、`CLAUDE.md`、`docs/ai-harness.md`、`docs/harness-quick-reference.md`、`docs/delivery-template.md`、`docs/review-checklist.md` 或 `docs/verification.md`,就读取 `.claude/agents/harness-reviewer.md` 并严格按其规则审查;否则读取 `.claude/agents/code-reviewer.md` 并严格按其规则审查。不要编辑任何文件。只返回 JSON:{\"ok\":true|false,\"reason\":\"HARNESS_REVIEW_RESULT: PASS|FAIL\\n简短总结。\"}。并提醒用户:Claude 审查不能替代人工最终复核。",
|
|
85
85
|
"timeout": 300,
|
|
86
86
|
"statusMessage": "运行强制代码审查"
|
|
87
87
|
}
|
|
@@ -20,12 +20,9 @@
|
|
|
20
20
|
- `.claude/rules/*.md`:可按任务加载的细分规则。
|
|
21
21
|
- `.claude/skills/`:Claude Code 项目 skills 源。
|
|
22
22
|
- `scripts/verify-skills.mjs`:校验 skills 文档和 lock 是否同步。
|
|
23
|
-
- `scripts/sync-harness-docs.mjs`:校验 harness 文档入口是否完整、是否仍引用废弃清单。
|
|
24
|
-
- `scripts/check-project-structure.mjs`:校验通用前端结构护栏,不固化当前占位业务。
|
|
25
23
|
- `scripts/*.test.mjs`:校验 harness 脚本自身行为,防止门禁脚本静默漂移。
|
|
26
24
|
- `docs/harness-quick-reference.md`:给团队和 AI agent 使用的速查表。
|
|
27
25
|
- `docs/delivery-template.md`:AI 交付说明模板,要求每次交付暴露改动、影响范围、验证、人工复核项、人工决策和剩余风险。
|
|
28
|
-
- `openspec/`:业务规格事实源;`specs/` 保存当前有效规格,`changes/` 保存 PRD 或口头需求变更。
|
|
29
26
|
|
|
30
27
|
## 事实来源
|
|
31
28
|
|
|
@@ -36,14 +33,13 @@
|
|
|
36
33
|
- Review agent 定义以 `.claude/agents/*.md` 为准;`.claude/settings.json` 中的 Stop agent 内联 prompt 是当前强制 review 入口。
|
|
37
34
|
- Rules 以 `.claude/rules/*.md` 为准。
|
|
38
35
|
- Skills 以 `.claude/skills/` 为源,`skills-lock.json` 保存校验 hash。
|
|
39
|
-
- 业务规格以 OpenSpec 的 `openspec/specs/` 为准;待确认或待实现的业务变更以 `openspec/changes/<change-id>/` 为准。
|
|
40
36
|
- 人工审核交付格式以 `docs/delivery-template.md` 为准。
|
|
41
37
|
|
|
42
38
|
## 执行流程
|
|
43
39
|
|
|
44
40
|
1. 主 agent 探索代码和规则。
|
|
45
41
|
2. 主 agent 实现变更。
|
|
46
|
-
3. `Stop` hook 先运行 `pnpm format` 自动修复格式,再运行 `pnpm type-check`、`pnpm lint`、`pnpm harness:check
|
|
42
|
+
3. `Stop` hook 先运行 `pnpm format` 自动修复格式,再运行 `pnpm type-check`、`pnpm lint`、`pnpm harness:check`。完整检查请手动运行 `pnpm check`。
|
|
47
43
|
4. `Stop` agent hook 启动 review subagent。
|
|
48
44
|
5. review subagent 返回 `HARNESS_REVIEW_RESULT: PASS` 或 `HARNESS_REVIEW_RESULT: FAIL`。
|
|
49
45
|
6. 通过后发送系统通知;失败时阻止停止并要求主 agent 修复。
|
|
@@ -56,9 +52,8 @@
|
|
|
56
52
|
- `pnpm lint`:oxlint + ESLint。
|
|
57
53
|
- `pnpm format:check`:校验 Prettier 格式一致性。
|
|
58
54
|
- `pnpm check:fix`:显式执行格式化与 lint 自动修复。
|
|
59
|
-
- `pnpm harness:
|
|
60
|
-
- `pnpm harness:
|
|
61
|
-
- `pnpm harness:check`:skills、docs、lock、入口文档、项目结构护栏和 harness 脚本测试。
|
|
55
|
+
- `pnpm harness:test`:运行 harness 脚本测试,覆盖 skill 校验和 hooks 基础行为。
|
|
56
|
+
- `pnpm harness:check`:skills、lock 校验和 hooks 脚本测试。
|
|
62
57
|
- `pnpm build`:生产构建校验,适用于构建配置、路由、样式或依赖变更。
|
|
63
58
|
|
|
64
59
|
## 维护规则
|
|
@@ -69,7 +64,5 @@
|
|
|
69
64
|
- 修改 harness 配置后,先运行 `pnpm harness:sync` 再运行 `pnpm harness:check`。
|
|
70
65
|
- 修改人工审核流程或交付要求时,同步 `docs/review-checklist.md` 和 `docs/delivery-template.md`。
|
|
71
66
|
- 当前业务代码是占位实现,不允许把项目看板、审核流、mock 数据或临时页面形态沉淀为 harness 规则或工程规范。
|
|
72
|
-
- 业务规格(页面布局、字段口径、状态机、角色权限矩阵、接口契约等)统一放在 OpenSpec。当前有效规格沉淀到 `openspec/specs/`;PRD 链接、钉钉文档更新或口头需求变更先建 `openspec/changes/<change-id>/`,明确来源、影响页面、接口影响、验收标准和待确认问题。
|
|
73
|
-
- OpenSpec 不自动进入 Claude Code 默认上下文,由 AI 按任务需要主动 view。harness 文档(本文件、`docs/harness-quick-reference.md`、`.claude/rules/*`、`CLAUDE.md`)只引用 OpenSpec 入口,不复制业务内容。
|
|
74
67
|
- 反复使用的业务规则(全局权限矩阵、跨页面状态机)应做成 `.claude/skills/<name>/`,由 AI 按需加载。
|
|
75
68
|
- `quality-gate.cjs stop` 是唯一允许自动格式化的 hook,用于 Stop 收尾;其他 hooks 不得自动格式化、暂存或改写 repo-tracked 文件。`lint-staged` 作为 pre-commit safety net 是另一处显式例外。
|
|
@@ -7,9 +7,6 @@
|
|
|
7
7
|
| 场景 | 优先使用 | 说明 |
|
|
8
8
|
| ----------------------------------------- | ------------------------- | -------------------------------------------------------------- |
|
|
9
9
|
| Vue、Vite、Pinia、Vue Router、`.vue` 组件 | `vue-best-practices` | 先确认 Composition API、组件边界、响应式数据和类型规则。 |
|
|
10
|
-
| Element Plus 表单、表格、弹窗、主题覆盖 | `element-plus-vue3` | 先查组件用法、props/events、主题和按需导入规则。 |
|
|
11
|
-
| 页面视觉优化、组件美化、前端交互体验 | `frontend-design` | 用于提高 UI 质量,但必须服从项目样式规则和 Element Plus 约束。 |
|
|
12
|
-
| 路由、权限、菜单来源、布局、全局样式 | `project-structure-guard` | 避免破坏后端菜单树动态路由模型和前端结构护栏。 |
|
|
13
10
|
| 不确定是否已有合适 skill | `find-skills` | 先查可用 skill,再决定是否安装或创建项目内 skill。 |
|
|
14
11
|
|
|
15
12
|
Harness 配置、hooks、review agents、skills 同步和治理文档变更没有专用 skill;先读 `docs/ai-harness.md` 和 `.claude/agents/harness-reviewer.md`。
|
|
@@ -29,7 +26,7 @@ Harness 配置、hooks、review agents、skills 同步和治理文档变更没
|
|
|
29
26
|
| Hook | 作用 |
|
|
30
27
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
|
|
31
28
|
| `guard-tool.cjs` | 阻断危险命令和敏感文件访问。 |
|
|
32
|
-
| `quality-gate.cjs` | Stop 时先自动格式化,再运行 type-check / lint / harness:check
|
|
29
|
+
| `quality-gate.cjs` | Stop 时先自动格式化,再运行 type-check / lint / harness:check;完整检查用 `pnpm check`。 |
|
|
33
30
|
| `notify.cjs` | 处理 macOS/Windows 完成通知和失败通知。 |
|
|
34
31
|
|
|
35
32
|
详细行为见 `docs/ai-harness.md`。
|
|
@@ -50,25 +47,15 @@ pnpm check:fix # 格式化 + lint 修复
|
|
|
50
47
|
pnpm format # 全量格式化
|
|
51
48
|
pnpm format:check # 只检查
|
|
52
49
|
pnpm check:fix # 格式化 + lint 修复
|
|
53
|
-
pnpm check # 完整质量门禁(type-check + lint + format:check +
|
|
50
|
+
pnpm check # 完整质量门禁(type-check + lint + format:check + harness:check)
|
|
54
51
|
pnpm build # 生产构建校验
|
|
55
|
-
pnpm openspec:check # 校验 OpenSpec specs/changes
|
|
56
|
-
pnpm openspec:list # 列出 OpenSpec 变更
|
|
57
|
-
pnpm openspec:specs # 列出 OpenSpec 当前规格
|
|
58
52
|
pnpm harness:sync # 同步 skill lock
|
|
59
53
|
pnpm harness:check # harness 脚本验证
|
|
60
|
-
pnpm harness:structure # 项目结构护栏检查
|
|
61
54
|
pnpm harness:test # harness 脚本测试
|
|
62
55
|
```
|
|
63
56
|
|
|
64
57
|
详细格式化规则见 `.claude/rules/formatting.md`。
|
|
65
58
|
|
|
66
|
-
## OpenSpec 业务规格
|
|
67
|
-
|
|
68
|
-
产品 PRD、钉钉文档链接和口头需求都是原始输入;可执行的业务规格事实源是 OpenSpec。当前有效规格放在 `openspec/specs/`,每次 PRD 更新或口头变更先建 `openspec/changes/<change-id>/`,再进入代码实现。
|
|
69
|
-
|
|
70
|
-
OpenSpec 只管理业务规格和需求变更;Harness 继续管理 AI 执行规则、hooks、skills、review 和质量门禁。不要把页面业务规则复制进 harness 文档,也不要用 OpenSpec 替代 `pnpm check`、review 或人工复核。
|
|
71
|
-
|
|
72
59
|
## 本地浏览器验证
|
|
73
60
|
|
|
74
61
|
涉及新页面、UI 调整、路由权限、表单、弹窗、上传、布局或全局样式时,按 `docs/verification.md` 做本地浏览器验证。纯文档、纯脚本、无运行时 UI 影响的 harness 变更可以不做浏览器验证,但交付说明中要说明原因。
|
|
@@ -39,7 +39,7 @@ Claude Code 完成任务后,提交前必须由人做最终复核。AI 的职
|
|
|
39
39
|
5. 重新验证:运行对应检查,必要时重新做浏览器验证。
|
|
40
40
|
6. 二次交接:输出第二版交接单,说明修了什么、哪些决策已确认、还剩什么风险。
|
|
41
41
|
|
|
42
|
-
如果人工确认某条复杂业务规则是长期规则,应要求 AI
|
|
42
|
+
如果人工确认某条复杂业务规则是长期规则,应要求 AI 后续沉淀到规格文档、类型、测试或代码约束中,而不是只留在聊天记录里。
|
|
43
43
|
|
|
44
44
|
## 验证记录
|
|
45
45
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 本地浏览器验证
|
|
2
|
+
|
|
3
|
+
涉及可见行为的改动,提交前必须在本地浏览器实际跑一遍,不能只靠类型检查和 lint。本文件定义何时验证、验证什么、以及如何在交付说明里记录。
|
|
4
|
+
|
|
5
|
+
## 何时必须做浏览器验证
|
|
6
|
+
|
|
7
|
+
出现以下任一情况时必须验证:
|
|
8
|
+
|
|
9
|
+
- 新增或修改页面、路由。
|
|
10
|
+
- 路由权限、菜单可见性、登录态相关改动。
|
|
11
|
+
- 表单(校验、提交、重置)、弹窗(打开/关闭/数据加载)、上传。
|
|
12
|
+
- 列表(分页、搜索、筛选)、表格交互。
|
|
13
|
+
- 全局样式、布局、主题覆盖。
|
|
14
|
+
|
|
15
|
+
纯文档、纯脚本、无运行时 UI 影响的 harness 变更可以不做浏览器验证,但要在交付说明中说明原因。
|
|
16
|
+
|
|
17
|
+
## 验证什么
|
|
18
|
+
|
|
19
|
+
- **功能正确**:操作路径走通,数据正确加载和提交,错误提示符合预期。
|
|
20
|
+
- **路由与权限**:目标页面可达,无权限时正确拦截,刷新后状态保持。
|
|
21
|
+
- **表单校验**:必填、格式(如手机号)、边界值都触发预期校验。
|
|
22
|
+
- **弹窗生命周期**:打开重新挂载、关闭销毁、成功后通知父组件刷新。
|
|
23
|
+
- **控制台**:无报错、无未捕获 Promise reject、无关键 warning。
|
|
24
|
+
|
|
25
|
+
## 如何记录
|
|
26
|
+
|
|
27
|
+
在交付说明(见 `docs/delivery-template.md`)的「AI 已验证 / 需要人工复核」中写清:
|
|
28
|
+
|
|
29
|
+
- 验证方式(手动点击 / 截图 / 录屏)。
|
|
30
|
+
- 使用的账号或角色、页面路径、数据条件。
|
|
31
|
+
- 实际看到的结果。
|
|
32
|
+
|
|
33
|
+
如果某些场景未验证(环境受限、缺测试数据、依赖后端联调等),必须在「剩余风险」中明确列出未验证项,不能默认通过。
|
|
34
|
+
|
|
35
|
+
> 强制 review 通过不代表 UI 已验证。可见行为的最终确认由人工浏览器复核完成。
|
|
@@ -21,18 +21,10 @@ const lockPath = path.join(root, 'skills-lock.json')
|
|
|
21
21
|
const writeMode = process.argv.includes('--write')
|
|
22
22
|
|
|
23
23
|
const defaultSources = {
|
|
24
|
-
'element-plus-vue3': {
|
|
25
|
-
source: 'partme-ai/full-stack-skills',
|
|
26
|
-
sourceType: 'github',
|
|
27
|
-
},
|
|
28
24
|
'find-skills': {
|
|
29
25
|
source: 'anthropics/skills',
|
|
30
26
|
sourceType: 'github',
|
|
31
27
|
},
|
|
32
|
-
'frontend-design': {
|
|
33
|
-
source: 'anthropics/claude-code',
|
|
34
|
-
sourceType: 'github',
|
|
35
|
-
},
|
|
36
28
|
'vue-best-practices': {
|
|
37
29
|
source: 'moeru-ai/airi',
|
|
38
30
|
sourceType: 'github',
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# OpenSpec 业务规格
|
|
2
|
+
|
|
3
|
+
本项目用 [OpenSpec](https://github.com/Fission-AI/OpenSpec) 管理业务规格:产品 PRD、钉钉文档、口头需求都是原始输入,可执行的事实源是 `openspec/`。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- `openspec/specs/`:当前已生效的业务规格(事实源)。
|
|
8
|
+
- `openspec/changes/`:进行中的变更提案;每个变更一个目录(proposal、tasks、受影响的 specs)。
|
|
9
|
+
- `openspec/changes/archive/`:已完成并归档的变更。
|
|
10
|
+
|
|
11
|
+
## 工作流(通过 Claude Code 斜杠命令驱动)
|
|
12
|
+
|
|
13
|
+
1. `/opsx:propose "需求描述"` — 基于需求生成变更提案(proposal + 受影响 specs + tasks)。
|
|
14
|
+
2. `/opsx:apply` — 按提案的 tasks 实现代码。
|
|
15
|
+
3. `/opsx:archive` — 变更完成后,把它合并进 `openspec/specs/` 并归档。
|
|
16
|
+
|
|
17
|
+
辅助命令:`/opsx:explore`(探索现有规格)、`/opsx:sync`(同步 specs)。
|
|
18
|
+
|
|
19
|
+
## 命令行
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm openspec:list # 列出当前变更
|
|
23
|
+
pnpm openspec:validate # 校验所有 specs 和 changes(pnpm check 也会跑)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 与 Harness 的分工
|
|
27
|
+
|
|
28
|
+
- OpenSpec 只管**业务规格和需求变更**(页面布局、字段口径、状态机、角色权限矩阵、接口契约)。
|
|
29
|
+
- Harness 继续管 **AI 执行规则、hooks、skills、review 和质量门禁**。
|
|
30
|
+
- 不要把业务规则复制进 harness 文档;也不要用 OpenSpec 替代 `pnpm check`、code review 或人工复核。
|
|
31
|
+
|
|
32
|
+
## 何时用
|
|
33
|
+
|
|
34
|
+
新增页面/接口、改业务流程、调整权限模型前,先在 OpenSpec 建变更,明确来源、影响范围、验收标准和待确认问题,再进入代码实现。纯样式微调、bug 修复等无规格影响的改动可不走 OpenSpec。
|