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,342 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ const objectOrder = [
6
+ 'research',
7
+ 'rule',
8
+ 'pattern',
9
+ 'prompt',
10
+ 'checklist',
11
+ 'reference_project',
12
+ 'review',
13
+ ];
14
+ const typeLabels = {
15
+ research: 'Research',
16
+ rule: 'Rules',
17
+ pattern: 'Patterns',
18
+ prompt: 'Prompts',
19
+ checklist: 'Checklists',
20
+ reference_project: 'Reference Projects',
21
+ review: 'Reviews',
22
+ };
23
+
24
+ function usage(command) {
25
+ if (command) return `Usage:
26
+ ${command} --task <query> [--platform <name>] [--intent implement|qa] [--format markdown|json]
27
+ ${command} --review <object-id-or-path> [--intent qa] [--format markdown|json]
28
+ ${command} --object <object-id-or-slug> [--format markdown|json]`;
29
+ return `Usage:
30
+ npm run context -- --task <query> [--platform <name>] [--intent implement|qa] [--format markdown|json]
31
+ npm run context -- --review <object-id-or-path> [--intent qa] [--format markdown|json]
32
+ npm run context -- --object <object-id-or-slug> [--intent implement|qa] [--format markdown|json]
33
+
34
+ Examples:
35
+ npm run context -- --task quick-capture --platform mobile --intent implement
36
+ npm run context -- --review examples/todo-reference --intent qa
37
+ npm run context -- --object PAT-00002 --format json`;
38
+ }
39
+
40
+ function parseArgs(argv) {
41
+ const options = { format: 'markdown' };
42
+
43
+ for (let index = 0; index < argv.length; index += 1) {
44
+ const argument = argv[index];
45
+ if (argument === '--help' || argument === '-h') {
46
+ options.help = true;
47
+ continue;
48
+ }
49
+ if (argument === '--json') {
50
+ options.format = 'json';
51
+ continue;
52
+ }
53
+ if (!['--task', '--review', '--object', '--platform', '--intent', '--format'].includes(argument)) {
54
+ throw new Error(`Unknown argument: ${argument}`);
55
+ }
56
+ const value = argv[index + 1];
57
+ if (!value || value.startsWith('--')) {
58
+ throw new Error(`${argument} requires a value`);
59
+ }
60
+ options[argument.slice(2)] = value;
61
+ index += 1;
62
+ }
63
+
64
+ if (options.help) return options;
65
+ const modes = ['task', 'review', 'object'].filter((mode) => options[mode]);
66
+ if (modes.length !== 1) {
67
+ throw new Error('Provide exactly one of --task, --review, or --object');
68
+ }
69
+ if (!['markdown', 'json'].includes(options.format)) {
70
+ throw new Error('--format must be markdown or json');
71
+ }
72
+ options.mode = modes[0];
73
+ options.intent ??= options.mode === 'review' ? 'qa' : 'implement';
74
+ if (!['implement', 'qa'].includes(options.intent)) {
75
+ throw new Error('--intent must be implement or qa');
76
+ }
77
+ return options;
78
+ }
79
+
80
+ function normalize(value) {
81
+ return String(value ?? '')
82
+ .normalize('NFKC')
83
+ .toLocaleLowerCase()
84
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
85
+ .trim()
86
+ .replace(/\s+/g, ' ');
87
+ }
88
+
89
+ function tokenise(value) {
90
+ return normalize(value)
91
+ .split(/\s+/u)
92
+ .filter(Boolean);
93
+ }
94
+
95
+ const taskFillers = new Set(['a', 'an', 'the', 'please', 'build', 'create', 'implement', 'make', 'for', 'me']);
96
+ const dependencyTypes = new Set(['requires', 'derived_from', 'inspired_by', 'implements']);
97
+
98
+ function searchIndex(objects, root) {
99
+ return objects.map((object) => {
100
+ const filePath = path.join(root, object.path);
101
+ const content = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
102
+ return {
103
+ object,
104
+ terms: new Set(tokenise([object.id, object.alias, object.slug, object.title, object.category, object.path, content].join('\n'))),
105
+ fields: [
106
+ ['title', object.title, 100],
107
+ ['slug', object.slug, 90],
108
+ ['alias', object.alias, 80],
109
+ ['category', object.category, 20],
110
+ ],
111
+ };
112
+ });
113
+ }
114
+
115
+ function findAnchors(index, query) {
116
+ const normalizedQuery = normalize(query);
117
+ const exact = findExactObject(index.map(({ object }) => object), query);
118
+ if (exact.length > 0) return exact;
119
+ const tokens = [...new Set(tokenise(query).filter((token) => !taskFillers.has(token)))];
120
+ if (!normalizedQuery || tokens.length === 0) return [];
121
+
122
+ const scored = index
123
+ .map(({ object, terms, fields }) => {
124
+ if (!tokens.every((token) => terms.has(token))) return null;
125
+ let score = 0;
126
+ let field = 'content';
127
+ let matchedTerms = tokens;
128
+ for (const [name, value, weight] of fields) {
129
+ const fieldTerms = new Set(tokenise(value));
130
+ const matched = tokens.filter((token) => fieldTerms.has(token)).length;
131
+ const fieldScore = matched === 0 ? 0 : weight * matched / tokens.length + matched / fieldTerms.size;
132
+ if (fieldScore > score) {
133
+ score = fieldScore;
134
+ field = name;
135
+ matchedTerms = tokens.filter((token) => fieldTerms.has(token));
136
+ }
137
+ }
138
+ return { object, score, reason: { kind: 'match', field, terms: matchedTerms } };
139
+ })
140
+ .filter(Boolean)
141
+ .sort((left, right) => right.score - left.score || left.object.id.localeCompare(right.object.id));
142
+
143
+ return scored.slice(0, 1);
144
+ }
145
+
146
+ function findExactObject(objects, query) {
147
+ const normalizedQuery = normalize(query);
148
+ if (!normalizedQuery) return [];
149
+
150
+ for (const object of objects) {
151
+ const field = ['id', 'alias', 'slug', 'path'].find((name) => normalize(object[name]) === normalizedQuery);
152
+ if (field) return [{ object, reason: { kind: 'match', field, terms: tokenise(query) } }];
153
+ }
154
+ return [];
155
+ }
156
+
157
+ function relatedContext(anchors, objectsById, relationships, platform, index) {
158
+ const selected = new Map();
159
+ const queue = [];
160
+ const outgoing = new Map();
161
+ const incoming = new Map();
162
+ for (const edge of [...relationships].sort((a, b) =>
163
+ a.source.localeCompare(b.source) || a.type.localeCompare(b.type) || a.target.localeCompare(b.target))) {
164
+ if (!outgoing.has(edge.source)) outgoing.set(edge.source, []);
165
+ if (!incoming.has(edge.target)) incoming.set(edge.target, []);
166
+ outgoing.get(edge.source).push(edge);
167
+ incoming.get(edge.target).push(edge);
168
+ }
169
+ const add = (id, reason) => {
170
+ const object = objectsById.get(id);
171
+ if (!object) throw new Error(`Context relationship target is not registered: ${id}`);
172
+ if (selected.has(id)) return;
173
+ selected.set(id, { object, reason });
174
+ queue.push(object);
175
+ };
176
+ const seeds = [...anchors];
177
+ if (platform) {
178
+ const matches = findAnchors(
179
+ index.filter(({ object }) => ['rule', 'pattern'].includes(object.object_type)), platform,
180
+ );
181
+ seeds.push(...matches.map(({ object, reason }) => ({ object, reason: { ...reason, kind: 'platform', value: platform } })));
182
+ }
183
+ for (const { object, reason } of seeds) add(object.id, reason);
184
+
185
+ // Only seed objects select optional neighbors; dependencies cannot pull in every consumer.
186
+ const reviewTargets = new Set(seeds.map(({ object }) => object.id));
187
+ for (const { object } of seeds) {
188
+ for (const edge of outgoing.get(object.id) ?? []) {
189
+ if (edge.type === 'related_to' && ['research', 'rule', 'pattern'].includes(object.object_type)) {
190
+ add(edge.target, { kind: 'related', source: edge.source, relationship: edge.type });
191
+ }
192
+ }
193
+ for (const edge of incoming.get(object.id) ?? []) {
194
+ const source = objectsById.get(edge.source);
195
+ if (object.object_type === 'research' && source?.object_type === 'rule' && ['derived_from', 'inspired_by'].includes(edge.type)) {
196
+ add(source.id, { kind: 'derived_rule', source: object.id, relationship: edge.type });
197
+ reviewTargets.add(source.id);
198
+ }
199
+ if (object.object_type === 'reference_project' && source?.object_type === 'review' && edge.type === 'validates') {
200
+ add(source.id, { kind: 'review', source: object.id, relationship: edge.type });
201
+ }
202
+ }
203
+ }
204
+ for (const id of reviewTargets) {
205
+ for (const edge of incoming.get(id) ?? []) {
206
+ const source = objectsById.get(edge.source);
207
+ if (['requires', 'related_to'].includes(edge.type) &&
208
+ (source?.object_type === 'checklist' || (source?.object_type === 'prompt' && source.category === 'review'))) {
209
+ add(source.id, { kind: 'review', source: id, relationship: edge.type });
210
+ }
211
+ }
212
+ }
213
+
214
+ // A growing queue computes the dependency closure and terminates even when the graph has cycles.
215
+ for (let cursor = 0; cursor < queue.length; cursor += 1) {
216
+ const object = queue[cursor];
217
+ for (const edge of outgoing.get(object.id) ?? []) {
218
+ if (dependencyTypes.has(edge.type) ||
219
+ (edge.type === 'validates' && ['reference_project', 'review'].includes(object.object_type))) {
220
+ add(edge.target, { kind: 'dependency', source: edge.source, relationship: edge.type });
221
+ }
222
+ }
223
+ }
224
+
225
+ return [...selected.values()].sort(
226
+ (left, right) => objectOrder.indexOf(left.object.object_type) - objectOrder.indexOf(right.object.object_type) || left.object.id.localeCompare(right.object.id),
227
+ );
228
+ }
229
+
230
+ function serializeObject({ object, reason }) {
231
+ return {
232
+ id: object.id,
233
+ alias: object.alias,
234
+ title: object.title,
235
+ type: object.object_type,
236
+ status: object.status,
237
+ maturity: object.maturity,
238
+ path: object.path,
239
+ reason,
240
+ };
241
+ }
242
+
243
+ function explainReason(reason) {
244
+ if (reason.kind === 'match') return `matched ${reason.field}: ${reason.terms.join(', ')}`;
245
+ if (reason.kind === 'platform') return `platform ${reason.value}, matched ${reason.field}`;
246
+ if (['review', 'derived_rule'].includes(reason.kind)) return `${reason.kind === 'review' ? 'review gate' : 'derived rule'} for ${reason.source} (${reason.relationship})`;
247
+ return `${reason.source} ${reason.relationship} this object`;
248
+ }
249
+
250
+ function asMarkdown(result) {
251
+ const lines = [
252
+ '# Agent Context',
253
+ '',
254
+ `Mode: ${result.query.mode}`,
255
+ `Query: ${result.query.value}`,
256
+ `Platform: ${result.query.platform ?? 'not specified'}`,
257
+ `Intent: ${result.query.intent}`,
258
+ '',
259
+ '## Anchors',
260
+ '',
261
+ ...result.anchors.map((object) => `- \`${object.id}\` / \`${object.alias}\` — ${object.title}`),
262
+ ];
263
+
264
+ for (const type of objectOrder) {
265
+ const objects = result.objects.filter((object) => object.type === type);
266
+ if (objects.length === 0) continue;
267
+ lines.push('', `## ${typeLabels[type]}`, '');
268
+ lines.push(
269
+ ...objects.map(
270
+ (object) =>
271
+ `- \`${object.id}\` / \`${object.alias}\` — ${object.title} (${object.status}, ${object.maturity}) · \`${object.absolutePath ?? object.path}\` — ${explainReason(object.reason)}`,
272
+ ),
273
+ );
274
+ }
275
+
276
+ const useOrder = result.query.intent === 'qa'
277
+ ? [
278
+ '1. Inspect the selected reference or implementation before judging it.',
279
+ '2. Check the listed rules and patterns against directly observed behavior.',
280
+ '3. Use the listed checklists and reviews as gates; report missing evidence as a gap.',
281
+ ]
282
+ : [
283
+ '1. Read the selected research and rules before changing a product decision.',
284
+ '2. Apply the selected patterns and prompts; do not invent advice beyond them.',
285
+ '3. Use listed checklists and reviews as later gates, and keep their evidence limits explicit.',
286
+ ];
287
+ lines.push(
288
+ '',
289
+ '## Use Order',
290
+ '',
291
+ ...useOrder,
292
+ );
293
+ return `${lines.join('\n')}\n`;
294
+ }
295
+
296
+ export function runContext(argv, { root = process.cwd(), readingPaths = false, command } = {}) {
297
+ let options;
298
+ try {
299
+ options = parseArgs(argv);
300
+ if (options.help) {
301
+ console.log(usage(command));
302
+ return;
303
+ }
304
+
305
+ const registry = JSON.parse(fs.readFileSync(path.join(root, 'registry/objects.json'), 'utf8'));
306
+ const relationshipRegistry = JSON.parse(
307
+ fs.readFileSync(path.join(root, 'registry/relationships.json'), 'utf8'),
308
+ );
309
+ const objectsById = new Map(registry.objects.map((object) => [object.id, object]));
310
+ const index = searchIndex(registry.objects, root);
311
+ const query = options[options.mode];
312
+ const anchors = options.mode === 'object'
313
+ ? findExactObject(registry.objects, query)
314
+ : findAnchors(index, query);
315
+ if (anchors.length === 0) throw new Error(`No graph object matches "${query}"`);
316
+
317
+ const result = {
318
+ query: { mode: options.mode, value: query, platform: options.platform ?? null, intent: options.intent },
319
+ anchors: anchors.map(serializeObject),
320
+ objects: relatedContext(anchors, objectsById, relationshipRegistry.relationships, options.platform, index).map(serializeObject),
321
+ };
322
+ if (readingPaths) {
323
+ result.knowledgeRoot = path.resolve(root);
324
+ for (const object of [...result.anchors, ...result.objects]) {
325
+ object.absolutePath = path.resolve(root, object.path);
326
+ }
327
+ }
328
+ console.log(options.format === 'json' ? JSON.stringify(result, null, 2) : asMarkdown(result));
329
+ } catch (error) {
330
+ if (options?.format === 'json' || argv.includes('--json')) {
331
+ console.error(JSON.stringify({ error: error.message }));
332
+ } else {
333
+ console.error(`Context resolution failed: ${error.message}`);
334
+ console.error(usage(command));
335
+ }
336
+ process.exitCode = 1;
337
+ }
338
+ }
339
+
340
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
341
+ runContext(process.argv.slice(2));
342
+ }
@@ -0,0 +1,77 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const root = process.cwd();
5
+ const registry = JSON.parse(fs.readFileSync(path.join(root, 'registry/objects.json'), 'utf8'));
6
+ const relationshipRegistry = JSON.parse(
7
+ fs.readFileSync(path.join(root, 'registry/relationships.json'), 'utf8'),
8
+ );
9
+ const objectsById = new Map(registry.objects.map((object) => [object.id, object]));
10
+ const relationships = relationshipRegistry.relationships;
11
+ const errors = [];
12
+
13
+ function outgoing(objectId) {
14
+ return relationships.filter((relationship) => relationship.source === objectId);
15
+ }
16
+
17
+ function hasOutgoing(object, relationshipTypes, targetTypes) {
18
+ return outgoing(object.id).some(
19
+ (relationship) =>
20
+ relationshipTypes.includes(relationship.type) && targetTypes.includes(objectsById.get(relationship.target)?.object_type),
21
+ );
22
+ }
23
+
24
+ for (const object of registry.objects) {
25
+ switch (object.object_type) {
26
+ case 'rule':
27
+ if (!hasOutgoing(object, ['derived_from', 'inspired_by'], ['research'])) {
28
+ errors.push(`${object.id}: rules must derive from or be inspired by registered research`);
29
+ }
30
+ break;
31
+ case 'pattern':
32
+ if (!hasOutgoing(object, ['requires'], ['rule'])) {
33
+ errors.push(`${object.id}: patterns must require at least one registered rule`);
34
+ }
35
+ break;
36
+ case 'prompt':
37
+ if (!hasOutgoing(object, ['requires', 'related_to'], ['pattern', 'rule', 'checklist'])) {
38
+ errors.push(`${object.id}: prompts must reference a registered pattern, rule, or checklist`);
39
+ }
40
+ break;
41
+ case 'checklist':
42
+ if (!hasOutgoing(object, ['requires', 'related_to'], ['pattern', 'rule'])) {
43
+ errors.push(`${object.id}: checklists must cover at least one registered pattern or rule`);
44
+ }
45
+ break;
46
+ case 'reference_project':
47
+ if (!hasOutgoing(object, ['implements'], ['prompt'])) {
48
+ errors.push(`${object.id}: reference projects must implement a registered prompt`);
49
+ }
50
+ if (!hasOutgoing(object, ['validates'], ['prompt', 'pattern'])) {
51
+ errors.push(`${object.id}: reference projects must validate a registered prompt or pattern`);
52
+ }
53
+ break;
54
+ case 'review':
55
+ if (!hasOutgoing(object, ['requires'], ['checklist'])) {
56
+ errors.push(`${object.id}: reviews must require a registered checklist`);
57
+ }
58
+ if (!hasOutgoing(object, ['validates'], ['research', 'rule', 'pattern', 'prompt', 'reference_project'])) {
59
+ errors.push(`${object.id}: reviews must validate a registered knowledge object`);
60
+ }
61
+ break;
62
+ default:
63
+ break;
64
+ }
65
+ }
66
+
67
+ if (errors.length > 0) {
68
+ console.error(`DesignLint v0 failed with ${errors.length} error(s):`);
69
+ for (const error of errors) {
70
+ console.error(`- ${error}`);
71
+ }
72
+ process.exit(1);
73
+ }
74
+
75
+ console.log(
76
+ `DesignLint v0 passed: ${registry.objects.length} registered objects satisfy evidence-chain relationship requirements.`,
77
+ );