@design-ai/cli 4.55.0 → 4.57.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 (301) hide show
  1. package/.claude-plugin/plugin.json +109 -25
  2. package/CHANGELOG.md +75 -0
  3. package/README.ko.md +15 -32
  4. package/README.md +18 -31
  5. package/cli/bin/design-ai.mjs +1 -0
  6. package/cli/commands/help.mjs +7 -4
  7. package/cli/commands/index.mjs +292 -0
  8. package/cli/commands/learn-help.mjs +149 -0
  9. package/cli/commands/learn-print-profile.mjs +373 -0
  10. package/cli/commands/learn-print-restore.mjs +349 -0
  11. package/cli/commands/learn-print-signals.mjs +453 -0
  12. package/cli/commands/learn.mjs +60 -1281
  13. package/cli/commands/pack.mjs +7 -3
  14. package/cli/commands/prompt.mjs +7 -3
  15. package/cli/commands/search.mjs +94 -1
  16. package/cli/lib/dispatch.mjs +3 -0
  17. package/cli/lib/embedding-index.mjs +199 -0
  18. package/cli/lib/embedding-provider.mjs +121 -0
  19. package/cli/lib/embedding-rerank.mjs +52 -0
  20. package/cli/lib/learn-args.mjs +490 -0
  21. package/cli/lib/learn-backup.mjs +748 -0
  22. package/cli/lib/learn-curation.mjs +612 -0
  23. package/cli/lib/learn-eval.mjs +459 -0
  24. package/cli/lib/learn-profile.mjs +763 -0
  25. package/cli/lib/learn-select.mjs +223 -0
  26. package/cli/lib/learn-shared.mjs +158 -0
  27. package/cli/lib/learn-test-support.mjs +218 -0
  28. package/cli/lib/learn-usage.mjs +360 -0
  29. package/cli/lib/learn.mjs +83 -3694
  30. package/cli/lib/lexical.mjs +137 -0
  31. package/cli/lib/local-config.mjs +110 -0
  32. package/cli/lib/mcp-server.mjs +221 -13
  33. package/cli/lib/pack.mjs +31 -4
  34. package/cli/lib/prompt.mjs +42 -4
  35. package/cli/lib/recall.mjs +193 -0
  36. package/cli/lib/retrieval-index.mjs +0 -0
  37. package/cli/lib/search-ranked.mjs +170 -0
  38. package/cli/lib/search.mjs +11 -1
  39. package/cli/lib/signals-backlog-commands.mjs +673 -0
  40. package/cli/lib/signals-backlog.mjs +361 -0
  41. package/cli/lib/signals-eval.mjs +176 -0
  42. package/cli/lib/signals-readiness.mjs +247 -0
  43. package/cli/lib/signals-registry.mjs +377 -0
  44. package/cli/lib/signals-render.mjs +478 -0
  45. package/cli/lib/signals-shared.mjs +75 -0
  46. package/cli/lib/signals.mjs +16 -2337
  47. package/cli/lib/site-analysis.mjs +297 -0
  48. package/cli/lib/site-args.mjs +433 -0
  49. package/cli/lib/site-bundle-build.mjs +127 -0
  50. package/cli/lib/site-bundle-check.mjs +454 -0
  51. package/cli/lib/site-bundle-commands.mjs +95 -0
  52. package/cli/lib/site-bundle-compare.mjs +157 -0
  53. package/cli/lib/site-bundle-contract.mjs +79 -0
  54. package/cli/lib/site-bundle-files.mjs +87 -0
  55. package/cli/lib/site-bundle-handoff-expected.mjs +173 -0
  56. package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
  57. package/cli/lib/site-bundle-handoff-runbook-actions.mjs +113 -0
  58. package/cli/lib/site-bundle-handoff-runbook-evidence-fields.mjs +164 -0
  59. package/cli/lib/site-bundle-handoff-runbook-evidence.mjs +334 -0
  60. package/cli/lib/site-bundle-handoff-runbook-format.mjs +31 -0
  61. package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
  62. package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
  63. package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
  64. package/cli/lib/site-bundle-handoff-runbook-stage-metadata.mjs +84 -0
  65. package/cli/lib/site-bundle-handoff-runbook.mjs +444 -0
  66. package/cli/lib/site-bundle-handoff-summary.mjs +183 -0
  67. package/cli/lib/site-bundle-handoff.mjs +271 -0
  68. package/cli/lib/site-bundle-readme.mjs +98 -0
  69. package/cli/lib/site-bundle-repair-report.mjs +143 -0
  70. package/cli/lib/site-bundle-repair.mjs +68 -0
  71. package/cli/lib/site-content.mjs +399 -0
  72. package/cli/lib/site-evidence.mjs +35 -0
  73. package/cli/lib/site-mcp-commands.mjs +28 -0
  74. package/cli/lib/site-mcp-probes.mjs +159 -0
  75. package/cli/lib/site-mcp-readiness.mjs +157 -0
  76. package/cli/lib/site-mcp-report.mjs +324 -0
  77. package/cli/lib/site-next-actions.mjs +333 -0
  78. package/cli/lib/site-options.mjs +104 -0
  79. package/cli/lib/site-prompts.mjs +332 -0
  80. package/cli/lib/site-starter.mjs +153 -0
  81. package/cli/lib/site-strings.mjs +23 -0
  82. package/cli/lib/site-tasks.mjs +93 -0
  83. package/cli/lib/site-test-support.mjs +68 -0
  84. package/cli/lib/site-workflow-graph.mjs +309 -0
  85. package/cli/lib/site-workspace.mjs +492 -0
  86. package/cli/lib/site.mjs +108 -6617
  87. package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
  88. package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
  89. package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
  90. package/cli/lib/skill-proposals-generate.mjs +298 -0
  91. package/cli/lib/skill-proposals-render.mjs +532 -0
  92. package/cli/lib/skill-proposals-review.mjs +262 -0
  93. package/cli/lib/skill-proposals.mjs +15 -2046
  94. package/cli/lib/workspace-args.mjs +83 -0
  95. package/cli/lib/workspace-git.mjs +169 -0
  96. package/cli/lib/workspace-learning.mjs +483 -0
  97. package/cli/lib/workspace-repo.mjs +139 -0
  98. package/cli/lib/workspace-report.mjs +283 -0
  99. package/cli/lib/workspace-test-support.mjs +99 -0
  100. package/cli/lib/workspace.mjs +30 -1056
  101. package/docs/AI-LEARNING-PHASE2.md +215 -0
  102. package/docs/DISTRIBUTION.ko.md +35 -6
  103. package/docs/DISTRIBUTION.md +35 -8
  104. package/docs/NEXT-SURFACE-DECISION.md +125 -0
  105. package/docs/PRODUCT-READINESS.md +3 -3
  106. package/docs/RELEASE-CHECKLIST.md +20 -3
  107. package/docs/RELEASE-GATES.ko.md +35 -0
  108. package/docs/RELEASE-GATES.md +234 -0
  109. package/docs/ROADMAP.md +2232 -8
  110. package/docs/external-status.md +20 -7
  111. package/docs/inspection-20260630.md +169 -0
  112. package/docs/integrations/design-ai-mcp-server.md +35 -3
  113. package/docs/integrations/vscode-walkthrough.ko.md +3 -3
  114. package/docs/integrations/vscode-walkthrough.md +3 -3
  115. package/docs/reference/ant-design.md +413 -0
  116. package/docs/reference/awesome-design-md.md +439 -0
  117. package/docs/reference/mui.md +783 -0
  118. package/docs/reference/shadcn-ui.md +298 -0
  119. package/docs/site-overrides/main.html +1 -1
  120. package/examples/component-accordion-actions.md +5 -5
  121. package/examples/component-accordion-details.md +5 -5
  122. package/examples/component-accordion-summary.md +5 -5
  123. package/examples/component-accordion.md +3 -3
  124. package/examples/component-affix.md +1 -1
  125. package/examples/component-alert-dialog.md +1 -1
  126. package/examples/component-alert-title.md +1 -1
  127. package/examples/component-alert.md +3 -3
  128. package/examples/component-anchor.md +1 -1
  129. package/examples/component-app-bar.md +1 -1
  130. package/examples/component-aspect-ratio.md +1 -1
  131. package/examples/component-auto-complete.md +2 -2
  132. package/examples/component-avatar-group.md +5 -5
  133. package/examples/component-avatar.md +3 -3
  134. package/examples/component-back-top.md +1 -1
  135. package/examples/component-backdrop.md +1 -1
  136. package/examples/component-badge.md +3 -3
  137. package/examples/component-border-beam.md +3 -3
  138. package/examples/component-bottom-navigation.md +1 -1
  139. package/examples/component-box.md +1 -1
  140. package/examples/component-breadcrumb.md +3 -3
  141. package/examples/component-button-base.md +3 -3
  142. package/examples/component-button-group.md +2 -2
  143. package/examples/component-button.md +3 -3
  144. package/examples/component-calendar.md +2 -2
  145. package/examples/component-card-actions.md +1 -1
  146. package/examples/component-card-content.md +1 -1
  147. package/examples/component-card-header.md +1 -1
  148. package/examples/component-card-media.md +1 -1
  149. package/examples/component-card.md +3 -3
  150. package/examples/component-carousel.md +2 -2
  151. package/examples/component-cascader.md +1 -1
  152. package/examples/component-chart.md +1 -1
  153. package/examples/component-checkbox.md +3 -3
  154. package/examples/component-click-away-listener.md +1 -1
  155. package/examples/component-code.md +1 -1
  156. package/examples/component-collapsible.md +1 -1
  157. package/examples/component-color-picker.md +1 -1
  158. package/examples/component-combobox.md +1 -1
  159. package/examples/component-command.md +1 -1
  160. package/examples/component-config-provider.md +3 -3
  161. package/examples/component-context-menu.md +1 -1
  162. package/examples/component-css-baseline.md +3 -3
  163. package/examples/component-date-picker.md +2 -2
  164. package/examples/component-descriptions.md +1 -1
  165. package/examples/component-dialog-actions.md +1 -1
  166. package/examples/component-dialog-content-text.md +1 -1
  167. package/examples/component-dialog-content.md +1 -1
  168. package/examples/component-dialog-title.md +1 -1
  169. package/examples/component-dialog.md +2 -2
  170. package/examples/component-divider.md +3 -3
  171. package/examples/component-drawer.md +3 -3
  172. package/examples/component-dropdown.md +3 -3
  173. package/examples/component-empty.md +2 -2
  174. package/examples/component-fade.md +1 -1
  175. package/examples/component-field.md +1 -1
  176. package/examples/component-filled-input.md +1 -1
  177. package/examples/component-flex.md +1 -1
  178. package/examples/component-float-button.md +2 -2
  179. package/examples/component-form-control-label.md +1 -1
  180. package/examples/component-form-control.md +1 -1
  181. package/examples/component-form-controls.md +1 -1
  182. package/examples/component-form-group.md +1 -1
  183. package/examples/component-form-helper-text.md +1 -1
  184. package/examples/component-form-label.md +1 -1
  185. package/examples/component-form.md +2 -2
  186. package/examples/component-grid.md +2 -2
  187. package/examples/component-grow.md +1 -1
  188. package/examples/component-hover-card.md +1 -1
  189. package/examples/component-icon-button.md +1 -1
  190. package/examples/component-icon.md +1 -1
  191. package/examples/component-image-list.md +1 -1
  192. package/examples/component-image.md +1 -1
  193. package/examples/component-input-adornment.md +1 -1
  194. package/examples/component-input-base.md +1 -1
  195. package/examples/component-input-number.md +1 -1
  196. package/examples/component-input-otp.md +2 -2
  197. package/examples/component-input.md +3 -3
  198. package/examples/component-item.md +1 -1
  199. package/examples/component-label.md +1 -1
  200. package/examples/component-layout.md +1 -1
  201. package/examples/component-link.md +1 -1
  202. package/examples/component-list-item-avatar.md +1 -1
  203. package/examples/component-list-item-button.md +1 -1
  204. package/examples/component-list-item-icon.md +1 -1
  205. package/examples/component-list-item-text.md +1 -1
  206. package/examples/component-list-item.md +1 -1
  207. package/examples/component-list-subheader.md +1 -1
  208. package/examples/component-list.md +2 -2
  209. package/examples/component-masonry.md +1 -1
  210. package/examples/component-mentions.md +1 -1
  211. package/examples/component-menu-item.md +1 -1
  212. package/examples/component-menu-list.md +1 -1
  213. package/examples/component-menu.md +1 -1
  214. package/examples/component-menubar.md +1 -1
  215. package/examples/component-message.md +1 -1
  216. package/examples/component-mobile-stepper.md +1 -1
  217. package/examples/component-modal.md +5 -5
  218. package/examples/component-navigation-menu.md +1 -1
  219. package/examples/component-notification.md +1 -1
  220. package/examples/component-outlined-input.md +1 -1
  221. package/examples/component-pagination.md +3 -3
  222. package/examples/component-paper.md +1 -1
  223. package/examples/component-popconfirm.md +1 -1
  224. package/examples/component-popover.md +3 -3
  225. package/examples/component-popper.md +1 -1
  226. package/examples/component-progress.md +2 -2
  227. package/examples/component-qr-code.md +1 -1
  228. package/examples/component-radio.md +3 -3
  229. package/examples/component-rate.md +2 -2
  230. package/examples/component-resizable.md +1 -1
  231. package/examples/component-result.md +1 -1
  232. package/examples/component-scroll-area.md +1 -1
  233. package/examples/component-segmented.md +3 -3
  234. package/examples/component-select.md +3 -3
  235. package/examples/component-separator.md +1 -1
  236. package/examples/component-sheet.md +1 -1
  237. package/examples/component-sidebar.md +1 -1
  238. package/examples/component-skeleton.md +3 -3
  239. package/examples/component-slide.md +1 -1
  240. package/examples/component-slider.md +3 -3
  241. package/examples/component-snackbar-content.md +1 -1
  242. package/examples/component-snackbar.md +1 -1
  243. package/examples/component-sonner.md +1 -1
  244. package/examples/component-space.md +1 -1
  245. package/examples/component-speed-dial-action.md +1 -1
  246. package/examples/component-speed-dial.md +1 -1
  247. package/examples/component-spin.md +2 -2
  248. package/examples/component-spinner.md +1 -1
  249. package/examples/component-splitter.md +2 -2
  250. package/examples/component-stack.md +1 -1
  251. package/examples/component-statistic.md +1 -1
  252. package/examples/component-step-button.md +4 -4
  253. package/examples/component-step-connector.md +4 -4
  254. package/examples/component-step-content.md +1 -1
  255. package/examples/component-step-icon.md +1 -1
  256. package/examples/component-step-label.md +1 -1
  257. package/examples/component-step.md +2 -2
  258. package/examples/component-steps.md +2 -2
  259. package/examples/component-swipeable-drawer.md +1 -1
  260. package/examples/component-switch.md +3 -3
  261. package/examples/component-tab-scroll-button.md +5 -5
  262. package/examples/component-tab.md +1 -1
  263. package/examples/component-table-body.md +1 -1
  264. package/examples/component-table-cell.md +1 -1
  265. package/examples/component-table-container.md +1 -1
  266. package/examples/component-table-footer.md +1 -1
  267. package/examples/component-table-head.md +1 -1
  268. package/examples/component-table-pagination.md +1 -1
  269. package/examples/component-table-row.md +1 -1
  270. package/examples/component-table-sort-label.md +1 -1
  271. package/examples/component-table.md +2 -2
  272. package/examples/component-tabs.md +3 -3
  273. package/examples/component-tag-badge.md +2 -2
  274. package/examples/component-tag.md +1 -1
  275. package/examples/component-textarea-autosize.md +1 -1
  276. package/examples/component-textarea.md +1 -1
  277. package/examples/component-time-picker.md +1 -1
  278. package/examples/component-timeline.md +1 -1
  279. package/examples/component-toast.md +4 -4
  280. package/examples/component-toggle-button.md +1 -1
  281. package/examples/component-toggle.md +1 -1
  282. package/examples/component-toolbar.md +1 -1
  283. package/examples/component-tooltip.md +3 -3
  284. package/examples/component-tour.md +1 -1
  285. package/examples/component-transfer.md +1 -1
  286. package/examples/component-tree-select.md +1 -1
  287. package/examples/component-tree.md +1 -1
  288. package/examples/component-typography.md +2 -2
  289. package/examples/component-upload.md +1 -1
  290. package/examples/component-watermark.md +1 -1
  291. package/examples/component-zoom.md +1 -1
  292. package/knowledge/COVERAGE.md +4 -3
  293. package/knowledge/components/INDEX.md +202 -202
  294. package/knowledge/patterns/brand-references.md +72 -72
  295. package/package.json +1 -1
  296. package/tools/audit/local-ci.py +16 -1
  297. package/tools/audit/package-smoke.py +717 -9
  298. package/tools/audit/registry-smoke.py +912 -10
  299. package/tools/audit/release-metadata.py +47 -1
  300. package/tools/audit/smoke_assertions.py +998 -35
  301. package/tools/migrations/refs-links-to-reference-pages.py +179 -0
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env python3
2
+ """One-shot migration: retarget markdown links into the gitignored refs/
3
+ mirror at the generated upstream reference pages (docs/reference/*.md).
4
+
5
+ Reference-link policy (docs/PRODUCT-READINESS.md): corpus pages must not
6
+ link into refs/ directly. Link text is preserved (it stays the refs/ path,
7
+ which documents provenance); only the link target changes.
8
+
9
+ Behavior:
10
+ - Scans every .md file in examples/ for `](../refs/...)`-style links
11
+ (any number of leading `../` segments). knowledge/ is excluded: its
12
+ refs-linking files are regenerated by tools/extractors/.
13
+ - Maps each target to docs/reference/<source>.md, appending the entry
14
+ anchor (`#button`, `#airbnb`, ...) when the path identifies an entry
15
+ that exists on the generated page; otherwise links to the page top.
16
+ - Skips fenced code blocks and inline code spans (illustrative links).
17
+ - Idempotent: rewritten links no longer match the refs/ pattern.
18
+
19
+ Usage:
20
+ python3 tools/migrations/refs-links-to-reference-pages.py # dry-run
21
+ python3 tools/migrations/refs-links-to-reference-pages.py --write # apply
22
+
23
+ Run tools/extractors/reference_pages.py first — anchors are validated
24
+ against the generated pages.
25
+
26
+ After running, verify with:
27
+ npm run audit && npm run ci:local
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import argparse
32
+ import os
33
+ import re
34
+ from pathlib import Path
35
+
36
+ ROOT = Path(__file__).resolve().parents[2]
37
+ EXAMPLES = ROOT / "examples"
38
+ REFERENCE_DIR = ROOT / "docs/reference"
39
+
40
+ LINK_RE = re.compile(r"\]\(((?:\.\./)+)(refs/[^)#\s]+)\)")
41
+ HEADING_RE = re.compile(r"^## (.+)$", re.MULTILINE)
42
+ INLINE_CODE_RE = re.compile(r"`[^`]*`")
43
+
44
+ # refs/<source>/ → (reference page stem, entry-path prefix inside the source)
45
+ SOURCE_PAGES = {
46
+ "ant-design": "components/",
47
+ "mui": "packages/mui-material/src/",
48
+ "shadcn-ui": "apps/v4/registry/new-york-v4/ui/",
49
+ "awesome-design-md": "design-md/",
50
+ }
51
+
52
+
53
+ def to_kebab(name: str) -> str:
54
+ out = []
55
+ for i, c in enumerate(name):
56
+ if c.isupper() and i > 0 and not name[i - 1].isupper():
57
+ out.append("-")
58
+ out.append(c.lower())
59
+ return "".join(out)
60
+
61
+
62
+ def anchor_slug(name: str) -> str:
63
+ """Match reference_pages.anchor_slug (Python-Markdown toc slugify)."""
64
+ value = to_kebab(name)
65
+ value = re.sub(r"[^\w\s-]", "", value)
66
+ return re.sub(r"\s+", "-", value.strip())
67
+
68
+
69
+ def load_page_anchors() -> dict[str, set[str]]:
70
+ anchors: dict[str, set[str]] = {}
71
+ for source in SOURCE_PAGES:
72
+ page = REFERENCE_DIR / f"{source}.md"
73
+ if not page.exists():
74
+ raise SystemExit(
75
+ f"Missing {page.relative_to(ROOT)} — run "
76
+ "tools/extractors/reference_pages.py first"
77
+ )
78
+ anchors[source] = set(HEADING_RE.findall(page.read_text(encoding="utf-8")))
79
+ return anchors
80
+
81
+
82
+ def map_target(refs_path: str, anchors: dict[str, set[str]]) -> str | None:
83
+ """Map a refs/... path to `<source>.md[#anchor]`, or None if unknown."""
84
+ parts = refs_path.split("/")
85
+ if len(parts) < 2 or parts[0] != "refs":
86
+ return None
87
+ source = parts[1]
88
+ entry_prefix = SOURCE_PAGES.get(source)
89
+ if entry_prefix is None:
90
+ return None
91
+
92
+ page = f"{source}.md"
93
+ remainder = "/".join(parts[2:])
94
+ if remainder.startswith(entry_prefix):
95
+ entry = remainder.removeprefix(entry_prefix).split("/")[0]
96
+ stem = entry.rsplit(".", 1)[0] if "." in entry else entry
97
+ for candidate in (entry, stem):
98
+ slug = anchor_slug(candidate) if candidate else ""
99
+ if slug and slug in anchors[source]:
100
+ return f"{page}#{slug}"
101
+ return page
102
+
103
+
104
+ def rewrite_line(line: str, rel_prefix: str, anchors: dict[str, set[str]],
105
+ unmapped: list[str]) -> str:
106
+ def replace(match: re.Match[str]) -> str:
107
+ refs_path = match.group(2)
108
+ target = map_target(refs_path, anchors)
109
+ if target is None:
110
+ unmapped.append(refs_path)
111
+ return match.group(0)
112
+ return f"]({rel_prefix}/{target})"
113
+
114
+ # Protect inline code spans: substitute only outside them.
115
+ pieces: list[str] = []
116
+ last = 0
117
+ for code in INLINE_CODE_RE.finditer(line):
118
+ pieces.append(LINK_RE.sub(replace, line[last:code.start()]))
119
+ pieces.append(code.group(0))
120
+ last = code.end()
121
+ pieces.append(LINK_RE.sub(replace, line[last:]))
122
+ return "".join(pieces)
123
+
124
+
125
+ def rewrite_file(path: Path, anchors: dict[str, set[str]],
126
+ unmapped: list[str]) -> tuple[str, int]:
127
+ rel_prefix = os.path.relpath(REFERENCE_DIR, path.parent).replace(os.sep, "/")
128
+ text = path.read_text(encoding="utf-8")
129
+ out_lines: list[str] = []
130
+ changed = 0
131
+ in_fence = False
132
+ for line in text.splitlines(keepends=True):
133
+ if line.strip().startswith("```"):
134
+ in_fence = not in_fence
135
+ out_lines.append(line)
136
+ continue
137
+ if in_fence:
138
+ out_lines.append(line)
139
+ continue
140
+ new_line = rewrite_line(line, rel_prefix, anchors, unmapped)
141
+ if new_line != line:
142
+ changed += len(LINK_RE.findall(line)) - len(LINK_RE.findall(new_line))
143
+ out_lines.append(new_line)
144
+ return "".join(out_lines), changed
145
+
146
+
147
+ def main() -> int:
148
+ parser = argparse.ArgumentParser(description=__doc__)
149
+ parser.add_argument("--write", action="store_true", help="Apply changes.")
150
+ args = parser.parse_args()
151
+
152
+ anchors = load_page_anchors()
153
+ unmapped: list[str] = []
154
+ total_links = 0
155
+ total_files = 0
156
+
157
+ for path in sorted(EXAMPLES.rglob("*.md")):
158
+ new_text, changed = rewrite_file(path, anchors, unmapped)
159
+ if changed == 0:
160
+ continue
161
+ total_links += changed
162
+ total_files += 1
163
+ if args.write:
164
+ path.write_text(new_text, encoding="utf-8")
165
+ print(f" {'rewrote' if args.write else 'would rewrite'} "
166
+ f"{changed} link(s) in {path.relative_to(ROOT)}")
167
+
168
+ mode = "applied" if args.write else "dry-run"
169
+ print(f"\n{mode}: {total_links} link(s) across {total_files} file(s)")
170
+ if unmapped:
171
+ for refs_path in sorted(set(unmapped)):
172
+ print(f" UNMAPPED: {refs_path}")
173
+ print(f"error: {len(unmapped)} link(s) target unknown refs/ sources")
174
+ return 1
175
+ return 0
176
+
177
+
178
+ if __name__ == "__main__":
179
+ raise SystemExit(main())