scrum-skills 0.3.4 → 0.3.5

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 (61) hide show
  1. package/README.md +224 -224
  2. package/assets/board/200.html +1 -1
  3. package/assets/board/404.html +1 -1
  4. package/assets/board/_nuxt/{D1V1gysQ.js → 1XlYaCp9.js} +1 -1
  5. package/assets/board/_nuxt/BKrRful0.js +3 -0
  6. package/assets/board/_nuxt/{DwzGxoX2.js → BXL77dAA.js} +1 -1
  7. package/assets/board/_nuxt/{Utb4TVOl.js → BbxmvdIF.js} +1 -1
  8. package/assets/board/_nuxt/{DwCE3305.js → BizDeOK5.js} +1 -1
  9. package/assets/board/_nuxt/{BGwhkDRl.js → BtzUbHO_.js} +1 -1
  10. package/assets/board/_nuxt/{BmrSh0rL.js → COecDWbb.js} +1 -1
  11. package/assets/board/_nuxt/{CoipdpDu.js → ChUHb39z.js} +1 -1
  12. package/assets/board/_nuxt/{C-fx-vFO.js → CtFknnHZ.js} +1 -1
  13. package/assets/board/_nuxt/{BU8vdZUs.js → DKRYKLGF.js} +61 -50
  14. package/assets/board/_nuxt/{Cajxc4Dw.js → DZ--fXuU.js} +1 -1
  15. package/assets/board/_nuxt/D_ct-Stz.js +1 -1
  16. package/assets/board/_nuxt/_id_.ItzoSaBL.css +1 -0
  17. package/assets/board/_nuxt/builds/latest.json +1 -1
  18. package/assets/board/_nuxt/builds/meta/205ddf7c-da58-4213-ac80-82930a216157.json +1 -0
  19. package/assets/board/_nuxt/burndown.CT8fx1Ge.css +1 -0
  20. package/assets/board/_nuxt/diagrams.BWgRXpo3.css +1 -0
  21. package/assets/board/_nuxt/{entry.BBv-sBnP.css → entry.DplN301E.css} +1 -1
  22. package/assets/board/_nuxt/epics.CUeDIEa0.css +1 -0
  23. package/assets/board/_nuxt/error-404.Y9imKiDC.css +1 -0
  24. package/assets/board/_nuxt/error-500.DufSBGVC.css +1 -0
  25. package/assets/board/_nuxt/gantt.CiC2Ts1t.css +1 -0
  26. package/assets/board/_nuxt/last-change.Byf5fhdY.css +1 -0
  27. package/assets/board/_nuxt/milestones.Civ3sMiQ.css +1 -0
  28. package/assets/board/_nuxt/{b7xtxFRl.js → oSJF4yHw.js} +1 -1
  29. package/assets/board/_nuxt/pages.BshWbOyo.css +1 -0
  30. package/assets/board/_nuxt/tests.Ci9fBCka.css +1 -0
  31. package/assets/board/burndown/index.html +1 -1
  32. package/assets/board/diagrams/index.html +1 -1
  33. package/assets/board/epics/index.html +1 -1
  34. package/assets/board/favicon.svg +16 -16
  35. package/assets/board/gantt/index.html +1 -1
  36. package/assets/board/index.html +1 -1
  37. package/assets/board/last-change/index.html +1 -1
  38. package/assets/board/milestones/index.html +1 -1
  39. package/assets/board/tests/index.html +1 -1
  40. package/bin/scrum-skills.mjs +204 -204
  41. package/docs/ai-assistant-guide.md +1009 -1008
  42. package/package.json +41 -42
  43. package/templates/AGENTS.md +84 -84
  44. package/tools/agile-tools.mjs +510 -510
  45. package/tools/ai.mjs +508 -464
  46. package/tools/git-commit.mjs +216 -216
  47. package/tools/projects.mjs +240 -240
  48. package/tools/serve.mjs +180 -180
  49. package/assets/board/_nuxt/Dmg78zOY.js +0 -3
  50. package/assets/board/_nuxt/_id_.B6iqDPxe.css +0 -1
  51. package/assets/board/_nuxt/builds/meta/08bebe14-0f1b-4d2a-8de4-e84e122d10e9.json +0 -1
  52. package/assets/board/_nuxt/burndown.Do6IbM6o.css +0 -1
  53. package/assets/board/_nuxt/diagrams.pyhqNtmr.css +0 -1
  54. package/assets/board/_nuxt/epics.Dmessplu.css +0 -1
  55. package/assets/board/_nuxt/error-404.CgJNdpB5.css +0 -1
  56. package/assets/board/_nuxt/error-500.WYSoH44y.css +0 -1
  57. package/assets/board/_nuxt/gantt.Dmp00GUo.css +0 -1
  58. package/assets/board/_nuxt/last-change.1Su4GrCN.css +0 -1
  59. package/assets/board/_nuxt/milestones.U0xKCsEo.css +0 -1
  60. package/assets/board/_nuxt/pages.Dbj2IVk8.css +0 -1
  61. package/assets/board/_nuxt/tests.dXlUX16A.css +0 -1
package/README.md CHANGED
@@ -1,224 +1,224 @@
1
- # scrum-skills
2
-
3
- 结构化的敏捷数据 + TDD 纪律 + 开箱就能看的看板,给「和 AI 一起做项目」这件事用。
4
-
5
- AI 只负责把需求、任务、实施过程写成**结构化 JSON**,人打开浏览器就能看到看板、甘特图、里程碑、燃尽图和每个任务的 TDD 追溯链。数据是唯一事实来源,对话记忆不作数。
6
-
7
- ![敏捷看板](docs/board.png)
8
-
9
- ## 为什么
10
-
11
- - **AI 写代码很快,但「做到哪了」全靠人问** → 每个故事/任务的状态、实施日志都落在 `agile/` 里,随时可查
12
- - **TDD 靠自觉,容易悄悄跳过** → 校验器强制「先有失败的测试,才有实现」:task 过了 red 阶段,日志里必须有一条 `fail` 记录和一条 `pass` 记录
13
- - **看板工具要么太重、要么数据锁死** → 几个 JSON 文件 + 一个零依赖 server,数据在你自己仓库里,`git diff` 就能审
14
-
15
- ## 快速开始
16
-
17
- ```bash
18
- # 1. 在任意项目里初始化(生成 AGENTS.md、四个 skill、数据骨架)
19
- npx scrum-skills init .
20
-
21
- # 2. 让 AI 读 AGENTS.md,用 scrum-planning 建第一个 story
22
- # (Claude Code / Cursor / CodeBuddy 等都会读这个文件)
23
-
24
- # 3. 校验数据
25
- npx scrum-skills validate
26
-
27
- # 4. 打开看板
28
- npx scrum-skills serve # → http://localhost:4173
29
- ```
30
-
31
- `init` 会建出 `agile/` 的数据骨架(含空的 `tests.json`),并把四个 skill 装进 `.agents/skills/`:
32
-
33
- | skill | 干什么 |
34
- |---|---|
35
- | `scrum-planning` | 需求澄清 → 写史诗/故事/验收标准/拆任务 |
36
- | `scrum-sprint` | 排期、组建迭代、状态流转、里程碑、收尾 |
37
- | `scrum-tdd` | red → green → refactor 循环 + 过程落库 |
38
- | `scrum-report` | 看板 / 甘特 / 里程碑 / 燃尽 / 测试用例 / 追溯链 |
39
-
40
- ## 命令
41
-
42
- | 命令 | 说明 |
43
- |---|---|
44
- | `scrum-skills init [dir] [--force]` | 初始化(已有 `AGENTS.md` 时拒绝,`--force` 先备份成 `AGENTS.md.<时间戳>.bak` 再覆盖) |
45
- | `scrum-skills update [dir] [--dry-run]` | 只刷新框架文件(skill / AGENTS.md / 校验器),**绝不碰 `agile/` 数据**;除 `AGENTS.md` 外一律无条件覆盖、不备份,`AGENTS.md` 覆盖前备份成 `AGENTS.md.<时间戳>.bak` |
46
- | `scrum-skills validate [dir]` | 校验数据(引用完整性 / 状态机 / TDD 纪律 / 测试资产 / 里程碑 / 依赖无环) |
47
- | `scrum-skills serve [dir] [port]` | 起看板(默认当前目录,端口 4173),**校验不通过时拒绝出报表**;打开的就是启动目录,上次打开过的项目只留在「最近项目」里供手动切回 |
48
-
49
- `scsk` 是 `scrum-skills` 的等价简写,全局安装后两个命令都可直接用,如 `scsk serve`。
50
-
51
- > **`update` 会整篇覆盖框架文件**(`AGENTS.md`、`.agents/`、`tools/`),并且按两类处理:
52
- > - `.agents/`、`tools/` 下的文件**无条件覆盖、不留备份**——它们是包里的框架文件,本地改过就直接被替换掉;
53
- > - `AGENTS.md` **覆盖前备份成 `AGENTS.md.<yyyyMMdd-HHmmss>.bak`**(不会顶掉上一次的备份),因为项目特有纪律都写在里面。
54
- >
55
- > 输出里会点名 `AGENTS.md` 的备份文件,并提示用什么话让 agent 合并:直接说「对比 `AGENTS.md` 与 `AGENTS.md.20261026-113045.bak`,把我的本地改动合并回来」即可。合并确认无误后再删备份。
56
- > 只想知道会动哪些文件,加 `--dry-run`:只报告,不写文件、不建备份。
57
-
58
- ## 数据长什么样
59
-
60
- `agile/` 下三个常驻文件 + 两个可选文件:
61
-
62
- | 文件 | 内容 |
63
- |---|---|
64
- | `agile/backlog.json` | 史诗 `epics`(含量化目标 `keyResults`)+ 故事 `stories`(内联 `tasks`) |
65
- | `agile/sprints.json` | 迭代 `sprints` + 里程碑 `milestones`(MVP 节点 = `kind: "mvp"`) |
66
- | `agile/journal.json` | 每个任务一条 TDD 日志 `journals`(内联 `entries`) |
67
- | `agile/tests.json` | 测试资产(可选):`suites` 按层 × 级别装用例(`covers` 指向验收),`runs` 存执行记录与代码覆盖率;结构见 schema 第 5 节 |
68
- | `agile/last-change.json` | 最近一次交付的汇报(可选;已提交才覆盖,未提交则合并):变更清单 / 额外工作 / 验证 / 入口与账号 / 测试前准备 / 测试场景 / 说明 |
69
-
70
- ```jsonc
71
- // 一条故事(节选)
72
- {
73
- "id": "S-017",
74
- "type": "bug", // feature | bug | debt | spike
75
- "priority": "P1", // P0~P3
76
- "value": 8, // WSJF = value / points
77
- "bug": { // type=bug 时必填三件套
78
- "severity": "major",
79
- "environment": "Chrome 120 / Windows 11",
80
- "steps": "1. 勾选子任务;2. 返回看板;3. 拖动卡片 —— 进度条仍是旧值"
81
- },
82
- "acceptance": [
83
- { "given": "…", "when": "…", "then": "…", "verifyBy": ["S-017-T1"] }
84
- ],
85
- "points": 2,
86
- "status": "ready",
87
- "sprintId": null,
88
- "schedule": { "start": "2026-10-20", "end": "2026-10-23" }, // 甘特图的计划条
89
- "dependsOn": ["S-004"], // 依赖,必须无环
90
- "blocked": { "reason": "等复现环境确认", "since": "2026-10-22" },
91
- "scopeChange": { "kind": "added-mid-sprint", "at": "2026-10-15", "reason": "客户临时插需求" },
92
- "tasks": [
93
- { "id": "S-017-T1", "title": "写复现缺陷的失败测试", "type": "test", "status": "todo", "files": [] }
94
- ]
95
- }
96
- ```
97
-
98
- 迭代 id 形如 `R1-S1`:`R<发布周期>`(一个发布周期 = 4 周 = 2 个冲刺)× `S<期内冲刺号>`;小团队也可以只用 `S-01` 纯序号,但同一项目不许混用。
99
-
100
- ## 看板有什么
101
-
102
- | 页面 | 内容 |
103
- |---|---|
104
- | `/` 看板 | 六列(草稿/就绪/进行中/评审/完成/已取消),按史诗/里程碑筛选,按优先级或 WSJF 重排;卡片带类型、阻塞、范围变更标记 |
105
- | `/last-change` 最后更新 | 渲染 `agile/last-change.json`:故事/任务变更清单、额外做的事、验证结果、调试入口与账号、测试前准备、逐条测试场景、说明与遗留;变更清单抬头可「复制变更信息」(生成 Conventional Commits 提交信息)或「自动提交」(看清信息与文件清单后 `git add -A` + `git commit`,只提交到本地、不 push);点「查看历史」按 git 提交翻看任一版本(只读,顶部页签可在最新/历史间切换);没有该文件时给空状态 |
106
- | `/epics` 史诗与目标 | 每个史诗的关键结果(KR)进度、故事完成度、阻塞与缺陷统计 |
107
- | `/gantt` 甘特图 | 迭代分带,浅色条=计划、实心条=journal 推出的实际执行区间,菱形=里程碑,依赖与阻塞清单 |
108
- | `/milestones` 里程碑 / MVP | 按 kind 分组,显示达成口径、挂的故事、剩余天数 |
109
- | `/burndown` 燃尽图 | 实线=真实剩余,虚线=理想线,竖线标出**哪一天插了需求**(范围变更) |
110
- | `/tests` 测试用例 | QA 交付视图:需求覆盖(按验收条的分子/分母)、前端/后端/E2E 分层概览与**故事 × 层覆盖矩阵**、用例清单(可展开步骤与预期)、执行记录、缺口与风险(未覆盖验收 + 最近失败用例);没有 `agile/tests.json` 时给空状态并说明谁来写 |
111
- | `/diagrams` 架构图例 | 「实现过程中顺手画的图」:左边按类型 / 关键词 / 是否过期筛,右边 sandbox iframe 预览;图挂在故事上,故事详情页有反向入口;没有 `agile/diagrams.json` 时给空状态并说明怎么登记 |
112
- | `/story/:id` 追溯链 | 验收标准 → 任务 → 每条 red/green/refactor 记录,含测试命令与产出文件;底部有该故事的架构图例入口 |
113
-
114
- > **并排指标卡的配色顺序固定为 绿 → 蓝 → 紫 → 黑,卡片更多才往后用黄、红**。顺序只在 `apps/board/app/composables/model.ts` 的 `CARD_TONES` 里定义一次,页面写 `:class="cardToneClass(i)"`、光斑写 `:tone="cardTone(i)"`;黑卡是深底,文字要翻白。新增页面照着取色,别另起一套。
115
-
116
- ## 内置 AI 助手
117
-
118
- 看板右下角有个悬浮按钮,点开是右侧抽屉——不用离开看板就能让模型查/改当前项目的敏捷数据。
119
-
120
- **第一次打开先填参数**(存在本机 `%APPDATA%/scrum-skills/ai.json`,权限 0600,不会写进项目目录):
121
-
122
- | 字段 | 说明 |
123
- |---|---|
124
- | 服务商 | DeepSeek / OpenAI / Moonshot / 硅基流动 / 本地 Ollama / 自定义,选中后自动带出接口地址与模型名 |
125
- | 接口地址 · API Key · 模型名 | OpenAI 兼容端点即可;Ollama 可以留空 Key |
126
- | 系统提示词 · 温度 | 系统提示词默认留空:内置说明只讲「能碰哪些数据 / 两个工具 / 四条回答纪律」,不写人设也不提厂商;温度默认 0.3(改数据这类活不需要发挥) |
127
- | 工具调用轮数上限 | 默认 30:模型连着调工具时跑到上限会强制收口一轮 |
128
- | 上下文上限 · 超长自动压缩 | 默认 256000 token;超了先把老对话压掉,系统提示词与当前提问永远保留 |
129
-
130
- **Key 不出本机**:浏览器只调本机 `/api/ai/*`,模型请求由 `tools/serve.mjs` 代为发出,读配置只回 `hasKey`。
131
-
132
- **回答长什么样**(照官方 chat 模板的版式):助手正文按 markdown 渲染(标题、列表、表格、行内代码都能显示),工具调用折成一行「更新敏捷数据 · 关键词」可展开看参数与结果,模型的思考过程收成「思考了 N 秒」可折叠块,待审变更是一张能点批准/驳回的卡片。markdown 走 `@comark/nuxt`(AST → Vue 组件),不用 `v-html` 拼 HTML。
133
-
134
- **它手上只有两件工具**:
135
-
136
- - `search_agile`:在 `agile/backlog|sprints|journal` 里按记录检索(`S-011 阻塞` 这种多关键词要同时命中同一条),命中带出可直接写补丁的路径
137
- - `update_agile`:按路径打补丁,例如 `stories.S-011.status = review`;写前跑 `tools/validate.mjs`,校验不过自动回滚成改前内容
138
-
139
- **输入框左下角四个图标按钮,从左到右固定这个顺序**(加东西在左、策略在最右):
140
-
141
- 1. **上传图片**:一次最多 4 张,输入框里按 <kbd>Ctrl</kbd>+<kbd>V</kbd> 也能粘贴截图。图片先在本地缩到长边 1400 再按 JPEG 编码,以缩略图挂在输入框上方(可逐张删除),发送后随文字按 OpenAI 多模态格式(`image_url`)带给模型。历史里只带最近 4 张图,更早的换成一句「图片已省略」占位;服务端按每张 800 token 计入上下文预算。模型本身不支持图片时(例如 DeepSeek 的 chat / reasoner),报错会直接提示换成支持视觉模型的接口。
142
- 2. **新建会话**(`+` 图标):先把当前这一轮完整存进本地历史,再清空对话区回到欢迎页
143
- 3. **历史会话**:弹出列表,每条给出标题(第一条提问的开头)、相对时间与消息条数,点一条即可切回去接着问;当前那条有底色
144
- 4. **审核策略**(盾牌 / 闪电图标,点一下弹下拉,当前项打勾):**手动审核**(默认)改文件前先出「待审变更」卡,逐条列出路径与改前改后值,点批准才写入,驳回则文件不动;**自动审核**直接写入 + 立即校验,失败回滚
145
-
146
- 会话只存在这台浏览器(`localStorage` 的 `scrum-skills:ai:threads:v1`,最多保留 20 个,超了丢最老的),服务端一概不知。**图片不落盘**——一张截图 base64 就有上百 KB,存几个会话就会把 localStorage 撑爆,所以落盘前清空图片 url;恢复后原位显示「图片未保存」占位,这类空图也不会被拼进下一次请求。抽屉每次打开时按主题存的那条会话自动恢复。抽屉是整屏覆盖的,打开期间会锁住背后主页的纵向滚动,关掉即恢复。
147
-
148
- 工具永远跟着「看板当前打开的项目」走,换项目不会串。
149
-
150
- > 纯静态托管(只放 `assets/board`)时没有 `/api/ai/*`,此时对话不可用;`pnpm serve` 才带这套能力。
151
-
152
- ## 交付说明:先查 git,再决定覆盖还是合并
153
-
154
- `agile/last-change.json` 只留最近一次交付,但「最近一次」的前提是**上一次已经进了版本库**。所以写入前先看这个文件在 git 里的状态:
155
-
156
- - **已提交、工作区干净** → 上一次已入库,整份覆盖,文件里只有本次交付;
157
- - **有未提交改动 / 已暂存未提交 / 未跟踪 / 不是 git 仓库** → 把本次结果**合并**进旧记录,旧的一条不丢(`changes` / `extras` / `verification` / `preparation` / `scenarios` 按 key 去重、新覆盖旧;`notes` / `open` 去重拼接;`access` 按地址与账号合并),并打上 `merged` + `mergedFrom`。
158
-
159
- ```bash
160
- node tools/last-change.mjs check # 退出码 0 = 可覆盖,2 = 必须先合并
161
- node tools/last-change.mjs write 记录.json # 按状态自动覆盖或合并,写后校验,不过就回滚
162
- node tools/last-change.mjs write 记录.json --replace # 确认旧记录不要了才强制覆盖
163
- ```
164
-
165
- 看板 `/last-change`(侧栏「最后更新」)页面上方就写着这条规则,并显示该文件当前是「已提交」还是「未提交改动」;合并过的记录会标出「合并记录 · N 次交付」。
166
-
167
- ## 校验器管什么
168
-
169
- `validate` 是这套体系的地基,**错误**会让它退出码非 0:
170
-
171
- - 引用完整性**双向**一致:`epic.stories` ↔ `story.epicId`、`sprint.stories` ↔ `story.sprintId`、`journal.taskId` ↔ task
172
- - 状态机:story `draft → ready → in-progress → review → done`(不做就走 `cancelled` 终态),task `todo → red → green → refactored → done`(只能前进)
173
- - **TDD 铁律**:task 到 green 之后,日志必须首条 `fail`、后有 `pass`;done 时末条必须是 `pass`
174
- - story 到 done:所有 task 必须 done,且不许仍标着 `blocked`
175
- - 已取消(`cancelled`):不要求验收标准、也不要求 task 全部 done;但还挂着 `blocked` 会报错,仍留在迭代里给 WARN —— 且它的点数**不计入承诺容量、燃尽图与里程碑完成度**,免得「不做了」把进度拖成永远完不成
176
- - 依赖:`dependsOn` 必须存在、不能自依赖、**不能成环**(DFS 检测)
177
- - 需求属性:`type` / `priority` / `value` 合法;`type: bug` 必须有 `severity` + `environment` + `steps`
178
- - 验收标准的 `verifyBy` 必须指向同一 story 的 task
179
- - 测试资产(`agile/tests.json`,可选):`layer`/`level`/`status` 枚举合法、用例 id 唯一、`covers` 指向的验收必须存在且序号不越界、`taskId` 属于 covers 里的故事、`runs` 时间正序、最新一次 `failedCases` 必须对应 `status: fail`、覆盖率 0~100
180
- - **收口纪律**(只查登记过用例的故事):done 的 `test` 型任务必须有对应用例;story 到 `review`/`done` 时每条验收必须被覆盖
181
- - 迭代 id 格式与序号连续、同时只能有一个活跃迭代
182
- - 里程碑 `reached` 时挂的故事必须全部完成
183
-
184
- 给 **WARN** 的(不阻塞):验收没绑验证任务、史诗缺 keyResults、计划区间超出迭代、承诺点数超容量、被阻塞的故事仍排在迭代里等。
185
-
186
- ## 本地开发
187
-
188
- ```bash
189
- pnpm install # 一次装齐根包与 apps/board(pnpm workspace,见 pnpm-workspace.yaml)
190
- pnpm dev # Nuxt dev(HMR),读仓库根的 agile/(/api/ai 走 server/middleware)
191
- AGILE_DIR=/别的项目 pnpm dev # 看别的项目的数据
192
-
193
- pnpm build # 构建看板静态产物 → assets/board
194
- pnpm validate # 只校验数据
195
- pnpm test # 回归测试(校验器 / 测试资产与覆盖计算 / 看板皮肤与配色 / 布局回归 / 卡片操作 / 项目目录 / 最后更新页与历史版本 / 交付说明写入策略 / 提交信息与自动提交 / AI 服务与面板,204 个用例)
196
- pnpm serve # 起看板(读目标目录的 agile/,同时提供 /api/ai)
197
- ```
198
-
199
- 看板源码在 `apps/board/`(Nuxt 4 + Nuxt UI,纯静态产物,无服务端、无外部字体/图标请求)。`assets/board/` 是构建产物,**不进 git**,发布前由 `prepublishOnly` 自动重建。
200
-
201
- ## 架构图例(内置 diagram-design)
202
-
203
- `.agents/skills/diagram-design/` 是内置的 [diagram-design](https://github.com/cathrynlavery/diagram-design)(MIT),
204
- 用 `scrum-skills init` / `update` 自动带上,**不需要另外安装,也不依赖 Python**:
205
-
206
- - 出图走 skill 的规范(45 类图型 + 设计铁律),产物是单文件自包含 HTML(内联 SVG);
207
- - 入门前先过质检:`node tools/diagram-lint.mjs <file>`(上游 `self_check.py` 的 Node 移植,
208
- 覆盖安全边界 / 文档完整性 / 无障碍契约 / viewBox 越界 / 动画契约);
209
- - 入库:`node tools/diagram.mjs add <file> --title "登录时序" --kind sequence --story S-001`,
210
- 它负责分配 id、拷进 `agile/diagrams/`、写索引、跑数据校验,失败整体回滚;
211
- - 索引是 `agile/diagrams.json`,产物在 `agile/diagrams/`,看板 `/diagrams` 页按故事与图型浏览,
212
- 列表悬停可放大预览,支持下载 SVG / PNG(PNG 由浏览器 canvas 现场栅格化,不引渲染依赖);
213
- - 老图要重画时,`node tools/diagram-import.mjs <file>` 把 `.mmd` / `.drawio` / `.excalidraw`
214
- 压成节点与连线摘要再交给规范重画。
215
-
216
- 内置副本由 `node scripts/sync-diagram-skill.mjs` 从上游同步(裁剪示例变体、剔除 `scripts/` 的 Python 实现、
217
- 把文档里的 Python 调用改写成上面的 Node 命令),来源与版本记在 `.agents/skills/diagram-design/UPSTREAM.json`。
218
-
219
- ## License
220
-
221
- MIT
222
-
223
- 内置的 `diagram-design` skill 同样是 MIT,版权归其作者(cathrynlavery),原文见
224
- `.agents/skills/diagram-design/LICENSE`。
1
+ # scrum-skills
2
+
3
+ 结构化的敏捷数据 + TDD 纪律 + 开箱就能看的看板,给「和 AI 一起做项目」这件事用。
4
+
5
+ AI 只负责把需求、任务、实施过程写成**结构化 JSON**,人打开浏览器就能看到看板、甘特图、里程碑、燃尽图和每个任务的 TDD 追溯链。数据是唯一事实来源,对话记忆不作数。
6
+
7
+ ![敏捷看板](docs/board.png)
8
+
9
+ ## 为什么
10
+
11
+ - **AI 写代码很快,但「做到哪了」全靠人问** → 每个故事/任务的状态、实施日志都落在 `agile/` 里,随时可查
12
+ - **TDD 靠自觉,容易悄悄跳过** → 校验器强制「先有失败的测试,才有实现」:task 过了 red 阶段,日志里必须有一条 `fail` 记录和一条 `pass` 记录
13
+ - **看板工具要么太重、要么数据锁死** → 几个 JSON 文件 + 一个零依赖 server,数据在你自己仓库里,`git diff` 就能审
14
+
15
+ ## 快速开始
16
+
17
+ ```bash
18
+ # 1. 在任意项目里初始化(生成 AGENTS.md、四个 skill、数据骨架)
19
+ npx scrum-skills init .
20
+
21
+ # 2. 让 AI 读 AGENTS.md,用 scrum-planning 建第一个 story
22
+ # (Claude Code / Cursor / CodeBuddy 等都会读这个文件)
23
+
24
+ # 3. 校验数据
25
+ npx scrum-skills validate
26
+
27
+ # 4. 打开看板
28
+ npx scrum-skills serve # → http://localhost:4173
29
+ ```
30
+
31
+ `init` 会建出 `agile/` 的数据骨架(含空的 `tests.json`),并把四个 skill 装进 `.agents/skills/`:
32
+
33
+ | skill | 干什么 |
34
+ |---|---|
35
+ | `scrum-planning` | 需求澄清 → 写史诗/故事/验收标准/拆任务 |
36
+ | `scrum-sprint` | 排期、组建迭代、状态流转、里程碑、收尾 |
37
+ | `scrum-tdd` | red → green → refactor 循环 + 过程落库 |
38
+ | `scrum-report` | 看板 / 甘特 / 里程碑 / 燃尽 / 测试用例 / 追溯链 |
39
+
40
+ ## 命令
41
+
42
+ | 命令 | 说明 |
43
+ |---|---|
44
+ | `scrum-skills init [dir] [--force]` | 初始化(已有 `AGENTS.md` 时拒绝,`--force` 先备份成 `AGENTS.md.<时间戳>.bak` 再覆盖) |
45
+ | `scrum-skills update [dir] [--dry-run]` | 只刷新框架文件(skill / AGENTS.md / 校验器),**绝不碰 `agile/` 数据**;除 `AGENTS.md` 外一律无条件覆盖、不备份,`AGENTS.md` 覆盖前备份成 `AGENTS.md.<时间戳>.bak` |
46
+ | `scrum-skills validate [dir]` | 校验数据(引用完整性 / 状态机 / TDD 纪律 / 测试资产 / 里程碑 / 依赖无环) |
47
+ | `scrum-skills serve [dir] [port]` | 起看板(默认当前目录,端口 4173),**校验不通过时拒绝出报表**;打开的就是启动目录,上次打开过的项目只留在「最近项目」里供手动切回 |
48
+
49
+ `scsk` 是 `scrum-skills` 的等价简写,全局安装后两个命令都可直接用,如 `scsk serve`。
50
+
51
+ > **`update` 会整篇覆盖框架文件**(`AGENTS.md`、`.agents/`、`tools/`),并且按两类处理:
52
+ > - `.agents/`、`tools/` 下的文件**无条件覆盖、不留备份**——它们是包里的框架文件,本地改过就直接被替换掉;
53
+ > - `AGENTS.md` **覆盖前备份成 `AGENTS.md.<yyyyMMdd-HHmmss>.bak`**(不会顶掉上一次的备份),因为项目特有纪律都写在里面。
54
+ >
55
+ > 输出里会点名 `AGENTS.md` 的备份文件,并提示用什么话让 agent 合并:直接说「对比 `AGENTS.md` 与 `AGENTS.md.20261026-113045.bak`,把我的本地改动合并回来」即可。合并确认无误后再删备份。
56
+ > 只想知道会动哪些文件,加 `--dry-run`:只报告,不写文件、不建备份。
57
+
58
+ ## 数据长什么样
59
+
60
+ `agile/` 下三个常驻文件 + 两个可选文件:
61
+
62
+ | 文件 | 内容 |
63
+ |---|---|
64
+ | `agile/backlog.json` | 史诗 `epics`(含量化目标 `keyResults`)+ 故事 `stories`(内联 `tasks`) |
65
+ | `agile/sprints.json` | 迭代 `sprints` + 里程碑 `milestones`(MVP 节点 = `kind: "mvp"`) |
66
+ | `agile/journal.json` | 每个任务一条 TDD 日志 `journals`(内联 `entries`) |
67
+ | `agile/tests.json` | 测试资产(可选):`suites` 按层 × 级别装用例(`covers` 指向验收),`runs` 存执行记录与代码覆盖率;结构见 schema 第 5 节 |
68
+ | `agile/last-change.json` | 最近一次交付的汇报(可选;已提交才覆盖,未提交则合并):变更清单 / 额外工作 / 验证 / 入口与账号 / 测试前准备 / 测试场景 / 说明 |
69
+
70
+ ```jsonc
71
+ // 一条故事(节选)
72
+ {
73
+ "id": "S-017",
74
+ "type": "bug", // feature | bug | debt | spike
75
+ "priority": "P1", // P0~P3
76
+ "value": 8, // WSJF = value / points
77
+ "bug": { // type=bug 时必填三件套
78
+ "severity": "major",
79
+ "environment": "Chrome 120 / Windows 11",
80
+ "steps": "1. 勾选子任务;2. 返回看板;3. 拖动卡片 —— 进度条仍是旧值"
81
+ },
82
+ "acceptance": [
83
+ { "given": "…", "when": "…", "then": "…", "verifyBy": ["S-017-T1"] }
84
+ ],
85
+ "points": 2,
86
+ "status": "ready",
87
+ "sprintId": null,
88
+ "schedule": { "start": "2026-10-20", "end": "2026-10-23" }, // 甘特图的计划条
89
+ "dependsOn": ["S-004"], // 依赖,必须无环
90
+ "blocked": { "reason": "等复现环境确认", "since": "2026-10-22" },
91
+ "scopeChange": { "kind": "added-mid-sprint", "at": "2026-10-15", "reason": "客户临时插需求" },
92
+ "tasks": [
93
+ { "id": "S-017-T1", "title": "写复现缺陷的失败测试", "type": "test", "status": "todo", "files": [] }
94
+ ]
95
+ }
96
+ ```
97
+
98
+ 迭代 id 形如 `R1-S1`:`R<发布周期>`(一个发布周期 = 4 周 = 2 个冲刺)× `S<期内冲刺号>`;小团队也可以只用 `S-01` 纯序号,但同一项目不许混用。
99
+
100
+ ## 看板有什么
101
+
102
+ | 页面 | 内容 |
103
+ |---|---|
104
+ | `/` 看板 | 六列(草稿/就绪/进行中/评审/完成/已取消),按史诗/里程碑筛选,按优先级或 WSJF 重排;卡片带类型、阻塞、范围变更标记 |
105
+ | `/last-change` 最后更新 | 渲染 `agile/last-change.json`:故事/任务变更清单、额外做的事、验证结果、调试入口与账号、测试前准备、逐条测试场景、说明与遗留;变更清单抬头可「复制变更信息」(生成 Conventional Commits 提交信息)或「自动提交」(看清信息与文件清单后 `git add -A` + `git commit`,只提交到本地、不 push);点「查看历史」按 git 提交翻看任一版本(只读,顶部页签可在最新/历史间切换);没有该文件时给空状态 |
106
+ | `/epics` 史诗与目标 | 每个史诗的关键结果(KR)进度、故事完成度、阻塞与缺陷统计 |
107
+ | `/gantt` 甘特图 | 迭代分带,浅色条=计划、实心条=journal 推出的实际执行区间,菱形=里程碑,依赖与阻塞清单 |
108
+ | `/milestones` 里程碑 / MVP | 按 kind 分组,显示达成口径、挂的故事、剩余天数 |
109
+ | `/burndown` 燃尽图 | 实线=真实剩余,虚线=理想线,竖线标出**哪一天插了需求**(范围变更) |
110
+ | `/tests` 测试用例 | QA 交付视图:需求覆盖(按验收条的分子/分母)、前端/后端/E2E 分层概览与**故事 × 层覆盖矩阵**、用例清单(可展开步骤与预期)、执行记录、缺口与风险(未覆盖验收 + 最近失败用例);没有 `agile/tests.json` 时给空状态并说明谁来写 |
111
+ | `/diagrams` 架构图例 | 「实现过程中顺手画的图」:左边按类型 / 关键词 / 是否过期筛,右边 sandbox iframe 预览;图挂在故事上,故事详情页有反向入口;没有 `agile/diagrams.json` 时给空状态并说明怎么登记 |
112
+ | `/story/:id` 追溯链 | 验收标准 → 任务 → 每条 red/green/refactor 记录,含测试命令与产出文件;底部有该故事的架构图例入口 |
113
+
114
+ > **并排指标卡的配色顺序固定为 绿 → 蓝 → 紫 → 黑,卡片更多才往后用黄、红**。顺序只在 `apps/board/app/composables/model.ts` 的 `CARD_TONES` 里定义一次,页面写 `:class="cardToneClass(i)"`、光斑写 `:tone="cardTone(i)"`;黑卡是深底,文字要翻白。新增页面照着取色,别另起一套。
115
+
116
+ ## 内置 AI 助手
117
+
118
+ 看板右下角有个悬浮按钮,点开是右侧抽屉——不用离开看板就能让模型查/改当前项目的敏捷数据。
119
+
120
+ **第一次打开先填参数**(存在本机 `%APPDATA%/scrum-skills/ai.json`,权限 0600,不会写进项目目录):
121
+
122
+ | 字段 | 说明 |
123
+ |---|---|
124
+ | 服务商 | DeepSeek / OpenAI / Moonshot / 硅基流动 / 本地 Ollama / 自定义,选中后自动带出接口地址与模型名 |
125
+ | 接口地址 · API Key · 模型名 | OpenAI 兼容端点即可;Ollama 可以留空 Key。填好 Key 后「模型名」右侧的「拉取列表」可点,直接从接口拉回模型 id 选一个(本地地址免 Key) |
126
+ | 系统提示词 · 温度 | 系统提示词默认留空:内置说明只讲「能碰哪些数据 / 两个工具 / 四条回答纪律」,不写人设也不提厂商;温度默认 0.3(改数据这类活不需要发挥) |
127
+ | 工具调用轮数上限 | 默认 30:模型连着调工具时跑到上限会强制收口一轮 |
128
+ | 上下文上限 · 超长自动压缩 | 默认 256000 token;超了先把老对话压掉,系统提示词与当前提问永远保留 |
129
+
130
+ **Key 不出本机**:浏览器只调本机 `/api/ai/*`,模型请求由 `tools/serve.mjs` 代为发出,读配置只回 `hasKey`。
131
+
132
+ **回答长什么样**(照官方 chat 模板的版式):助手正文按 markdown 渲染(标题、列表、表格、行内代码都能显示),工具调用折成一行「更新敏捷数据 · 关键词」可展开看参数与结果,模型的思考过程收成「思考了 N 秒」可折叠块,待审变更是一张能点批准/驳回的卡片。markdown 走 `@comark/nuxt`(AST → Vue 组件),不用 `v-html` 拼 HTML。
133
+
134
+ **它手上只有两件工具**:
135
+
136
+ - `search_agile`:在 `agile/backlog|sprints|journal` 里按记录检索(`S-011 阻塞` 这种多关键词要同时命中同一条),命中带出可直接写补丁的路径
137
+ - `update_agile`:按路径打补丁,例如 `stories.S-011.status = review`;写前跑 `tools/validate.mjs`,校验不过自动回滚成改前内容
138
+
139
+ **输入框左下角四个图标按钮,从左到右固定这个顺序**(加东西在左、策略在最右):
140
+
141
+ 1. **上传图片**:一次最多 4 张,输入框里按 <kbd>Ctrl</kbd>+<kbd>V</kbd> 也能粘贴截图。图片先在本地缩到长边 1400 再按 JPEG 编码,以缩略图挂在输入框上方(可逐张删除),发送后随文字按 OpenAI 多模态格式(`image_url`)带给模型。历史里只带最近 4 张图,更早的换成一句「图片已省略」占位;服务端按每张 800 token 计入上下文预算。模型本身不支持图片时(例如 DeepSeek 的 chat / reasoner),报错会直接提示换成支持视觉模型的接口。
142
+ 2. **新建会话**(`+` 图标):先把当前这一轮完整存进本地历史,再清空对话区回到欢迎页
143
+ 3. **历史会话**:弹出列表,每条给出标题(第一条提问的开头)、相对时间与消息条数,点一条即可切回去接着问;当前那条有底色
144
+ 4. **审核策略**(盾牌 / 闪电图标,点一下弹下拉,当前项打勾):**手动审核**(默认)改文件前先出「待审变更」卡,逐条列出路径与改前改后值,点批准才写入,驳回则文件不动;**自动审核**直接写入 + 立即校验,失败回滚
145
+
146
+ 会话只存在这台浏览器(`localStorage` 的 `scrum-skills:ai:threads:v1`,最多保留 20 个,超了丢最老的),服务端一概不知。**图片不落盘**——一张截图 base64 就有上百 KB,存几个会话就会把 localStorage 撑爆,所以落盘前清空图片 url;恢复后原位显示「图片未保存」占位,这类空图也不会被拼进下一次请求。抽屉每次打开时按主题存的那条会话自动恢复。抽屉是整屏覆盖的,打开期间会锁住背后主页的纵向滚动,关掉即恢复。
147
+
148
+ 工具永远跟着「看板当前打开的项目」走,换项目不会串。
149
+
150
+ > 纯静态托管(只放 `assets/board`)时没有 `/api/ai/*`,此时对话不可用;`pnpm serve` 才带这套能力。
151
+
152
+ ## 交付说明:先查 git,再决定覆盖还是合并
153
+
154
+ `agile/last-change.json` 只留最近一次交付,但「最近一次」的前提是**上一次已经进了版本库**。所以写入前先看这个文件在 git 里的状态:
155
+
156
+ - **已提交、工作区干净** → 上一次已入库,整份覆盖,文件里只有本次交付;
157
+ - **有未提交改动 / 已暂存未提交 / 未跟踪 / 不是 git 仓库** → 把本次结果**合并**进旧记录,旧的一条不丢(`changes` / `extras` / `verification` / `preparation` / `scenarios` 按 key 去重、新覆盖旧;`notes` / `open` 去重拼接;`access` 按地址与账号合并),并打上 `merged` + `mergedFrom`。
158
+
159
+ ```bash
160
+ node tools/last-change.mjs check # 退出码 0 = 可覆盖,2 = 必须先合并
161
+ node tools/last-change.mjs write 记录.json # 按状态自动覆盖或合并,写后校验,不过就回滚
162
+ node tools/last-change.mjs write 记录.json --replace # 确认旧记录不要了才强制覆盖
163
+ ```
164
+
165
+ 看板 `/last-change`(侧栏「最后更新」)页面上方就写着这条规则,并显示该文件当前是「已提交」还是「未提交改动」;合并过的记录会标出「合并记录 · N 次交付」。
166
+
167
+ ## 校验器管什么
168
+
169
+ `validate` 是这套体系的地基,**错误**会让它退出码非 0:
170
+
171
+ - 引用完整性**双向**一致:`epic.stories` ↔ `story.epicId`、`sprint.stories` ↔ `story.sprintId`、`journal.taskId` ↔ task
172
+ - 状态机:story `draft → ready → in-progress → review → done`(不做就走 `cancelled` 终态),task `todo → red → green → refactored → done`(只能前进)
173
+ - **TDD 铁律**:task 到 green 之后,日志必须首条 `fail`、后有 `pass`;done 时末条必须是 `pass`
174
+ - story 到 done:所有 task 必须 done,且不许仍标着 `blocked`
175
+ - 已取消(`cancelled`):不要求验收标准、也不要求 task 全部 done;但还挂着 `blocked` 会报错,仍留在迭代里给 WARN —— 且它的点数**不计入承诺容量、燃尽图与里程碑完成度**,免得「不做了」把进度拖成永远完不成
176
+ - 依赖:`dependsOn` 必须存在、不能自依赖、**不能成环**(DFS 检测)
177
+ - 需求属性:`type` / `priority` / `value` 合法;`type: bug` 必须有 `severity` + `environment` + `steps`
178
+ - 验收标准的 `verifyBy` 必须指向同一 story 的 task
179
+ - 测试资产(`agile/tests.json`,可选):`layer`/`level`/`status` 枚举合法、用例 id 唯一、`covers` 指向的验收必须存在且序号不越界、`taskId` 属于 covers 里的故事、`runs` 时间正序、最新一次 `failedCases` 必须对应 `status: fail`、覆盖率 0~100
180
+ - **收口纪律**(只查登记过用例的故事):done 的 `test` 型任务必须有对应用例;story 到 `review`/`done` 时每条验收必须被覆盖
181
+ - 迭代 id 格式与序号连续、同时只能有一个活跃迭代
182
+ - 里程碑 `reached` 时挂的故事必须全部完成
183
+
184
+ 给 **WARN** 的(不阻塞):验收没绑验证任务、史诗缺 keyResults、计划区间超出迭代、承诺点数超容量、被阻塞的故事仍排在迭代里等。
185
+
186
+ ## 本地开发
187
+
188
+ ```bash
189
+ pnpm install # 一次装齐根包与 apps/board(pnpm workspace,见 pnpm-workspace.yaml)
190
+ pnpm dev # Nuxt dev(HMR),读仓库根的 agile/(/api/ai 走 server/middleware)
191
+ AGILE_DIR=/别的项目 pnpm dev # 看别的项目的数据
192
+
193
+ pnpm build # 构建看板静态产物 → assets/board
194
+ pnpm validate # 只校验数据
195
+ pnpm test # 回归测试(校验器 / 测试资产与覆盖计算 / 看板皮肤与配色 / 布局回归 / 卡片操作 / 项目目录 / 最后更新页与历史版本 / 交付说明写入策略 / 提交信息与自动提交 / AI 服务与面板,293 个用例)
196
+ pnpm serve # 起看板(读目标目录的 agile/,同时提供 /api/ai)
197
+ ```
198
+
199
+ 看板源码在 `apps/board/`(Nuxt 4 + Nuxt UI,纯静态产物,无服务端、无外部字体/图标请求)。`assets/board/` 是构建产物,**不进 git**,发布前由 `prepublishOnly` 自动重建。
200
+
201
+ ## 架构图例(内置 diagram-design)
202
+
203
+ `.agents/skills/diagram-design/` 是内置的 [diagram-design](https://github.com/cathrynlavery/diagram-design)(MIT),
204
+ 用 `scrum-skills init` / `update` 自动带上,**不需要另外安装,也不依赖 Python**:
205
+
206
+ - 出图走 skill 的规范(45 类图型 + 设计铁律),产物是单文件自包含 HTML(内联 SVG);
207
+ - 入门前先过质检:`node tools/diagram-lint.mjs <file>`(上游 `self_check.py` 的 Node 移植,
208
+ 覆盖安全边界 / 文档完整性 / 无障碍契约 / viewBox 越界 / 动画契约);
209
+ - 入库:`node tools/diagram.mjs add <file> --title "登录时序" --kind sequence --story S-001`,
210
+ 它负责分配 id、拷进 `agile/diagrams/`、写索引、跑数据校验,失败整体回滚;
211
+ - 索引是 `agile/diagrams.json`,产物在 `agile/diagrams/`,看板 `/diagrams` 页按故事与图型浏览,
212
+ 列表悬停可放大预览,支持下载 SVG / PNG(PNG 由浏览器 canvas 现场栅格化,不引渲染依赖);
213
+ - 老图要重画时,`node tools/diagram-import.mjs <file>` 把 `.mmd` / `.drawio` / `.excalidraw`
214
+ 压成节点与连线摘要再交给规范重画。
215
+
216
+ 内置副本由 `node scripts/sync-diagram-skill.mjs` 从上游同步(裁剪示例变体、剔除 `scripts/` 的 Python 实现、
217
+ 把文档里的 Python 调用改写成上面的 Node 命令),来源与版本记在 `.agents/skills/diagram-design/UPSTREAM.json`。
218
+
219
+ ## License
220
+
221
+ MIT
222
+
223
+ 内置的 `diagram-design` skill 同样是 MIT,版权归其作者(cathrynlavery),原文见
224
+ `.agents/skills/diagram-design/LICENSE`。
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/BU8vdZUs.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.BBv-sBnP.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BU8vdZUs.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CFCM18fF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BxuBMtk_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DxBwVNyP.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BygIGL69.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SywdkA98.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BW3xJdEx.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SrNUvNwO.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CI0scS-N.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D_ct-Stz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/P6qlvBHA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CdPGFZyy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bubk0xn-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DJM7u8P3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DUgGsde6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BrWE0up6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BE6t7Q2m.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DqUSsCJ2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/q_8bxRMt.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DFQDFspC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9MgRdhu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BQgzP7ur.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CgQ30j_6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DzmVW5-D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B0C2jsdk.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CYGiZn-X.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C3SpAhCW.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Cdf_T8Xa.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DV4ti-Pq.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CXG7GZes.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dki9VkAL.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLr85ebo.js"><script type="module" src="/_nuxt/BU8vdZUs.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/I-sGtDIB.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BuBzMkFZ.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BAQQngAq.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.CgJNdpB5.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D1V1gysQ.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.WYSoH44y.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BGwhkDRl.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"08bebe14-0f1b-4d2a-8de4-e84e122d10e9",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793514377276,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DKRYKLGF.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.DplN301E.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DKRYKLGF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CFCM18fF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BxuBMtk_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DxBwVNyP.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BygIGL69.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SywdkA98.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BW3xJdEx.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SrNUvNwO.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CI0scS-N.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D_ct-Stz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/P6qlvBHA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CdPGFZyy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bubk0xn-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DJM7u8P3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DUgGsde6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BrWE0up6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BE6t7Q2m.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DqUSsCJ2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/q_8bxRMt.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DFQDFspC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9MgRdhu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BQgzP7ur.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CgQ30j_6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DzmVW5-D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B0C2jsdk.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CYGiZn-X.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C3SpAhCW.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Cdf_T8Xa.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DV4ti-Pq.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CXG7GZes.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dki9VkAL.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLr85ebo.js"><script type="module" src="/_nuxt/DKRYKLGF.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/I-sGtDIB.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BuBzMkFZ.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BAQQngAq.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Y9imKiDC.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/1XlYaCp9.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.DufSBGVC.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BtzUbHO_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"205ddf7c-da58-4213-ac80-82930a216157",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1794050656472,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/BU8vdZUs.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.BBv-sBnP.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BU8vdZUs.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CFCM18fF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BxuBMtk_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DxBwVNyP.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BygIGL69.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SywdkA98.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BW3xJdEx.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SrNUvNwO.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CI0scS-N.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D_ct-Stz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/P6qlvBHA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CdPGFZyy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bubk0xn-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DJM7u8P3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DUgGsde6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BrWE0up6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BE6t7Q2m.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DqUSsCJ2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/q_8bxRMt.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DFQDFspC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9MgRdhu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BQgzP7ur.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CgQ30j_6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DzmVW5-D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B0C2jsdk.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CYGiZn-X.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C3SpAhCW.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Cdf_T8Xa.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DV4ti-Pq.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CXG7GZes.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dki9VkAL.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLr85ebo.js"><script type="module" src="/_nuxt/BU8vdZUs.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/I-sGtDIB.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BuBzMkFZ.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BAQQngAq.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.CgJNdpB5.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D1V1gysQ.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.WYSoH44y.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BGwhkDRl.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"08bebe14-0f1b-4d2a-8de4-e84e122d10e9",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793514377276,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DKRYKLGF.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.DplN301E.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DKRYKLGF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CFCM18fF.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BxuBMtk_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DxBwVNyP.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BygIGL69.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SywdkA98.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BW3xJdEx.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/SrNUvNwO.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CI0scS-N.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D_ct-Stz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/P6qlvBHA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CdPGFZyy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bubk0xn-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DJM7u8P3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DUgGsde6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BrWE0up6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BE6t7Q2m.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DqUSsCJ2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/q_8bxRMt.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DFQDFspC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9MgRdhu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BQgzP7ur.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CgQ30j_6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DzmVW5-D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B0C2jsdk.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CYGiZn-X.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C3SpAhCW.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Cdf_T8Xa.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DV4ti-Pq.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CXG7GZes.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dki9VkAL.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLr85ebo.js"><script type="module" src="/_nuxt/DKRYKLGF.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/I-sGtDIB.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BuBzMkFZ.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BAQQngAq.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Y9imKiDC.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/1XlYaCp9.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.DufSBGVC.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BtzUbHO_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"205ddf7c-da58-4213-ac80-82930a216157",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1794050656472,false]</script></body></html>
@@ -1 +1 @@
1
- import{t as e}from"./CI0scS-N.js";import{n as t}from"./SrNUvNwO.js";import{U as n,Xt as r,b as i,d as a,lt as o,m as s,y as c}from"./BxuBMtk_.js";import{i as l}from"#entry";var u={class:`antialiased bg-white dark:bg-neutral-950 dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-neutral-950 tracking-wide`},d={class:`max-w-520px text-center`},f=[`textContent`],p=[`textContent`],m=[`textContent`],h={class:`flex items-center justify-center w-full`},g=[`textContent`],_=l({__name:`error-404`,props:{appName:{type:String,default:`Nuxt`},status:{type:Number,default:404},statusText:{type:String,default:`Page not found`},description:{type:String,default:`Sorry, the page you are looking for could not be found.`},backHome:{type:String,default:`Go back home`},backToPrevious:{type:String,default:`Go back`}},setup(l){let _=l;return t({title:`${_.status} - ${_.statusText} | ${_.appName}`,script:[{innerHTML:`(()=>{if(!(window.history.state&&window.history.state.back||document.referrer.startsWith(window.location.origin+"/")))return;const t=document.querySelector("[data-back-home]"),e=document.querySelector("[data-back-previous]");t&&e&&(e.onclick=()=>window.history.back(),t.hidden=!0,e.hidden=!1)})();`,tagPosition:`bodyClose`}],style:[{innerHTML:`*,:after,:before{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb);--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 #0000;--un-ring-shadow:0 0 #0000;--un-shadow-inset: ;--un-shadow:0 0 #0000;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:#93c5fd80;--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }:after,:before{--un-content:""}html{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body,button{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}a{-webkit-text-decoration:inherit;text-decoration:inherit}a,button{color:inherit}button{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;padding:0;text-transform:none;cursor:pointer}[type=button],button{-webkit-appearance:button;background-color:#0000;background-image:none}h1,h2,p{margin:0}[hidden]:where(:not([hidden=until-found])){display:none}`}]}),(t,_)=>{let v=e;return n(),s(`div`,u,[a(`div`,d,[a(`h1`,{class:`font-medium leading-none mb-4 sm:text-[72px] tabular-nums text-[56px]`,textContent:r(l.status)},null,8,f),a(`h2`,{class:`font-semibold mb-2 sm:text-3xl text-2xl`,textContent:r(l.statusText)},null,8,p),a(`p`,{class:`mb-4 px-2 text-md text-neutral-500`,textContent:r(l.description)},null,8,m),a(`div`,h,[i(v,{to:`/`,"data-back-home":``,class:`font-medium hover:text-[#00DC82] text-sm underline underline-offset-3`},{default:o(()=>[c(r(l.backHome),1)]),_:1}),_[0]||=c(),a(`button`,{type:`button`,"data-back-previous":``,hidden:``,class:`appearance-none bg-transparent border-0 cursor-pointer font-medium font-sans hover:text-[#00DC82] p-0 text-inherit text-sm underline underline-offset-3`,textContent:r(l.backToPrevious)},null,8,g)])])])}}},[[`__scopeId`,`data-v-8e352ff6`]]);export{_ as default};
1
+ import{t as e}from"./CI0scS-N.js";import{n as t}from"./SrNUvNwO.js";import{U as n,Xt as r,b as i,d as a,lt as o,m as s,y as c}from"./BxuBMtk_.js";import{i as l}from"#entry";var u={class:`antialiased bg-white dark:bg-neutral-950 dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-neutral-950 tracking-wide`},d={class:`max-w-520px text-center`},f=[`textContent`],p=[`textContent`],m=[`textContent`],h={class:`flex items-center justify-center w-full`},g=[`textContent`],_=l({__name:`error-404`,props:{appName:{type:String,default:`Nuxt`},status:{type:Number,default:404},statusText:{type:String,default:`Page not found`},description:{type:String,default:`Sorry, the page you are looking for could not be found.`},backHome:{type:String,default:`Go back home`},backToPrevious:{type:String,default:`Go back`}},setup(l){let _=l;return t({title:`${_.status} - ${_.statusText} | ${_.appName}`,script:[{innerHTML:`(()=>{if(!(window.history.state&&window.history.state.back||document.referrer.startsWith(window.location.origin+"/")))return;const t=document.querySelector("[data-back-home]"),e=document.querySelector("[data-back-previous]");t&&e&&(e.onclick=()=>window.history.back(),t.hidden=!0,e.hidden=!1)})();`,tagPosition:`bodyClose`}],style:[{innerHTML:`*,:after,:before{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb);--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 #0000;--un-ring-shadow:0 0 #0000;--un-shadow-inset: ;--un-shadow:0 0 #0000;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:#93c5fd80;--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }:after,:before{--un-content:""}html{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body,button{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}a{-webkit-text-decoration:inherit;text-decoration:inherit}a,button{color:inherit}button{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;padding:0;text-transform:none;cursor:pointer}[type=button],button{-webkit-appearance:button;background-color:#0000;background-image:none}h1,h2,p{margin:0}[hidden]:where(:not([hidden=until-found])){display:none}`}]}),(t,_)=>{let v=e;return n(),s(`div`,u,[a(`div`,d,[a(`h1`,{class:`font-medium leading-none mb-4 sm:text-[72px] tabular-nums text-[56px]`,textContent:r(l.status)},null,8,f),a(`h2`,{class:`font-semibold mb-2 sm:text-3xl text-2xl`,textContent:r(l.statusText)},null,8,p),a(`p`,{class:`mb-4 px-2 text-md text-neutral-500`,textContent:r(l.description)},null,8,m),a(`div`,h,[i(v,{to:`/`,"data-back-home":``,class:`font-medium hover:text-[#00DC82] text-sm underline underline-offset-3`},{default:o(()=>[c(r(l.backHome),1)]),_:1}),_[0]||=c(),a(`button`,{type:`button`,"data-back-previous":``,hidden:``,class:`appearance-none bg-transparent border-0 cursor-pointer font-medium font-sans hover:text-[#00DC82] p-0 text-inherit text-sm underline underline-offset-3`,textContent:r(l.backToPrevious)},null,8,g)])])])}}},[[`__scopeId`,`data-v-a27dea0c`]]);export{_ as default};