@aipper/aiws 0.0.28 → 0.0.30

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 (267) hide show
  1. package/README.md +18 -2
  2. package/bin/aiws.js +2 -2
  3. package/dist/aiws-package.d.ts +4 -0
  4. package/dist/aiws-package.js +11 -0
  5. package/dist/aiws-package.js.map +1 -0
  6. package/dist/aiws-paths.d.ts +48 -0
  7. package/dist/aiws-paths.js +129 -0
  8. package/dist/aiws-paths.js.map +1 -0
  9. package/dist/backup.d.ts +27 -0
  10. package/dist/backup.js +125 -0
  11. package/dist/backup.js.map +1 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +773 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/codex-prompts.d.ts +17 -0
  16. package/dist/codex-prompts.js +49 -0
  17. package/dist/codex-prompts.js.map +1 -0
  18. package/dist/codex-skills.d.ts +17 -0
  19. package/dist/codex-skills.js +75 -0
  20. package/dist/codex-skills.js.map +1 -0
  21. package/dist/commands/change-advice.d.ts +25 -0
  22. package/dist/commands/change-advice.js +197 -0
  23. package/dist/commands/change-advice.js.map +1 -0
  24. package/dist/commands/change-evidence-command.d.ts +29 -0
  25. package/dist/commands/change-evidence-command.js +213 -0
  26. package/dist/commands/change-evidence-command.js.map +1 -0
  27. package/dist/commands/change-evidence-entry.d.ts +16 -0
  28. package/dist/commands/change-evidence-entry.js +35 -0
  29. package/dist/commands/change-evidence-entry.js.map +1 -0
  30. package/dist/commands/change-evidence.d.ts +108 -0
  31. package/dist/commands/change-evidence.js +341 -0
  32. package/dist/commands/change-evidence.js.map +1 -0
  33. package/dist/commands/change-finish.d.ts +71 -0
  34. package/dist/commands/change-finish.js +387 -0
  35. package/dist/commands/change-finish.js.map +1 -0
  36. package/dist/commands/change-git-status.d.ts +22 -0
  37. package/dist/commands/change-git-status.js +50 -0
  38. package/dist/commands/change-git-status.js.map +1 -0
  39. package/dist/commands/change-lifecycle-entry.d.ts +29 -0
  40. package/{src → dist}/commands/change-lifecycle-entry.js +21 -28
  41. package/dist/commands/change-lifecycle-entry.js.map +1 -0
  42. package/dist/commands/change-lifecycle.d.ts +56 -0
  43. package/dist/commands/change-lifecycle.js +211 -0
  44. package/dist/commands/change-lifecycle.js.map +1 -0
  45. package/dist/commands/change-metrics-command.d.ts +12 -0
  46. package/dist/commands/change-metrics-command.js +110 -0
  47. package/dist/commands/change-metrics-command.js.map +1 -0
  48. package/dist/commands/change-next-command.d.ts +18 -0
  49. package/dist/commands/change-next-command.js +138 -0
  50. package/dist/commands/change-next-command.js.map +1 -0
  51. package/dist/commands/change-review-gates.d.ts +107 -0
  52. package/dist/commands/change-review-gates.js +329 -0
  53. package/dist/commands/change-review-gates.js.map +1 -0
  54. package/dist/commands/change-scope-gate.d.ts +21 -0
  55. package/dist/commands/change-scope-gate.js +32 -0
  56. package/dist/commands/change-scope-gate.js.map +1 -0
  57. package/dist/commands/change-start.d.ts +30 -0
  58. package/dist/commands/change-start.js +91 -0
  59. package/dist/commands/change-start.js.map +1 -0
  60. package/dist/commands/change-state-command.d.ts +3 -0
  61. package/dist/commands/change-state-command.js +32 -0
  62. package/dist/commands/change-state-command.js.map +1 -0
  63. package/dist/commands/change-status-command.d.ts +13 -0
  64. package/dist/commands/change-status-command.js +126 -0
  65. package/dist/commands/change-status-command.js.map +1 -0
  66. package/dist/commands/change-status-context.d.ts +58 -0
  67. package/dist/commands/change-status-context.js +166 -0
  68. package/dist/commands/change-status-context.js.map +1 -0
  69. package/dist/commands/change-task-stats.d.ts +24 -0
  70. package/dist/commands/change-task-stats.js +66 -0
  71. package/dist/commands/change-task-stats.js.map +1 -0
  72. package/dist/commands/change-validate-entry.d.ts +17 -0
  73. package/dist/commands/change-validate-entry.js +44 -0
  74. package/dist/commands/change-validate-entry.js.map +1 -0
  75. package/dist/commands/change-validate.d.ts +32 -0
  76. package/dist/commands/change-validate.js +128 -0
  77. package/dist/commands/change-validate.js.map +1 -0
  78. package/dist/commands/change.d.ts +322 -0
  79. package/dist/commands/change.js +1731 -0
  80. package/dist/commands/change.js.map +1 -0
  81. package/dist/commands/codex-install-prompts.d.ts +4 -0
  82. package/dist/commands/codex-install-prompts.js +68 -0
  83. package/dist/commands/codex-install-prompts.js.map +1 -0
  84. package/dist/commands/codex-install-skills.d.ts +4 -0
  85. package/dist/commands/codex-install-skills.js +74 -0
  86. package/dist/commands/codex-install-skills.js.map +1 -0
  87. package/dist/commands/codex-status-prompts.d.ts +4 -0
  88. package/dist/commands/codex-status-prompts.js +44 -0
  89. package/dist/commands/codex-status-prompts.js.map +1 -0
  90. package/dist/commands/codex-status-skills.d.ts +4 -0
  91. package/dist/commands/codex-status-skills.js +54 -0
  92. package/dist/commands/codex-status-skills.js.map +1 -0
  93. package/dist/commands/codex-uninstall-prompts.d.ts +4 -0
  94. package/dist/commands/codex-uninstall-prompts.js +51 -0
  95. package/dist/commands/codex-uninstall-prompts.js.map +1 -0
  96. package/dist/commands/codex-uninstall-skills.d.ts +4 -0
  97. package/dist/commands/codex-uninstall-skills.js +59 -0
  98. package/dist/commands/codex-uninstall-skills.js.map +1 -0
  99. package/dist/commands/dashboard.d.ts +222 -0
  100. package/dist/commands/dashboard.js +193 -0
  101. package/dist/commands/dashboard.js.map +1 -0
  102. package/dist/commands/hooks-install.d.ts +4 -0
  103. package/dist/commands/hooks-install.js +90 -0
  104. package/dist/commands/hooks-install.js.map +1 -0
  105. package/dist/commands/hooks-status.d.ts +4 -0
  106. package/dist/commands/hooks-status.js +89 -0
  107. package/dist/commands/hooks-status.js.map +1 -0
  108. package/dist/commands/init.d.ts +4 -0
  109. package/dist/commands/init.js +100 -0
  110. package/dist/commands/init.js.map +1 -0
  111. package/dist/commands/opencode-auto.d.ts +11 -0
  112. package/dist/commands/opencode-auto.js +158 -0
  113. package/dist/commands/opencode-auto.js.map +1 -0
  114. package/dist/commands/opencode-status.d.ts +4 -0
  115. package/dist/commands/opencode-status.js +75 -0
  116. package/dist/commands/opencode-status.js.map +1 -0
  117. package/dist/commands/opencode-supervise.d.ts +24 -0
  118. package/dist/commands/opencode-supervise.js +188 -0
  119. package/dist/commands/opencode-supervise.js.map +1 -0
  120. package/dist/commands/rollback.d.ts +4 -0
  121. package/dist/commands/rollback.js +12 -0
  122. package/dist/commands/rollback.js.map +1 -0
  123. package/dist/commands/update.d.ts +4 -0
  124. package/dist/commands/update.js +212 -0
  125. package/dist/commands/update.js.map +1 -0
  126. package/dist/commands/validate.d.ts +4 -0
  127. package/dist/commands/validate.js +222 -0
  128. package/dist/commands/validate.js.map +1 -0
  129. package/dist/commands/ws-commit.d.ts +5 -0
  130. package/dist/commands/ws-commit.js +39 -0
  131. package/dist/commands/ws-commit.js.map +1 -0
  132. package/dist/commands/ws-deliver.d.ts +5 -0
  133. package/dist/commands/ws-deliver.js +59 -0
  134. package/dist/commands/ws-deliver.js.map +1 -0
  135. package/dist/commands/ws-finish.d.ts +5 -0
  136. package/dist/commands/ws-finish.js +142 -0
  137. package/dist/commands/ws-finish.js.map +1 -0
  138. package/dist/commands/ws-handoff.d.ts +5 -0
  139. package/dist/commands/ws-handoff.js +35 -0
  140. package/dist/commands/ws-handoff.js.map +1 -0
  141. package/dist/commands/ws-migrate.d.ts +5 -0
  142. package/dist/commands/ws-migrate.js +41 -0
  143. package/dist/commands/ws-migrate.js.map +1 -0
  144. package/dist/commands/ws-plan-verify.d.ts +5 -0
  145. package/dist/commands/ws-plan-verify.js +38 -0
  146. package/dist/commands/ws-plan-verify.js.map +1 -0
  147. package/dist/commands/ws-pull.d.ts +5 -0
  148. package/dist/commands/ws-pull.js +26 -0
  149. package/dist/commands/ws-pull.js.map +1 -0
  150. package/dist/commands/ws-push.d.ts +5 -0
  151. package/dist/commands/ws-push.js +68 -0
  152. package/dist/commands/ws-push.js.map +1 -0
  153. package/dist/commands/ws-submodule-setup.d.ts +5 -0
  154. package/dist/commands/ws-submodule-setup.js +55 -0
  155. package/dist/commands/ws-submodule-setup.js.map +1 -0
  156. package/dist/commands/ws-verify-before-complete.d.ts +5 -0
  157. package/dist/commands/ws-verify-before-complete.js +35 -0
  158. package/dist/commands/ws-verify-before-complete.js.map +1 -0
  159. package/dist/dashboard/app.d.ts +1 -0
  160. package/dist/dashboard/app.js +370 -0
  161. package/dist/dashboard/app.js.map +1 -0
  162. package/dist/delegation/cli.d.ts +6 -0
  163. package/dist/delegation/cli.js +14 -0
  164. package/dist/delegation/cli.js.map +1 -0
  165. package/dist/delegation/index.d.ts +35 -0
  166. package/dist/delegation/index.js +118 -0
  167. package/dist/delegation/index.js.map +1 -0
  168. package/dist/errors.d.ts +8 -0
  169. package/dist/errors.js +11 -0
  170. package/dist/errors.js.map +1 -0
  171. package/dist/exec.d.ts +9 -0
  172. package/dist/exec.js +22 -0
  173. package/dist/exec.js.map +1 -0
  174. package/dist/fs.d.ts +9 -0
  175. package/dist/fs.js +62 -0
  176. package/dist/fs.js.map +1 -0
  177. package/dist/governance.d.ts +10 -0
  178. package/dist/governance.js +135 -0
  179. package/dist/governance.js.map +1 -0
  180. package/dist/hash.d.ts +3 -0
  181. package/dist/hash.js +13 -0
  182. package/dist/hash.js.map +1 -0
  183. package/dist/hooks/deploy-claude.d.ts +13 -0
  184. package/dist/hooks/deploy-claude.js +67 -0
  185. package/dist/hooks/deploy-claude.js.map +1 -0
  186. package/dist/journal.d.ts +22 -0
  187. package/dist/journal.js +112 -0
  188. package/dist/journal.js.map +1 -0
  189. package/dist/json-schema-lite.d.ts +22 -0
  190. package/dist/json-schema-lite.js +148 -0
  191. package/dist/json-schema-lite.js.map +1 -0
  192. package/dist/managed-blocks.d.ts +24 -0
  193. package/dist/managed-blocks.js +84 -0
  194. package/dist/managed-blocks.js.map +1 -0
  195. package/dist/manifest.d.ts +24 -0
  196. package/dist/manifest.js +145 -0
  197. package/dist/manifest.js.map +1 -0
  198. package/dist/opencode-env.d.ts +62 -0
  199. package/dist/opencode-env.js +178 -0
  200. package/dist/opencode-env.js.map +1 -0
  201. package/dist/path-utils.d.ts +2 -0
  202. package/dist/path-utils.js +8 -0
  203. package/dist/path-utils.js.map +1 -0
  204. package/dist/spec.d.ts +17 -0
  205. package/dist/spec.js +174 -0
  206. package/dist/spec.js.map +1 -0
  207. package/dist/template.d.ts +20 -0
  208. package/dist/template.js +123 -0
  209. package/dist/template.js.map +1 -0
  210. package/dist/workspace.d.ts +4 -0
  211. package/dist/workspace.js +19 -0
  212. package/dist/workspace.js.map +1 -0
  213. package/package.json +11 -3
  214. package/src/aiws-package.js +0 -15
  215. package/src/backup.js +0 -149
  216. package/src/cli.js +0 -647
  217. package/src/codex-prompts.js +0 -74
  218. package/src/codex-skills.js +0 -111
  219. package/src/commands/change-advice.js +0 -200
  220. package/src/commands/change-evidence-command.js +0 -242
  221. package/src/commands/change-evidence-entry.js +0 -40
  222. package/src/commands/change-evidence.js +0 -340
  223. package/src/commands/change-finish.js +0 -484
  224. package/src/commands/change-git-status.js +0 -53
  225. package/src/commands/change-lifecycle.js +0 -236
  226. package/src/commands/change-metrics-command.js +0 -110
  227. package/src/commands/change-next-command.js +0 -129
  228. package/src/commands/change-review-gates.js +0 -315
  229. package/src/commands/change-scope-gate.js +0 -34
  230. package/src/commands/change-start.js +0 -120
  231. package/src/commands/change-state-command.js +0 -40
  232. package/src/commands/change-status-command.js +0 -134
  233. package/src/commands/change-status-context.js +0 -202
  234. package/src/commands/change-task-stats.js +0 -64
  235. package/src/commands/change-validate-entry.js +0 -45
  236. package/src/commands/change-validate.js +0 -127
  237. package/src/commands/change.js +0 -2087
  238. package/src/commands/codex-install-prompts.js +0 -68
  239. package/src/commands/codex-install-skills.js +0 -74
  240. package/src/commands/codex-status-prompts.js +0 -55
  241. package/src/commands/codex-status-skills.js +0 -63
  242. package/src/commands/codex-uninstall-prompts.js +0 -55
  243. package/src/commands/codex-uninstall-skills.js +0 -62
  244. package/src/commands/dashboard.js +0 -205
  245. package/src/commands/hooks-install.js +0 -92
  246. package/src/commands/hooks-status.js +0 -95
  247. package/src/commands/init.js +0 -93
  248. package/src/commands/opencode-status.js +0 -61
  249. package/src/commands/rollback.js +0 -13
  250. package/src/commands/update.js +0 -133
  251. package/src/commands/validate.js +0 -222
  252. package/src/dashboard/app.js +0 -371
  253. package/src/errors.js +0 -15
  254. package/src/exec.js +0 -34
  255. package/src/fs.js +0 -91
  256. package/src/governance.js +0 -159
  257. package/src/hash.js +0 -25
  258. package/src/json-schema-lite.js +0 -164
  259. package/src/managed-blocks.js +0 -131
  260. package/src/manifest.js +0 -153
  261. package/src/opencode-env.js +0 -76
  262. package/src/path-utils.js +0 -20
  263. package/src/spec.js +0 -195
  264. package/src/template.js +0 -187
  265. package/src/workspace.js +0 -23
  266. /package/{src → dist}/dashboard/index.html +0 -0
  267. /package/{src → dist}/dashboard/style.css +0 -0
@@ -1,371 +0,0 @@
1
- async function fetchJson(url) {
2
- const res = await fetch(url, { headers: { "Accept": "application/json" } });
3
- const text = await res.text();
4
- let data = null;
5
- try {
6
- data = JSON.parse(text);
7
- } catch {
8
- data = { ok: false, error: "invalid json", raw: text };
9
- }
10
- if (!res.ok) {
11
- return { ok: false, status: res.status, ...data };
12
- }
13
- return data;
14
- }
15
-
16
- function escapeHtml(s) {
17
- return String(s || "")
18
- .replaceAll("&", "&")
19
- .replaceAll("<", "&lt;")
20
- .replaceAll(">", "&gt;")
21
- .replaceAll("\"", "&quot;")
22
- .replaceAll("'", "&#39;");
23
- }
24
-
25
- function blockersBadge(n) {
26
- const cls = n === 0 ? "good" : n <= 2 ? "warn" : "bad";
27
- return `<span class="badge"><span class="dot ${cls}"></span>${n === 0 ? "none" : `${n} blockers`}</span>`;
28
- }
29
-
30
- function driftBadge(n) {
31
- const cls = n === 0 ? "good" : "warn";
32
- return `<span class="badge"><span class="dot ${cls}"></span>${n === 0 ? "clean" : `${n} drift`}</span>`;
33
- }
34
-
35
- function tasksBadge(done, total, unchecked) {
36
- const cls = unchecked === 0 && total > 0 ? "good" : unchecked === 0 ? "warn" : "warn";
37
- const label = total > 0 ? `${done}/${total} (unchecked=${unchecked})` : "no tasks";
38
- return `<span class="badge"><span class="dot ${cls}"></span>${label}</span>`;
39
- }
40
-
41
- function summarizeNextAdvice(advice) {
42
- const a = advice && typeof advice === "object" ? advice : {};
43
- const actions = Array.isArray(a.actions) ? a.actions : [];
44
- const recommended = Array.isArray(a.recommended) ? a.recommended : [];
45
- const prohibitions = Array.isArray(a.prohibitions) ? a.prohibitions : [];
46
- return actions[0] || recommended[0] || prohibitions[0] || "";
47
- }
48
-
49
- function renderDetails(obj) {
50
- return JSON.stringify(obj, null, 2);
51
- }
52
-
53
- function renderValidateGroups(groups) {
54
- const order = [
55
- ["truth_drift", "Truth drift"],
56
- ["missing_files", "Missing files"],
57
- ["placeholders", "Placeholders / WS:TODO"],
58
- ["bindings", "Bindings"],
59
- ["scope_gate", "Scope gate"],
60
- ["finish_gate", "Finish gate"],
61
- ["plan_quality", "Plan quality"],
62
- ["other", "Other"],
63
- ];
64
- const g = groups && typeof groups === "object" ? groups : {};
65
- const lines = [];
66
- for (const [key, label] of order) {
67
- const it = g[key] || { errors: [], warnings: [] };
68
- const errs = Array.isArray(it.errors) ? it.errors : [];
69
- const warns = Array.isArray(it.warnings) ? it.warnings : [];
70
- if (errs.length === 0 && warns.length === 0) continue;
71
- lines.push(`${label}:`);
72
- for (const e of errs) lines.push(` - error: ${e}`);
73
- for (const w of warns) lines.push(` - warn: ${w}`);
74
- lines.push("");
75
- }
76
- return lines.join("\n").trim();
77
- }
78
-
79
- async function runStrictValidate(changeId) {
80
- return fetchJson(`/api/change/${encodeURIComponent(changeId)}/validate?strict=1`);
81
- }
82
-
83
- function normalizeStageId(value) {
84
- return String(value || "").trim();
85
- }
86
-
87
- function stageLabel(stageId) {
88
- return normalizeStageId(stageId) || "(unknown)";
89
- }
90
-
91
- function renderContractField(label, value) {
92
- return `<div class="contract-field">
93
- <div class="contract-field-label">${escapeHtml(label)}</div>
94
- <div>${escapeHtml(value || "(none)")}</div>
95
- </div>`;
96
- }
97
-
98
- function renderContractCard(title, contract) {
99
- if (!contract) return "";
100
- return `<div class="contract-card">
101
- <div class="contract-title">${escapeHtml(title)}</div>
102
- <div class="contract-stage"><code>${escapeHtml(contract.stage || "")}</code></div>
103
- ${renderContractField("目标", contract.goal)}
104
- ${renderContractField("必需输入", contract.requiredInputs)}
105
- ${renderContractField("必需输出", contract.requiredOutputs)}
106
- ${renderContractField("阻断条件", contract.blockers)}
107
- ${renderContractField("下一步", contract.next)}
108
- </div>`;
109
- }
110
-
111
- function normalizeGateStatus(value) {
112
- return String(value || "missing").trim() || "missing";
113
- }
114
-
115
- function reviewGateText(reviewGates) {
116
- const gates = reviewGates && typeof reviewGates === "object" ? reviewGates : {};
117
- const spec = normalizeGateStatus(gates?.specReview?.status);
118
- const quality = normalizeGateStatus(gates?.qualityReview?.status);
119
- const validate = normalizeGateStatus(gates?.validateStamp?.status);
120
- const preComplete = normalizeGateStatus(gates?.verifyBeforeComplete?.status);
121
- return `review-gates: spec=${spec}, quality=${quality}, validate=${validate}, pre-complete=${preComplete}`;
122
- }
123
-
124
- function renderReviewGateBadges(reviewGates) {
125
- const gates = reviewGates && typeof reviewGates === "object" ? reviewGates : {};
126
- const items = [
127
- ["spec", gates?.specReview],
128
- ["quality", gates?.qualityReview],
129
- ["validate", gates?.validateStamp],
130
- ["pre-complete", gates?.verifyBeforeComplete],
131
- ];
132
- return `<div class="governance-head">${items
133
- .map(([label, gate]) => {
134
- const ready = gate?.ready === true;
135
- const status = normalizeGateStatus(gate?.status);
136
- const cls = ready ? "good" : "warn";
137
- return `<span class="badge"><span class="dot ${cls}"></span>${escapeHtml(`${label}=${status}`)}</span>`;
138
- })
139
- .join("")}</div>`;
140
- }
141
-
142
- function scopeGateText(scopeGate) {
143
- const gate = scopeGate && typeof scopeGate === "object" ? scopeGate : {};
144
- const status = String(gate.status || "not_run").trim() || "not_run";
145
- const details = [];
146
- if (typeof gate.errors === "number") details.push(`errors=${gate.errors}`);
147
- if (typeof gate.warnings === "number") details.push(`warnings=${gate.warnings}`);
148
- if (gate.lastRunAt) details.push(`at=${gate.lastRunAt}`);
149
- return `scope-gate: ${status}${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
150
- }
151
-
152
- function renderScopeGateBadge(scopeGate) {
153
- const gate = scopeGate && typeof scopeGate === "object" ? scopeGate : {};
154
- const status = String(gate.status || "not_run").trim() || "not_run";
155
- const ready = gate.ready === true;
156
- const cls = ready ? "good" : status === "failed" ? "bad" : "warn";
157
- return `<span class="badge"><span class="dot ${cls}"></span>${escapeHtml(`scope=${status}`)}</span>`;
158
- }
159
-
160
- function renderGovernanceSummary(selected, stageData) {
161
- const governance = selected?.governance && typeof selected.governance === "object" ? selected.governance : {};
162
- const currentStage = normalizeStageId(governance.currentStage);
163
- const recommendedStage = normalizeStageId(governance.recommendedStage);
164
- const phase = normalizeStageId(governance.phase || selected?.phase);
165
- const contracts = Array.isArray(stageData?.stages) ? stageData.stages : [];
166
- const byStage = new Map(contracts.map((contract) => [normalizeStageId(contract.stage), contract]));
167
- const currentContract = byStage.get(currentStage) || null;
168
- const recommendedContract = byStage.get(recommendedStage) || null;
169
-
170
- const rail = contracts.length > 0
171
- ? `<div class="governance-rail">${contracts
172
- .map((contract, index) => {
173
- const stage = normalizeStageId(contract.stage);
174
- const cls = [
175
- "stage-chip",
176
- stage === currentStage ? "active" : "",
177
- stage === recommendedStage && stage !== currentStage ? "recommended" : "",
178
- ]
179
- .filter(Boolean)
180
- .join(" ");
181
- const arrow = index < contracts.length - 1 ? `<span class="rail-arrow">&rarr;</span>` : "";
182
- return `<span class="${cls}"><code>${escapeHtml(stageLabel(stage))}</code></span>${arrow}`;
183
- })
184
- .join("")}</div>`
185
- : `<div class="muted">(workflow-stage-contracts unavailable)</div>`;
186
-
187
- const cards = [];
188
- if (currentContract) cards.push(renderContractCard(`Current Contract (${phase || "unknown"})`, currentContract));
189
- if (recommendedContract && recommendedStage && recommendedStage !== currentStage) {
190
- cards.push(renderContractCard("Next Contract", recommendedContract));
191
- }
192
-
193
- return `<div class="governance-head">
194
- <span class="badge"><span class="dot good"></span>phase=${escapeHtml(phase || "(unknown)")}</span>
195
- <span class="badge"><span class="dot warn"></span>current=${escapeHtml(stageLabel(currentStage))}</span>
196
- <span class="badge"><span class="dot good"></span>next=${escapeHtml(stageLabel(recommendedStage || currentStage))}</span>
197
- </div>
198
- ${governance.rationale ? `<div class="muted">${escapeHtml(governance.rationale)}</div>` : ""}
199
- ${governance.warning ? `<div class="muted">warning: ${escapeHtml(governance.warning)}</div>` : ""}
200
- ${selected?.reviewGates ? renderReviewGateBadges(selected.reviewGates) : ""}
201
- ${selected?.scopeGate ? `<div class="governance-head">${renderScopeGateBadge(selected.scopeGate)}</div>` : ""}
202
- ${rail}
203
- <div class="governance-grid">
204
- ${cards.join("") || `<div class="muted">No contract summary available.</div>`}
205
- </div>`;
206
- }
207
-
208
- async function refresh() {
209
- const summaryEl = document.getElementById("summary");
210
- const bodyEl = document.getElementById("changes-body");
211
- const detailsTitleEl = document.getElementById("details-title");
212
- const detailsEl = document.getElementById("details");
213
- const governanceEl = document.getElementById("governance-summary");
214
-
215
- summaryEl.textContent = "Loading…";
216
- bodyEl.innerHTML = "";
217
- if (governanceEl) governanceEl.innerHTML = `<div class="muted">Loading workflow governance…</div>`;
218
-
219
- const [data, stageData] = await Promise.all([fetchJson("/api/changes"), fetchJson("/api/workflow-stages")]);
220
- if (!data || data.ok === false) {
221
- summaryEl.textContent = "Failed";
222
- detailsTitleEl.textContent = "Error";
223
- detailsEl.textContent = renderDetails(data);
224
- if (governanceEl) governanceEl.innerHTML = `<div class="muted">Failed to load governance summary.</div>`;
225
- return;
226
- }
227
-
228
- const items = Array.isArray(data.changes) ? data.changes : [];
229
- summaryEl.textContent = `${items.length} change(s)`;
230
-
231
- bodyEl.innerHTML = items
232
- .map((c) => {
233
- const changeId = c.changeId || c.id || "";
234
- const tasks = c.tasks || {};
235
- const drift = Array.isArray(c.driftFiles) ? c.driftFiles.length : 0;
236
- const blockers = Array.isArray(c.blockersStrict) ? c.blockersStrict.length : 0;
237
- const bindings = c.bindings || {};
238
- const planFile = bindings.planFile || "";
239
- const evidencePaths = Array.isArray(bindings.evidencePaths) ? bindings.evidencePaths : [];
240
- const collaboration = c.collaboration && typeof c.collaboration === "object" ? c.collaboration : {};
241
- const nextHint = summarizeNextAdvice(c.nextAdvice);
242
- const governance = c.governance && typeof c.governance === "object" ? c.governance : {};
243
- const reviewGateLine = reviewGateText(c.reviewGates);
244
- const scopeGateLine = scopeGateText(c.scopeGate);
245
- const collabLabel = `analysis=${collaboration.analysis || 0}, patches=${collaboration.patches || 0}, review=${collaboration.review || 0}`;
246
- const stageLine = `phase=${governance.phase || c.phase || "unknown"}, current=${governance.currentStage || "unknown"}, next=${governance.recommendedStage || governance.currentStage || "unknown"}`;
247
- const rationaleLine = governance.rationale ? `<div class="muted">${escapeHtml(governance.rationale)}</div>` : "";
248
- const warningLine = governance.warning ? `<div class="muted">warning: ${escapeHtml(governance.warning)}</div>` : "";
249
- const pe = `${planFile ? `<div><code>${escapeHtml(planFile)}</code></div>` : `<div class="muted">(no Plan_File)</div>`}
250
- <div class="muted">${evidencePaths.length} evidence path(s)</div>
251
- <div class="muted">${escapeHtml(collabLabel)}</div>
252
- <div class="muted">${escapeHtml(stageLine)}</div>
253
- <div class="muted">${escapeHtml(reviewGateLine)}</div>
254
- <div class="muted">${escapeHtml(scopeGateLine)}</div>
255
- ${rationaleLine}
256
- ${warningLine}
257
- ${nextHint ? `<div class="muted">${escapeHtml(nextHint)}</div>` : ""}`;
258
- return `<tr data-change="${escapeHtml(changeId)}">
259
- <td><a class="link" href="#" data-open="${escapeHtml(changeId)}">${escapeHtml(changeId)}</a></td>
260
- <td>${tasksBadge(tasks.done || 0, tasks.total || 0, tasks.unchecked || 0)}</td>
261
- <td>${driftBadge(drift)}</td>
262
- <td>${blockersBadge(blockers)}</td>
263
- <td>${pe}</td>
264
- <td>
265
- <button class="btn secondary" data-validate="${escapeHtml(changeId)}">Validate --strict</button>
266
- </td>
267
- </tr>`;
268
- })
269
- .join("");
270
-
271
- bodyEl.querySelectorAll("[data-open]").forEach((a) => {
272
- a.addEventListener("click", (ev) => {
273
- ev.preventDefault();
274
- const changeId = a.getAttribute("data-open") || "";
275
- const selected = items.find((x) => (x.changeId || x.id) === changeId) || null;
276
- detailsTitleEl.textContent = changeId ? `change: ${changeId}` : "Details";
277
- if (!selected) {
278
- detailsEl.textContent = renderDetails(selected);
279
- if (governanceEl) governanceEl.innerHTML = `<div class="muted">No governance summary available.</div>`;
280
- return;
281
- }
282
- if (governanceEl) governanceEl.innerHTML = renderGovernanceSummary(selected, stageData);
283
- const b = selected.bindings || {};
284
- const governance = selected.governance && typeof selected.governance === "object" ? selected.governance : {};
285
- const evid = Array.isArray(b.evidencePaths) ? b.evidencePaths : [];
286
- const lines = [];
287
- lines.push(`changeId: ${selected.changeId || ""}`);
288
- if (selected.reqId) lines.push(`Req_ID: ${selected.reqId}`);
289
- if (selected.probId) lines.push(`Problem_ID: ${selected.probId}`);
290
- if (b.planFile) lines.push(`Plan_File: ${b.planFile}`);
291
- if (b.contractRow) lines.push(`Contract_Row: ${b.contractRow}`);
292
- if (evid.length > 0) {
293
- lines.push("Evidence_Path:");
294
- for (const p of evid) lines.push(`- ${p}`);
295
- }
296
- if (selected.collaboration && typeof selected.collaboration === "object") {
297
- lines.push("Collaboration:");
298
- lines.push(`- analysis: ${selected.collaboration.analysis || 0}`);
299
- lines.push(`- patches: ${selected.collaboration.patches || 0}`);
300
- lines.push(`- review: ${selected.collaboration.review || 0}`);
301
- lines.push(`- evidence: ${selected.collaboration.evidence || 0}`);
302
- }
303
- if (selected.reviewGates && typeof selected.reviewGates === "object") {
304
- lines.push("");
305
- lines.push("Review gates:");
306
- lines.push(`- ${reviewGateText(selected.reviewGates)}`);
307
- for (const [label, gate] of [
308
- ["ws-spec-review", selected.reviewGates.specReview],
309
- ["ws-quality-review", selected.reviewGates.qualityReview],
310
- ["validate-stamp", selected.reviewGates.validateStamp],
311
- ["ws-verify-before-complete", selected.reviewGates.verifyBeforeComplete],
312
- ]) {
313
- if (!gate || typeof gate !== "object") continue;
314
- const foundPath = gate.foundPath ? ` path=${gate.foundPath}` : "";
315
- lines.push(`- ${label}: ready=${gate.ready === true} status=${normalizeGateStatus(gate.status)}${foundPath}`);
316
- }
317
- }
318
- if (selected.scopeGate && typeof selected.scopeGate === "object") {
319
- lines.push("");
320
- lines.push("Scope gate:");
321
- lines.push(`- ${scopeGateText(selected.scopeGate)}`);
322
- }
323
- if (selected.nextAdvice && typeof selected.nextAdvice === "object") {
324
- const actions = Array.isArray(selected.nextAdvice.actions) ? selected.nextAdvice.actions : [];
325
- const recommended = Array.isArray(selected.nextAdvice.recommended) ? selected.nextAdvice.recommended : [];
326
- const prohibitions = Array.isArray(selected.nextAdvice.prohibitions) ? selected.nextAdvice.prohibitions : [];
327
- if (actions.length > 0 || recommended.length > 0 || prohibitions.length > 0) {
328
- lines.push("");
329
- lines.push("Next:");
330
- for (const x of actions) lines.push(`- action: ${x}`);
331
- for (const x of recommended) lines.push(`- recommended: ${x}`);
332
- for (const x of prohibitions) lines.push(`- do-not: ${x}`);
333
- }
334
- }
335
- if (governance.rationale) {
336
- lines.push("");
337
- lines.push(`Governance rationale: ${governance.rationale}`);
338
- }
339
- if (governance.warning) {
340
- lines.push(`Governance warning: ${governance.warning}`);
341
- }
342
- if (Array.isArray(selected.blockersStrict) && selected.blockersStrict.length > 0) {
343
- lines.push("");
344
- lines.push("Blockers (strict):");
345
- for (const x of selected.blockersStrict) lines.push(`- ${x}`);
346
- }
347
- lines.push("");
348
- lines.push("Raw JSON:");
349
- lines.push(renderDetails(selected));
350
- detailsEl.textContent = lines.join("\n");
351
- });
352
- });
353
-
354
- bodyEl.querySelectorAll("[data-validate]").forEach((btn) => {
355
- btn.addEventListener("click", async () => {
356
- const changeId = btn.getAttribute("data-validate") || "";
357
- if (!changeId) return;
358
- btn.disabled = true;
359
- btn.textContent = "Running…";
360
- detailsTitleEl.textContent = `validate --strict: ${changeId}`;
361
- const res = await runStrictValidate(changeId);
362
- const grouped = renderValidateGroups(res && res.groups ? res.groups : null);
363
- detailsEl.textContent = grouped ? `${grouped}\n\nRaw JSON:\n${renderDetails(res)}` : renderDetails(res);
364
- btn.disabled = false;
365
- btn.textContent = "Validate --strict";
366
- });
367
- });
368
- }
369
-
370
- document.getElementById("refresh")?.addEventListener("click", refresh);
371
- refresh();
package/src/errors.js DELETED
@@ -1,15 +0,0 @@
1
- export class UserError extends Error {
2
- /**
3
- * @param {string} message
4
- * @param {{ exitCode?: number, details?: string }=} options
5
- */
6
- constructor(message, options) {
7
- super(message);
8
- this.name = "UserError";
9
- /** @type {number} */
10
- this.exitCode = options?.exitCode ?? 2;
11
- /** @type {string | undefined} */
12
- this.details = options?.details;
13
- }
14
- }
15
-
package/src/exec.js DELETED
@@ -1,34 +0,0 @@
1
- import { spawn } from "node:child_process";
2
-
3
- /**
4
- * @param {string} command
5
- * @param {string[]} args
6
- * @param {{ cwd?: string }=} options
7
- * @returns {Promise<{ code: number, stdout: string, stderr: string }>}
8
- */
9
- export function runCommand(command, args, options) {
10
- return new Promise((resolve, reject) => {
11
- const child = spawn(command, args, {
12
- cwd: options?.cwd,
13
- stdio: ["ignore", "pipe", "pipe"],
14
- });
15
-
16
- /** @type {Buffer[]} */
17
- const out = [];
18
- /** @type {Buffer[]} */
19
- const err = [];
20
-
21
- child.stdout.on("data", (b) => out.push(Buffer.from(b)));
22
- child.stderr.on("data", (b) => err.push(Buffer.from(b)));
23
-
24
- child.on("error", (e) => reject(e));
25
- child.on("close", (code) => {
26
- resolve({
27
- code: typeof code === "number" ? code : 1,
28
- stdout: Buffer.concat(out).toString("utf8"),
29
- stderr: Buffer.concat(err).toString("utf8"),
30
- });
31
- });
32
- });
33
- }
34
-
package/src/fs.js DELETED
@@ -1,91 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- /**
5
- * @param {string} p
6
- */
7
- export async function pathExists(p) {
8
- try {
9
- await fs.access(p);
10
- return true;
11
- } catch {
12
- return false;
13
- }
14
- }
15
-
16
- /**
17
- * @param {string} p
18
- */
19
- export async function ensureDir(p) {
20
- await fs.mkdir(p, { recursive: true });
21
- }
22
-
23
- /**
24
- * @param {string} p
25
- */
26
- export async function readText(p) {
27
- return await fs.readFile(p, "utf8");
28
- }
29
-
30
- /**
31
- * @param {string} p
32
- * @param {string} content
33
- */
34
- export async function writeText(p, content) {
35
- await ensureDir(path.dirname(p));
36
- await fs.writeFile(p, content, "utf8");
37
- }
38
-
39
- /**
40
- * @param {string} src
41
- * @param {string} dest
42
- * @param {{ chmod?: number }=} options
43
- */
44
- export async function copyFile(src, dest, options) {
45
- await ensureDir(path.dirname(dest));
46
- await fs.copyFile(src, dest);
47
- if (typeof options?.chmod === "number") {
48
- await fs.chmod(dest, options.chmod);
49
- return;
50
- }
51
- try {
52
- const st = await fs.stat(src);
53
- await fs.chmod(dest, st.mode & 0o777);
54
- } catch {
55
- // ignore chmod errors (e.g. on certain filesystems)
56
- }
57
- }
58
-
59
- /**
60
- * Very small glob support for patterns ending with `/**`.
61
- *
62
- * @param {string} rootDir
63
- * @param {string} prefixDir
64
- * @returns {Promise<string[]>} paths relative to rootDir
65
- */
66
- export async function listFilesRecursive(rootDir, prefixDir) {
67
- /** @type {string[]} */
68
- const out = [];
69
- const start = path.join(rootDir, prefixDir);
70
- if (!(await pathExists(start))) return out;
71
-
72
- /** @type {Array<{ abs: string, rel: string }>} */
73
- const stack = [{ abs: start, rel: prefixDir }];
74
- while (stack.length > 0) {
75
- const cur = stack.pop();
76
- if (!cur) break;
77
- const entries = await fs.readdir(cur.abs, { withFileTypes: true });
78
- for (const ent of entries) {
79
- const abs = path.join(cur.abs, ent.name);
80
- const rel = path.posix.join(cur.rel.replaceAll(path.sep, "/"), ent.name);
81
- if (ent.isDirectory()) {
82
- stack.push({ abs, rel });
83
- } else if (ent.isFile()) {
84
- out.push(rel);
85
- }
86
- }
87
- }
88
- out.sort();
89
- return out;
90
- }
91
-
package/src/governance.js DELETED
@@ -1,159 +0,0 @@
1
- import { UserError } from "./errors.js";
2
- import { loadWorkflowGovernanceRules } from "./spec.js";
3
-
4
- /**
5
- * @param {any} status
6
- */
7
- export function effectiveReviewCount(status) {
8
- return Number(status?.reviewSignals?.effectiveCount ?? status?.collaboration?.counts?.review ?? 0);
9
- }
10
-
11
- /**
12
- * @param {any} status
13
- * @param {any} [governance]
14
- */
15
- function collectGovernanceSignals(status, governance) {
16
- const git = status?.git && typeof status.git === "object" ? status.git : {};
17
- const lifecycle = status?.lifecycle && typeof status.lifecycle === "object" ? status.lifecycle : {};
18
- const repo = status?.repo && typeof status.repo === "object" ? status.repo : {};
19
- const gov = governance && typeof governance === "object" ? governance : status?.governance && typeof status.governance === "object" ? status.governance : {};
20
- const reviewGates = status?.reviewGates && typeof status.reviewGates === "object" ? status.reviewGates : {};
21
- const reviewGateSummary = reviewGates?.summary && typeof reviewGates.summary === "object" ? reviewGates.summary : {};
22
- const reviewEffectiveCount = effectiveReviewCount(status);
23
- const specReviewReady = reviewGates?.specReview?.ready === true || (!reviewGates.specReview && reviewEffectiveCount > 0);
24
- const qualityReviewReady = reviewGates?.qualityReview?.ready === true || (!reviewGates.qualityReview && reviewEffectiveCount > 0);
25
- const verifyBeforeCompleteReady = reviewGates?.verifyBeforeComplete?.ready === true;
26
- const validateStampReady = reviewGates?.validateStamp?.ready === true;
27
-
28
- return {
29
- change_id: String(status?.changeId || ""),
30
- blockers_strict_count: Array.isArray(status?.blockersStrict) ? status.blockersStrict.length : 0,
31
- finish_resume_required: status?.terminatedReuse?.kind === "finished_unarchived",
32
- tasks_unchecked: Number(status?.tasks?.unchecked || 0),
33
- review_effective_count: reviewEffectiveCount,
34
- evidence_persistent_count: Number(status?.evidence?.counts?.persistent || 0),
35
- git_total: Number(git.total || 0),
36
- git_staged: Number(git.staged || 0),
37
- git_conflicted: Number(git.conflicted || 0),
38
- lifecycle_evidence_runs: Number(lifecycle.evidenceRuns || 0),
39
- finish_cleanup_pending_reason: String(lifecycle.finishCleanupPendingReason || ""),
40
- finish_state: String(lifecycle.finishState || ""),
41
- finish_state_reason: String(lifecycle.finishStateReason || ""),
42
- repo_submodules: Number(repo.submodules || 0),
43
- spec_review_ready: specReviewReady,
44
- quality_review_ready: qualityReviewReady,
45
- verify_before_complete_ready: verifyBeforeCompleteReady,
46
- validate_stamp_ready: validateStampReady,
47
- spec_review_status: String(reviewGates?.specReview?.status || (specReviewReady ? "ready" : "missing")),
48
- quality_review_status: String(reviewGates?.qualityReview?.status || (qualityReviewReady ? "ready" : "missing")),
49
- verify_before_complete_status: String(
50
- reviewGates?.verifyBeforeComplete?.status || (verifyBeforeCompleteReady ? "ready" : "missing"),
51
- ),
52
- validate_stamp_status: String(reviewGates?.validateStamp?.status || (validateStampReady ? "ready" : "missing")),
53
- dual_review_missing_count: Number(reviewGateSummary?.dualReviewMissingCount || 0),
54
- finish_gate_missing_count: Number(reviewGateSummary?.finishGateMissingCount || 0),
55
- governance_current_stage: String(gov.currentStage || ""),
56
- governance_recommended_stage: String(gov.recommendedStage || ""),
57
- governance_rule_id: String(gov.ruleId || ""),
58
- };
59
- }
60
-
61
- /**
62
- * @param {string} template
63
- * @param {Record<string, string | number | boolean>} signals
64
- */
65
- function renderTemplate(template, signals) {
66
- return String(template || "").replace(/\{([a-z0-9_]+)\}/gi, (_, key) => String(signals[key] ?? ""));
67
- }
68
-
69
- /**
70
- * @param {unknown} actual
71
- * @param {any} condition
72
- */
73
- function matchesLeafCondition(actual, condition) {
74
- if (Object.prototype.hasOwnProperty.call(condition, "eq") && actual !== condition.eq) return false;
75
- if (Object.prototype.hasOwnProperty.call(condition, "ne") && actual === condition.ne) return false;
76
- if (Object.prototype.hasOwnProperty.call(condition, "gt") && !(Number(actual) > Number(condition.gt))) return false;
77
- if (Object.prototype.hasOwnProperty.call(condition, "gte") && !(Number(actual) >= Number(condition.gte))) return false;
78
- if (Object.prototype.hasOwnProperty.call(condition, "lt") && !(Number(actual) < Number(condition.lt))) return false;
79
- if (Object.prototype.hasOwnProperty.call(condition, "lte") && !(Number(actual) <= Number(condition.lte))) return false;
80
- if (Array.isArray(condition?.in) && !condition.in.includes(actual)) return false;
81
- if (Array.isArray(condition?.notIn) && condition.notIn.includes(actual)) return false;
82
- if (condition?.truthy === true && !actual) return false;
83
- if (condition?.falsy === true && !!actual) return false;
84
- return true;
85
- }
86
-
87
- /**
88
- * @param {any} when
89
- * @param {Record<string, string | number | boolean>} signals
90
- */
91
- function matchesCondition(when, signals) {
92
- if (!when || (typeof when === "object" && Object.keys(when).length === 0)) return true;
93
- if (Array.isArray(when?.all)) return when.all.every((item) => matchesCondition(item, signals));
94
- if (Array.isArray(when?.any)) return when.any.some((item) => matchesCondition(item, signals));
95
- if (when?.not) return !matchesCondition(when.not, signals);
96
- if (typeof when?.signal === "string") {
97
- return matchesLeafCondition(signals[when.signal], when);
98
- }
99
- return false;
100
- }
101
-
102
- /**
103
- * @param {string[]} lines
104
- */
105
- function uniqueLines(lines) {
106
- const seen = new Set();
107
- /** @type {string[]} */
108
- const out = [];
109
- for (const raw of lines || []) {
110
- const line = String(raw || "").trim();
111
- if (!line || seen.has(line)) continue;
112
- seen.add(line);
113
- out.push(line);
114
- }
115
- return out;
116
- }
117
-
118
- /**
119
- * @param {any} status
120
- */
121
- export async function inferChangeGovernance(status) {
122
- const spec = await loadWorkflowGovernanceRules();
123
- const signals = collectGovernanceSignals(status);
124
- const matched = spec.governanceRules.find((rule) => matchesCondition(rule?.when, signals));
125
- if (!matched) {
126
- throw new UserError("Failed to infer workflow governance from spec rules.", {
127
- details: JSON.stringify({ source: spec.source, signals }, null, 2),
128
- });
129
- }
130
- return {
131
- phase: String(status?.phase || ""),
132
- currentStage: String(matched.currentStage || ""),
133
- recommendedStage: String(matched.recommendedStage || matched.currentStage || ""),
134
- rationale: renderTemplate(String(matched.rationale || ""), signals),
135
- ruleId: String(matched.id || ""),
136
- source: spec.source,
137
- };
138
- }
139
-
140
- /**
141
- * @param {any} status
142
- */
143
- export async function governanceGuidanceLines(status) {
144
- const spec = await loadWorkflowGovernanceRules();
145
- const governance = status?.governance && typeof status.governance === "object" ? status.governance : await inferChangeGovernance(status);
146
- const signals = collectGovernanceSignals(status, governance);
147
- /** @type {string[]} */
148
- const lines = [];
149
-
150
- for (const rule of spec.guidanceRules) {
151
- if (!matchesCondition(rule?.when, signals)) continue;
152
- const entries = Array.isArray(rule?.lines) ? rule.lines : [];
153
- for (const entry of entries) lines.push(renderTemplate(String(entry || ""), signals));
154
- }
155
-
156
- const deduped = uniqueLines(lines);
157
- if (deduped.length > 0) return deduped;
158
- return ["按阶段契约继续推进当前 change"];
159
- }