@hustle-together/api-dev-tools 3.12.16 → 4.5.3

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 (180) hide show
  1. package/.claude/adr-requests/.gitkeep +10 -0
  2. package/.claude/agents/adr-researcher.md +109 -0
  3. package/.claude/agents/visual-analyzer.md +183 -0
  4. package/.claude/api-dev-state.json +10 -0
  5. package/.claude/documentation-audit.json +114 -0
  6. package/.claude/registry.json +289 -0
  7. package/.claude/settings.json +45 -1
  8. package/.claude/settings.local.json +1 -7
  9. package/.claude/workflow-logs/None.json +49 -0
  10. package/.claude/workflow-logs/session-20251230-143727.json +106 -0
  11. package/.skills/adr-deep-research/SKILL.md +351 -0
  12. package/.skills/api-create/SKILL.md +34 -20
  13. package/.skills/api-research/SKILL.md +130 -0
  14. package/.skills/docs-update/SKILL.md +205 -0
  15. package/.skills/hustle-brand/SKILL.md +368 -0
  16. package/.skills/hustle-build/SKILL.md +365 -38
  17. package/.skills/parallel-spawn/SKILL.md +212 -0
  18. package/.skills/ralph-continue/SKILL.md +151 -0
  19. package/.skills/ralph-loop/SKILL.md +341 -0
  20. package/.skills/ralph-status/SKILL.md +87 -0
  21. package/.skills/refactor/SKILL.md +59 -0
  22. package/.skills/shadcn/SKILL.md +522 -0
  23. package/.skills/test-all/SKILL.md +210 -0
  24. package/.skills/test-builds/SKILL.md +208 -0
  25. package/.skills/test-debug/SKILL.md +212 -0
  26. package/.skills/test-e2e/SKILL.md +168 -0
  27. package/.skills/test-review/SKILL.md +707 -0
  28. package/.skills/test-unit/SKILL.md +143 -0
  29. package/.skills/test-visual/SKILL.md +301 -0
  30. package/.skills/token-report/SKILL.md +132 -0
  31. package/CHANGELOG.md +488 -0
  32. package/README.md +346 -53
  33. package/bin/cli.js +359 -123
  34. package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
  35. package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
  36. package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
  37. package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
  38. package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
  39. package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
  40. package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
  41. package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
  42. package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
  43. package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
  44. package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
  45. package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
  46. package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
  47. package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
  48. package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
  49. package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
  50. package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
  51. package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
  52. package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
  53. package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
  54. package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
  55. package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
  56. package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
  57. package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
  58. package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
  59. package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
  60. package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
  61. package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
  62. package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
  63. package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
  64. package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
  65. package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
  66. package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
  67. package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
  68. package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
  69. package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
  70. package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
  71. package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
  72. package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
  73. package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
  74. package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
  75. package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
  76. package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
  77. package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
  78. package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
  79. package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
  80. package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
  81. package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
  82. package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
  83. package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
  84. package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
  85. package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
  86. package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
  87. package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
  88. package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
  89. package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
  90. package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
  91. package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
  92. package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
  93. package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
  94. package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
  95. package/hooks/api-workflow-check.py +34 -0
  96. package/hooks/auto-answer.py +97 -20
  97. package/{.claude/hooks → hooks}/completion-promise-detector.py +0 -0
  98. package/{.claude/hooks → hooks}/context-capacity-warning.py +0 -0
  99. package/{.claude/hooks → hooks}/docs-update-check.py +0 -0
  100. package/{.claude/hooks → hooks}/enforce-dry-run.py +0 -0
  101. package/hooks/enforce-external-research.py +25 -0
  102. package/hooks/enforce-interview.py +20 -0
  103. package/{.claude/hooks → hooks}/generate-adr-options.py +0 -0
  104. package/{.claude/hooks → hooks}/hook_utils.py +0 -0
  105. package/hooks/ntfy-on-question.py +15 -2
  106. package/hooks/orchestrator-handoff.py +81 -3
  107. package/{.claude/hooks → hooks}/parallel-orchestrator.py +0 -0
  108. package/hooks/periodic-reground.py +40 -0
  109. package/{.claude/hooks → hooks}/remote-question-server.py +0 -0
  110. package/hooks/run-code-review.py +176 -29
  111. package/{.claude/hooks → hooks}/run-visual-qa.py +0 -0
  112. package/hooks/session-logger.py +27 -1
  113. package/hooks/session-startup.py +113 -0
  114. package/{.claude/hooks → hooks}/update-adr-decision.py +0 -0
  115. package/package.json +1 -1
  116. package/templates/.skills/hustle-interview/SKILL.md +174 -0
  117. package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
  118. package/templates/api-dev-state.json +33 -1
  119. package/templates/brand-page/page.tsx +645 -0
  120. package/templates/component/Component.visual.spec.ts +30 -24
  121. package/templates/eslint-plugin-zod-schema/index.js +446 -0
  122. package/templates/eslint-plugin-zod-schema/package.json +26 -0
  123. package/templates/github-workflows/security.yml +274 -0
  124. package/templates/hustle-build-defaults.json +53 -1
  125. package/templates/page/page.e2e.test.ts +30 -26
  126. package/templates/performance-budgets.json +63 -5
  127. package/templates/registry.json +279 -3
  128. package/templates/review-dashboard/page.tsx +510 -0
  129. package/templates/settings.json +74 -7
  130. package/templates/ui-showcase/_components/UIShowcase.tsx +47 -0
  131. package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
  132. package/.claude/commands/hustle-combine.md +0 -1089
  133. package/.claude/commands/hustle-ui-create-page.md +0 -1078
  134. package/.claude/commands/hustle-ui-create.md +0 -1058
  135. package/.claude/hooks/auto-answer.py +0 -305
  136. package/.claude/hooks/cache-research.py +0 -337
  137. package/.claude/hooks/check-api-routes.py +0 -168
  138. package/.claude/hooks/check-playwright-setup.py +0 -103
  139. package/.claude/hooks/check-storybook-setup.py +0 -81
  140. package/.claude/hooks/check-update.py +0 -132
  141. package/.claude/hooks/detect-interruption.py +0 -165
  142. package/.claude/hooks/enforce-a11y-audit.py +0 -202
  143. package/.claude/hooks/enforce-brand-guide.py +0 -241
  144. package/.claude/hooks/enforce-component-type-confirm.py +0 -97
  145. package/.claude/hooks/enforce-freshness.py +0 -184
  146. package/.claude/hooks/enforce-page-components.py +0 -186
  147. package/.claude/hooks/enforce-page-data-schema.py +0 -155
  148. package/.claude/hooks/enforce-questions-sourced.py +0 -146
  149. package/.claude/hooks/enforce-schema-from-interview.py +0 -248
  150. package/.claude/hooks/enforce-ui-disambiguation.py +0 -108
  151. package/.claude/hooks/enforce-ui-interview.py +0 -130
  152. package/.claude/hooks/generate-manifest-entry.py +0 -1161
  153. package/.claude/hooks/lib/__init__.py +0 -1
  154. package/.claude/hooks/lib/greptile.py +0 -355
  155. package/.claude/hooks/lib/ntfy.py +0 -209
  156. package/.claude/hooks/notify-input-needed.py +0 -73
  157. package/.claude/hooks/notify-phase-complete.py +0 -90
  158. package/.claude/hooks/ntfy-on-question.py +0 -240
  159. package/.claude/hooks/orchestrator-completion.py +0 -313
  160. package/.claude/hooks/orchestrator-handoff.py +0 -267
  161. package/.claude/hooks/orchestrator-session-startup.py +0 -146
  162. package/.claude/hooks/run-code-review.py +0 -393
  163. package/.claude/hooks/session-logger.py +0 -323
  164. package/.claude/hooks/test-orchestrator-reground.py +0 -248
  165. package/.claude/hooks/track-scope-coverage.py +0 -220
  166. package/.claude/hooks/track-token-usage.py +0 -121
  167. package/.claude/hooks/update-api-showcase.py +0 -161
  168. package/.claude/hooks/update-registry.py +0 -352
  169. package/.claude/hooks/update-ui-showcase.py +0 -224
  170. package/.claude/test-auto-answer-bot.py +0 -183
  171. package/.claude/test-completion-detector.py +0 -263
  172. package/.claude/test-orchestrator-state.json +0 -20
  173. package/.claude/test-orchestrator.sh +0 -271
  174. /package/{.claude/commands → commands}/hustle-build.md +0 -0
  175. /package/{.claude/hooks → hooks}/lib/__pycache__/__init__.cpython-314.pyc +0 -0
  176. /package/{.claude/hooks → hooks}/lib/__pycache__/greptile.cpython-314.pyc +0 -0
  177. /package/{.claude/hooks → hooks}/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
  178. /package/{.claude/hooks → hooks}/project-document-prompt.py +0 -0
  179. /package/{.claude/hooks → hooks}/remote-question-proxy.py +0 -0
  180. /package/{.claude/hooks → hooks}/update-testing-checklist.py +0 -0
@@ -0,0 +1,326 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+
5
+ interface ADR {
6
+ number: number;
7
+ title: string;
8
+ status: 'proposed' | 'accepted' | 'deprecated' | 'superseded';
9
+ date: string;
10
+ phase: 'initial_research' | 'interview' | 'deep_research';
11
+ endpoint: string;
12
+ file: string;
13
+ supersededBy?: number;
14
+ }
15
+
16
+ interface ADRIndex {
17
+ adrs: ADR[];
18
+ }
19
+
20
+ const statusColors: Record<ADR['status'], string> = {
21
+ proposed: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
22
+ accepted: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
23
+ deprecated: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
24
+ superseded: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200',
25
+ };
26
+
27
+ const phaseLabels: Record<ADR['phase'], string> = {
28
+ initial_research: 'Initial Research',
29
+ interview: 'Interview',
30
+ deep_research: 'Deep Research',
31
+ };
32
+
33
+ function StatusBadge({ status }: { status: ADR['status'] }) {
34
+ return (
35
+ <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${statusColors[status]}`}>
36
+ {status.charAt(0).toUpperCase() + status.slice(1)}
37
+ </span>
38
+ );
39
+ }
40
+
41
+ function PhaseBadge({ phase }: { phase: ADR['phase'] }) {
42
+ const phaseColors: Record<ADR['phase'], string> = {
43
+ initial_research: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
44
+ interview: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200',
45
+ deep_research: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200',
46
+ };
47
+
48
+ return (
49
+ <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${phaseColors[phase]}`}>
50
+ {phaseLabels[phase]}
51
+ </span>
52
+ );
53
+ }
54
+
55
+ function ADRCard({ adr, onClick }: { adr: ADR; onClick: () => void }) {
56
+ return (
57
+ <div
58
+ onClick={onClick}
59
+ className="p-4 border rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
60
+ >
61
+ <div className="flex items-start justify-between">
62
+ <div>
63
+ <div className="flex items-center gap-2 mb-1">
64
+ <span className="text-sm font-mono text-gray-500">
65
+ ADR-{adr.number.toString().padStart(4, '0')}
66
+ </span>
67
+ <StatusBadge status={adr.status} />
68
+ </div>
69
+ <h3 className="text-lg font-semibold">{adr.title}</h3>
70
+ </div>
71
+ <span className="text-sm text-gray-500">{adr.date}</span>
72
+ </div>
73
+
74
+ <div className="mt-2 flex items-center gap-2">
75
+ <PhaseBadge phase={adr.phase} />
76
+ <span className="text-sm text-gray-500">→</span>
77
+ <span className="text-sm font-medium text-blue-600 dark:text-blue-400">
78
+ {adr.endpoint}
79
+ </span>
80
+ </div>
81
+
82
+ {adr.supersededBy && (
83
+ <div className="mt-2 text-sm text-gray-500">
84
+ Superseded by ADR-{adr.supersededBy.toString().padStart(4, '0')}
85
+ </div>
86
+ )}
87
+ </div>
88
+ );
89
+ }
90
+
91
+ function ADRDetail({ adr, content, onBack }: { adr: ADR; content: string; onBack: () => void }) {
92
+ return (
93
+ <div>
94
+ <button
95
+ onClick={onBack}
96
+ className="mb-4 flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
97
+ >
98
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
99
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
100
+ </svg>
101
+ Back to list
102
+ </button>
103
+
104
+ <div className="flex items-center gap-4 mb-4">
105
+ <span className="text-lg font-mono text-gray-500">
106
+ ADR-{adr.number.toString().padStart(4, '0')}
107
+ </span>
108
+ <StatusBadge status={adr.status} />
109
+ <PhaseBadge phase={adr.phase} />
110
+ </div>
111
+
112
+ <h1 className="text-2xl font-bold mb-2">{adr.title}</h1>
113
+
114
+ <div className="text-sm text-gray-500 mb-6">
115
+ {adr.date} • {adr.endpoint}
116
+ </div>
117
+
118
+ <div
119
+ className="prose dark:prose-invert max-w-none"
120
+ dangerouslySetInnerHTML={{ __html: content }}
121
+ />
122
+ </div>
123
+ );
124
+ }
125
+
126
+ function EmptyState() {
127
+ return (
128
+ <div className="text-center py-12">
129
+ <svg className="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
130
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
131
+ </svg>
132
+ <h3 className="mt-2 text-sm font-medium text-gray-900 dark:text-gray-100">No ADRs yet</h3>
133
+ <p className="mt-1 text-sm text-gray-500">
134
+ ADRs are created during Interview and Deep Research phases.
135
+ </p>
136
+ <p className="mt-2 text-xs text-gray-400">
137
+ Run <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded">/api-create</code> to generate your first ADR.
138
+ </p>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function FilterBar({
144
+ statusFilter,
145
+ setStatusFilter,
146
+ phaseFilter,
147
+ setPhaseFilter,
148
+ searchQuery,
149
+ setSearchQuery,
150
+ }: {
151
+ statusFilter: ADR['status'] | 'all';
152
+ setStatusFilter: (v: ADR['status'] | 'all') => void;
153
+ phaseFilter: ADR['phase'] | 'all';
154
+ setPhaseFilter: (v: ADR['phase'] | 'all') => void;
155
+ searchQuery: string;
156
+ setSearchQuery: (v: string) => void;
157
+ }) {
158
+ return (
159
+ <div className="flex flex-wrap gap-4 mb-6">
160
+ <input
161
+ type="text"
162
+ placeholder="Search ADRs..."
163
+ value={searchQuery}
164
+ onChange={(e) => setSearchQuery(e.target.value)}
165
+ className="flex-1 min-w-[200px] px-3 py-2 border rounded-lg bg-white dark:bg-gray-800 focus:ring-2 focus:ring-blue-500 outline-none"
166
+ />
167
+
168
+ <select
169
+ value={statusFilter}
170
+ onChange={(e) => setStatusFilter(e.target.value as ADR['status'] | 'all')}
171
+ className="px-3 py-2 border rounded-lg bg-white dark:bg-gray-800"
172
+ >
173
+ <option value="all">All Statuses</option>
174
+ <option value="proposed">Proposed</option>
175
+ <option value="accepted">Accepted</option>
176
+ <option value="deprecated">Deprecated</option>
177
+ <option value="superseded">Superseded</option>
178
+ </select>
179
+
180
+ <select
181
+ value={phaseFilter}
182
+ onChange={(e) => setPhaseFilter(e.target.value as ADR['phase'] | 'all')}
183
+ className="px-3 py-2 border rounded-lg bg-white dark:bg-gray-800"
184
+ >
185
+ <option value="all">All Phases</option>
186
+ <option value="initial_research">Initial Research</option>
187
+ <option value="interview">Interview</option>
188
+ <option value="deep_research">Deep Research</option>
189
+ </select>
190
+ </div>
191
+ );
192
+ }
193
+
194
+ export default function ADRViewer() {
195
+ const [adrs, setAdrs] = useState<ADR[]>([]);
196
+ const [selectedAdr, setSelectedAdr] = useState<ADR | null>(null);
197
+ const [adrContent, setAdrContent] = useState<string>('');
198
+ const [loading, setLoading] = useState(true);
199
+ const [statusFilter, setStatusFilter] = useState<ADR['status'] | 'all'>('all');
200
+ const [phaseFilter, setPhaseFilter] = useState<ADR['phase'] | 'all'>('all');
201
+ const [searchQuery, setSearchQuery] = useState('');
202
+
203
+ useEffect(() => {
204
+ // In production, fetch from .claude/adrs/index.json
205
+ // For now, use mock data or empty state
206
+ const fetchADRs = async () => {
207
+ try {
208
+ const res = await fetch('/.claude/adrs/index.json');
209
+ if (res.ok) {
210
+ const data: ADRIndex = await res.json();
211
+ setAdrs(data.adrs);
212
+ }
213
+ } catch {
214
+ // No ADRs yet - show empty state
215
+ setAdrs([]);
216
+ } finally {
217
+ setLoading(false);
218
+ }
219
+ };
220
+
221
+ fetchADRs();
222
+ }, []);
223
+
224
+ const handleSelectAdr = async (adr: ADR) => {
225
+ setSelectedAdr(adr);
226
+ try {
227
+ const res = await fetch(`/.claude/adrs/${adr.file}`);
228
+ if (res.ok) {
229
+ const text = await res.text();
230
+ // Basic markdown to HTML (in production, use a proper markdown parser)
231
+ const html = text
232
+ .replace(/^### (.+)$/gm, '<h3>$1</h3>')
233
+ .replace(/^## (.+)$/gm, '<h2>$1</h2>')
234
+ .replace(/^# (.+)$/gm, '<h1>$1</h1>')
235
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
236
+ .replace(/\n\n/g, '</p><p>')
237
+ .replace(/^- (.+)$/gm, '<li>$1</li>');
238
+ setAdrContent(`<p>${html}</p>`);
239
+ }
240
+ } catch {
241
+ setAdrContent('<p>Failed to load ADR content.</p>');
242
+ }
243
+ };
244
+
245
+ const filteredAdrs = adrs.filter((adr) => {
246
+ if (statusFilter !== 'all' && adr.status !== statusFilter) return false;
247
+ if (phaseFilter !== 'all' && adr.phase !== phaseFilter) return false;
248
+ if (searchQuery) {
249
+ const query = searchQuery.toLowerCase();
250
+ return (
251
+ adr.title.toLowerCase().includes(query) ||
252
+ adr.endpoint.toLowerCase().includes(query) ||
253
+ `adr-${adr.number}`.includes(query)
254
+ );
255
+ }
256
+ return true;
257
+ });
258
+
259
+ if (loading) {
260
+ return (
261
+ <div className="flex items-center justify-center py-12">
262
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
263
+ </div>
264
+ );
265
+ }
266
+
267
+ if (selectedAdr) {
268
+ return (
269
+ <ADRDetail
270
+ adr={selectedAdr}
271
+ content={adrContent}
272
+ onBack={() => setSelectedAdr(null)}
273
+ />
274
+ );
275
+ }
276
+
277
+ return (
278
+ <div>
279
+ <div className="mb-6">
280
+ <h1 className="text-2xl font-bold">Architecture Decision Records</h1>
281
+ <p className="text-gray-500 mt-1">
282
+ Significant decisions made during research and interview phases
283
+ </p>
284
+ </div>
285
+
286
+ {adrs.length > 0 && (
287
+ <FilterBar
288
+ statusFilter={statusFilter}
289
+ setStatusFilter={setStatusFilter}
290
+ phaseFilter={phaseFilter}
291
+ setPhaseFilter={setPhaseFilter}
292
+ searchQuery={searchQuery}
293
+ setSearchQuery={setSearchQuery}
294
+ />
295
+ )}
296
+
297
+ {filteredAdrs.length === 0 ? (
298
+ adrs.length === 0 ? (
299
+ <EmptyState />
300
+ ) : (
301
+ <div className="text-center py-8 text-gray-500">
302
+ No ADRs match your filters
303
+ </div>
304
+ )
305
+ ) : (
306
+ <div className="space-y-4">
307
+ {filteredAdrs.map((adr) => (
308
+ <ADRCard key={adr.number} adr={adr} onClick={() => handleSelectAdr(adr)} />
309
+ ))}
310
+ </div>
311
+ )}
312
+
313
+ <div className="mt-8 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
314
+ <h3 className="font-medium mb-2">About ADRs</h3>
315
+ <p className="text-sm text-gray-600 dark:text-gray-400">
316
+ Architecture Decision Records capture significant decisions made during the development workflow.
317
+ They are created automatically during Interview (Phase 4) and Deep Research (Phase 5)
318
+ when you make choices about authentication, error handling, caching, and other architectural concerns.
319
+ </p>
320
+ <p className="text-sm text-gray-500 mt-2">
321
+ See <code className="bg-gray-200 dark:bg-gray-700 px-1 rounded">docs/ARCHITECTURE_DECISION_RECORDS.md</code> for details.
322
+ </p>
323
+ </div>
324
+ </div>
325
+ );
326
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.10.0",
2
+ "version": "3.11.0",
3
3
  "created_at": null,
4
4
  "workflow": null,
5
5
  "_workflow_options": [
@@ -12,6 +12,38 @@
12
12
  "active_element": null,
13
13
  "endpoints": {},
14
14
  "elements": {},
15
+
16
+ "session_archives": {
17
+ "_description": "Completed workflow sessions archived for reference and learning",
18
+ "_archive_policy": "Sessions archived on workflow completion or manual archival",
19
+ "completed": [],
20
+ "interrupted": [],
21
+ "_archive_template": {
22
+ "id": "uuid",
23
+ "workflow": "api-create",
24
+ "element_name": "stripe-subscriptions",
25
+ "status": "completed",
26
+ "started_at": "2025-12-29T10:00:00Z",
27
+ "completed_at": "2025-12-29T14:30:00Z",
28
+ "duration_minutes": 270,
29
+ "phases_completed": 14,
30
+ "total_turns": 45,
31
+ "research_sources_count": 8,
32
+ "interview_decisions_count": 5,
33
+ "files_created": ["src/app/api/stripe/route.ts", "src/lib/stripe.ts"],
34
+ "files_modified": ["registry.json"],
35
+ "test_coverage": 92,
36
+ "learnings": ["stripe webhooks require idempotency keys", "always validate signature first"],
37
+ "archived_at": "2025-12-29T14:31:00Z",
38
+ "archive_reason": "workflow_completed"
39
+ },
40
+ "_learnings_aggregated": {
41
+ "_description": "Patterns learned across all archived sessions - used for re-grounding",
42
+ "common_pitfalls": [],
43
+ "successful_patterns": [],
44
+ "service_specific": {}
45
+ }
46
+ },
15
47
  "combine_config": {
16
48
  "_comment": "Configuration for combine-api workflow",
17
49
  "mode": null,