@event4u/agent-config 14.21.0 → 14.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +164 -187
  5. package/CREDITS.md +37 -0
  6. package/README.md +1 -1
  7. package/dist/agent-src/commands/analyze/inbox.md +380 -5
  8. package/dist/agent-src/commands/analyze/repo.md +4 -2
  9. package/dist/agent-src/commands/pr/merge.md +47 -38
  10. package/dist/agent-src/commands/roadmap/next.md +8 -0
  11. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
  14. package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
  15. package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
  16. package/dist/agent-src/rules/token-efficiency.md +26 -3
  17. package/dist/agent-src/scripts/_cli_entry.ts +51 -0
  18. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  19. package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
  20. package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
  21. package/dist/agent-src/scripts/stubs_due.ts +4 -18
  22. package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
  23. package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
  24. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
  25. package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
  26. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
  27. package/dist/agent-src/templates/agent-settings.md +0 -1
  28. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  29. package/dist/cli/registry.js +1 -1
  30. package/dist/cli-delegate/_cli_entry.js +8 -0
  31. package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
  32. package/dist/cli-delegate/archival_sweep.js +8 -0
  33. package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
  34. package/dist/cli-delegate/check_update_banner.js +923 -0
  35. package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
  36. package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
  37. package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
  38. package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
  39. package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
  40. package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
  41. package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
  42. package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
  43. package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
  44. package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
  45. package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
  46. package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
  47. package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
  48. package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
  49. package/dist/cli-delegate/cmd_conformance.js +2 -2
  50. package/dist/cli-delegate/cmd_export.js +2 -2
  51. package/dist/cli-delegate/cmd_handoff.js +45 -13
  52. package/dist/cli-delegate/cmd_prune.js +2 -2
  53. package/dist/cli-delegate/cmd_session_recycle.js +4 -14
  54. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  55. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  56. package/dist/cli-delegate/cmd_sync.js +2 -2
  57. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  58. package/dist/cli-delegate/cmd_validate.js +2 -2
  59. package/dist/cli-delegate/dashboard_mode.js +16 -0
  60. package/dist/cli-delegate/gate_budget.js +24 -0
  61. package/dist/cli-delegate/gate_execute.js +25 -0
  62. package/dist/cli-delegate/guarded_baseline.js +18 -0
  63. package/dist/cli-delegate/resume_probe.js +22 -0
  64. package/dist/cli-delegate/roadmap_gates.js +638 -0
  65. package/dist/cli-delegate/roadmap_set_step.js +173 -0
  66. package/dist/cli-delegate/stub_queue.js +47 -0
  67. package/dist/cli-delegate/stubs_due.js +38 -0
  68. package/dist/cli-delegate/ticket_bundles.js +59 -0
  69. package/dist/cli-delegate/update_roadmap_progress.js +60 -0
  70. package/dist/discovery/deprecation-report.md +1 -1
  71. package/dist/discovery/discovery-manifest.json +13 -13
  72. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  73. package/dist/discovery/discovery-manifest.summary.md +1 -1
  74. package/dist/discovery/dormancy-report.md +1 -1
  75. package/dist/discovery/orphan-report.md +1 -1
  76. package/dist/discovery/packs.json +2 -2
  77. package/dist/discovery/trust-report.md +1 -1
  78. package/dist/discovery/workspaces.json +2 -2
  79. package/dist/hooks/dispatch.js +1813 -946
  80. package/dist/install/claudeLayerCarriage.js +237 -0
  81. package/dist/install/claudePathsPlan.js +79 -0
  82. package/dist/install/hostLayerFingerprint.js +1 -1
  83. package/dist/install/install.mjs +21 -3
  84. package/dist/install/partitionEligibility.js +231 -117
  85. package/dist/install/ruleLayerPartition.js +10 -7
  86. package/dist/mcp/registry-manifest.json +2 -2
  87. package/dist/mcp/server.json +2 -2
  88. package/dist/mcp/server.mjs +10870 -7641
  89. package/dist/scripts/_lib/lean_projection_mode.js +108 -0
  90. package/dist/scripts/_lib/router_match.js +243 -0
  91. package/dist/scripts/_lib/rule_injection.js +216 -0
  92. package/dist/scripts/condense.js +52 -45
  93. package/dist/scripts/project_thin_rules.js +139 -19
  94. package/dist/server/schemas/settings.js +16 -3
  95. package/docs/MIGRATION.md +105 -1
  96. package/docs/contracts/settings-classes.md +40 -8
  97. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
  98. package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  99. package/llms.txt +1 -1
  100. package/package.json +7 -4
  101. package/src/agent-src/scripts/_cli_entry.ts +51 -0
  102. package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  103. package/src/agent-src/scripts/roadmap_gates.ts +4 -20
  104. package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
  105. package/src/agent-src/scripts/stubs_due.ts +4 -18
  106. package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
  107. package/src/config/agent-settings.template.yml +47 -1
  108. package/src/config/continuity-surface.json +516 -0
  109. package/src/config/estate-count-budget.json +1 -1
  110. package/src/config/evaluator-budgets.json +4 -4
  111. package/src/config/gate-coverage.yml +29 -8
  112. package/src/config/gate-violation-baselines.json +8 -8
  113. package/src/config/gitignore-block.txt +19 -0
  114. package/src/config/hook-token-budget.json +7 -6
  115. package/src/config/host-injection-effect.json +73 -0
  116. package/src/config/packed-binary-manifest.json +33 -0
  117. package/src/config/preamble-payload-budget.json +112 -105
  118. package/src/config/publish-surface.json +4 -2
  119. package/src/config/routing-coverage-seed.json +9 -2
  120. package/src/install/claudeLayerCarriage.ts +273 -0
  121. package/src/install/claudePathsPlan.ts +76 -0
  122. package/src/install/hostLayerFingerprint.ts +1 -1
  123. package/src/install/partitionEligibility.ts +261 -127
  124. package/src/install/ruleLayerPartition.ts +19 -23
  125. package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
  126. package/src/scripts/_cli/handoff_sessions.ts +14 -44
  127. package/src/scripts/_dispatch.bash +77 -27
  128. package/src/scripts/_lib/auto_dispatch.ts +2 -2
  129. package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
  130. package/src/scripts/_lib/continuity_slot.ts +295 -0
  131. package/src/scripts/_lib/continuity_surface.ts +296 -0
  132. package/src/scripts/_lib/continuity_writer.ts +253 -0
  133. package/src/scripts/_lib/corpus_manifest.ts +26 -9
  134. package/src/scripts/_lib/hook_settings.ts +86 -1
  135. package/src/scripts/_lib/injection_effect.ts +151 -0
  136. package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
  137. package/src/scripts/_lib/lean_projection_mode.ts +127 -0
  138. package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
  139. package/src/scripts/_lib/pin_resolver.ts +13 -0
  140. package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
  141. package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
  142. package/src/scripts/_lib/retired_status.ts +74 -0
  143. package/src/scripts/_lib/session_eol.ts +44 -0
  144. package/src/scripts/_lib/session_index_trust.ts +346 -0
  145. package/src/scripts/_lib/skill_catalogue.ts +156 -21
  146. package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
  147. package/src/scripts/_lib/subagent_capsule.ts +117 -8
  148. package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
  149. package/src/scripts/ai_council/api_transport.ts +5 -0
  150. package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
  151. package/src/scripts/ai_council/prompts.ts +9 -0
  152. package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
  153. package/src/scripts/audit_mcp_tools.ts +69 -25
  154. package/src/scripts/build_mcp_catalog.ts +24 -1
  155. package/src/scripts/capture_skill_catalogue.ts +24 -11
  156. package/src/scripts/check_candidate_lines.ts +499 -0
  157. package/src/scripts/check_continuity_surface.ts +475 -0
  158. package/src/scripts/check_dependency_floors.ts +13 -6
  159. package/src/scripts/check_estate_count.ts +40 -79
  160. package/src/scripts/check_host_tree_parity.ts +291 -0
  161. package/src/scripts/check_pack_size.ts +262 -23
  162. package/src/scripts/check_preamble_payload_budget.ts +136 -7
  163. package/src/scripts/check_roadmap_trackable.ts +19 -16
  164. package/src/scripts/check_rule_layer_partition.ts +54 -34
  165. package/src/scripts/check_rule_projection_integrity.ts +102 -2
  166. package/src/scripts/check_single_delivery.ts +97 -31
  167. package/src/scripts/check_update_banner.ts +13 -0
  168. package/src/scripts/ci_settle.ts +205 -5
  169. package/src/scripts/code_graph/build.ts +217 -15
  170. package/src/scripts/code_graph/cli.ts +334 -15
  171. package/src/scripts/code_graph/detect.ts +38 -0
  172. package/src/scripts/code_graph/extract.ts +11 -0
  173. package/src/scripts/code_graph/grammar_probe.ts +43 -0
  174. package/src/scripts/code_graph/loader.ts +88 -14
  175. package/src/scripts/code_graph/query.ts +144 -11
  176. package/src/scripts/code_graph/resolution_tiers.ts +238 -0
  177. package/src/scripts/code_graph/sqlite_store.ts +262 -6
  178. package/src/scripts/code_graph/types.ts +117 -3
  179. package/src/scripts/code_graph/validate.ts +26 -1
  180. package/src/scripts/code_graph/verbs.ts +590 -0
  181. package/src/scripts/condense.ts +54 -42
  182. package/src/scripts/corpus_manifest.ts +5 -5
  183. package/src/scripts/council_cli.ts +11 -12
  184. package/src/scripts/count_intent_disagreement.ts +426 -0
  185. package/src/scripts/handoff_context_hook.ts +118 -1
  186. package/src/scripts/hook_effect_doctor.ts +82 -1
  187. package/src/scripts/hook_manifest.json +1 -1
  188. package/src/scripts/hook_manifest.yaml +87 -12
  189. package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
  190. package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
  191. package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
  192. package/src/scripts/hooks/concern_registry.ts +4 -2
  193. package/src/scripts/hooks/host_semantics.ts +1 -1
  194. package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
  195. package/src/scripts/hooks/push_settle_hook.ts +3 -2
  196. package/src/scripts/hooks/rule_inject_hook.ts +108 -21
  197. package/src/scripts/hooks/session_eol_hook.ts +120 -2
  198. package/src/scripts/hooks/skill_route_hook.ts +11 -4
  199. package/src/scripts/hot_context_hook.ts +62 -7
  200. package/src/scripts/inbox_source_census.ts +322 -0
  201. package/src/scripts/install-hooks.sh +82 -0
  202. package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
  203. package/src/scripts/lint_plan_risk_register.ts +13 -17
  204. package/src/scripts/lint_roadmap_blockers.ts +164 -1
  205. package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
  206. package/src/scripts/mcp_server/graph_tools.ts +294 -0
  207. package/src/scripts/mcp_server/path_util.ts +31 -0
  208. package/src/scripts/mcp_server/suggest_skill.ts +72 -0
  209. package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
  210. package/src/scripts/mcp_server/tools.ts +5 -69
  211. package/src/scripts/media/lib/adapter-contract.md +7 -2
  212. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  213. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  214. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  215. package/src/scripts/model_rule_injection.ts +61 -4
  216. package/src/scripts/project_thin_rules.ts +149 -16
  217. package/src/scripts/rdp_candidates_delta.ts +276 -0
  218. package/src/scripts/rdp_quality_eval.ts +217 -19
  219. package/src/scripts/release_mix_taxonomy.json +3 -2
  220. package/src/scripts/report_host_injection_effect.ts +189 -0
  221. package/src/scripts/report_layer_overlap.ts +41 -0
  222. package/src/scripts/report_skill_menu_census.ts +341 -0
  223. package/src/scripts/report_standing_payload_by_host.ts +650 -0
  224. package/src/scripts/schemas/agent-settings.schema.json +7 -1
  225. package/src/scripts/session_memory_index.ts +81 -8
  226. package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
  227. package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
  228. package/src/server/schemas/settings.ts +23 -4
  229. package/src/vendor/grammars/README.md +86 -0
  230. package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
  231. package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
  232. package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  233. package/src/scripts/_lib/carrier_status.ts +0 -118
  234. package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
  235. package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
@@ -0,0 +1,999 @@
1
+ import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
+ import {
3
+ headerFragment
4
+ } from "./chunk-ULET4BYS.js";
5
+ import {
6
+ isCliEntry
7
+ } from "./chunk-5EK5WQN6.js";
8
+ import {
9
+ run_archival_sweep
10
+ } from "./chunk-YWVXLR44.js";
11
+ import {
12
+ evaluateDashboardOnDisk,
13
+ parseModeToken,
14
+ reportCheckVerdict
15
+ } from "./chunk-7XGD2OEU.js";
16
+ import {
17
+ reportGuardedBaselines
18
+ } from "./chunk-YDQSHTD7.js";
19
+
20
+ // src/agent-src/scripts/update_roadmap_progress.ts
21
+ import { createRequire } from "node:module";
22
+ import { spawnSync } from "node:child_process";
23
+ import * as fs from "node:fs";
24
+ import * as path from "node:path";
25
+ import { fileURLToPath } from "node:url";
26
+ var _HERE = fileURLToPath(import.meta.url);
27
+ var _require = createRequire(import.meta.url);
28
+ var CHECKBOX_RE = /^[ \t\n\r\f\v]*[-*][ \t\n\r\f\v]+\[([ xX~\-])\][ \t\n\r\f\v]/gm;
29
+ var PHASE_RE = /^(#{2,3})[ \t\n\r\f\v]+Phase[ \t\n\r\f\v]+(\d+(?:\.\d+)*[a-z]?|[IVX]+|[A-Z](?:\d+)?)(?:[\s:—\-]+([\s\S]*?))?[ \t\f\v\r]*$/gm;
30
+ var TITLE_RE = /^#[ \t\n\r\f\v]+(?:Roadmap:[ \t\n\r\f\v]*)?(.+?)[ \t\f\v\r]*$/m;
31
+ var EXCLUDE_NAMES = /* @__PURE__ */ new Set([
32
+ "template.md",
33
+ "README.md",
34
+ "progress.md",
35
+ "roadmaps-progress.md"
36
+ ]);
37
+ var EXCLUDE_PREFIXES = ["open-questions"];
38
+ var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["archive", "skipped", "stubs", "later"]);
39
+ var FRONTMATTER_RE = /^---\n([\s\S]*?)\n---[ \t\n\r\f\v]*\n/;
40
+ var UNSCHEDULED_VALUES = /* @__PURE__ */ new Set(["draft"]);
41
+ var MERGE_GATED_RE = /merge-gated/i;
42
+ var PR_NUM_RE = /pr[ \t\n\r\f\v]*[=#:]?[ \t\n\r\f\v]*#?[ \t\n\r\f\v]*(\d+)/gi;
43
+ function _pyRound(x) {
44
+ const floor = Math.floor(x);
45
+ const diff = x - floor;
46
+ if (diff < 0.5) return floor;
47
+ if (diff > 0.5) return floor + 1;
48
+ return floor % 2 === 0 ? floor : floor + 1;
49
+ }
50
+ var PhaseStats = class {
51
+ id;
52
+ name;
53
+ done;
54
+ open_;
55
+ deferred;
56
+ cancelled;
57
+ merge_gated;
58
+ merge_gated_prs;
59
+ constructor(id, name, done = 0, open_ = 0, deferred = 0, cancelled = 0, merge_gated = 0, merge_gated_prs = []) {
60
+ this.id = id;
61
+ this.name = name;
62
+ this.done = done;
63
+ this.open_ = open_;
64
+ this.deferred = deferred;
65
+ this.cancelled = cancelled;
66
+ this.merge_gated = merge_gated;
67
+ this.merge_gated_prs = merge_gated_prs;
68
+ }
69
+ get total_active() {
70
+ return this.done + this.open_;
71
+ }
72
+ get total_all() {
73
+ return this.done + this.open_ + this.deferred + this.cancelled;
74
+ }
75
+ get percent() {
76
+ return this.total_active ? _pyRound(this.done * 100 / this.total_active) : 0;
77
+ }
78
+ get state() {
79
+ if (this.total_active === 0 && (this.deferred || this.cancelled)) {
80
+ return "\u23ED\uFE0F skipped";
81
+ }
82
+ if (this.total_active === 0) {
83
+ return "\u2B1C empty";
84
+ }
85
+ if (this.done === 0) {
86
+ return "\u2B1C not started";
87
+ }
88
+ if (this.open_ === 0) {
89
+ return "\u2705 done";
90
+ }
91
+ return "\u{1F7E1} in progress";
92
+ }
93
+ };
94
+ var RoadmapStats = class {
95
+ path;
96
+ rel;
97
+ title;
98
+ phases;
99
+ blockers;
100
+ constructor(p, rel, title, phases = [], blockers = []) {
101
+ this.path = p;
102
+ this.rel = rel;
103
+ this.title = title;
104
+ this.phases = phases;
105
+ this.blockers = blockers;
106
+ }
107
+ get open_blockers() {
108
+ return this.blockers.filter((b) => !blocker_is_resolved(b));
109
+ }
110
+ get resolved_blockers() {
111
+ return this.blockers.filter((b) => blocker_is_resolved(b));
112
+ }
113
+ get done() {
114
+ return this.phases.reduce((s, p) => s + p.done, 0);
115
+ }
116
+ get open_() {
117
+ return this.phases.reduce((s, p) => s + p.open_, 0);
118
+ }
119
+ get deferred() {
120
+ return this.phases.reduce((s, p) => s + p.deferred, 0);
121
+ }
122
+ get cancelled() {
123
+ return this.phases.reduce((s, p) => s + p.cancelled, 0);
124
+ }
125
+ get merge_gated() {
126
+ return this.phases.reduce((s, p) => s + p.merge_gated, 0);
127
+ }
128
+ get merge_gated_prs() {
129
+ const seen = [];
130
+ for (const p of this.phases) {
131
+ for (const n of p.merge_gated_prs) {
132
+ if (!seen.includes(n)) {
133
+ seen.push(n);
134
+ }
135
+ }
136
+ }
137
+ return seen;
138
+ }
139
+ get total_active() {
140
+ return this.done + this.open_;
141
+ }
142
+ get total_all() {
143
+ return this.done + this.open_ + this.deferred + this.cancelled;
144
+ }
145
+ get percent() {
146
+ return this.total_active ? _pyRound(this.done * 100 / this.total_active) : 0;
147
+ }
148
+ };
149
+ function _strip(s) {
150
+ return s.replace(/^[ \t\n\r\f\v]+/, "").replace(/[ \t\n\r\f\v]+$/, "");
151
+ }
152
+ function _splitlines(s) {
153
+ if (s === "") return [];
154
+ const parts = s.split(/\r\n|[\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029]/);
155
+ if (parts.length && parts[parts.length - 1] === "") {
156
+ parts.pop();
157
+ }
158
+ return parts;
159
+ }
160
+ function parse_frontmatter(text) {
161
+ const m = FRONTMATTER_RE.exec(text);
162
+ if (!m) {
163
+ return {};
164
+ }
165
+ const fm = {};
166
+ for (const line of _splitlines(m[1])) {
167
+ const stripped = _strip(line);
168
+ if (!stripped || stripped.startsWith("#") || !line.includes(":")) {
169
+ continue;
170
+ }
171
+ const idx = line.indexOf(":");
172
+ const key = line.slice(0, idx);
173
+ const value = line.slice(idx + 1);
174
+ fm[_strip(key)] = _stripQuotes(_strip(value));
175
+ }
176
+ return fm;
177
+ }
178
+ function _stripQuotes(s) {
179
+ let out = s.replace(/^"+/, "").replace(/"+$/, "");
180
+ out = out.replace(/^'+/, "").replace(/'+$/, "");
181
+ return out;
182
+ }
183
+ function is_unscheduled(fm) {
184
+ return UNSCHEDULED_VALUES.has((fm["status"] ?? "").toLowerCase());
185
+ }
186
+ function is_roadmap_name(name) {
187
+ if (EXCLUDE_NAMES.has(name)) {
188
+ return false;
189
+ }
190
+ return !EXCLUDE_PREFIXES.some((pre) => name.startsWith(pre));
191
+ }
192
+ function is_roadmap_candidate(p) {
193
+ if (!is_roadmap_name(path.basename(p))) {
194
+ return false;
195
+ }
196
+ const parts = p.split(path.sep).filter((s) => s !== "");
197
+ if (parts.some((part) => EXCLUDE_DIRS.has(part))) {
198
+ return false;
199
+ }
200
+ return true;
201
+ }
202
+ function count_checkboxes(text) {
203
+ let done = 0;
204
+ let open_ = 0;
205
+ let deferred = 0;
206
+ let cancelled = 0;
207
+ let merge_gated = 0;
208
+ const prs = [];
209
+ const matches = [];
210
+ CHECKBOX_RE.lastIndex = 0;
211
+ let m;
212
+ while ((m = CHECKBOX_RE.exec(text)) !== null) {
213
+ matches.push({ start: m.index, group1: m[1] });
214
+ if (m.index === CHECKBOX_RE.lastIndex) {
215
+ CHECKBOX_RE.lastIndex++;
216
+ }
217
+ }
218
+ for (let i = 0; i < matches.length; i++) {
219
+ const c = matches[i].group1.toLowerCase();
220
+ if (c === "x") {
221
+ done += 1;
222
+ } else if (c === " ") {
223
+ open_ += 1;
224
+ const span_end = i + 1 < matches.length ? matches[i + 1].start : text.length;
225
+ const span = text.slice(matches[i].start, span_end);
226
+ if (MERGE_GATED_RE.test(span)) {
227
+ merge_gated += 1;
228
+ PR_NUM_RE.lastIndex = 0;
229
+ let pm;
230
+ while ((pm = PR_NUM_RE.exec(span)) !== null) {
231
+ prs.push(parseInt(pm[1], 10));
232
+ }
233
+ }
234
+ } else if (c === "~") {
235
+ deferred += 1;
236
+ } else if (c === "-") {
237
+ cancelled += 1;
238
+ }
239
+ }
240
+ return [done, open_, deferred, cancelled, merge_gated, prs];
241
+ }
242
+ function blocker_is_resolved(b) {
243
+ return /^resolved\b/.test(_strip(b.status).toLowerCase());
244
+ }
245
+ function blocker_class(b) {
246
+ const c = _strip(b.blockerClass).split(/[\s—–-]/)[0];
247
+ return c === "0" || c === "1" || c === "2" ? c : "3";
248
+ }
249
+ var FENCED_CODE_RE = /^[ \t]*```[^\n]*\n[\s\S]*?^[ \t]*```[ \t]*$/gm;
250
+ var BLOCKERS_SECTION_RE = /^##[ \t]+Blockers[ \t]*$/im;
251
+ var BLOCKER_HEADING_RE = /^###[ \t]+blocker:[ \t]*(.+?)[ \t]*$/gim;
252
+ var NEXT_H2_RE = /^##[ \t]+\S/m;
253
+ var LEGACY_BLOCKED_UNTIL_RE = /^>[ \t]*Blocked until:?[ \t]*(.+)$/im;
254
+ function _stripFencedCode(text) {
255
+ return text.replace(FENCED_CODE_RE, (m) => "\n".repeat(_splitlines(m).length));
256
+ }
257
+ var BLOCKER_FIELD_RE = /^-[ \t]*\*\*[^*]+:\*\*/;
258
+ function _stripComment(s) {
259
+ return _strip(s.replace(/<!--[\s\S]*?-->/g, ""));
260
+ }
261
+ function _blockerField(slice, label) {
262
+ const lines = _splitlines(slice);
263
+ const re = new RegExp(`^-[ \\t]*\\*\\*${label}:\\*\\*[ \\t]*(.*)$`, "i");
264
+ const startIdx = lines.findIndex((l) => re.test(_strip(l)));
265
+ if (startIdx === -1) {
266
+ return null;
267
+ }
268
+ const m = re.exec(_strip(lines[startIdx]));
269
+ const parts = [];
270
+ const first = _stripComment(m?.[1] ?? "");
271
+ if (first) {
272
+ parts.push(first);
273
+ }
274
+ for (let i = startIdx + 1; i < lines.length; i++) {
275
+ const trimmed = _strip(lines[i]);
276
+ if (trimmed === "" || BLOCKER_FIELD_RE.test(trimmed) || trimmed.startsWith("#")) {
277
+ break;
278
+ }
279
+ parts.push(_stripComment(trimmed));
280
+ }
281
+ return parts.length ? parts.join(" ") : null;
282
+ }
283
+ function _blockerTodo(slice) {
284
+ const lines = _splitlines(slice);
285
+ const markerRe = /^-[ \t]*\*\*What to do:\*\*[ \t]*(.*)$/i;
286
+ const startIdx = lines.findIndex((l) => markerRe.test(_strip(l)));
287
+ if (startIdx === -1) {
288
+ return [];
289
+ }
290
+ const out = [];
291
+ const inline = _stripComment(
292
+ markerRe.exec(_strip(lines[startIdx]))?.[1] ?? ""
293
+ );
294
+ if (inline) {
295
+ out.push(inline);
296
+ }
297
+ for (let i = startIdx + 1; i < lines.length; i++) {
298
+ const trimmed = _strip(lines[i]);
299
+ if (BLOCKER_FIELD_RE.test(trimmed) || trimmed.startsWith("#")) {
300
+ break;
301
+ }
302
+ if (trimmed !== "") {
303
+ out.push(trimmed);
304
+ }
305
+ }
306
+ return out;
307
+ }
308
+ function parse_blockers(raw_text) {
309
+ const text = _stripFencedCode(raw_text);
310
+ const blockers = [];
311
+ const sectionMatch = BLOCKERS_SECTION_RE.exec(text);
312
+ if (sectionMatch) {
313
+ const sectionStart = sectionMatch.index + sectionMatch[0].length;
314
+ const rest = text.slice(sectionStart);
315
+ const h2 = NEXT_H2_RE.exec(rest);
316
+ const sectionEnd = h2 ? sectionStart + h2.index : text.length;
317
+ const section = text.slice(sectionStart, sectionEnd);
318
+ BLOCKER_HEADING_RE.lastIndex = 0;
319
+ const heads = [];
320
+ let hm;
321
+ while ((hm = BLOCKER_HEADING_RE.exec(section)) !== null) {
322
+ heads.push({
323
+ start: hm.index,
324
+ end: hm.index + hm[0].length,
325
+ id: _strip(hm[1])
326
+ });
327
+ if (hm.index === BLOCKER_HEADING_RE.lastIndex) {
328
+ BLOCKER_HEADING_RE.lastIndex++;
329
+ }
330
+ }
331
+ for (let i = 0; i < heads.length; i++) {
332
+ const cur = heads[i];
333
+ const bodyEnd = i + 1 < heads.length ? heads[i + 1].start : section.length;
334
+ const body = section.slice(cur.end, bodyEnd);
335
+ blockers.push({
336
+ id: cur.id,
337
+ status: (_blockerField(body, "Status") ?? "open").toLowerCase(),
338
+ owner: _blockerField(body, "Owner") ?? "user",
339
+ blocks: _blockerField(body, "Blocks") ?? "(unspecified)",
340
+ todo: _blockerTodo(body),
341
+ resolvedWhen: _blockerField(body, "Resolved when") ?? "(unspecified)",
342
+ recommendation: _blockerField(body, "Recommendation") ?? "",
343
+ ifNothing: _blockerField(body, "If you do nothing") ?? "",
344
+ question: _blockerField(body, "Question") ?? "",
345
+ blockerClass: _blockerField(body, "Class") ?? "",
346
+ run: _blockerField(body, "Run") ?? "",
347
+ budget: _blockerField(body, "Budget") ?? ""
348
+ });
349
+ }
350
+ }
351
+ const legacyMatch = LEGACY_BLOCKED_UNTIL_RE.exec(text);
352
+ if (legacyMatch) {
353
+ const parts = [_strip(legacyMatch[1])];
354
+ const allLines = _splitlines(text);
355
+ const noteIdx = allLines.findIndex((l) => LEGACY_BLOCKED_UNTIL_RE.test(_strip(l)));
356
+ for (let i = noteIdx + 1; noteIdx !== -1 && i < allLines.length; i++) {
357
+ const quoted = /^>[ \t]?(.*)$/.exec(_strip(allLines[i]));
358
+ if (!quoted) {
359
+ break;
360
+ }
361
+ const cont = _strip(quoted[1]);
362
+ if (cont === "") {
363
+ break;
364
+ }
365
+ parts.push(cont);
366
+ }
367
+ blockers.push({
368
+ id: "legacy",
369
+ status: "open",
370
+ owner: "user",
371
+ blocks: "entire roadmap",
372
+ todo: [parts.join(" ")],
373
+ resolvedWhen: "condition described above clears",
374
+ recommendation: "",
375
+ ifNothing: "",
376
+ question: "",
377
+ blockerClass: "",
378
+ run: "",
379
+ budget: ""
380
+ });
381
+ }
382
+ return blockers;
383
+ }
384
+ function blocker_needs_user(owner) {
385
+ return /\buser\b/i.test(owner);
386
+ }
387
+ function parse_roadmap(p, roadmap_root) {
388
+ const text = fs.readFileSync(p, { encoding: "utf-8" });
389
+ const phase_matches = [];
390
+ PHASE_RE.lastIndex = 0;
391
+ let pm;
392
+ while ((pm = PHASE_RE.exec(text)) !== null) {
393
+ phase_matches.push({
394
+ start: pm.index,
395
+ end: pm.index + pm[0].length,
396
+ g2: pm[2],
397
+ g3: pm[3]
398
+ });
399
+ if (pm.index === PHASE_RE.lastIndex) {
400
+ PHASE_RE.lastIndex++;
401
+ }
402
+ }
403
+ if (phase_matches.length === 0) {
404
+ return null;
405
+ }
406
+ const title_match = TITLE_RE.exec(text);
407
+ const title = title_match ? _strip(title_match[1]) : _stem(p);
408
+ const rel = _relPosix(roadmap_root, p);
409
+ const stats = new RoadmapStats(p, rel, title, [], parse_blockers(text));
410
+ for (let i = 0; i < phase_matches.length; i++) {
411
+ const cur = phase_matches[i];
412
+ const start = cur.end;
413
+ const end = i + 1 < phase_matches.length ? phase_matches[i + 1].start : text.length;
414
+ const [d, o, df, c, mg, prs] = count_checkboxes(text.slice(start, end));
415
+ const phase_id = cur.g2;
416
+ const name = _strip(cur.g3 ?? "") || `Phase ${phase_id}`;
417
+ stats.phases.push(new PhaseStats(phase_id, name, d, o, df, c, mg, prs));
418
+ }
419
+ return stats;
420
+ }
421
+ function _stem(p) {
422
+ const base = path.basename(p);
423
+ const dot = base.lastIndexOf(".");
424
+ return dot > 0 ? base.slice(0, dot) : base;
425
+ }
426
+ function _relPosix(root, p) {
427
+ return path.relative(root, p).split(path.sep).join("/");
428
+ }
429
+ function _blockerAnchor(rel) {
430
+ const stem = rel.endsWith(".md") ? rel.slice(0, -3) : rel;
431
+ return "blockers-" + stem.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
432
+ }
433
+ function bar(pct, width = 10) {
434
+ const filled = _pyRound(pct * width / 100);
435
+ return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
436
+ }
437
+ function _rglobMdSorted(root) {
438
+ const out = [];
439
+ const walk = (dir) => {
440
+ let entries;
441
+ try {
442
+ entries = fs.readdirSync(dir, { withFileTypes: true });
443
+ } catch {
444
+ return;
445
+ }
446
+ for (const ent of entries) {
447
+ const full = path.join(dir, ent.name);
448
+ if (ent.isDirectory()) {
449
+ walk(full);
450
+ } else if (ent.isSymbolicLink() && _isDir(full)) {
451
+ walk(full);
452
+ } else if (ent.name.endsWith(".md")) {
453
+ out.push(full);
454
+ }
455
+ }
456
+ };
457
+ walk(root);
458
+ out.sort();
459
+ return out;
460
+ }
461
+ function _isDir(p) {
462
+ try {
463
+ return fs.statSync(p).isDirectory();
464
+ } catch {
465
+ return false;
466
+ }
467
+ }
468
+ function _isFile(p) {
469
+ try {
470
+ return fs.statSync(p).isFile();
471
+ } catch {
472
+ return false;
473
+ }
474
+ }
475
+ function collect(roadmap_root) {
476
+ const results = [];
477
+ for (const p of _rglobMdSorted(roadmap_root)) {
478
+ if (!_isFile(p) || !is_roadmap_candidate(p)) {
479
+ continue;
480
+ }
481
+ const text = fs.readFileSync(p, { encoding: "utf-8" });
482
+ if (is_unscheduled(parse_frontmatter(text))) {
483
+ continue;
484
+ }
485
+ const stats = parse_roadmap(p, roadmap_root);
486
+ if (stats) {
487
+ results.push(stats);
488
+ }
489
+ }
490
+ return results;
491
+ }
492
+ var RESUME_STATEMENT = /\b(blocked until|resume when|resume-when|blocked-until)\b/i;
493
+ var RESUME_LOOSE = /\btrigger\b/i;
494
+ function collect_parked(roadmap_root) {
495
+ const dir = path.join(roadmap_root, "later");
496
+ let names;
497
+ try {
498
+ names = fs.readdirSync(dir).sort();
499
+ } catch {
500
+ return [];
501
+ }
502
+ const out = [];
503
+ for (const name of names) {
504
+ const full = path.join(dir, name);
505
+ if (!_isFile(full) || !is_roadmap_name(name)) {
506
+ continue;
507
+ }
508
+ let text;
509
+ try {
510
+ text = fs.readFileSync(full, { encoding: "utf-8" });
511
+ } catch {
512
+ continue;
513
+ }
514
+ if (is_unscheduled(parse_frontmatter(text))) {
515
+ continue;
516
+ }
517
+ const cell = resume_cell(text);
518
+ const open = parse_blockers(text).filter((b) => !blocker_is_resolved(b));
519
+ out.push({
520
+ rel: name,
521
+ resume: cell.stated,
522
+ unlabelled: cell.unlabelled,
523
+ open_blockers: open.length,
524
+ needs_user: open.filter((b) => blocker_needs_user(b.owner)).length
525
+ });
526
+ }
527
+ return out;
528
+ }
529
+ function resume_cell(text) {
530
+ const lines = text.split("\n");
531
+ for (let i = 0; i < lines.length; i++) {
532
+ const raw = lines[i];
533
+ if (!RESUME_STATEMENT.test(raw)) {
534
+ continue;
535
+ }
536
+ const parts = [raw];
537
+ for (let j = i + 1; j < lines.length; j++) {
538
+ const next = lines[j].replace(/^\s*>\s?/, "").trim();
539
+ if (next === "" || /^([-*+]\s|#{1,6}\s|```|\||>\s*$)/.test(next)) {
540
+ break;
541
+ }
542
+ parts.push(next);
543
+ }
544
+ const joined = cell_text(parts.join(" "));
545
+ if (joined === "") {
546
+ continue;
547
+ }
548
+ return { stated: joined, unlabelled: false };
549
+ }
550
+ return { stated: null, unlabelled: lines.some((l) => RESUME_LOOSE.test(l)) };
551
+ }
552
+ function cell_text(raw) {
553
+ let out = raw.replace(/^\s*>\s?/, "").replace(/<!--[\s\S]*?-->/g, "").replace(/<!--|-->/g, "").replace(/\*\*/g, "").replace(/\s+/g, " ").trim();
554
+ if (out.length > 200) {
555
+ out = `${out.slice(0, 197).trimEnd()}...`;
556
+ }
557
+ return out.replace(/\|/g, "\\|");
558
+ }
559
+ function unarchived_complete(roadmaps) {
560
+ return roadmaps.filter((r) => r.total_active > 0 && r.open_ === 0 && r.deferred === 0);
561
+ }
562
+ function merge_gated_pending(roadmaps) {
563
+ return roadmaps.filter((r) => r.open_ > 0 && r.merge_gated === r.open_);
564
+ }
565
+ function pending_iron_law_3(roadmaps) {
566
+ return roadmaps.filter((r) => r.total_active > 0 && r.open_ === 0 && r.deferred > 0);
567
+ }
568
+ function collect_bundles(repo_root) {
569
+ const reg = path.join(repo_root, "agents", "tickets", "_registry.yml");
570
+ if (!fs.existsSync(reg)) {
571
+ return [];
572
+ }
573
+ let YAML;
574
+ try {
575
+ YAML = _require("yaml");
576
+ } catch {
577
+ return [];
578
+ }
579
+ let data;
580
+ try {
581
+ data = YAML.parse(fs.readFileSync(reg, { encoding: "utf-8" }), { version: "1.1" }) ?? {};
582
+ } catch {
583
+ return [];
584
+ }
585
+ const out = [];
586
+ const bundles = data && typeof data === "object" && !Array.isArray(data) ? data["bundles"] ?? null : null;
587
+ const bundleMap = bundles && typeof bundles === "object" && !Array.isArray(bundles) ? bundles : {};
588
+ for (const slug of Object.keys(bundleMap).sort()) {
589
+ const metaRaw = bundleMap[slug];
590
+ const meta = metaRaw && typeof metaRaw === "object" && !Array.isArray(metaRaw) ? metaRaw : {};
591
+ const bdir = path.join(repo_root, "agents", "tickets", slug);
592
+ let n = 0;
593
+ if (_isDir(bdir)) {
594
+ try {
595
+ n = fs.readdirSync(bdir).filter((f) => f.startsWith("T-") && f.endsWith(".md")).length;
596
+ } catch {
597
+ n = 0;
598
+ }
599
+ }
600
+ out.push({
601
+ slug,
602
+ tickets: n,
603
+ status: meta["status"] ?? "?",
604
+ roadmap: meta["source_roadmap"] ?? ""
605
+ });
606
+ }
607
+ return out;
608
+ }
609
+ function render(roadmaps, bundles, roadmap_root) {
610
+ const total_done = roadmaps.reduce((s, r) => s + r.done, 0);
611
+ const total_active = roadmaps.reduce((s, r) => s + r.total_active, 0);
612
+ const overall_pct = total_active ? _pyRound(total_done * 100 / total_active) : 0;
613
+ const pending = pending_iron_law_3(roadmaps);
614
+ const gated = merge_gated_pending(roadmaps);
615
+ const total_open_blockers = roadmaps.reduce((s, r) => s + r.open_blockers.length, 0);
616
+ const user_open_blockers = roadmaps.reduce(
617
+ (s, r) => s + r.open_blockers.filter((b) => blocker_needs_user(b.owner)).length,
618
+ 0
619
+ );
620
+ const lines = [];
621
+ lines.push("# Roadmap Progress\n");
622
+ const header_meta = `> ${roadmaps.length} open roadmap${roadmaps.length !== 1 ? "s" : ""} \xB7 [roadmaps/](roadmaps/) \xB7 [archive/](roadmaps/archive/) \xB7 [skipped/](roadmaps/skipped/) \xB7 [later/](roadmaps/later/)` + (total_open_blockers > 0 ? ` \xB7 **${total_open_blockers}** open blocker${total_open_blockers !== 1 ? "s" : ""} in the active tree` + (user_open_blockers > 0 ? `, **${user_open_blockers}** need you \u2192 \`agent-config gates\`` : "") : "") + // Two integers, never a row — see `stubs_due.headerFragment`.
623
+ headerFragment(path.join(roadmap_root, "stubs")) + "\n";
624
+ lines.push(
625
+ // Honest provenance (road-to-roadmap-archival-robustness, gap C): name a
626
+ // regen path that exists in EVERY install, not a hardcoded
627
+ // `.augment/scripts/...` that a script-less / non-Augment consumer does
628
+ // not have. `task roadmap-progress` covers Taskfile projects; the bare
629
+ // script name covers the rest.
630
+ "> Auto-generated \u2014 do not edit. Regenerate with `task roadmap-progress` or by running the `update_roadmap_progress` script for your install; rewritten on every roadmap create / execute / completion change. A repository that does not commit this file has no git history for it \u2014 regenerate to see the current state.\n>\n" + header_meta
631
+ );
632
+ lines.push("## Overall\n");
633
+ lines.push(`**${total_done} / ${total_active} steps done \xB7 ${overall_pct}%**
634
+ `);
635
+ lines.push("```text\n" + bar(overall_pct, 40) + ` ${overall_pct}%
636
+ \`\`\`
637
+ `);
638
+ if (pending.length) {
639
+ lines.push("## \u26A0\uFE0F Iron Law 3 \u2014 unresolved deferred items\n");
640
+ lines.push(
641
+ "These roadmaps have `count_open == 0` but carry `[~]` deferred items. Per `roadmap-progress-sync` Iron Law 3 they do NOT auto-archive \u2014 the user must resolve the deferrals first (spawn follow-up, restore, or cancel). See [`roadmap-management \xA7 4b`](../packages/core/.agent-src.uncondensed/skills/roadmap-management/SKILL.md).\n"
642
+ );
643
+ lines.push("| Roadmap | Done | Deferred | Cancelled |");
644
+ lines.push("|---|---:|---:|---:|");
645
+ for (const r of pending) {
646
+ lines.push(
647
+ `| [${r.rel}](roadmaps/${r.rel}) | ${r.done} | ${r.deferred} | ${r.cancelled} |`
648
+ );
649
+ }
650
+ lines.push("");
651
+ }
652
+ if (gated.length) {
653
+ lines.push("## \u23F3 Merge-gated \u2014 pending post-merge archival\n");
654
+ lines.push(
655
+ "Every open item in these roadmaps is `merge-gated`: held open on purpose while a closing PR is in flight, so inbound references keep resolving until the file archives. **The moment the gating PR merges**, flip the merge-gated box \u2192 `[x]`, `git mv` the roadmap to `archive/`, migrate inbound refs, and regenerate this dashboard \u2014 all in the same response (per `roadmap-progress-sync` Iron Law 1). Do NOT leave it lingering at < 100%.\n"
656
+ );
657
+ lines.push("| Roadmap | Done | Merge-gated open | Gating PR |");
658
+ lines.push("|---|---:|---:|---|");
659
+ for (const r of gated) {
660
+ const prs = r.merge_gated_prs.map((n) => `#${n}`).join(", ") || "\u2014";
661
+ lines.push(
662
+ `| [${r.rel}](roadmaps/${r.rel}) | ${r.done} | ${r.merge_gated} | ${prs} |`
663
+ );
664
+ }
665
+ lines.push("");
666
+ }
667
+ const unarchived = unarchived_complete(roadmaps);
668
+ if (unarchived.length) {
669
+ lines.push("## \u2705 Completed \u2014 pending archival\n");
670
+ lines.push(
671
+ "These roadmaps are **complete** (`count_open == 0`, `count_deferred == 0`) but still sit in the active tree. They should be in `agents/roadmaps/archive/`. Run the archival sweep `archive_completed_roadmaps --all` (untracked-safe), or follow the manual fallback documented in the `roadmap-management` skill, then regenerate this dashboard.\n"
672
+ );
673
+ lines.push("| Roadmap | Done | Total |");
674
+ lines.push("|---|---:|---:|");
675
+ for (const r of unarchived) {
676
+ lines.push(`| [${r.rel}](roadmaps/${r.rel}) | ${r.done} | ${r.total_active} |`);
677
+ }
678
+ lines.push("");
679
+ }
680
+ if (roadmaps.length === 0) {
681
+ lines.push("_No open roadmaps._\n");
682
+ return lines.join("\n") + "\n";
683
+ }
684
+ lines.push("## Open roadmaps\n");
685
+ lines.push(
686
+ "| # | Roadmap | Phases | Steps | Open | Done | Deferred | Cancelled | Blocker | Progress |"
687
+ );
688
+ lines.push("|---|---|---:|---:|---:|---:|---:|---:|---:|---|");
689
+ roadmaps.forEach((r, idx) => {
690
+ const i = idx + 1;
691
+ const openBlockers = r.open_blockers.length;
692
+ const blockerCell = openBlockers > 0 ? `[${openBlockers}](#${_blockerAnchor(r.rel)})` : "0";
693
+ lines.push(
694
+ `| ${i} | [${r.rel}](roadmaps/${r.rel}) | ${r.phases.length} | ${r.total_all} | ${r.open_} | ${r.done} | ${r.deferred} | ${r.cancelled} | ${blockerCell} | ${bar(r.percent)} ${r.percent}% |`
695
+ );
696
+ });
697
+ lines.push("");
698
+ lines.push("---\n");
699
+ const parked = collect_parked(roadmap_root);
700
+ if (parked.length > 0) {
701
+ lines.push(
702
+ `## Parked \u2014 \`later/\` (${parked.length} roadmap${parked.length !== 1 ? "s" : ""}, not active backlog)
703
+ `
704
+ );
705
+ lines.push(
706
+ "> Excluded from the table above and from `/roadmap:process-*` by design. Listed here so a resume condition is visible without opening the file.\n"
707
+ );
708
+ const parked_open = parked.reduce((n, r) => n + r.open_blockers, 0);
709
+ const parked_user = parked.reduce((n, r) => n + r.needs_user, 0);
710
+ if (parked_open > 0) {
711
+ lines.push(
712
+ `> Carrying **${parked_open}** open blocker${parked_open !== 1 ? "s" : ""}` + (parked_user > 0 ? `, **${parked_user}** owned by you` : "") + " \u2014 parking resolves nothing, so these are NOT in the active-tree count above.\n"
713
+ );
714
+ }
715
+ lines.push("| Roadmap | Open blockers | Resume when |");
716
+ lines.push("|---|---:|---|");
717
+ for (const r of parked) {
718
+ const cell = r.resume ?? (r.unlabelled ? "_condition present but unlabelled \u2014 see file_" : "_no resume line recorded_");
719
+ const blockers = r.open_blockers === 0 ? "0" : `${r.open_blockers}${r.needs_user > 0 ? ` (${r.needs_user} you)` : ""}`;
720
+ lines.push(
721
+ `| [${r.rel}](roadmaps/later/${r.rel}) | ${blockers} | ${cell} | <!-- ref-ignore -->`
722
+ );
723
+ }
724
+ lines.push("");
725
+ lines.push("---\n");
726
+ }
727
+ lines.push("## Per-roadmap phase breakdown\n");
728
+ for (const r of roadmaps) {
729
+ lines.push(`### [${r.rel}](roadmaps/${r.rel})
730
+ `);
731
+ lines.push(`**${r.title}** \u2014 ${r.done} / ${r.total_active} done (${r.percent}%)
732
+ `);
733
+ lines.push("| # | Phase | State | Open | Done | Deferred | Cancelled | % |");
734
+ lines.push("|---|---|---|---:|---:|---:|---:|---:|");
735
+ for (const p of r.phases) {
736
+ lines.push(
737
+ `| ${p.id} | ${p.name} | ${p.state} | ${p.open_} | ${p.done} | ${p.deferred} | ${p.cancelled} | ${p.percent}% |`
738
+ );
739
+ }
740
+ lines.push("");
741
+ const openBlockers = r.open_blockers;
742
+ if (openBlockers.length) {
743
+ lines.push(`<a id="${_blockerAnchor(r.rel)}"></a>`);
744
+ lines.push("**Blockers**\n");
745
+ for (const b of openBlockers) {
746
+ lines.push(`- **${b.id}** (owner: ${b.owner}) \u2014 blocks ${b.blocks}`);
747
+ if (b.recommendation) {
748
+ lines.push(` - **Recommendation:** ${b.recommendation}`);
749
+ }
750
+ if (b.ifNothing) {
751
+ lines.push(` - **If you do nothing:** ${b.ifNothing}`);
752
+ }
753
+ lines.push(" - **What to do:**");
754
+ for (const step of b.todo) {
755
+ lines.push(` ${step}`);
756
+ }
757
+ lines.push(` - **Resolved when:** ${b.resolvedWhen}`);
758
+ }
759
+ const resolvedCount = r.resolved_blockers.length;
760
+ if (resolvedCount > 0) {
761
+ lines.push(
762
+ `
763
+ _${resolvedCount} blocker${resolvedCount !== 1 ? "s" : ""} resolved._`
764
+ );
765
+ }
766
+ lines.push("");
767
+ }
768
+ }
769
+ if (bundles && bundles.length) {
770
+ lines.push("---\n");
771
+ lines.push("## Ticket bundles\n");
772
+ lines.push(
773
+ "Materialised ticket bundles under [`agents/tickets/`](tickets/) (via `/roadmap:materialize`), counted from `agents/tickets/_registry.yml`.\n"
774
+ );
775
+ lines.push("| Bundle | Tickets | Status | Source roadmap |");
776
+ lines.push("|---|---:|---|---|");
777
+ for (const b of bundles) {
778
+ lines.push(
779
+ `| ${b.slug} | ${b.tickets} | ${b.status} | ${b.roadmap} |`
780
+ );
781
+ }
782
+ lines.push("");
783
+ }
784
+ return lines.join("\n") + "\n";
785
+ }
786
+ var ArgparseExit = class extends Error {
787
+ code;
788
+ constructor(code) {
789
+ super(`ArgparseExit(${code})`);
790
+ this.name = "ArgparseExit";
791
+ this.code = code;
792
+ }
793
+ };
794
+ var _PROG = "update_roadmap_progress.py";
795
+ function _usage() {
796
+ return `usage: ${_PROG} [-h] [--check] [--tracked-mode | --untracked-mode]
797
+ [--archive | --no-archive] [--repo-root REPO_ROOT] [--dashboard-only]
798
+ `;
799
+ }
800
+ function _parseArgs(argv) {
801
+ let check = false;
802
+ let mode = "tracked";
803
+ let archive = false;
804
+ let dashboard_only = false;
805
+ let repo_root = process.cwd();
806
+ const emitError = (msg) => {
807
+ process.stderr.write(_usage());
808
+ process.stderr.write(`${_PROG}: error: ${msg}
809
+ `);
810
+ throw new ArgparseExit(2);
811
+ };
812
+ let i = 0;
813
+ while (i < argv.length) {
814
+ const tok = argv[i];
815
+ if (tok === "-h" || tok === "--help") {
816
+ process.stdout.write(_usage());
817
+ throw new ArgparseExit(0);
818
+ } else if (tok === "--check") {
819
+ check = true;
820
+ i += 1;
821
+ } else if (parseModeToken(tok) !== null) {
822
+ mode = parseModeToken(tok);
823
+ i += 1;
824
+ } else if (tok === "--dashboard-only") {
825
+ dashboard_only = true;
826
+ i += 1;
827
+ } else if (tok === "--archive") {
828
+ archive = true;
829
+ i += 1;
830
+ } else if (tok === "--no-archive") {
831
+ archive = false;
832
+ i += 1;
833
+ } else if (tok === "--repo-root") {
834
+ const val = argv[i + 1];
835
+ if (val === void 0) {
836
+ emitError("argument --repo-root: expected one argument");
837
+ }
838
+ repo_root = val;
839
+ i += 2;
840
+ } else if (tok.startsWith("--repo-root=")) {
841
+ repo_root = tok.slice("--repo-root=".length);
842
+ i += 1;
843
+ } else {
844
+ emitError(`unrecognized arguments: ${tok}`);
845
+ }
846
+ }
847
+ if (check && archive) {
848
+ emitError("argument --archive: not allowed with --check");
849
+ }
850
+ if (dashboard_only && !check) {
851
+ emitError("argument --dashboard-only: only meaningful with --check");
852
+ }
853
+ return { check, mode, archive, repo_root, dashboard_only };
854
+ }
855
+ function _fallback_git_toplevel(repo_root) {
856
+ try {
857
+ const r = spawnSync("git", ["rev-parse", "--show-toplevel"], {
858
+ cwd: repo_root,
859
+ encoding: "utf-8",
860
+ timeout: 1e4
861
+ });
862
+ const top = (r.stdout || "").trim();
863
+ if (r.status === 0 && top !== "" && _isDir(path.join(top, "agents", "roadmaps"))) {
864
+ return top;
865
+ }
866
+ } catch {
867
+ }
868
+ return repo_root;
869
+ }
870
+ function main(argv) {
871
+ const args = _parseArgs(argv ?? process.argv.slice(2));
872
+ let repo_root = args.repo_root;
873
+ if (repo_root === process.cwd() && !_isDir(path.join(repo_root, "agents", "roadmaps"))) {
874
+ repo_root = _fallback_git_toplevel(repo_root);
875
+ }
876
+ const roadmap_root = path.join(repo_root, "agents", "roadmaps");
877
+ const target = path.join(repo_root, "agents", "roadmaps-progress.md");
878
+ if (!_isDir(roadmap_root)) {
879
+ if (args.check) {
880
+ return 0;
881
+ }
882
+ process.stdout.write(`\u2139\uFE0F No roadmaps directory at ${roadmap_root} \u2014 nothing to do.
883
+ `);
884
+ return 0;
885
+ }
886
+ let sweep_out = "";
887
+ if (!args.check && args.archive && unarchived_complete(collect(roadmap_root)).length > 0) {
888
+ const swept = run_archival_sweep(repo_root);
889
+ sweep_out = swept.stdout;
890
+ if (swept.stderr) {
891
+ process.stderr.write(swept.stderr);
892
+ }
893
+ if (!swept.ok) {
894
+ process.stderr.write(
895
+ "\u274C The archival sweep failed \u2014 dashboard NOT regenerated. Fix the tree, then re-run.\n"
896
+ );
897
+ return 1;
898
+ }
899
+ }
900
+ const roadmaps = collect(roadmap_root), guarded = reportGuardedBaselines(roadmaps, repo_root);
901
+ const new_text = render(roadmaps, collect_bundles(repo_root), roadmap_root) + guarded.section;
902
+ const complete = unarchived_complete(roadmaps);
903
+ const pending = pending_iron_law_3(roadmaps);
904
+ const gated = merge_gated_pending(roadmaps);
905
+ const _warn_merge_gated = () => {
906
+ process.stderr.write(
907
+ "\u23F3 Merge-gated roadmaps (every open item gated on a PR) \u2014 flip + archive the moment the gating PR merges (`roadmap-progress-sync` Iron Law 1):\n"
908
+ );
909
+ for (const r of gated) {
910
+ const prs = r.merge_gated_prs.map((n) => `#${n}`).join(", ") || "PR unknown";
911
+ process.stderr.write(
912
+ ` - ${r.rel} (${r.done}/${r.total_active} done \xB7 ${r.merge_gated} merge-gated \xB7 ${prs})
913
+ `
914
+ );
915
+ }
916
+ };
917
+ if (args.check) {
918
+ const rel = _relPosix(repo_root, target);
919
+ const r = reportCheckVerdict({
920
+ verdict: evaluateDashboardOnDisk({ mode: args.mode, target, repo_root, rendered: new_text, rel }),
921
+ complete,
922
+ pending,
923
+ dashboardOnly: args.dashboard_only,
924
+ warnMergeGated: _warn_merge_gated,
925
+ gatedCount: gated.length
926
+ });
927
+ if (r.stderr) process.stderr.write(r.stderr);
928
+ if (r.stdout) process.stdout.write(r.stdout);
929
+ return guarded.problems > 0 ? 1 : r.rc;
930
+ }
931
+ fs.writeFileSync(target, new_text, { encoding: "utf-8" });
932
+ process.stdout.write(
933
+ `\u2705 Wrote ${_relPosix(repo_root, target)} \xB7 ${roadmaps.length} roadmap(s) \xB7 ${roadmaps.reduce((s, r) => s + r.done, 0)}/${roadmaps.reduce((s, r) => s + r.total_active, 0)} steps done.
934
+ `
935
+ );
936
+ if (sweep_out) {
937
+ process.stdout.write(sweep_out);
938
+ }
939
+ if (complete.length) {
940
+ process.stderr.write(
941
+ "\u26A0\uFE0F Completed roadmaps not yet archived \u2014 move to `agents/roadmaps/archive/`:\n"
942
+ );
943
+ for (const r of complete) {
944
+ process.stderr.write(` - ${r.rel}
945
+ `);
946
+ }
947
+ }
948
+ if (pending.length) {
949
+ process.stderr.write(
950
+ "\u26A0\uFE0F Iron Law 3 \u2014 roadmaps with unresolved `[~]` deferred items. Surface them and ask the user (`roadmap-management \xA7 4b`) before any archive:\n"
951
+ );
952
+ for (const r of pending) {
953
+ process.stderr.write(` - ${r.rel} (${r.deferred} deferred)
954
+ `);
955
+ }
956
+ }
957
+ if (gated.length) {
958
+ _warn_merge_gated();
959
+ }
960
+ return guarded.problems > 0 ? 1 : 0;
961
+ }
962
+ if (isCliEntry(import.meta.url, "update_roadmap_progress")) {
963
+ try {
964
+ process.exitCode = main();
965
+ } catch (exc) {
966
+ if (exc instanceof ArgparseExit) {
967
+ process.exitCode = exc.code;
968
+ } else {
969
+ throw exc;
970
+ }
971
+ }
972
+ }
973
+
974
+ export {
975
+ CHECKBOX_RE,
976
+ PHASE_RE,
977
+ TITLE_RE,
978
+ FRONTMATTER_RE,
979
+ _pyRound,
980
+ PhaseStats,
981
+ RoadmapStats,
982
+ parse_frontmatter,
983
+ is_unscheduled,
984
+ is_roadmap_candidate,
985
+ count_checkboxes,
986
+ blocker_is_resolved,
987
+ blocker_class,
988
+ parse_blockers,
989
+ blocker_needs_user,
990
+ parse_roadmap,
991
+ bar,
992
+ collect,
993
+ unarchived_complete,
994
+ merge_gated_pending,
995
+ pending_iron_law_3,
996
+ collect_bundles,
997
+ render,
998
+ main
999
+ };