ai-design-context 0.4.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 (288) hide show
  1. package/AGENTS.md +156 -0
  2. package/CHANGELOG.md +71 -0
  3. package/CODE_OF_CONDUCT.md +29 -0
  4. package/CONTRIBUTING.md +128 -0
  5. package/LICENSE +21 -0
  6. package/README.md +283 -0
  7. package/RELEASE_NOTES_v0.1.0.md +45 -0
  8. package/RELEASE_NOTES_v0.3.0.md +47 -0
  9. package/RELEASE_NOTES_v0.4.0.md +29 -0
  10. package/RELEASE_READINESS.md +107 -0
  11. package/ROADMAP.md +60 -0
  12. package/SECURITY.md +40 -0
  13. package/assets/logo.svg +9 -0
  14. package/benchmarks/README.md +19 -0
  15. package/benchmarks/chat.md +37 -0
  16. package/benchmarks/mobile-home-screen.md +38 -0
  17. package/benchmarks/notes-app.md +37 -0
  18. package/benchmarks/settings.md +37 -0
  19. package/benchmarks/shopping-list.md +37 -0
  20. package/benchmarks/todo-app.md +144 -0
  21. package/checklists/DESIGN_QA.md +102 -0
  22. package/checklists/GENERATED_INDEX.md +7 -0
  23. package/docs/AGENT_CONTEXT.md +111 -0
  24. package/docs/BENCHMARK.md +170 -0
  25. package/docs/DESIGNLINT_READINESS.md +110 -0
  26. package/docs/DESIGN_SYSTEM.md +23 -0
  27. package/docs/EVALUATION_RUBRIC.md +154 -0
  28. package/docs/GITHUB_SETUP.md +28 -0
  29. package/docs/GLOSSARY.md +45 -0
  30. package/docs/INDEX.md +109 -0
  31. package/docs/KNOWLEDGE_ENGINE.md +594 -0
  32. package/docs/MOBILE_FIRST.md +18 -0
  33. package/docs/NPM_RELEASE.md +43 -0
  34. package/docs/PATTERN_SPEC.md +113 -0
  35. package/docs/PHILOSOPHY.md +25 -0
  36. package/docs/PRODUCT_THINKING.md +26 -0
  37. package/docs/STYLE_GUIDE.md +49 -0
  38. package/docs/TRACEABILITY.md +45 -0
  39. package/evidence/README.md +64 -0
  40. package/evidence/TEMPLATE.md +46 -0
  41. package/evidence/chat/.gitkeep +1 -0
  42. package/evidence/mobile-home/.gitkeep +1 -0
  43. package/evidence/notes/.gitkeep +1 -0
  44. package/evidence/settings/.gitkeep +1 -0
  45. package/evidence/shopping/.gitkeep +1 -0
  46. package/evidence/todo/.gitkeep +1 -0
  47. package/evidence/todo/2026-06-25-codex-gpt-5/EVALUATION.md +66 -0
  48. package/evidence/todo/2026-06-25-codex-gpt-5/README.md +40 -0
  49. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/generated-output.md +163 -0
  50. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/metadata.json +38 -0
  51. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/notes.md +20 -0
  52. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/prompt.md +16 -0
  53. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/scores.md +18 -0
  54. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/generated-output.md +98 -0
  55. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/metadata.json +19 -0
  56. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/notes.md +19 -0
  57. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/prompt.md +9 -0
  58. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/scores.md +18 -0
  59. package/evidence/todo/2026-09-28-codex-paired-todo/EVALUATION.md +70 -0
  60. package/evidence/todo/2026-09-28-codex-paired-todo/README.md +52 -0
  61. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/GENERATION_NOTES.md +133 -0
  62. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/app.js +289 -0
  63. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/context-preserving-preview.json +443 -0
  64. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/daily-home-surface.json +373 -0
  65. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/quick-capture.json +301 -0
  66. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/sources-read.txt +36 -0
  67. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/generated-output.md +11 -0
  68. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/index.html +64 -0
  69. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/launch-prompt.md +1 -0
  70. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/metadata.json +55 -0
  71. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/notes.md +15 -0
  72. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/parent-status-message.md +1 -0
  73. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/prompt.md +49 -0
  74. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/runtime-checks.json +776 -0
  75. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/scores.md +20 -0
  76. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-completed.png +0 -0
  77. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-default.png +0 -0
  78. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail-reduced-motion.png +0 -0
  79. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail.png +0 -0
  80. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-empty.png +0 -0
  81. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-keyboard-focus.png +0 -0
  82. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-save-error.png +0 -0
  83. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saved.png +0 -0
  84. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saving.png +0 -0
  85. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-validation.png +0 -0
  86. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-completed.png +0 -0
  87. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-default.png +0 -0
  88. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail-reduced-motion.png +0 -0
  89. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail.png +0 -0
  90. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-empty.png +0 -0
  91. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-keyboard-focus.png +0 -0
  92. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-save-error.png +0 -0
  93. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saved.png +0 -0
  94. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saving.png +0 -0
  95. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-validation.png +0 -0
  96. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/source-hashes.json +5 -0
  97. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/styles.css +122 -0
  98. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/GENERATION_NOTES.md +86 -0
  99. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/app.js +303 -0
  100. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/generated-output.md +11 -0
  101. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/index.html +71 -0
  102. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/launch-prompt.md +1 -0
  103. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/metadata.json +55 -0
  104. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/notes.md +15 -0
  105. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/parent-status-message.md +1 -0
  106. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/prompt.md +49 -0
  107. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/runtime-checks.json +772 -0
  108. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/scores.md +20 -0
  109. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-completed.png +0 -0
  110. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-default.png +0 -0
  111. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail-reduced-motion.png +0 -0
  112. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail.png +0 -0
  113. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-empty.png +0 -0
  114. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-keyboard-focus.png +0 -0
  115. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-save-error.png +0 -0
  116. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saved.png +0 -0
  117. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saving.png +0 -0
  118. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-validation.png +0 -0
  119. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-completed.png +0 -0
  120. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-default.png +0 -0
  121. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail-reduced-motion.png +0 -0
  122. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail.png +0 -0
  123. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-empty.png +0 -0
  124. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-keyboard-focus.png +0 -0
  125. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-save-error.png +0 -0
  126. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saved.png +0 -0
  127. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saving.png +0 -0
  128. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-validation.png +0 -0
  129. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/source-hashes.json +5 -0
  130. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/styles.css +9 -0
  131. package/evidence/todo/2026-09-28-codex-paired-todo/capture-ai-design-rules.js +60 -0
  132. package/evidence/todo/2026-09-28-codex-paired-todo/capture-baseline.js +60 -0
  133. package/evidence/todo/2026-09-28-codex-paired-todo/common/brief.md +35 -0
  134. package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge-files.json +127 -0
  135. package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge.patch +890 -0
  136. package/evidence/todo/2026-09-28-codex-paired-todo/common/review-context.json +550 -0
  137. package/evidence/todo/2026-09-28-codex-paired-todo/common/seed.json +86 -0
  138. package/evidence/todo/2026-09-28-codex-paired-todo/common/setup.json +47 -0
  139. package/evidence/todo/2026-09-28-codex-paired-todo/common/technical-envelope.md +11 -0
  140. package/evidence/todo/2026-09-28-codex-paired-todo/layout-metrics.json +133 -0
  141. package/evidence/todo/2026-09-28-codex-paired-todo/layout-probe.js +15 -0
  142. package/examples/GENERATED_INDEX.md +8 -0
  143. package/examples/INDEX.md +16 -0
  144. package/examples/README.md +15 -0
  145. package/examples/consequential-action-confirmation-reference.md +83 -0
  146. package/examples/todo-app-benchmark.md +117 -0
  147. package/examples/todo-reference/README.md +29 -0
  148. package/examples/todo-reference/app.js +154 -0
  149. package/examples/todo-reference/favicon.svg +4 -0
  150. package/examples/todo-reference/index.html +67 -0
  151. package/examples/todo-reference/review-evidence/2026-07-15/desktop-default.png +0 -0
  152. package/examples/todo-reference/review-evidence/2026-07-15/desktop-detail.png +0 -0
  153. package/examples/todo-reference/review-evidence/2026-07-15/mobile-detail.png +0 -0
  154. package/examples/todo-reference/review-evidence/2026-07-15/mobile-error.png +0 -0
  155. package/examples/todo-reference/styles.css +241 -0
  156. package/graph/GENERATED_GRAPH.md +71 -0
  157. package/observations/README.md +23 -0
  158. package/observations/accessibility/reduced-interaction-motion.md +49 -0
  159. package/observations/accessibility/textual-error-recovery.md +50 -0
  160. package/observations/accessibility/visible-keyboard-focus.md +49 -0
  161. package/observations/ai/agent-consequential-action-controls.md +56 -0
  162. package/observations/ai/agent-work-visibility-and-intervention.md +51 -0
  163. package/observations/ai/figma-agent-context-and-skills.md +52 -0
  164. package/observations/finance/high-risk-wallet-recovery-flows.md +55 -0
  165. package/observations/finance/market-interface-adaptive-trading-surfaces.md +55 -0
  166. package/observations/interaction/chrome-scoped-view-transitions.md +52 -0
  167. package/observations/interaction/figma-motion-as-system-capability.md +53 -0
  168. package/observations/mobile/mobile-bottom-sheet-choice-contracts.md +55 -0
  169. package/observations/performance/reserved-loading-space.md +51 -0
  170. package/observations/product-repos/pinned-ai-design-rules-integration.md +49 -0
  171. package/observations/visual/apple-liquid-glass-functional-layer.md +52 -0
  172. package/observations/visual/chrome-content-first-adaptive-ui.md +51 -0
  173. package/observations/visual/material-3-expressive-system.md +52 -0
  174. package/package.json +65 -0
  175. package/patterns/GENERATED_INDEX.md +13 -0
  176. package/patterns/INDEX.md +41 -0
  177. package/patterns/README.md +5 -0
  178. package/patterns/consequential-action-confirmation.md +127 -0
  179. package/patterns/context-preserving-preview.md +145 -0
  180. package/patterns/daily-home-surface.md +134 -0
  181. package/patterns/mobile-primary-action.md +110 -0
  182. package/patterns/object-status-list.md +114 -0
  183. package/patterns/progressive-detail.md +113 -0
  184. package/patterns/quick-capture.md +132 -0
  185. package/prompts/CONSEQUENTIAL_ACTION_REVIEW.md +100 -0
  186. package/prompts/DESIGN_EVIDENCE_REVIEW.md +49 -0
  187. package/prompts/GENERATED_INDEX.md +11 -0
  188. package/prompts/INDEX.md +24 -0
  189. package/prompts/PROTOTYPE_REVIEW.md +189 -0
  190. package/prompts/QUICK_CAPTURE_STATE_REVIEW.md +74 -0
  191. package/prompts/TODO_APP_BENCHMARK.md +88 -0
  192. package/registry/objects.json +568 -0
  193. package/registry/relationships.json +972 -0
  194. package/research/GENERATED_INDEX.md +18 -0
  195. package/research/INDEX.md +40 -0
  196. package/research/accessibility/keyboard-focus-and-interaction-motion.md +62 -0
  197. package/research/accessibility/textual-error-recovery.md +57 -0
  198. package/research/performance/reserved-loading-space.md +56 -0
  199. package/research/products/apple-reminders.md +59 -0
  200. package/research/products/arc.md +61 -0
  201. package/research/products/linear.md +62 -0
  202. package/research/products/telegram.md +61 -0
  203. package/research/products/things-3.md +60 -0
  204. package/research/ux/contextual-agentic-interface.md +68 -0
  205. package/research/ux/high-risk-operational-flows.md +89 -0
  206. package/research/ux/motion-as-state-continuity.md +71 -0
  207. package/research/visual/expressive-system-ui-2026.md +75 -0
  208. package/reviews/GENERATED_INDEX.md +9 -0
  209. package/reviews/README.md +25 -0
  210. package/reviews/consequential-action-confirmation-review.md +92 -0
  211. package/reviews/todo-app-benchmark-reference-review.md +86 -0
  212. package/reviews/todo-reference-fixture-review.md +77 -0
  213. package/rules/GENERATED_INDEX.md +23 -0
  214. package/rules/INDEX.md +37 -0
  215. package/rules/accessibility/A11Y-001.md +58 -0
  216. package/rules/accessibility/A11Y-002.md +55 -0
  217. package/rules/accessibility/A11Y-003.md +54 -0
  218. package/rules/accessibility/A11Y-004.md +54 -0
  219. package/rules/ia/IA-001.md +60 -0
  220. package/rules/ia/IA-002.md +61 -0
  221. package/rules/performance/PERF-001.md +55 -0
  222. package/rules/product/PRD-001.md +61 -0
  223. package/rules/product/PRD-002.md +61 -0
  224. package/rules/ux/UX-001.md +58 -0
  225. package/rules/ux/UX-002.md +61 -0
  226. package/rules/ux/UX-003.md +60 -0
  227. package/rules/ux/UX-004.md +61 -0
  228. package/rules/ux/UX-005.md +62 -0
  229. package/rules/ux/UX-006.md +69 -0
  230. package/rules/visual/VIS-001.md +59 -0
  231. package/rules/visual/VIS-002.md +59 -0
  232. package/schema/checklist.schema.json +21 -0
  233. package/schema/common.schema.json +178 -0
  234. package/schema/observation.schema.json +21 -0
  235. package/schema/pattern.schema.json +21 -0
  236. package/schema/prompt.schema.json +21 -0
  237. package/schema/reference-project.schema.json +21 -0
  238. package/schema/research.schema.json +21 -0
  239. package/schema/review.schema.json +21 -0
  240. package/schema/rule.schema.json +21 -0
  241. package/skills/README.md +48 -0
  242. package/skills/accessibility-reviewer/SKILL.md +101 -0
  243. package/skills/agent-context/SKILL.md +34 -0
  244. package/skills/agent-context/agents/openai.yaml +4 -0
  245. package/skills/design-evidence-researcher/SKILL.md +47 -0
  246. package/skills/design-evidence-researcher/agents/openai.yaml +4 -0
  247. package/skills/design-reviewer/SKILL.md +113 -0
  248. package/skills/design-system-architect/SKILL.md +101 -0
  249. package/skills/information-architect/SKILL.md +97 -0
  250. package/skills/interaction-designer/SKILL.md +104 -0
  251. package/skills/knowledge-graph-architect/SKILL.md +96 -0
  252. package/skills/mobile-ux-expert/SKILL.md +105 -0
  253. package/skills/motion-designer/SKILL.md +100 -0
  254. package/skills/performance-reviewer/SKILL.md +97 -0
  255. package/skills/product-designer/SKILL.md +104 -0
  256. package/skills/prompt-architect/SKILL.md +105 -0
  257. package/skills/reference-driven-design/SKILL.md +119 -0
  258. package/skills/ux-reviewer/SKILL.md +99 -0
  259. package/skills/visual-designer/SKILL.md +107 -0
  260. package/starter-kit/AGENTS.md +37 -0
  261. package/starter-kit/BOOTSTRAP.md +13 -0
  262. package/starter-kit/INSTALL_WITH_AGENT.md +133 -0
  263. package/starter-kit/PROJECT_INTEGRATION.md +60 -0
  264. package/starter-kit/README.md +41 -0
  265. package/starter-kit/benchmarks/BENCHMARK_CHECKLIST.md +10 -0
  266. package/starter-kit/docs/DESIGN_DECISIONS.md +22 -0
  267. package/starter-kit/docs/INFORMATION_ARCHITECTURE.md +25 -0
  268. package/starter-kit/docs/PERSONAS.md +14 -0
  269. package/starter-kit/docs/PRD.md +32 -0
  270. package/starter-kit/docs/USER_FLOWS.md +19 -0
  271. package/starter-kit/reviews/DESIGN_REVIEW.md +49 -0
  272. package/starter-kit/templates/FEATURE_TEMPLATE.md +36 -0
  273. package/starter-kit/templates/TASK_TEMPLATE.md +22 -0
  274. package/templates/CHECKLIST_TEMPLATE.md +43 -0
  275. package/templates/OBSERVATION_TEMPLATE.md +41 -0
  276. package/templates/PATTERN_TEMPLATE.md +100 -0
  277. package/templates/PROMPT_TEMPLATE.md +46 -0
  278. package/templates/PROTOTYPE_REVIEW.md +50 -0
  279. package/templates/REFERENCE_PROJECT_TEMPLATE.md +49 -0
  280. package/templates/REVIEW_TEMPLATE.md +55 -0
  281. package/templates/RULE_TEMPLATE.md +57 -0
  282. package/tools/cli.mjs +46 -0
  283. package/tools/context.mjs +342 -0
  284. package/tools/designlint.mjs +77 -0
  285. package/tools/generate-indexes.mjs +621 -0
  286. package/tools/init.mjs +99 -0
  287. package/tools/validate-benchmark-evidence.mjs +248 -0
  288. package/tools/validate-knowledge.mjs +481 -0
@@ -0,0 +1,621 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ const generatedNotice = 'This file is generated from registry metadata. Do not edit manually.';
6
+
7
+ function readRegistry(root) {
8
+ const objects = JSON.parse(fs.readFileSync(path.join(root, 'registry/objects.json'), 'utf8')).objects;
9
+ const relationships = JSON.parse(
10
+ fs.readFileSync(path.join(root, 'registry/relationships.json'), 'utf8'),
11
+ ).relationships;
12
+
13
+ return {
14
+ objects,
15
+ relationships,
16
+ objectsById: new Map(objects.map((object) => [object.id, object])),
17
+ };
18
+ }
19
+
20
+ function escapeCell(value) {
21
+ return String(value ?? 'None').replaceAll('|', '\\|');
22
+ }
23
+
24
+ function objectLabel(object) {
25
+ if (!object) {
26
+ return 'Missing object';
27
+ }
28
+
29
+ const alias = object.alias ? `${object.alias} / ` : '';
30
+ return `${alias}${object.id} ${object.title}`;
31
+ }
32
+
33
+ function listOrNone(values) {
34
+ return values.length > 0 ? values.join('<br>') : 'None';
35
+ }
36
+
37
+ function linesOrNone(values) {
38
+ return values.length > 0 ? values.map((value) => `- ${value}`).join('\n') : 'None';
39
+ }
40
+
41
+ function table(headers, rows) {
42
+ return [
43
+ `| ${headers.map(escapeCell).join(' | ')} |`,
44
+ `| ${headers.map(() => '---').join(' | ')} |`,
45
+ ...rows.map((row) => `| ${row.map(escapeCell).join(' | ')} |`),
46
+ ].join('\n');
47
+ }
48
+
49
+ function relationshipsFor(relationships, id) {
50
+ return relationships.filter((relationship) => relationship.source === id || relationship.target === id);
51
+ }
52
+
53
+ function generateRulesIndex(registry) {
54
+ const rules = registry.objects
55
+ .filter((object) => object.object_type === 'rule')
56
+ .sort((a, b) => a.id.localeCompare(b.id));
57
+
58
+ const rows = rules.map((rule) => {
59
+ const usedByPatterns = registry.relationships
60
+ .filter(
61
+ (relationship) =>
62
+ relationship.type === 'requires' &&
63
+ relationship.target === rule.id &&
64
+ registry.objectsById.get(relationship.source)?.object_type === 'pattern',
65
+ )
66
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.source)));
67
+
68
+ const relatedObjects = relationshipsFor(registry.relationships, rule.id)
69
+ .filter((relationship) => relationship.type !== 'requires')
70
+ .map((relationship) => {
71
+ const otherId = relationship.source === rule.id ? relationship.target : relationship.source;
72
+ return `${relationship.type}: ${objectLabel(registry.objectsById.get(otherId))}`;
73
+ });
74
+
75
+ return [
76
+ rule.id,
77
+ rule.alias,
78
+ rule.title,
79
+ rule.category,
80
+ rule.status,
81
+ rule.maturity,
82
+ rule.path,
83
+ listOrNone(usedByPatterns),
84
+ listOrNone(relatedObjects),
85
+ ];
86
+ });
87
+
88
+ return [
89
+ '# Generated Rules Index',
90
+ '',
91
+ generatedNotice,
92
+ '',
93
+ table(
94
+ ['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Used By Patterns', 'Related Objects'],
95
+ rows,
96
+ ),
97
+ '',
98
+ ].join('\n');
99
+ }
100
+
101
+ function generateResearchIndex(registry) {
102
+ const researchObjects = registry.objects
103
+ .filter((object) => object.object_type === 'research')
104
+ .sort((a, b) => a.id.localeCompare(b.id));
105
+
106
+ const rows = researchObjects.map((research) => {
107
+ const derivedRules = registry.relationships
108
+ .filter(
109
+ (relationship) =>
110
+ ['derived_from', 'inspired_by'].includes(relationship.type) &&
111
+ relationship.target === research.id &&
112
+ registry.objectsById.get(relationship.source)?.object_type === 'rule',
113
+ )
114
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.source))}`);
115
+
116
+ return [
117
+ research.id,
118
+ research.alias,
119
+ research.title,
120
+ research.category,
121
+ research.status,
122
+ research.maturity,
123
+ research.path,
124
+ listOrNone(derivedRules),
125
+ ];
126
+ });
127
+
128
+ return [
129
+ '# Generated Research Index',
130
+ '',
131
+ generatedNotice,
132
+ '',
133
+ table(['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Derived Or Inspired Rules'], rows),
134
+ '',
135
+ ].join('\n');
136
+ }
137
+
138
+ function generatePatternsIndex(registry) {
139
+ const patterns = registry.objects
140
+ .filter((object) => object.object_type === 'pattern')
141
+ .sort((a, b) => a.id.localeCompare(b.id));
142
+
143
+ const rows = patterns.map((pattern) => {
144
+ const requiredRules = registry.relationships
145
+ .filter(
146
+ (relationship) =>
147
+ relationship.type === 'requires' &&
148
+ relationship.source === pattern.id &&
149
+ registry.objectsById.get(relationship.target)?.object_type === 'rule',
150
+ )
151
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
152
+
153
+ const directResearch = registry.relationships
154
+ .filter(
155
+ (relationship) =>
156
+ relationship.source === pattern.id &&
157
+ ['derived_from', 'cites', 'inspired_by'].includes(relationship.type) &&
158
+ registry.objectsById.get(relationship.target)?.object_type === 'research',
159
+ )
160
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
161
+
162
+ const researchFromRules = requiredRules.length
163
+ ? registry.relationships
164
+ .filter(
165
+ (relationship) =>
166
+ ['derived_from', 'inspired_by'].includes(relationship.type) &&
167
+ registry.objectsById.get(relationship.source)?.object_type === 'rule' &&
168
+ registry.objectsById.get(relationship.target)?.object_type === 'research' &&
169
+ registry.relationships.some(
170
+ (requiredRelationship) =>
171
+ requiredRelationship.type === 'requires' &&
172
+ requiredRelationship.source === pattern.id &&
173
+ requiredRelationship.target === relationship.source,
174
+ ),
175
+ )
176
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)))
177
+ : [];
178
+
179
+ const relatedResearch = [...new Set([...directResearch, ...researchFromRules])];
180
+
181
+ return [
182
+ pattern.id,
183
+ pattern.alias,
184
+ pattern.title,
185
+ pattern.category,
186
+ pattern.status,
187
+ pattern.maturity,
188
+ pattern.path,
189
+ listOrNone(requiredRules),
190
+ listOrNone(relatedResearch),
191
+ ];
192
+ });
193
+
194
+ return [
195
+ '# Generated Patterns Index',
196
+ '',
197
+ generatedNotice,
198
+ '',
199
+ table(
200
+ ['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Required Rules', 'Related Research'],
201
+ rows,
202
+ ),
203
+ '',
204
+ ].join('\n');
205
+ }
206
+
207
+ function generatePromptsIndex(registry) {
208
+ const prompts = registry.objects
209
+ .filter((object) => object.object_type === 'prompt')
210
+ .sort((a, b) => a.id.localeCompare(b.id));
211
+
212
+ const rows = prompts.map((prompt) => {
213
+ const patternReferences = registry.relationships
214
+ .filter(
215
+ (relationship) =>
216
+ ['requires', 'related_to'].includes(relationship.type) &&
217
+ relationship.source === prompt.id &&
218
+ registry.objectsById.get(relationship.target)?.object_type === 'pattern',
219
+ )
220
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.target))}`);
221
+
222
+ const ruleReferences = registry.relationships
223
+ .filter(
224
+ (relationship) =>
225
+ ['requires', 'related_to'].includes(relationship.type) &&
226
+ relationship.source === prompt.id &&
227
+ registry.objectsById.get(relationship.target)?.object_type === 'rule',
228
+ )
229
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.target))}`);
230
+
231
+ const checklistReferences = registry.relationships
232
+ .filter(
233
+ (relationship) =>
234
+ ['requires', 'related_to'].includes(relationship.type) &&
235
+ relationship.source === prompt.id &&
236
+ registry.objectsById.get(relationship.target)?.object_type === 'checklist',
237
+ )
238
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.target))}`);
239
+
240
+ return [
241
+ prompt.id,
242
+ prompt.alias,
243
+ prompt.title,
244
+ prompt.category,
245
+ prompt.status,
246
+ prompt.maturity,
247
+ prompt.path,
248
+ listOrNone(patternReferences),
249
+ listOrNone(ruleReferences),
250
+ listOrNone(checklistReferences),
251
+ ];
252
+ });
253
+
254
+ return [
255
+ '# Generated Prompts Index',
256
+ '',
257
+ generatedNotice,
258
+ '',
259
+ table(
260
+ [
261
+ 'ID',
262
+ 'Alias',
263
+ 'Title',
264
+ 'Category',
265
+ 'Status',
266
+ 'Maturity',
267
+ 'File Path',
268
+ 'Pattern References',
269
+ 'Rule References',
270
+ 'Checklist References',
271
+ ],
272
+ rows,
273
+ ),
274
+ '',
275
+ ].join('\n');
276
+ }
277
+
278
+ function generateChecklistsIndex(registry) {
279
+ const checklists = registry.objects
280
+ .filter((object) => object.object_type === 'checklist')
281
+ .sort((a, b) => a.id.localeCompare(b.id));
282
+
283
+ const rows = checklists.map((checklist) => {
284
+ const coverageReferences = registry.relationships
285
+ .filter(
286
+ (relationship) =>
287
+ relationship.target === checklist.id &&
288
+ ['prompt', 'review', 'skill'].includes(registry.objectsById.get(relationship.source)?.object_type),
289
+ )
290
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.source))}`);
291
+
292
+ const relatedRules = registry.relationships
293
+ .filter(
294
+ (relationship) =>
295
+ relationship.source === checklist.id &&
296
+ registry.objectsById.get(relationship.target)?.object_type === 'rule',
297
+ )
298
+ .map((relationship) => `${relationship.type}: ${objectLabel(registry.objectsById.get(relationship.target))}`);
299
+
300
+ return [
301
+ checklist.id,
302
+ checklist.alias,
303
+ checklist.title,
304
+ checklist.category,
305
+ checklist.status,
306
+ checklist.maturity,
307
+ checklist.path,
308
+ listOrNone(relatedRules),
309
+ listOrNone(coverageReferences),
310
+ ];
311
+ });
312
+
313
+ return [
314
+ '# Generated Checklists Index',
315
+ '',
316
+ generatedNotice,
317
+ '',
318
+ table(
319
+ ['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Rule Coverage', 'Used By'],
320
+ rows,
321
+ ),
322
+ '',
323
+ ].join('\n');
324
+ }
325
+
326
+ function generateReviewsIndex(registry) {
327
+ const reviews = registry.objects
328
+ .filter((object) => object.object_type === 'review')
329
+ .sort((a, b) => a.id.localeCompare(b.id));
330
+
331
+ const rows = reviews.map((review) => {
332
+ const requiredChecklists = registry.relationships
333
+ .filter(
334
+ (relationship) =>
335
+ relationship.type === 'requires' &&
336
+ relationship.source === review.id &&
337
+ registry.objectsById.get(relationship.target)?.object_type === 'checklist',
338
+ )
339
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
340
+
341
+ const validatedObjects = registry.relationships
342
+ .filter((relationship) => relationship.type === 'validates' && relationship.source === review.id)
343
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
344
+
345
+ return [
346
+ review.id,
347
+ review.alias,
348
+ review.title,
349
+ review.category,
350
+ review.status,
351
+ review.maturity,
352
+ review.path,
353
+ listOrNone(requiredChecklists),
354
+ listOrNone(validatedObjects),
355
+ ];
356
+ });
357
+
358
+ return [
359
+ '# Generated Reviews Index',
360
+ '',
361
+ generatedNotice,
362
+ '',
363
+ table(
364
+ ['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Required Checklists', 'Validates'],
365
+ rows,
366
+ ),
367
+ '',
368
+ ].join('\n');
369
+ }
370
+
371
+ function generateExamplesIndex(registry) {
372
+ const referenceProjects = registry.objects
373
+ .filter((object) => object.object_type === 'reference_project')
374
+ .sort((a, b) => a.id.localeCompare(b.id));
375
+
376
+ const rows = referenceProjects.map((referenceProject) => {
377
+ const implementedPrompts = registry.relationships
378
+ .filter(
379
+ (relationship) =>
380
+ relationship.type === 'implements' &&
381
+ relationship.source === referenceProject.id &&
382
+ registry.objectsById.get(relationship.target)?.object_type === 'prompt',
383
+ )
384
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
385
+
386
+ const validatedObjects = registry.relationships
387
+ .filter((relationship) => relationship.type === 'validates' && relationship.source === referenceProject.id)
388
+ .map((relationship) => objectLabel(registry.objectsById.get(relationship.target)));
389
+
390
+ return [
391
+ referenceProject.id,
392
+ referenceProject.alias,
393
+ referenceProject.title,
394
+ referenceProject.category,
395
+ referenceProject.status,
396
+ referenceProject.maturity,
397
+ referenceProject.path,
398
+ listOrNone(implementedPrompts),
399
+ listOrNone(validatedObjects),
400
+ ];
401
+ });
402
+
403
+ return [
404
+ '# Generated Examples Index',
405
+ '',
406
+ generatedNotice,
407
+ '',
408
+ table(
409
+ ['ID', 'Alias', 'Title', 'Category', 'Status', 'Maturity', 'File Path', 'Implements', 'Validates'],
410
+ rows,
411
+ ),
412
+ '',
413
+ ].join('\n');
414
+ }
415
+
416
+ function countBy(items, getKey) {
417
+ const counts = new Map();
418
+ for (const item of items) {
419
+ const key = getKey(item);
420
+ counts.set(key, (counts.get(key) ?? 0) + 1);
421
+ }
422
+ return [...counts.entries()].sort(([a], [b]) => a.localeCompare(b));
423
+ }
424
+
425
+ function generateGraphReport(registry) {
426
+ const relationshipCounts = countBy(registry.relationships, (relationship) => relationship.type);
427
+ const objectCounts = countBy(registry.objects, (object) => object.object_type);
428
+ const knownIds = new Set(registry.objects.map((object) => object.id));
429
+ const connectedIds = new Set();
430
+ const missingTargets = [];
431
+
432
+ for (const relationship of registry.relationships) {
433
+ if (knownIds.has(relationship.source)) {
434
+ connectedIds.add(relationship.source);
435
+ } else {
436
+ missingTargets.push(`source ${relationship.source} in ${relationship.type}`);
437
+ }
438
+
439
+ if (knownIds.has(relationship.target)) {
440
+ connectedIds.add(relationship.target);
441
+ } else {
442
+ missingTargets.push(`target ${relationship.target} in ${relationship.type}`);
443
+ }
444
+ }
445
+
446
+ const orphanObjects = registry.objects
447
+ .filter((object) => !connectedIds.has(object.id))
448
+ .sort((a, b) => a.id.localeCompare(b.id))
449
+ .map(objectLabel);
450
+
451
+ const connectionCounts = registry.objects
452
+ .map((object) => ({
453
+ object,
454
+ count: registry.relationships.filter(
455
+ (relationship) => relationship.source === object.id || relationship.target === object.id,
456
+ ).length,
457
+ }))
458
+ .sort((a, b) => b.count - a.count || a.object.id.localeCompare(b.object.id))
459
+ .slice(0, 10)
460
+ .map(({ object, count }) => `${objectLabel(object)}: ${count}`);
461
+
462
+ const patternsWithoutRequiredRules = registry.objects
463
+ .filter((object) => object.object_type === 'pattern')
464
+ .filter(
465
+ (pattern) =>
466
+ !registry.relationships.some(
467
+ (relationship) =>
468
+ relationship.type === 'requires' &&
469
+ relationship.source === pattern.id &&
470
+ registry.objectsById.get(relationship.target)?.object_type === 'rule',
471
+ ),
472
+ )
473
+ .map(objectLabel);
474
+
475
+ const rulesNotUsedByAnyPattern = registry.objects
476
+ .filter((object) => object.object_type === 'rule')
477
+ .filter(
478
+ (rule) =>
479
+ !registry.relationships.some(
480
+ (relationship) =>
481
+ relationship.type === 'requires' &&
482
+ relationship.target === rule.id &&
483
+ registry.objectsById.get(relationship.source)?.object_type === 'pattern',
484
+ ),
485
+ )
486
+ .map(objectLabel);
487
+
488
+ const researchWithoutDerivedOrInspiredRules = registry.objects
489
+ .filter((object) => object.object_type === 'research')
490
+ .filter(
491
+ (research) =>
492
+ !registry.relationships.some(
493
+ (relationship) =>
494
+ ['derived_from', 'inspired_by'].includes(relationship.type) &&
495
+ relationship.target === research.id &&
496
+ registry.objectsById.get(relationship.source)?.object_type === 'rule',
497
+ ),
498
+ )
499
+ .map(objectLabel);
500
+
501
+ const rulesWithoutResearch = registry.objects
502
+ .filter((object) => object.object_type === 'rule')
503
+ .filter(
504
+ (rule) =>
505
+ !registry.relationships.some(
506
+ (relationship) =>
507
+ ['derived_from', 'inspired_by'].includes(relationship.type) &&
508
+ relationship.source === rule.id &&
509
+ registry.objectsById.get(relationship.target)?.object_type === 'research',
510
+ ),
511
+ )
512
+ .map(objectLabel);
513
+
514
+ const promptsWithoutKnowledgeReferences = registry.objects
515
+ .filter((object) => object.object_type === 'prompt')
516
+ .filter(
517
+ (prompt) =>
518
+ !registry.relationships.some(
519
+ (relationship) =>
520
+ ['requires', 'related_to'].includes(relationship.type) &&
521
+ relationship.source === prompt.id &&
522
+ ['pattern', 'rule', 'checklist'].includes(registry.objectsById.get(relationship.target)?.object_type),
523
+ ),
524
+ )
525
+ .map(objectLabel);
526
+
527
+ const referenceProjectsWithoutValidatesRelationships = registry.objects
528
+ .filter((object) => object.object_type === 'reference_project')
529
+ .filter(
530
+ (referenceProject) =>
531
+ !registry.relationships.some(
532
+ (relationship) => relationship.type === 'validates' && relationship.source === referenceProject.id,
533
+ ),
534
+ )
535
+ .map(objectLabel);
536
+
537
+ return [
538
+ '# Generated Graph Report',
539
+ '',
540
+ generatedNotice,
541
+ '',
542
+ '## Object Counts By Type',
543
+ '',
544
+ table(['Object Type', 'Count'], objectCounts.map(([type, count]) => [type, count])),
545
+ '',
546
+ '## Relationship Counts By Type',
547
+ '',
548
+ table(['Relationship Type', 'Count'], relationshipCounts.map(([type, count]) => [type, count])),
549
+ '',
550
+ '## Orphan Objects',
551
+ '',
552
+ linesOrNone(orphanObjects),
553
+ '',
554
+ '## Missing Targets',
555
+ '',
556
+ linesOrNone(missingTargets),
557
+ '',
558
+ '## Research Without Derived Or Inspired Rules',
559
+ '',
560
+ linesOrNone(researchWithoutDerivedOrInspiredRules),
561
+ '',
562
+ '## Rules Without Research',
563
+ '',
564
+ linesOrNone(rulesWithoutResearch),
565
+ '',
566
+ '## Prompts Without Knowledge References',
567
+ '',
568
+ linesOrNone(promptsWithoutKnowledgeReferences),
569
+ '',
570
+ '## Reference Projects Without Validates Relationships',
571
+ '',
572
+ linesOrNone(referenceProjectsWithoutValidatesRelationships),
573
+ '',
574
+ '## Most Connected Objects',
575
+ '',
576
+ linesOrNone(connectionCounts),
577
+ '',
578
+ '## Patterns Without Required Rules',
579
+ '',
580
+ linesOrNone(patternsWithoutRequiredRules),
581
+ '',
582
+ '## Rules Not Used By Any Pattern',
583
+ '',
584
+ linesOrNone(rulesNotUsedByAnyPattern),
585
+ '',
586
+ ].join('\n');
587
+ }
588
+
589
+ export function generateIndexFiles(root = process.cwd()) {
590
+ const registry = readRegistry(root);
591
+
592
+ return {
593
+ 'research/GENERATED_INDEX.md': generateResearchIndex(registry),
594
+ 'rules/GENERATED_INDEX.md': generateRulesIndex(registry),
595
+ 'patterns/GENERATED_INDEX.md': generatePatternsIndex(registry),
596
+ 'prompts/GENERATED_INDEX.md': generatePromptsIndex(registry),
597
+ 'checklists/GENERATED_INDEX.md': generateChecklistsIndex(registry),
598
+ 'reviews/GENERATED_INDEX.md': generateReviewsIndex(registry),
599
+ 'examples/GENERATED_INDEX.md': generateExamplesIndex(registry),
600
+ 'graph/GENERATED_GRAPH.md': generateGraphReport(registry),
601
+ };
602
+ }
603
+
604
+ export function writeGeneratedIndexes(root = process.cwd()) {
605
+ const files = generateIndexFiles(root);
606
+
607
+ for (const [filePath, content] of Object.entries(files)) {
608
+ const fullPath = path.join(root, filePath);
609
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
610
+ fs.writeFileSync(fullPath, content, 'utf8');
611
+ }
612
+
613
+ return files;
614
+ }
615
+
616
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
617
+ const files = writeGeneratedIndexes();
618
+ for (const filePath of Object.keys(files)) {
619
+ console.log(`Generated ${filePath}`);
620
+ }
621
+ }