coding-agent-harness 1.0.1 → 1.0.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.
Files changed (262) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/CONTRIBUTING.md +98 -0
  3. package/README.en-US.md +14 -0
  4. package/README.md +230 -80
  5. package/README.zh-CN.md +290 -0
  6. package/SKILL.md +132 -198
  7. package/docs-release/README.md +80 -9
  8. package/docs-release/architecture/overview.md +298 -28
  9. package/docs-release/architecture/overview.zh-CN.md +292 -0
  10. package/docs-release/assets/dashboard-overview.png +0 -0
  11. package/docs-release/assets/harness-architecture.svg +163 -0
  12. package/docs-release/assets/harness-workflow.svg +64 -0
  13. package/docs-release/guides/agent-installation.en-US.md +237 -0
  14. package/docs-release/guides/agent-installation.md +149 -27
  15. package/docs-release/guides/contributing.md +100 -0
  16. package/docs-release/guides/contributing.zh-CN.md +99 -0
  17. package/docs-release/guides/document-audience-and-surfaces.en-US.md +113 -0
  18. package/docs-release/guides/document-audience-and-surfaces.md +113 -0
  19. package/docs-release/guides/full-legacy-migration-subagent-strategy.md +334 -0
  20. package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +334 -0
  21. package/docs-release/guides/legacy-migration-agent-prompt.md +373 -0
  22. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +350 -0
  23. package/docs-release/guides/migration-playbook.en-US.md +324 -0
  24. package/docs-release/guides/migration-playbook.md +328 -0
  25. package/docs-release/guides/parent-control-repository-pattern.en-US.md +254 -0
  26. package/docs-release/guides/parent-control-repository-pattern.md +254 -0
  27. package/docs-release/guides/preset-development.md +214 -0
  28. package/docs-release/guides/repository-operating-models.en-US.md +197 -0
  29. package/docs-release/guides/repository-operating-models.md +197 -0
  30. package/docs-release/guides/task-state-machine.en-US.md +207 -0
  31. package/docs-release/guides/task-state-machine.md +214 -0
  32. package/docs-release/intl/README.md +15 -0
  33. package/docs-release/intl/de-DE.md +18 -0
  34. package/docs-release/intl/en-US.md +18 -0
  35. package/docs-release/intl/es-ES.md +18 -0
  36. package/docs-release/intl/fr-FR.md +18 -0
  37. package/docs-release/intl/ja-JP.md +18 -0
  38. package/docs-release/intl/ko-KR.md +18 -0
  39. package/docs-release/intl/zh-CN.md +18 -0
  40. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/brief.md +13 -0
  41. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
  42. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/lesson_candidates.md +24 -0
  43. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +1 -1
  44. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/task_plan.md +4 -2
  45. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/{visual_roadmap.md → visual_map.md} +9 -1
  46. package/package.json +10 -3
  47. package/presets/legacy-migration/checks/preset-check.mjs +3 -0
  48. package/presets/legacy-migration/preset.yaml +134 -0
  49. package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
  50. package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
  51. package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
  52. package/presets/legacy-migration/templates/findings.seed.md +17 -0
  53. package/presets/legacy-migration/templates/review.seed.md +12 -0
  54. package/presets/legacy-migration/templates/task_plan.append.md +9 -0
  55. package/presets/legacy-migration/templates/visual_map.append.md +12 -0
  56. package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
  57. package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
  58. package/presets/lesson-sedimentation/preset.yaml +23 -0
  59. package/presets/lesson-sedimentation/templates/prompt.md +23 -0
  60. package/presets/module/preset.yaml +25 -0
  61. package/presets/module/templates/execution_strategy.append.md +8 -0
  62. package/presets/module/templates/task_plan.append.md +17 -0
  63. package/presets/standard-task/preset.yaml +31 -0
  64. package/presets/standard-task/templates/task_plan.append.md +7 -0
  65. package/references/adversarial-review-standard.md +2 -2
  66. package/references/agents-md-pattern.md +5 -5
  67. package/references/delivery-operating-model-standard.md +3 -3
  68. package/references/docs-directory-standard.md +53 -10
  69. package/references/external-source-intake-standard.md +75 -0
  70. package/references/harness-ledger.md +53 -94
  71. package/references/legacy-12-phase-bootstrap.md +41 -0
  72. package/references/lessons-governance.md +100 -88
  73. package/references/module-parallel-standard.md +14 -14
  74. package/references/planning-loop.md +51 -7
  75. package/references/project-onboarding-audit.md +10 -0
  76. package/references/pull-request-standard.md +118 -0
  77. package/references/repo-governance-standard.md +12 -1
  78. package/references/review-routing-standard.md +7 -1
  79. package/references/ssot-governance.md +67 -59
  80. package/references/taskr-gap-analysis.md +600 -0
  81. package/references/testing-standard.md +50 -0
  82. package/references/walkthrough-closeout.md +10 -9
  83. package/scripts/check-harness.mjs +111 -331
  84. package/scripts/commands/dashboard-command.mjs +67 -0
  85. package/scripts/commands/migration-command.mjs +96 -0
  86. package/scripts/commands/preset-command.mjs +73 -0
  87. package/scripts/commands/task-command.mjs +327 -0
  88. package/scripts/harness.mjs +106 -20
  89. package/scripts/lib/capability-registry.mjs +591 -0
  90. package/scripts/lib/check-module-parallel.mjs +237 -0
  91. package/scripts/lib/check-profiles.mjs +418 -0
  92. package/scripts/lib/check-task-contracts.mjs +47 -0
  93. package/scripts/lib/core-shared.mjs +196 -0
  94. package/scripts/lib/dashboard-data.mjs +412 -0
  95. package/scripts/lib/dashboard-workbench.mjs +257 -0
  96. package/scripts/lib/dashboard-writer.mjs +107 -4
  97. package/scripts/lib/git-status-summary.mjs +46 -0
  98. package/scripts/lib/governance-index-generator.mjs +174 -0
  99. package/scripts/lib/governance-sync.mjs +514 -0
  100. package/scripts/lib/governance-table-boundary.mjs +175 -0
  101. package/scripts/lib/harness-core.mjs +15 -1318
  102. package/scripts/lib/lesson-maintenance.mjs +152 -0
  103. package/scripts/lib/markdown-utils.mjs +158 -0
  104. package/scripts/lib/migration-planner.mjs +478 -0
  105. package/scripts/lib/migration-support.mjs +312 -0
  106. package/scripts/lib/preset-audit-contracts.mjs +37 -0
  107. package/scripts/lib/preset-engine.mjs +497 -0
  108. package/scripts/lib/preset-registry.mjs +627 -0
  109. package/scripts/lib/preset-resource-contracts.mjs +83 -0
  110. package/scripts/lib/review-confirm-git-gate.mjs +248 -0
  111. package/scripts/lib/status-dashboard-renderer.mjs +102 -0
  112. package/scripts/lib/subagent-authorization-audit.mjs +196 -0
  113. package/scripts/lib/task-completion-consistency.mjs +16 -0
  114. package/scripts/lib/task-index.mjs +93 -0
  115. package/scripts/lib/task-lesson-candidates.mjs +242 -0
  116. package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
  117. package/scripts/lib/task-lifecycle/review-confirm.mjs +101 -0
  118. package/scripts/lib/task-lifecycle/review-gates.mjs +70 -0
  119. package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
  120. package/scripts/lib/task-lifecycle.mjs +649 -0
  121. package/scripts/lib/task-review-model.mjs +469 -0
  122. package/scripts/lib/task-scanner.mjs +576 -0
  123. package/scripts/lib/task-tombstone-commands.mjs +140 -0
  124. package/scripts/postinstall.mjs +14 -0
  125. package/skills/preset-creator/SKILL.md +179 -0
  126. package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
  127. package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
  128. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -0
  129. package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
  130. package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
  131. package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
  132. package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
  133. package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
  134. package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
  135. package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
  136. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
  137. package/{templates/planning/visual_roadmap.md → skills/preset-creator/references/complex-task-skeleton/visual_map.md} +24 -2
  138. package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
  139. package/templates/AGENTS.md.template +51 -36
  140. package/templates/architecture/Architecture-SSoT.md +21 -0
  141. package/templates/architecture/README.md +49 -0
  142. package/templates/architecture/critical-flows.md +22 -0
  143. package/templates/architecture/local-repo-context.md +20 -0
  144. package/templates/architecture/service-catalog.md +17 -0
  145. package/templates/architecture/services/service-template.md +31 -0
  146. package/templates/architecture/system-map.md +22 -0
  147. package/templates/dashboard/assets/app-src/00-state.js +42 -0
  148. package/templates/dashboard/assets/app-src/10-router.js +77 -0
  149. package/templates/dashboard/assets/app-src/20-overview.js +241 -0
  150. package/templates/dashboard/assets/app-src/30-tasks.js +409 -0
  151. package/templates/dashboard/assets/app-src/35-task-detail.js +246 -0
  152. package/templates/dashboard/assets/app-src/40-modules.js +58 -0
  153. package/templates/dashboard/assets/app-src/45-review.js +347 -0
  154. package/templates/dashboard/assets/app-src/50-migration.js +183 -0
  155. package/templates/dashboard/assets/app-src/60-shared.js +61 -0
  156. package/templates/dashboard/assets/app-src/90-bindings.js +524 -0
  157. package/templates/dashboard/assets/app.css +3107 -300
  158. package/templates/dashboard/assets/app.css.manifest.json +9 -0
  159. package/templates/dashboard/assets/app.js +2068 -306
  160. package/templates/dashboard/assets/app.manifest.json +12 -0
  161. package/templates/dashboard/assets/css-src/00-foundation.css +342 -0
  162. package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
  163. package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
  164. package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
  165. package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
  166. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +427 -0
  167. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
  168. package/templates/dashboard/assets/i18n.js +531 -44
  169. package/templates/dashboard/assets/mermaid-renderer.js +58 -8
  170. package/templates/development/README.md +52 -0
  171. package/templates/development/codebase-map.md +11 -0
  172. package/templates/development/cross-repo-debugging.md +18 -0
  173. package/templates/development/external-context/service-template.md +33 -0
  174. package/templates/development/external-source-packs/README.md +24 -0
  175. package/templates/development/external-source-packs/digest-template.md +28 -0
  176. package/templates/development/local-setup.md +16 -0
  177. package/templates/development/stubs-and-mocks.md +11 -0
  178. package/templates/integrations/README.md +40 -0
  179. package/templates/integrations/api-contract.md +42 -0
  180. package/templates/integrations/event-contract.md +46 -0
  181. package/templates/integrations/third-party/vendor-template.md +42 -0
  182. package/templates/integrations/webhook-contract.md +41 -0
  183. package/templates/ledger/Harness-Ledger.md +13 -25
  184. package/templates/lessons/lesson-arch-process-change.md +1 -1
  185. package/templates/lessons/lesson-new-doc.md +1 -1
  186. package/templates/lessons/lesson-ref-change.md +1 -1
  187. package/templates/planning/brief.md +32 -0
  188. package/templates/planning/execution_strategy.md +31 -0
  189. package/templates/planning/lesson_candidates.md +70 -0
  190. package/templates/planning/long-running-task-contract.md +7 -0
  191. package/templates/planning/module_brief.md +25 -0
  192. package/templates/planning/module_session_prompt.md +6 -0
  193. package/templates/planning/optional/artifacts/INDEX.md +3 -3
  194. package/templates/planning/optional/references/INDEX.md +3 -3
  195. package/templates/planning/review.md +59 -0
  196. package/templates/planning/task_plan.md +40 -15
  197. package/templates/planning/visual_map.md +50 -0
  198. package/templates/reference/docs-library-standard.md +31 -0
  199. package/templates/reference/execution-workflow-standard.md +5 -2
  200. package/templates/reference/external-source-intake-standard.md +82 -0
  201. package/templates/reference/harness-ledger-standard.md +1 -0
  202. package/templates/reference/pull-request-standard.md +80 -0
  203. package/templates/reference/repo-governance-standard.md +8 -5
  204. package/templates/reference/review-routing-standard.md +6 -0
  205. package/templates/reference/walkthrough-standard.md +3 -1
  206. package/templates/verifier/verifier-output.md +1 -1
  207. package/templates/walkthrough/walkthrough-template.md +2 -2
  208. package/templates-zh-CN/AGENTS.md.template +73 -70
  209. package/templates-zh-CN/architecture/Architecture-SSoT.md +21 -0
  210. package/templates-zh-CN/architecture/README.md +51 -0
  211. package/templates-zh-CN/architecture/critical-flows.md +24 -0
  212. package/templates-zh-CN/architecture/local-repo-context.md +20 -0
  213. package/templates-zh-CN/architecture/service-catalog.md +17 -0
  214. package/templates-zh-CN/architecture/services/service-template.md +31 -0
  215. package/templates-zh-CN/architecture/system-map.md +22 -0
  216. package/templates-zh-CN/development/README.md +54 -0
  217. package/templates-zh-CN/development/codebase-map.md +11 -0
  218. package/templates-zh-CN/development/cross-repo-debugging.md +18 -0
  219. package/templates-zh-CN/development/external-context/service-template.md +33 -0
  220. package/templates-zh-CN/development/external-source-packs/README.md +24 -0
  221. package/templates-zh-CN/development/external-source-packs/digest-template.md +28 -0
  222. package/templates-zh-CN/development/local-setup.md +16 -0
  223. package/templates-zh-CN/development/stubs-and-mocks.md +11 -0
  224. package/templates-zh-CN/integrations/README.md +42 -0
  225. package/templates-zh-CN/integrations/api-contract.md +42 -0
  226. package/templates-zh-CN/integrations/event-contract.md +46 -0
  227. package/templates-zh-CN/integrations/third-party/vendor-template.md +42 -0
  228. package/templates-zh-CN/integrations/webhook-contract.md +41 -0
  229. package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
  230. package/templates-zh-CN/planning/brief.md +32 -0
  231. package/templates-zh-CN/planning/execution_strategy.md +30 -0
  232. package/templates-zh-CN/planning/lesson_candidates.md +70 -0
  233. package/templates-zh-CN/planning/long-running-task-contract.md +1 -1
  234. package/templates-zh-CN/planning/module_brief.md +25 -0
  235. package/templates-zh-CN/planning/module_plan.md +2 -2
  236. package/templates-zh-CN/planning/module_session_prompt.md +4 -3
  237. package/templates-zh-CN/planning/review.md +59 -1
  238. package/templates-zh-CN/planning/task_plan.md +37 -11
  239. package/templates-zh-CN/planning/{visual_roadmap.md → visual_map.md} +21 -2
  240. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  241. package/templates-zh-CN/reference/docs-library-standard.md +36 -1
  242. package/templates-zh-CN/reference/execution-workflow-standard.md +10 -2
  243. package/templates-zh-CN/reference/external-source-intake-standard.md +82 -0
  244. package/templates-zh-CN/reference/harness-ledger-standard.md +7 -4
  245. package/templates-zh-CN/reference/pull-request-standard.md +106 -0
  246. package/templates-zh-CN/reference/repo-governance-standard.md +4 -1
  247. package/templates-zh-CN/reference/review-routing-standard.md +8 -1
  248. package/templates-zh-CN/reference/walkthrough-standard.md +6 -5
  249. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +2 -2
  250. package/templates-zh-CN/walkthrough/walkthrough-template.md +2 -2
  251. package/scripts/smoke-dashboard.mjs +0 -70
  252. package/scripts/test-harness.mjs +0 -483
  253. package/templates/ssot/Feature-SSoT.md +0 -43
  254. package/templates/ssot/Lessons-SSoT.md +0 -44
  255. package/templates-zh-CN/dashboard/assets/app.css +0 -399
  256. package/templates-zh-CN/dashboard/assets/app.js +0 -435
  257. package/templates-zh-CN/dashboard/assets/i18n.js +0 -47
  258. package/templates-zh-CN/dashboard/assets/markdown-reader.js +0 -116
  259. package/templates-zh-CN/dashboard/assets/mermaid-renderer.js +0 -59
  260. package/templates-zh-CN/dashboard/index.html +0 -18
  261. package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
  262. package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
@@ -1,435 +0,0 @@
1
- const bundle = window.__HARNESS_DASHBOARD__ || {};
2
- const state = {
3
- page: "overview",
4
- lang: "zh",
5
- theme: localStorage.getItem("harness.theme") || "system",
6
- density: localStorage.getItem("harness.density") || "comfortable",
7
- selected: null,
8
- tab: "plan",
9
- renderMode: "rendered",
10
- };
11
-
12
- const pageKeys = ["overview", "ledger", "tasks", "modules", "evidence", "lessons", "adoption", "settings"];
13
- const taskDocTabs = [
14
- ["plan", "task_plan.md"],
15
- ["strategy", "execution_strategy.md"],
16
- ["roadmap", "visual_roadmap.md"],
17
- ["progress", "progress.md"],
18
- ["review", "review.md"],
19
- ["findings", "findings.md"],
20
- ["references", "references/INDEX.md"],
21
- ["artifacts", "artifacts/INDEX.md"],
22
- ];
23
-
24
- function t(key) {
25
- return (window.HarnessI18n?.zh || {})[key] || key;
26
- }
27
-
28
- function app() {
29
- const systemTheme = window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
30
- document.documentElement.dataset.theme = state.theme === "system" ? systemTheme : state.theme;
31
- document.documentElement.dataset.density = state.density;
32
- document.documentElement.lang = "zh-CN";
33
- const root = document.getElementById("app");
34
- root.innerHTML = `
35
- <div class="layout">
36
- <aside class="sidebar">
37
- <div class="brand">
38
- <strong>${escapeHtml(bundle.status?.project?.name || "Harness")}</strong>
39
- <span>${escapeHtml(bundle.status?.project?.root || "TARGET:.")}</span>
40
- </div>
41
- <nav class="nav">${pageKeys.map((page) => navButton(page)).join("")}</nav>
42
- </aside>
43
- <main class="main">
44
- ${topbar()}
45
- ${renderPage()}
46
- </main>
47
- </div>`;
48
- bind();
49
- }
50
-
51
- function navButton(page) {
52
- return `<button data-page="${page}" class="${state.page === page ? "active" : ""}">${t(page)}</button>`;
53
- }
54
-
55
- function topbar() {
56
- return `<div class="topbar">
57
- <div>
58
- <p class="eyebrow">Coding Agent Harness 控制台</p>
59
- <h1>${escapeHtml(pageTitle())}</h1>
60
- </div>
61
- <div class="controls">
62
- <div class="control">
63
- <button data-theme="light" class="${state.theme === "light" ? "active" : ""}">${t("light")}</button>
64
- <button data-theme="dark" class="${state.theme === "dark" ? "active" : ""}">${t("dark")}</button>
65
- <button data-theme="system" class="${state.theme === "system" ? "active" : ""}">${t("system")}</button>
66
- </div>
67
- <div class="control">
68
- <button data-density="compact" class="${state.density === "compact" ? "active" : ""}">${t("compact")}</button>
69
- <button data-density="comfortable" class="${state.density === "comfortable" ? "active" : ""}">${t("comfortable")}</button>
70
- </div>
71
- </div>
72
- </div>`;
73
- }
74
-
75
- function pageTitle() {
76
- const project = bundle.status?.project?.name || "project";
77
- if (state.page === "overview") return `${project} 项目驾驶舱`;
78
- return t(state.page);
79
- }
80
-
81
- function renderPage() {
82
- if (state.page === "overview") return overview();
83
- if (state.page === "ledger") return withDrawer(ledgerTable());
84
- if (state.page === "tasks") return withDrawer(taskTable());
85
- if (state.page === "modules") return withDrawer(moduleTable());
86
- if (state.page === "evidence") return withDrawer(evidenceTable());
87
- if (state.page === "lessons") return withDrawer(lessonsTable());
88
- if (state.page === "adoption") return adoption();
89
- return settings();
90
- }
91
-
92
- function overview() {
93
- const status = bundle.status || {};
94
- const evidence = evidenceHealth(status.tasks || []);
95
- const blockers = status.checkState?.failures || 0;
96
- const warnings = status.checkState?.warnings || 0;
97
- return `<section class="status-grid">
98
- <div class="readiness panel">
99
- <span class="state ${status.checkState?.status || "warn"}">${t("readiness")}: ${label(status.checkState?.status || "unknown")}</span>
100
- <h2>${nextActionText()}</h2>
101
- <p class="muted">这里把真实阻塞、证据缺口和旧版升级建议拆开显示。</p>
102
- </div>
103
- ${metric(t("activeTasks"), status.tasks?.length || 0)}
104
- ${metric(t("blockers"), blockers)}
105
- ${metric(t("warnings"), warnings)}
106
- </section>
107
- <section class="page-grid">
108
- <div>
109
- ${overviewTasks()}
110
- ${ledgerSummary()}
111
- ${riskPanel()}
112
- </div>
113
- ${drawer()}
114
- </section>`;
115
- }
116
-
117
- function metric(label, value) {
118
- return `<div class="metric"><span>${label}</span><strong>${value}</strong></div>`;
119
- }
120
-
121
- function nextActionText() {
122
- const failures = bundle.status?.checkState?.failures || 0;
123
- if (failures > 0) return "先处理发布阻塞项";
124
- const advice = bundle.adoption?.warnings?.length || 0;
125
- if (advice > 0) return "可以继续,但需要处理升级建议";
126
- return "当前快照没有阻塞项";
127
- }
128
-
129
- function overviewTasks() {
130
- const rows = (bundle.status?.tasks || []).slice(0, 6);
131
- return tablePanel(t("tasks"), [t("task"), t("state"), t("completion"), t("roadmapSource")], rows.map((task) => [
132
- clickable(task.title, "task", task.id),
133
- tag(task.state, label(task.state)),
134
- progress(task.completion),
135
- escapeHtml(label(task.roadmapSource || "unknown")),
136
- ]));
137
- }
138
-
139
- function riskPanel() {
140
- const warnings = bundle.adoption?.warnings || [];
141
- const grouped = groupBy(warnings, (item) => item.category);
142
- return `<section class="panel" style="padding:16px;margin-top:16px">
143
- <h2>风险与升级建议</h2>
144
- <div class="risk-list">${Object.entries(grouped).map(([category, items]) => `
145
- <div class="risk-item"><strong>${escapeHtml(category)}</strong><p class="muted">${items.length} 项</p></div>
146
- `).join("") || `<p class="empty">没有建议</p>`}</div>
147
- </section>`;
148
- }
149
-
150
- function taskTable() {
151
- const rows = bundle.status?.tasks || [];
152
- return tablePanel(t("tasks"), [t("task"), t("state"), t("completion"), t("evidence"), t("roadmapSource")], rows.map((task) => [
153
- clickable(task.title, "task", task.id),
154
- tag(task.state, label(task.state)),
155
- progress(task.completion),
156
- progress(evidenceHealth([task])),
157
- escapeHtml(label(task.roadmapSource || "unknown")),
158
- ]));
159
- }
160
-
161
- function ledgerSummary() {
162
- const tables = (bundle.tables?.tables || []).filter((table) => table.kind === "harness-ledger");
163
- if (tables.length === 0) return "";
164
- const rows = tables.flatMap((table) => table.rows).slice(0, 5);
165
- return `<section class="panel ledger-summary">
166
- <div class="panel-head"><h2>${t("ledger")}</h2><span class="muted">${rows.length}</span></div>
167
- <div class="risk-list">${rows.map((row) => {
168
- const cells = row.cells || {};
169
- const title = cells.Task || cells.ID || cells.Item || cells.Title || cells.Module || "Ledger item";
170
- const state = cells.State || cells.Status || cells.Review || cells["Review State"] || "";
171
- const action = cells["Next Action"] || cells.Action || cells.Owner || cells["Required Action"] || "";
172
- return `<div class="risk-item"><strong>${escapeHtml(title)}</strong><p class="muted">${escapeHtml([label(state), action].filter(Boolean).join(" · "))}</p></div>`;
173
- }).join("")}</div>
174
- </section>`;
175
- }
176
-
177
- function ledgerTable() {
178
- const tables = (bundle.tables?.tables || []).filter((table) => table.kind === "harness-ledger");
179
- if (tables.length === 0) return emptyTable(t("ledger"), "未找到 Harness Ledger。");
180
- return genericTables(t("ledger"), tables);
181
- }
182
-
183
- function moduleTable() {
184
- const tables = (bundle.tables?.tables || []).filter((table) => table.kind === "module-registry");
185
- const graph = graphPanel();
186
- if (tables.length === 0) return `${graph}${emptyTable(t("modules"), "未找到 Module Registry。")}`;
187
- return `${graph}${genericTables(t("modules"), tables)}`;
188
- }
189
-
190
- function evidenceTable() {
191
- const items = evidenceItems();
192
- return tablePanel(t("evidence"), [t("origin"), t("task"), t("state"), t("title"), t("affected")], items.map((item) => [
193
- escapeHtml(label(item.source)),
194
- escapeHtml(item.task || "-"),
195
- tag(item.state || "present", label(item.state || "present")),
196
- escapeHtml(item.title),
197
- escapeHtml(item.affected || ""),
198
- ]));
199
- }
200
-
201
- function evidenceItems() {
202
- const taskEvidence = (bundle.status?.tasks || []).flatMap((task) => [
203
- ...(task.evidence || []).map((item) => ({
204
- source: item.type || "task-progress",
205
- task: task.title,
206
- state: item.status || "present",
207
- title: item.summary || item.id,
208
- affected: item.path || "",
209
- })),
210
- ...(task.risks || []).map((item) => ({
211
- source: "task-review",
212
- task: task.title,
213
- state: item.open ? "open" : "closed",
214
- title: item.summary || item.id,
215
- affected: `${item.severity || ""}${item.blocksRelease ? " · blocks" : ""}`.trim(),
216
- })),
217
- ]);
218
- const qaTables = (bundle.tables?.tables || [])
219
- .filter((table) => ["task-review", "regression-ssot", "cadence-ledger"].includes(table.kind))
220
- .flatMap((table) => table.rows.slice(0, 12).map((row) => {
221
- const cells = row.cells || {};
222
- return {
223
- source: table.kind,
224
- task: cells.Task || cells.Module || cells.ID || "-",
225
- state: cells.Status || cells.State || cells.Open || cells.Verdict || "present",
226
- title: cells.Finding || cells.Summary || cells.Check || cells.Item || cells.Title || table.source,
227
- affected: cells["Evidence Checked"] || cells.Path || cells.Owner || cells["Required Action"] || table.source,
228
- };
229
- }));
230
- return [...taskEvidence, ...qaTables];
231
- }
232
-
233
- function lessonsTable() {
234
- const tables = (bundle.tables?.tables || []).filter((table) => table.kind === "lessons-ssot");
235
- if (tables.length === 0) return emptyTable(t("lessons"), "未找到 Lessons SSoT。");
236
- return genericTables(t("lessons"), tables);
237
- }
238
-
239
- function adoption() {
240
- const advice = bundle.adoption?.warnings || [];
241
- return `<section class="page-grid">
242
- <div>
243
- ${tablePanel(t("adoption"), [t("category"), t("severity"), t("title"), t("affected"), t("requiredAction")], advice.map((item) => [
244
- escapeHtml(item.category),
245
- tag(item.severity, label(item.severity)),
246
- escapeHtml(item.title),
247
- escapeHtml(item.affected),
248
- escapeHtml(item.requiredAction),
249
- ]))}
250
- <section class="panel" style="padding:16px;margin-top:16px">
251
- <h2>${t("nextAction")}</h2>
252
- <ol>${((bundle.adoption?.manualSteps || {})[state.lang] || []).map((step) => `<li>${escapeHtml(step)}</li>`).join("")}</ol>
253
- </section>
254
- </div>
255
- ${drawer()}
256
- </section>`;
257
- }
258
-
259
- function settings() {
260
- return `<section class="panel" style="padding:18px">
261
- <h2>${t("settings")}</h2>
262
- <p class="muted">这些设置只保存在浏览器本地,不会写回项目。</p>
263
- <p>${t("rendered")} / ${t("source")}: 在详情抽屉里切换。</p>
264
- </section>`;
265
- }
266
-
267
- function withDrawer(content) {
268
- return `<section class="page-grid"><div>${content}</div>${drawer()}</section>`;
269
- }
270
-
271
- function tablePanel(title, headers, rows) {
272
- return `<section class="table-panel">
273
- <div class="panel-head"><h2>${escapeHtml(title)}</h2><span class="muted">${rows.length}</span></div>
274
- <div class="table-wrap"><table>
275
- <thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead>
276
- <tbody>${rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("")}</tbody>
277
- </table></div>
278
- </section>`;
279
- }
280
-
281
- function genericTables(title, tables) {
282
- return tables.map((table) => tablePanel(`${title} · ${table.source}`, table.columns, table.rows.map((row) => table.columns.map((column) => escapeHtml(row.cells[column] || ""))))).join("");
283
- }
284
-
285
- function graphPanel() {
286
- const graph = bundle.graph || { nodes: [], edges: [] };
287
- const modules = graph.nodes.filter((node) => node.type === "module");
288
- const fallbackTasks = graph.nodes.filter((node) => node.type === "task");
289
- const laneNodes = modules.length > 0 ? modules : fallbackTasks;
290
- const edges = graph.edges || [];
291
- return `<section class="panel graph-panel">
292
- <div class="panel-head"><h2>${t("graph")}</h2><span class="muted">${modules.length} modules · ${fallbackTasks.length} tasks · ${edges.length} edges</span></div>
293
- <div class="graph-lanes">${laneNodes.slice(0, 12).map((module) => {
294
- const owned = edges.filter((edge) => edge.from === module.id).slice(0, 8);
295
- return `<div class="lane"><strong>${escapeHtml(module.label)}</strong><span>${label(module.state || "")}</span>${owned.map((edge) => `<small>${escapeHtml(edge.type)} → ${escapeHtml(edge.to.replace(/^step:/, ""))}</small>`).join("")}</div>`;
296
- }).join("") || `<p class="empty">暂无模块图数据</p>`}</div>
297
- </section>`;
298
- }
299
-
300
- function emptyTable(title, message) {
301
- return `<section class="table-panel"><div class="panel-head"><h2>${escapeHtml(title)}</h2></div><p class="empty">${escapeHtml(message)}</p></section>`;
302
- }
303
-
304
- function drawer() {
305
- const selection = state.selected;
306
- if (!selection) {
307
- return `<aside class="drawer"><div class="drawer-head"><h2>${t("detail")}</h2></div><div class="drawer-body empty">${t("noSelection")}</div></aside>`;
308
- }
309
- const docs = docsForSelection(selection);
310
- const active = docs.find((doc) => doc.tab === state.tab) || docs[0];
311
- return `<aside class="drawer">
312
- <div class="drawer-head"><h2>${escapeHtml(selection.label)}</h2><p class="muted">${escapeHtml(active?.path || "")}</p></div>
313
- <div class="drawer-tabs">${docs.map((doc) => `<button data-tab="${doc.tab}" class="${active?.tab === doc.tab ? "active" : ""}">${t(doc.tab)}</button>`).join("")}</div>
314
- <div class="drawer-mode"><button data-render-mode="rendered" class="${state.renderMode === "rendered" ? "active" : ""}">${t("rendered")}</button><button data-render-mode="source" class="${state.renderMode === "source" ? "active" : ""}">${t("source")}</button></div>
315
- <div class="drawer-body"><article class="markdown">${active ? window.HarnessMarkdown.render(active.content, state.renderMode) : t("noSelection")}</article></div>
316
- </aside>`;
317
- }
318
-
319
- function docsForSelection(selection) {
320
- if (selection.type !== "task") return [];
321
- const task = (bundle.status?.tasks || []).find((item) => item.id === selection.id);
322
- if (!task) return [];
323
- return taskDocTabs.map(([tab, suffix]) => {
324
- const doc = findDocument(`${task.path}/${suffix}`);
325
- return doc ? { tab, ...doc } : null;
326
- }).filter(Boolean);
327
- }
328
-
329
- function findDocument(pathSuffix) {
330
- return (bundle.documents?.documents || []).find((doc) => doc.path.endsWith(pathSuffix));
331
- }
332
-
333
- function clickable(label, type, id) {
334
- return `<button class="linklike" data-select-type="${type}" data-select-id="${id}" data-select-label="${escapeAttr(label)}">${escapeHtml(label)}</button>`;
335
- }
336
-
337
- function tag(value, text = value) {
338
- const raw = String(value || "unknown");
339
- const klass = /fail|blocked|open/i.test(raw) ? "fail" : /warn|advice|planned|missing/i.test(raw) ? "warn" : /pass|done|present|verified/i.test(raw) ? "pass" : "";
340
- return `<span class="tag ${klass}">${escapeHtml(text)}</span>`;
341
- }
342
-
343
- function label(value) {
344
- const labels = {
345
- pass: "通过",
346
- warn: "需关注",
347
- fail: "阻塞",
348
- in_progress: "进行中",
349
- planned: "计划中",
350
- done: "完成",
351
- blocked: "阻塞",
352
- missing: "缺失",
353
- present: "已有",
354
- closed: "关闭",
355
- advice: "建议",
356
- standalone: "独立文件",
357
- legacy: "旧版兼容",
358
- "task-review": "审查",
359
- "task-progress": "进度",
360
- "regression-ssot": "回归",
361
- "cadence-ledger": "节奏",
362
- unknown: "未知",
363
- };
364
- return labels[value] || value;
365
- }
366
-
367
- function progress(value) {
368
- const score = Math.max(0, Math.min(100, Number(value) || 0));
369
- return `<span>${score}%</span><div class="bar"><i style="width:${score}%"></i></div>`;
370
- }
371
-
372
- function evidenceHealth(tasks) {
373
- const phases = tasks.flatMap((task) => task.phases || []).filter((phase) => phase.state !== "skipped");
374
- if (phases.length === 0) return 0;
375
- const score = phases.reduce((sum, phase) => {
376
- if (["present", "waived"].includes(phase.evidenceStatus)) return sum + 100;
377
- if (phase.evidenceStatus === "partial") return sum + 50;
378
- return sum;
379
- }, 0);
380
- return Math.round(score / phases.length);
381
- }
382
-
383
- function groupBy(items, fn) {
384
- return items.reduce((acc, item) => {
385
- const key = fn(item);
386
- acc[key] ||= [];
387
- acc[key].push(item);
388
- return acc;
389
- }, {});
390
- }
391
-
392
- function bind() {
393
- document.querySelectorAll("[data-page]").forEach((button) => button.addEventListener("click", () => {
394
- state.page = button.dataset.page;
395
- state.selected = null;
396
- app();
397
- }));
398
- document.querySelectorAll("[data-theme]").forEach((button) => button.addEventListener("click", () => {
399
- state.theme = button.dataset.theme;
400
- localStorage.setItem("harness.theme", state.theme);
401
- app();
402
- }));
403
- document.querySelectorAll("[data-density]").forEach((button) => button.addEventListener("click", () => {
404
- state.density = button.dataset.density;
405
- localStorage.setItem("harness.density", state.density);
406
- app();
407
- }));
408
- document.querySelectorAll("[data-select-type]").forEach((button) => button.addEventListener("click", () => {
409
- state.selected = { type: button.dataset.selectType, id: button.dataset.selectId, label: button.dataset.selectLabel };
410
- state.tab = "plan";
411
- app();
412
- }));
413
- document.querySelectorAll("[data-tab]").forEach((button) => button.addEventListener("click", () => {
414
- state.tab = button.dataset.tab;
415
- app();
416
- }));
417
- document.querySelectorAll("[data-render-mode]").forEach((button) => button.addEventListener("click", () => {
418
- state.renderMode = button.dataset.renderMode;
419
- app();
420
- }));
421
- }
422
-
423
- function escapeHtml(value) {
424
- return String(value ?? "")
425
- .replaceAll("&", "&amp;")
426
- .replaceAll("<", "&lt;")
427
- .replaceAll(">", "&gt;")
428
- .replaceAll('"', "&quot;");
429
- }
430
-
431
- function escapeAttr(value) {
432
- return escapeHtml(value).replaceAll("'", "&#39;");
433
- }
434
-
435
- app();
@@ -1,47 +0,0 @@
1
- window.HarnessI18n = {
2
- zh: {
3
- overview: "总览",
4
- ledger: "总表",
5
- tasks: "任务",
6
- modules: "模块",
7
- evidence: "证据",
8
- lessons: "教训",
9
- adoption: "升级",
10
- settings: "设置",
11
- system: "跟随系统",
12
- readiness: "发布状态",
13
- blockers: "阻塞",
14
- warnings: "建议",
15
- nextAction: "下一步",
16
- activeTasks: "活跃任务",
17
- evidenceHealth: "证据完整度",
18
- detail: "详情",
19
- plan: "计划",
20
- strategy: "执行策略",
21
- roadmap: "可视化路线",
22
- progress: "进度",
23
- review: "审查",
24
- findings: "发现",
25
- references: "参考",
26
- artifacts: "产物",
27
- source: "源码",
28
- rendered: "渲染",
29
- light: "白天",
30
- dark: "黑夜",
31
- compact: "紧凑",
32
- comfortable: "舒适",
33
- noSelection: "选择一行查看文档快照",
34
- task: "任务",
35
- state: "状态",
36
- completion: "完成度",
37
- roadmapSource: "路线来源",
38
- category: "类别",
39
- severity: "级别",
40
- title: "标题",
41
- affected: "影响对象",
42
- requiredAction: "建议动作",
43
- message: "详情",
44
- graph: "依赖图",
45
- origin: "来源",
46
- },
47
- };
@@ -1,116 +0,0 @@
1
- window.HarnessMarkdown = {
2
- render(markdown = "", mode = "rendered") {
3
- if (mode === "source") return `<pre><code>${escapeHtml(markdown)}</code></pre>`;
4
- return renderBlocks(markdown);
5
- },
6
- };
7
-
8
- function renderBlocks(markdown) {
9
- const lines = String(markdown || "").split(/\r?\n/);
10
- const blocks = [];
11
- let index = 0;
12
- while (index < lines.length) {
13
- const line = lines[index];
14
- if (!line.trim()) {
15
- index += 1;
16
- continue;
17
- }
18
- if (line.startsWith("```")) {
19
- const lang = line.slice(3).trim();
20
- const code = [];
21
- index += 1;
22
- while (index < lines.length && !lines[index].startsWith("```")) {
23
- code.push(lines[index]);
24
- index += 1;
25
- }
26
- index += 1;
27
- blocks.push(lang === "mermaid" ? window.HarnessMermaid.render(code.join("\n")) : `<pre><code>${escapeHtml(code.join("\n"))}</code></pre>`);
28
- continue;
29
- }
30
- if (/^#{1,3}\s+/.test(line)) {
31
- const level = line.match(/^#+/)[0].length;
32
- blocks.push(`<h${level}>${inline(line.replace(/^#{1,3}\s+/, ""))}</h${level}>`);
33
- index += 1;
34
- continue;
35
- }
36
- if (isTableStart(lines, index)) {
37
- const table = [];
38
- while (index < lines.length && lines[index].trim().startsWith("|")) {
39
- table.push(lines[index]);
40
- index += 1;
41
- }
42
- blocks.push(renderTable(table));
43
- continue;
44
- }
45
- if (/^[-*]\s+/.test(line)) {
46
- const items = [];
47
- while (index < lines.length && /^[-*]\s+/.test(lines[index])) {
48
- items.push(`<li>${inline(lines[index].replace(/^[-*]\s+/, ""))}</li>`);
49
- index += 1;
50
- }
51
- blocks.push(`<ul>${items.join("")}</ul>`);
52
- continue;
53
- }
54
- const paragraph = [];
55
- while (index < lines.length && lines[index].trim() && !/^#{1,3}\s+/.test(lines[index]) && !lines[index].startsWith("```") && !isTableStart(lines, index)) {
56
- paragraph.push(lines[index]);
57
- index += 1;
58
- }
59
- blocks.push(`<p>${inline(paragraph.join(" "))}</p>`);
60
- }
61
- return blocks.join("");
62
- }
63
-
64
- function isTableStart(lines, index) {
65
- return lines[index]?.trim().startsWith("|") && /^(\s*\|?\s*:?-{3,}:?\s*)+\|?\s*$/.test(lines[index + 1] || "");
66
- }
67
-
68
- function renderTable(lines) {
69
- const rows = lines.map(splitMarkdownRow);
70
- const header = rows[0] || [];
71
- const body = rows.slice(2).filter((row) => row.length === header.length);
72
- return `<div class="rendered-table-wrap"><table class="rendered-table">
73
- <thead><tr>${header.map((cell) => `<th>${inline(cell)}</th>`).join("")}</tr></thead>
74
- <tbody>${body.map((row) => `<tr>${row.map((cell) => `<td>${inline(cell)}</td>`).join("")}</tr>`).join("")}</tbody>
75
- </table></div>`;
76
- }
77
-
78
- function splitMarkdownRow(line) {
79
- let text = String(line || "").trim();
80
- if (text.startsWith("|")) text = text.slice(1);
81
- if (text.endsWith("|") && !text.endsWith("\\|")) text = text.slice(0, -1);
82
- const cells = [];
83
- let current = "";
84
- let inCode = false;
85
- for (let index = 0; index < text.length; index += 1) {
86
- const char = text[index];
87
- if (char === "\\" && text[index + 1] === "|") {
88
- current += "|";
89
- index += 1;
90
- continue;
91
- }
92
- if (char === "`") inCode = !inCode;
93
- if (char === "|" && !inCode) {
94
- cells.push(current.trim());
95
- current = "";
96
- continue;
97
- }
98
- current += char;
99
- }
100
- cells.push(current.trim());
101
- return cells;
102
- }
103
-
104
- function inline(value) {
105
- return escapeHtml(value)
106
- .replace(/`([^`]+)`/g, "<code>$1</code>")
107
- .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
108
- }
109
-
110
- function escapeHtml(value) {
111
- return String(value ?? "")
112
- .replaceAll("&", "&amp;")
113
- .replaceAll("<", "&lt;")
114
- .replaceAll(">", "&gt;")
115
- .replaceAll('"', "&quot;");
116
- }
@@ -1,59 +0,0 @@
1
- window.HarnessMermaid = {
2
- render(code = "") {
3
- const parsed = parseFlowchart(code);
4
- if (!parsed) {
5
- return `<figure class="mermaid-fallback">
6
- <figcaption>Mermaid source</figcaption>
7
- <pre>${escapeMermaid(code)}</pre>
8
- </figure>`;
9
- }
10
- return renderSvg(parsed);
11
- },
12
- };
13
-
14
- function parseFlowchart(code) {
15
- const lines = String(code || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
16
- if (!/^flowchart\s+(LR|TB|TD|RL|BT)/i.test(lines[0] || "")) return null;
17
- const nodes = new Map();
18
- const edges = [];
19
- for (const line of lines.slice(1)) {
20
- const edgeMatch = line.match(/^([A-Za-z0-9_-]+)(?:\["([^"]+)"\])?\s*-+>\s*([A-Za-z0-9_-]+)(?:\["([^"]+)"\])?/);
21
- if (!edgeMatch) continue;
22
- const [, from, fromLabel, to, toLabel] = edgeMatch;
23
- nodes.set(from, fromLabel || from);
24
- nodes.set(to, toLabel || to);
25
- edges.push([from, to]);
26
- }
27
- if (nodes.size === 0) return null;
28
- return { nodes: [...nodes.entries()], edges };
29
- }
30
-
31
- function renderSvg({ nodes, edges }) {
32
- const width = Math.max(360, nodes.length * 150);
33
- const height = 150;
34
- const positions = new Map(nodes.map(([id], index) => [id, { x: 70 + index * 140, y: 70 }]));
35
- const nodeSvg = nodes.map(([id, label]) => {
36
- const pos = positions.get(id);
37
- return `<g class="mermaid-node">
38
- <rect x="${pos.x - 54}" y="${pos.y - 24}" width="108" height="48" rx="8"></rect>
39
- <text x="${pos.x}" y="${pos.y + 4}" text-anchor="middle">${escapeMermaid(label).replaceAll("\\n", " ")}</text>
40
- </g>`;
41
- }).join("");
42
- const edgeSvg = edges.map(([from, to]) => {
43
- const a = positions.get(from);
44
- const b = positions.get(to);
45
- if (!a || !b) return "";
46
- return `<path class="mermaid-edge" d="M ${a.x + 54} ${a.y} L ${b.x - 62} ${b.y}"></path><path class="mermaid-arrow" d="M ${b.x - 62} ${b.y} l -7 -5 v 10 z"></path>`;
47
- }).join("");
48
- return `<figure class="mermaid-rendered">
49
- <figcaption>Mermaid</figcaption>
50
- <svg viewBox="0 0 ${width} ${height}" role="img" aria-label="Mermaid flowchart">${edgeSvg}${nodeSvg}</svg>
51
- </figure>`;
52
- }
53
-
54
- function escapeMermaid(value) {
55
- return String(value ?? "")
56
- .replaceAll("&", "&amp;")
57
- .replaceAll("<", "&lt;")
58
- .replaceAll(">", "&gt;");
59
- }
@@ -1,18 +0,0 @@
1
- <!doctype html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Harness 控制台</title>
7
- <link rel="icon" href="data:,">
8
- <link rel="stylesheet" href="./assets/app.css">
9
- </head>
10
- <body>
11
- <div id="app" class="app-shell" aria-live="polite"></div>
12
- <script src="./assets/dashboard-data.js"></script>
13
- <script src="./assets/i18n.js"></script>
14
- <script src="./assets/markdown-reader.js"></script>
15
- <script src="./assets/mermaid-renderer.js"></script>
16
- <script src="./assets/app.js"></script>
17
- </body>
18
- </html>