scrum-skills 0.2.3 → 0.2.4
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/.agents/skills/scrum-planning/references/schema.md +3 -2
- package/README.md +15 -6
- package/assets/board/200.html +1 -1
- package/assets/board/404.html +1 -1
- package/assets/board/_nuxt/{BannxgAO.js → 0VBLkWnu.js} +1 -1
- package/assets/board/_nuxt/{C9Eg7sGC.js → BNKWWG8o.js} +1 -1
- package/assets/board/_nuxt/{BQSM1Ln0.js → BPTrRhcw.js} +1 -1
- package/assets/board/_nuxt/{B7foC6AH.js → BSov4ehT.js} +1 -1
- package/assets/board/_nuxt/BTxVIV74.js +1 -0
- package/assets/board/_nuxt/{D8NAsEfE.js → Bb3Limos.js} +1 -1
- package/assets/board/_nuxt/{CPfeOlrH.js → Bh7eUS6e.js} +1 -1
- package/assets/board/_nuxt/{DA0S9GHd.js → Bu60uYgs.js} +1 -1
- package/assets/board/_nuxt/{DiYBussm.js → BuuAjxsV.js} +1 -1
- package/assets/board/_nuxt/{BvaAl29K.js → C-GYjxwr.js} +3 -3
- package/assets/board/_nuxt/{DEBhVwtl.js → C9StZL3D.js} +1 -1
- package/assets/board/_nuxt/{ADHYAojV.js → CD0uJuyA.js} +1 -1
- package/assets/board/_nuxt/{C3ssq3NT.js → CHZ_IHOa.js} +1 -1
- package/assets/board/_nuxt/{C9lP5soO.js → CNbS93H9.js} +1 -1
- package/assets/board/_nuxt/{DVC07Kf9.js → CUnX_QKi.js} +1 -1
- package/assets/board/_nuxt/CeiBZIn1.js +95 -0
- package/assets/board/_nuxt/{Dpzighda.js → Cfk_R787.js} +1 -1
- package/assets/board/_nuxt/{DhkiugsE.js → ChJuu-Ky.js} +1 -1
- package/assets/board/_nuxt/{DwNOEVN_.js → CjRYYbsC.js} +1 -1
- package/assets/board/_nuxt/CuJUHLl8.js +1 -0
- package/assets/board/_nuxt/{BKgQBiDB.js → CvFE1CeN.js} +1 -1
- package/assets/board/_nuxt/{CAJYxF-h.js → CyAQx5wv.js} +1 -1
- package/assets/board/_nuxt/{BJ0EVa07.js → D1Yxjnz9.js} +1 -1
- package/assets/board/_nuxt/{CviJuCa4.js → D3YCAmfu.js} +1 -1
- package/assets/board/_nuxt/{joIhBaO-.js → D8Bpf7sE.js} +1 -1
- package/assets/board/_nuxt/{GzmVZqnA.js → DJUwhJ8T.js} +1 -1
- package/assets/board/_nuxt/{BXQZAVYz.js → DLkAKotw.js} +1 -1
- package/assets/board/_nuxt/{DQRn5Z3H.js → DaqxYsbV.js} +1 -1
- package/assets/board/_nuxt/{jCMbKsJ3.js → DhhR1iR4.js} +1 -1
- package/assets/board/_nuxt/{CL3OrPP7.js → DmKZi-Wo.js} +1 -1
- package/assets/board/_nuxt/{4FVXNzN-.js → DrbU50Zd.js} +1 -1
- package/assets/board/_nuxt/{DcWIG384.js → DsNkyZPc.js} +1 -1
- package/assets/board/_nuxt/{BCXs9rBJ.js → DwcwzwSx.js} +1 -1
- package/assets/board/_nuxt/{BuVRe79A.js → HrhQecsY.js} +1 -1
- package/assets/board/_nuxt/{Db4IStii.js → KhBLkIKl.js} +1 -1
- package/assets/board/_nuxt/{bXQUY80V.js → MTucdeiW.js} +1 -1
- package/assets/board/_nuxt/{CZPwmwA5.js → RJtjdZeG.js} +1 -1
- package/assets/board/_nuxt/{CTOFhZkc.js → RM4VQc4e.js} +1 -1
- package/assets/board/_nuxt/Vo4wMbnS.js +1 -0
- package/assets/board/_nuxt/builds/latest.json +1 -1
- package/assets/board/_nuxt/builds/meta/ecbff4a6-6538-49b0-9844-64f03ebd103a.json +1 -0
- package/assets/board/_nuxt/{entry.ChOvfapw.css → entry.nk2xQ-sa.css} +1 -1
- package/assets/board/_nuxt/{Cw7Z2eHM.js → fFGyRlCx.js} +1 -1
- package/assets/board/_nuxt/{-_kK9OzO.js → l05SqxhY.js} +1 -1
- package/assets/board/_nuxt/milestones.z-UuT44X.css +1 -0
- package/assets/board/_nuxt/{B5u1XkK4.js → obNw9aRV.js} +1 -1
- package/assets/board/_nuxt/pages.Bi6nErnd.css +1 -0
- package/assets/board/_nuxt/uGmbQYdY.js +1 -0
- package/assets/board/burndown/index.html +1 -1
- package/assets/board/epics/index.html +1 -1
- package/assets/board/gantt/index.html +1 -1
- package/assets/board/index.html +1 -1
- package/assets/board/milestones/index.html +1 -1
- package/bin/scrum-skills.mjs +34 -11
- package/package.json +40 -41
- package/templates/AGENTS.md +1 -1
- package/tools/ai.mjs +24 -2
- package/tools/validate.mjs +21 -7
- package/assets/board/_nuxt/BpR5gMZX.js +0 -95
- package/assets/board/_nuxt/CZc2Mypg.js +0 -1
- package/assets/board/_nuxt/DWxQkuvT.js +0 -1
- package/assets/board/_nuxt/DttMo5b5.js +0 -1
- package/assets/board/_nuxt/FCuSfyjP.js +0 -1
- package/assets/board/_nuxt/builds/meta/24603984-d514-4dff-badc-898a1fd5e770.json +0 -1
- package/assets/board/_nuxt/milestones.CVvAMKg4.css +0 -1
- package/assets/board/_nuxt/pages.DSSttn9g.css +0 -1
|
@@ -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/
|
|
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/CeiBZIn1.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.nk2xQ-sa.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CeiBZIn1.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/u88eXCXy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CsyY7ot8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Baq-Hrpf.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/oXSDGTSr.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuBpFHLu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DsF2J4jZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BJW3UkS7.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/HrhQecsY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B43-KCi6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CjRYYbsC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChJuu-Ky.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D8Bpf7sE.js"><script type="module" src="/_nuxt/CeiBZIn1.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/C-GYjxwr.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CoPhTDCT.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/3G_ZLrk2.js"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"ecbff4a6-6538-49b0-9844-64f03ebd103a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1792999430299,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/
|
|
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/CeiBZIn1.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.nk2xQ-sa.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CeiBZIn1.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/u88eXCXy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CsyY7ot8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Baq-Hrpf.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/oXSDGTSr.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuBpFHLu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DsF2J4jZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BJW3UkS7.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/HrhQecsY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B43-KCi6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CjRYYbsC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChJuu-Ky.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D8Bpf7sE.js"><script type="module" src="/_nuxt/CeiBZIn1.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/C-GYjxwr.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CoPhTDCT.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/3G_ZLrk2.js"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"ecbff4a6-6538-49b0-9844-64f03ebd103a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1792999430300,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/
|
|
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/CeiBZIn1.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.nk2xQ-sa.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CeiBZIn1.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/u88eXCXy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CsyY7ot8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Baq-Hrpf.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/oXSDGTSr.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuBpFHLu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DsF2J4jZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BJW3UkS7.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/HrhQecsY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B43-KCi6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CjRYYbsC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChJuu-Ky.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D8Bpf7sE.js"><script type="module" src="/_nuxt/CeiBZIn1.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/C-GYjxwr.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CoPhTDCT.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/3G_ZLrk2.js"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"ecbff4a6-6538-49b0-9844-64f03ebd103a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1792999430300,false]</script></body></html>
|
package/assets/board/index.html
CHANGED
|
@@ -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/
|
|
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/CeiBZIn1.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.nk2xQ-sa.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CeiBZIn1.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/u88eXCXy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CsyY7ot8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Baq-Hrpf.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/oXSDGTSr.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuBpFHLu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DsF2J4jZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BJW3UkS7.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/HrhQecsY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B43-KCi6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CjRYYbsC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChJuu-Ky.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D8Bpf7sE.js"><script type="module" src="/_nuxt/CeiBZIn1.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/C-GYjxwr.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CoPhTDCT.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/3G_ZLrk2.js"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"ecbff4a6-6538-49b0-9844-64f03ebd103a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1792999430301,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/
|
|
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/CeiBZIn1.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.nk2xQ-sa.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CeiBZIn1.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/u88eXCXy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CsyY7ot8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Baq-Hrpf.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/oXSDGTSr.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuBpFHLu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DsF2J4jZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BJW3UkS7.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/HrhQecsY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B43-KCi6.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CjRYYbsC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChJuu-Ky.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D8Bpf7sE.js"><script type="module" src="/_nuxt/CeiBZIn1.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/C-GYjxwr.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CoPhTDCT.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/3G_ZLrk2.js"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"ecbff4a6-6538-49b0-9844-64f03ebd103a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1792999430300,false]</script></body></html>
|
package/bin/scrum-skills.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* scrum-skills CLI(零依赖,node >= 18)
|
|
4
4
|
* 命令:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* scrum-skills validate [dir] 校验敏捷数据(默认当前目录)
|
|
13
13
|
*/
|
|
14
14
|
import { cpSync, mkdirSync, existsSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
15
|
-
import { join, resolve, dirname } from 'node:path';
|
|
15
|
+
import { join, resolve, dirname, basename } from 'node:path';
|
|
16
16
|
import { spawnSync } from 'node:child_process';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
|
|
@@ -31,6 +31,20 @@ scsk 是 scrum-skills 的等价简写,如: scsk serve`;
|
|
|
31
31
|
const [cmd, ...args] = process.argv.slice(2);
|
|
32
32
|
const fail = msg => { console.error(`✘ ${msg}`); process.exit(1); };
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* 备份一个即将被覆盖的文件,返回备份路径。
|
|
36
|
+
* 名字带时间戳:老做法固定叫 `*.bak`,第二次 update 就把上一次的备份顶掉了,
|
|
37
|
+
* 而 AGENTS.md 几乎一定会被本地改过,备份互相覆盖等于没备份。
|
|
38
|
+
*/
|
|
39
|
+
function backupFile(file) {
|
|
40
|
+
const d = new Date();
|
|
41
|
+
const pad = n => String(n).padStart(2, '0');
|
|
42
|
+
const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
43
|
+
let target = `${file}.${stamp}.bak`;
|
|
44
|
+
for (let i = 2; existsSync(target); i += 1) target = `${file}.${stamp}-${i}.bak`;
|
|
45
|
+
cpSync(file, target);
|
|
46
|
+
return target;
|
|
47
|
+
}
|
|
34
48
|
function init() {
|
|
35
49
|
const force = args.includes('--force');
|
|
36
50
|
const dir = resolve(args.find(a => !a.startsWith('--')) || process.cwd());
|
|
@@ -38,9 +52,9 @@ function init() {
|
|
|
38
52
|
|
|
39
53
|
const agentsMd = join(dir, 'AGENTS.md');
|
|
40
54
|
if (existsSync(agentsMd) && !force)
|
|
41
|
-
fail(`目标目录已有 AGENTS.md。确认覆盖请加 --force
|
|
42
|
-
const backed = existsSync(agentsMd);
|
|
43
|
-
if (backed)
|
|
55
|
+
fail(`目标目录已有 AGENTS.md。确认覆盖请加 --force(旧文件会先备份成 AGENTS.md.<时间戳>.bak)`);
|
|
56
|
+
const backed = existsSync(agentsMd) ? backupFile(agentsMd) : null;
|
|
57
|
+
if (backed) console.log(`· 已把现有 AGENTS.md 备份为 ${basename(backed)}`);
|
|
44
58
|
|
|
45
59
|
cpSync(join(PKG_ROOT, '.agents'), join(dir, '.agents'), { recursive: true, force: true });
|
|
46
60
|
writeFileSync(agentsMd, readFileSync(join(PKG_ROOT, 'templates/AGENTS.md'), 'utf8'));
|
|
@@ -65,7 +79,7 @@ function init() {
|
|
|
65
79
|
|
|
66
80
|
console.log(`✔ 已初始化到 ${dir}
|
|
67
81
|
- .agents/skills/ 四个敏捷 skill(planning / sprint / tdd / report)
|
|
68
|
-
- AGENTS.md agent 入口纪律文件${backed ?
|
|
82
|
+
- AGENTS.md agent 入口纪律文件${backed ? `(旧文件已备份为 ${basename(backed)})` : ''}
|
|
69
83
|
- tools/validate.mjs 数据校验脚本
|
|
70
84
|
- agile/ 三文件结构${created.length ? `(新建 ${created.join('、')})` : '(已存在,未覆盖)'}
|
|
71
85
|
|
|
@@ -104,6 +118,8 @@ function update() {
|
|
|
104
118
|
fail(`${dir} 未初始化(找不到 .agents/ 与 AGENTS.md)。请先运行: scrum-skills init`);
|
|
105
119
|
|
|
106
120
|
const added = [], changed = [], same = [];
|
|
121
|
+
// 相对路径 → 备份文件名,覆盖完统一提示怎么把本地改动捞回来
|
|
122
|
+
const backups = [];
|
|
107
123
|
for (const { rel, src } of frameworkFiles()) {
|
|
108
124
|
const dest = join(dir, rel);
|
|
109
125
|
const srcText = readFileSync(src, 'utf8');
|
|
@@ -114,19 +130,26 @@ function update() {
|
|
|
114
130
|
if (readFileSync(dest, 'utf8') === srcText) { same.push(rel); continue; }
|
|
115
131
|
changed.push(rel);
|
|
116
132
|
if (!dryRun) {
|
|
117
|
-
|
|
133
|
+
const bak = backupFile(dest); // 覆盖前备份,本地自定义不丢
|
|
134
|
+
backups.push({ rel, bak: basename(bak) });
|
|
118
135
|
writeFileSync(dest, srcText);
|
|
119
136
|
}
|
|
120
137
|
}
|
|
121
138
|
|
|
122
139
|
const verb = dryRun ? '(dry-run,未写入)' : '';
|
|
140
|
+
const bakLines = backups.map(b => ` - ${b.rel} 的旧版本 → ${b.bak}`).join('\n');
|
|
123
141
|
console.log(`✔ skill 框架文件更新完成${verb} — ${dir}
|
|
124
142
|
- 新增 ${added.length} 个${added.length ? `: ${added.join(', ')}` : ''}
|
|
125
|
-
- 更新 ${changed.length}
|
|
143
|
+
- 更新 ${changed.length} 个${changed.length ? `: ${changed.join(', ')}` : ''}
|
|
126
144
|
- 未变 ${same.length} 个
|
|
127
|
-
- agile/
|
|
128
|
-
|
|
129
|
-
${
|
|
145
|
+
- agile/ 数据目录未做任何改动${bakLines ? `\n - 覆盖前已备份${bakLines.replace(/^ {2}/gm, '\n ')}` : ''}
|
|
146
|
+
|
|
147
|
+
${backups.length
|
|
148
|
+
? `提示: 框架文件是整篇覆盖的,你在这几个文件里的本地改动都在上面的备份里。
|
|
149
|
+
让 agent 助手(CodeBuddy / Claude Code 等)合并回来即可,直接说:
|
|
150
|
+
「对比 ${backups[0].rel} 与 ${backups[0].bak},把我的本地改动合并回来」${backups.length > 1 ? `\n (其余 ${backups.length - 1} 个文件同理,备份文件名见上方)` : ''}
|
|
151
|
+
确认合并无误后再删掉备份。AGENTS.md 是 agent 的入口纪律文件,先合并它、再让它去改别的文件最稳。`
|
|
152
|
+
: '已是最新,无需操作。'}`);
|
|
130
153
|
}
|
|
131
154
|
|
|
132
155
|
function runTool(script, needPort) {
|
package/package.json
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "scrum-skills",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "结构化敏捷开发管理 skills + CLI:AI 对话产出结构化敏捷数据,TDD 驱动实施,Nuxt 看板可视化",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"agile",
|
|
8
|
-
"scrum",
|
|
9
|
-
"sprint",
|
|
10
|
-
"tdd",
|
|
11
|
-
"kanban",
|
|
12
|
-
"gantt",
|
|
13
|
-
"burndown",
|
|
14
|
-
"skills",
|
|
15
|
-
"cli"
|
|
16
|
-
],
|
|
17
|
-
"bin": {
|
|
18
|
-
"scrum-skills": "./bin/scrum-skills.mjs",
|
|
19
|
-
"scsk": "./bin/scrum-skills.mjs"
|
|
20
|
-
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "scrum-skills",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "结构化敏捷开发管理 skills + CLI:AI 对话产出结构化敏捷数据,TDD 驱动实施,Nuxt 看板可视化",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"agile",
|
|
8
|
+
"scrum",
|
|
9
|
+
"sprint",
|
|
10
|
+
"tdd",
|
|
11
|
+
"kanban",
|
|
12
|
+
"gantt",
|
|
13
|
+
"burndown",
|
|
14
|
+
"skills",
|
|
15
|
+
"cli"
|
|
16
|
+
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"scrum-skills": "./bin/scrum-skills.mjs",
|
|
19
|
+
"scsk": "./bin/scrum-skills.mjs"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin/",
|
|
23
|
+
"tools/",
|
|
24
|
+
"templates/",
|
|
25
|
+
".agents/",
|
|
26
|
+
"assets/",
|
|
27
|
+
"docs/"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"validate": "node tools/validate.mjs",
|
|
35
|
+
"serve": "node tools/serve.mjs",
|
|
36
|
+
"test": "node --test",
|
|
37
|
+
"dev": "pnpm --dir apps/board run dev",
|
|
38
|
+
"build": "node scripts/build-board.mjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
## 状态机(摘要)
|
|
34
34
|
|
|
35
|
-
- story: `draft → ready → in-progress → review → done`
|
|
35
|
+
- story: `draft → ready → in-progress → review → done`;不做就走 `cancelled`(已取消,终态,不要求验收与任务全 done)
|
|
36
36
|
- task: `todo → red → green → refactored → done`(只能前进;task 状态编码了 TDD 阶段)
|
|
37
37
|
- 详细规则与硬约束见 `.agents/skills/scrum-planning/references/schema.md`。
|
|
38
38
|
|
package/tools/ai.mjs
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* 边收 SSE 边把 text / reasoning / tool 事件吐给前端,浏览器不必碰 Key 也不受 CORS 限制。
|
|
9
9
|
* 3. 工具循环:模型要 search_agile / update_agile 就执行,结果回灌再来一轮;
|
|
10
10
|
* 到轮数上限强制收口(最后一轮不带工具),否则模型会一直调下去。
|
|
11
|
+
* 4. 多模态:前端可以把图片按 OpenAI 的 image_url 一起塞进 user content 数组,
|
|
12
|
+
* 这里原样透传;图片没有文字可估,按 IMAGE_TOKEN_ESTIMATE 计入上下文预算。
|
|
11
13
|
*
|
|
12
14
|
* 审核策略(reviewMode)在这里落地:manual 只调 tools.propose 产待审变更,
|
|
13
15
|
* auto 直接 tools.patch 落盘(写入方自己带校验与回滚)。
|
|
@@ -42,6 +44,8 @@ export const BASE_SYSTEM = [
|
|
|
42
44
|
'2. 不要凭空编 id、状态、日期、人名,也不要造测试数据。',
|
|
43
45
|
'3. 改数据前用一句话说明要改什么,改完说明改了哪几条。',
|
|
44
46
|
'4. 用简体中文,直接给结论:不复述问题、不做自我介绍、不寒暄。',
|
|
47
|
+
'5. 故事状态只有 draft / ready / in-progress / review / done / cancelled(已取消)这六种:'
|
|
48
|
+
+ 'cancelled 是终态,表示这事不做了,要在回答里说清为什么取消;不做了的故事就标 cancelled,不要塞进 done 充数。',
|
|
45
49
|
].join('\n');
|
|
46
50
|
|
|
47
51
|
export const DEFAULT_CONFIG = {
|
|
@@ -123,7 +127,21 @@ export function estimateTokens(text) {
|
|
|
123
127
|
return cjk * 0.7 + rest / 4;
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
|
|
130
|
+
/**
|
|
131
|
+
* 一张图在请求里没有文字可估,按固定额度算。
|
|
132
|
+
* 不估(当成 0)会让「塞一堆截图」的对话永远不触发裁剪,请求体越滚越大直到接口 413;
|
|
133
|
+
* 估得偏高则是安全的:多裁几轮老对话,总比请求发不出去强。
|
|
134
|
+
*/
|
|
135
|
+
export const IMAGE_TOKEN_ESTIMATE = 800;
|
|
136
|
+
|
|
137
|
+
const contentCost = content => (Array.isArray(content)
|
|
138
|
+
? content.reduce(
|
|
139
|
+
(sum, part) => sum + (part?.type === 'image_url' ? IMAGE_TOKEN_ESTIMATE : estimateTokens(part?.text ?? '')),
|
|
140
|
+
0,
|
|
141
|
+
)
|
|
142
|
+
: estimateTokens(content));
|
|
143
|
+
|
|
144
|
+
const costOf = messages => messages.reduce((sum, m) => sum + contentCost(m.content) + 8, 0);
|
|
127
145
|
|
|
128
146
|
/**
|
|
129
147
|
* 上下文预算:保留系统提示词与最后一块(当前提问),从最早处整块丢弃。
|
|
@@ -328,7 +346,11 @@ export function createAiService({ root, store = defaultStore, token = '', tools,
|
|
|
328
346
|
});
|
|
329
347
|
if (!response.ok) {
|
|
330
348
|
const detail = await response.text().catch(() => '');
|
|
331
|
-
|
|
349
|
+
// 带图被拒最常见的原因是模型不是多模态:DeepSeek 的 chat / reasoner 都不收 image_url。
|
|
350
|
+
// 不点破的话用户只看到一行 400,会以为是 Key 或网络问题。
|
|
351
|
+
const withImages = convo.some(m => Array.isArray(m.content) && m.content.some(p => p?.type === 'image_url'));
|
|
352
|
+
const hint = withImages ? '(这一轮带了图片:当前模型可能不支持图片输入,请在设置里换成支持视觉模型的接口,或去掉图片再发)' : '';
|
|
353
|
+
yield { type: 'error', message: `模型接口返回 ${response.status}:${detail.slice(0, 300) || response.statusText}${hint}` };
|
|
332
354
|
return;
|
|
333
355
|
}
|
|
334
356
|
|
package/tools/validate.mjs
CHANGED
|
@@ -50,7 +50,9 @@ const sprints = Array.isArray(sprintsFile.sprints) ? sprintsFile.sprints : [];
|
|
|
50
50
|
const milestones = Array.isArray(sprintsFile.milestones) ? sprintsFile.milestones : [];
|
|
51
51
|
const journals = Array.isArray(journalFile.journals) ? journalFile.journals : [];
|
|
52
52
|
|
|
53
|
-
const STORY_FLOW = ['draft', 'ready', 'in-progress', 'review', 'done'];
|
|
53
|
+
const STORY_FLOW = ['draft', 'ready', 'in-progress', 'review', 'done', 'cancelled'];
|
|
54
|
+
// 终态:done 与 cancelled 都不再往前走,也不再要求验收 / 任务全部做完
|
|
55
|
+
const STORY_TERMINAL = ['done', 'cancelled'];
|
|
54
56
|
const TASK_FLOW = ['todo', 'red', 'green', 'refactored', 'done'];
|
|
55
57
|
const EPIC_FLOW = ['draft', 'active', 'done', 'cancelled'];
|
|
56
58
|
const MILESTONE_FLOW = ['planned', 'at-risk', 'reached', 'missed'];
|
|
@@ -136,7 +138,8 @@ for (const s of stories) {
|
|
|
136
138
|
else if (!(epicById.get(epicId).stories ?? []).includes(id))
|
|
137
139
|
err(`story ${id}: 挂在 ${epicId} 上,但该 epic 的 stories 里没有它(双向引用不一致)`);
|
|
138
140
|
|
|
139
|
-
if (!STORY_FLOW.includes(status)
|
|
141
|
+
if (!STORY_FLOW.includes(status))
|
|
142
|
+
err(`story ${id}: 非法状态 ${status}(合法:${STORY_FLOW.join('/')})`);
|
|
140
143
|
if (!Number.isFinite(points)) err(`story ${id}: points 必须是数字`);
|
|
141
144
|
else if (![1, 2, 3, 5, 8, 13].includes(points)) warn(`story ${id}: points ${points} 不在斐波那契序列`);
|
|
142
145
|
|
|
@@ -153,7 +156,8 @@ for (const s of stories) {
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
// 取消掉的故事不会再交付,别逼着为它补验收标准
|
|
160
|
+
if (['ready', 'in-progress', 'review', 'done'].includes(status) && (!acceptance || acceptance.length === 0))
|
|
157
161
|
err(`story ${id}: 状态 ${status} 但验收标准为空`);
|
|
158
162
|
for (const [i, a] of (acceptance ?? []).entries()) {
|
|
159
163
|
if (!a.given || !a.when || !a.then) err(`story ${id}: 第 ${i + 1} 条验收标准缺 given/when/then`);
|
|
@@ -163,7 +167,8 @@ for (const s of stories) {
|
|
|
163
167
|
if (!hit) err(`story ${id}: 第 ${i + 1} 条验收的 verifyBy ${tid} 不存在`);
|
|
164
168
|
else if (hit.story.id !== id) err(`story ${id}: 第 ${i + 1} 条验收的 verifyBy ${tid} 属于 ${hit.story.id}`);
|
|
165
169
|
}
|
|
166
|
-
if (!(a.verifyBy ?? []).length &&
|
|
170
|
+
if (!(a.verifyBy ?? []).length && !['draft', 'cancelled'].includes(status))
|
|
171
|
+
warn(`story ${id}: 第 ${i + 1} 条验收没有 verifyBy(没绑定验证任务)`);
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
// 阻塞 / 依赖
|
|
@@ -175,7 +180,9 @@ for (const s of stories) {
|
|
|
175
180
|
if (!s.blocked.reason) err(`story ${id}: blocked 缺少 reason(堵在哪要说清)`);
|
|
176
181
|
if (!DATE_RE.test(s.blocked.since ?? '')) err(`story ${id}: blocked.since 格式应为 YYYY-MM-DD`);
|
|
177
182
|
if (status === 'done') err(`story ${id}: 已 done 却还标着 blocked`);
|
|
178
|
-
if (
|
|
183
|
+
if (status === 'cancelled') err(`story ${id}: 已取消却还标着 blocked(不做了就别再挂着阻塞)`);
|
|
184
|
+
if (sprintId && !STORY_TERMINAL.includes(status))
|
|
185
|
+
warn(`story ${id}: 处于 ${sprintId} 但被阻塞(${s.blocked.reason}),站会必须处理`);
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
// 范围变更标记
|
|
@@ -191,7 +198,8 @@ for (const s of stories) {
|
|
|
191
198
|
if (!sp) err(`story ${id}: sprintId ${sprintId} 不存在`);
|
|
192
199
|
else if (!(sp.stories ?? []).includes(id))
|
|
193
200
|
err(`story ${id}: 指向 ${sprintId},但该 sprint 的 stories 里没有它(双向引用不一致)`);
|
|
194
|
-
if (!acceptance || acceptance.length === 0)
|
|
201
|
+
if (status !== 'cancelled' && (!acceptance || acceptance.length === 0))
|
|
202
|
+
err(`story ${id}: 已进 sprint 但无验收标准(不满足 ready)`);
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
if (s.schedule) {
|
|
@@ -230,8 +238,14 @@ for (const sp of sprints) {
|
|
|
230
238
|
const st = storyById.get(sid);
|
|
231
239
|
if (!st) { err(`sprint ${sp.id}: 引用 story ${sid} 不存在`); continue; }
|
|
232
240
|
if (st.sprintId !== sp.id) err(`sprint ${sp.id}: story ${sid} 的 sprintId 是 ${st.sprintId},双向引用不一致`);
|
|
241
|
+
if (st.status === 'cancelled')
|
|
242
|
+
warn(`story ${sid}: 已取消但仍排在 ${sp.id} 里,建议从迭代移出(点数已不计入承诺)`);
|
|
233
243
|
}
|
|
234
|
-
|
|
244
|
+
// 已取消的故事不再占迭代容量:承诺点数只算还可能交付的
|
|
245
|
+
const committed = (sp.stories ?? []).reduce((a, sid) => {
|
|
246
|
+
const st = storyById.get(sid);
|
|
247
|
+
return st && st.status !== 'cancelled' ? a + (st.points ?? 0) : a;
|
|
248
|
+
}, 0);
|
|
235
249
|
if (Number.isFinite(sp.capacity) && committed > sp.capacity)
|
|
236
250
|
warn(`sprint ${sp.id}: 承诺 ${committed} 点超过容量 ${sp.capacity} 点`);
|
|
237
251
|
if (sp.start <= today && sp.end >= today) active++;
|