@dianzhong/create-harness-app 0.1.3 → 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 CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import path7 from "path";
5
- import fse2 from "fs-extra";
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
- return { projectName, router, pinia, vitest, uiLibrary, axios, harness };
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 path6 from "path";
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 = path6.join(projectRoot, "package.json");
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,6 +373,18 @@ 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);
377
+ const existingScripts = pkg.scripts ?? {};
378
+ if (existingScripts.format?.includes("--write") && !existingScripts["format:check"]) {
379
+ add.scripts["format:check"] = existingScripts.format.replace("--write", "--check");
380
+ }
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
+ }
332
388
  }
333
389
  pkg = mergePackageJson(pkg, add);
334
390
  await writeJson(pkgPath, pkg);
@@ -349,6 +405,7 @@ function parseArgs(argv) {
349
405
  uiLibrary: argv.find((a) => a.startsWith("--ui="))?.split("=")[1],
350
406
  harness: argv.find((a) => a.startsWith("--harness="))?.split("=")[1],
351
407
  noAxios: argv.includes("--no-axios"),
408
+ noOpenspec: argv.includes("--no-openspec"),
352
409
  vitest: argv.includes("--vitest")
353
410
  };
354
411
  }
@@ -358,17 +415,18 @@ async function main() {
358
415
  log(`Usage: create-harness-app [project-name] [options]
359
416
 
360
417
  Options:
361
- --yes Non-interactive: TS + Router + Pinia + ElementPlus + axios + harness:full
418
+ --yes Non-interactive: TS + Router + Pinia + ElementPlus + axios + harness:full + OpenSpec
362
419
  --ui=<lib> element-plus | ant-design-vue | none (default: element-plus)
363
420
  --harness=<level> full | minimal | none (default: full)
364
421
  --no-axios Skip axios layer
422
+ --no-openspec Skip OpenSpec spec management
365
423
  --vitest Enable Vitest
366
424
  -h, --help Show this help
367
425
 
368
426
  Examples:
369
427
  npm create @dianzhong/harness-app my-app
370
428
  npm create @dianzhong/harness-app my-app -- --yes
371
- 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
372
430
  `);
373
431
  process.exit(0);
374
432
  }
@@ -380,22 +438,23 @@ Examples:
380
438
  uiLibrary: args.uiLibrary ?? base.uiLibrary,
381
439
  harness: args.harness ?? base.harness,
382
440
  axios: !args.noAxios,
441
+ openspec: !args.noOpenspec,
383
442
  vitest: args.vitest
384
443
  };
385
- 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}`);
386
445
  } else {
387
446
  config = await collectConfig(args.projectName);
388
447
  }
389
- const targetDir = path7.resolve(process.cwd(), config.projectName);
390
- if (await fse2.pathExists(targetDir)) {
391
- const entries = await fse2.readdir(targetDir);
448
+ const targetDir = path8.resolve(process.cwd(), config.projectName);
449
+ if (await fse4.pathExists(targetDir)) {
450
+ const entries = await fse4.readdir(targetDir);
392
451
  if (entries.length > 0) {
393
452
  error(`\u76EE\u5F55 "${config.projectName}" \u5DF2\u5B58\u5728\u4E14\u4E0D\u4E3A\u7A7A`);
394
453
  process.exit(1);
395
454
  }
396
455
  }
397
456
  info("\u4F7F\u7528 create-vue \u751F\u6210\u57FA\u7840\u9AA8\u67B6...");
398
- await spawnCreateVue(config, path7.dirname(targetDir));
457
+ await spawnCreateVue(config, path8.dirname(targetDir));
399
458
  info("\u53E0\u52A0 features...");
400
459
  await overlay(targetDir, config);
401
460
  success("\u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
@@ -405,6 +464,7 @@ Examples:
405
464
  log(" pnpm install");
406
465
  if (config.harness === "full") log(" pnpm harness:sync # \u91CD\u7B97 skills \u6307\u7EB9");
407
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');
408
468
  }
409
469
  main().catch((err) => {
410
470
  error(String(err.message ?? err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dianzhong/create-harness-app",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "点众前端项目初始化器:封装 create-vue,叠加 UI 库、axios 层与 AI Harness 治理体系",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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/sync-harness-docs.mjs`、`scripts/check-project-structure.mjs`、`scripts/*.test.mjs`、`skills-lock.json`、`package.json`、`CLAUDE.md`、`CONTRIBUTING.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 审查不能替代人工最终复核。",
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`。注意:Stop hook 有意省略 `openspec:check`(该检查较慢,且仅在业务规格变更时有意义);完整检查请手动运行 `pnpm 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:structure`:通用前端结构护栏检查,覆盖 route meta 基础约束、动态菜单组件解析、明显第二套菜单/权限源和高风险 Element Plus 深层覆盖提示。
60
- - `pnpm harness:test`:运行 harness 脚本测试,覆盖结构检查、skill 校验和 hooks 基础行为。
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 是另一处显式例外。
@@ -63,4 +63,4 @@ AI 已验证:
63
63
  - ...
64
64
  ```
65
65
 
66
- 如果人工确认某条复杂业务规则是长期规则,后续应沉淀到 OpenSpec 规格、类型、测试或代码约束中,而不是只留在聊天记录里。
66
+ 如果人工确认某条复杂业务规则是长期规则,后续应沉淀到规格文档、类型、测试或代码约束中,而不是只留在聊天记录里。
@@ -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(有意省略 openspec:check,完整检查用 `pnpm 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 + openspec:check + harness: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 后续沉淀到 OpenSpec 规格、类型、测试或代码约束中,而不是只留在聊天记录里。
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。