@appsforgood/next-supabase-kit 0.1.4 → 0.1.6

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 (49) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/DOGFOOD.md +24 -0
  3. package/LOOP_CODING.md +107 -0
  4. package/MAINTAINER_RELEASE.md +100 -0
  5. package/README.md +40 -4
  6. package/REPOSITORY_SETTINGS.md +7 -3
  7. package/SUPPLY_CHAIN.md +5 -5
  8. package/UPGRADE.md +2 -1
  9. package/antigravity/commands/accessibility-pass.toml +16 -0
  10. package/antigravity/commands/browser-qa.toml +18 -0
  11. package/antigravity/commands/distinctiveness-pass.toml +16 -0
  12. package/antigravity/commands/frontend.toml +5 -4
  13. package/antigravity/commands/layout-cleanup.toml +16 -0
  14. package/antigravity/commands/responsive-cleanup.toml +16 -0
  15. package/antigravity/commands/screenshot-critique.toml +16 -0
  16. package/antigravity/commands/ui-audit.toml +17 -0
  17. package/antigravity/commands/ui-polish.toml +17 -0
  18. package/antigravity/plugin.json +9 -0
  19. package/checklists/ui-acceptance-rubric.md +58 -0
  20. package/checklists/ui-detectors.md +75 -0
  21. package/dist/index.js +1090 -411
  22. package/dist/index.js.map +1 -1
  23. package/dist/studio/office/assets/office.css +188 -29
  24. package/dist/studio/office/assets/office.js +72 -50
  25. package/dist/studio/wizard/assets/wizard.css +157 -26
  26. package/dist/studio/wizard/assets/wizard.js +78 -70
  27. package/examples/next-supabase-installed/.agent-kit/agent-roster.json +7 -3
  28. package/examples/next-supabase-installed/.agent-kit/manifest.json +13 -11
  29. package/examples/next-supabase-installed/audit-output.json +22 -2
  30. package/examples/next-supabase-installed/tree.txt +1 -0
  31. package/package.json +28 -7
  32. package/prompts/ui-command-index.md +124 -0
  33. package/research/summaries/agentic-engineering-maturity-levels.md +54 -0
  34. package/rosters/next-supabase-default-council.json +37 -12
  35. package/runtime-skills/ui-improvement-harness/SKILL.md +12 -0
  36. package/schemas/agentic-level.schema.json +47 -0
  37. package/schemas/onboarding-state.schema.json +4 -1
  38. package/skills/ui-improvement-harness.md +96 -0
  39. package/templates/next-supabase/AGENT_ROSTER.md +6 -3
  40. package/templates/next-supabase/ASSISTANT_ADAPTERS.md +3 -1
  41. package/templates/next-supabase/DECISIONS.md +14 -0
  42. package/templates/next-supabase/DESIGN.md +3 -0
  43. package/templates/next-supabase/DOCS.md +7 -1
  44. package/templates/next-supabase/LOOP_CODING.md +98 -0
  45. package/templates/next-supabase/QUALITY_GATES.md +4 -2
  46. package/templates/next-supabase/SKILLS.md +14 -0
  47. package/templates/next-supabase/SPEC.md +5 -1
  48. package/templates/next-supabase/STYLE_GUIDE.md +3 -1
  49. package/templates/next-supabase/TESTING.md +14 -0
@@ -13,13 +13,17 @@
13
13
  }
14
14
 
15
15
  @media (prefers-reduced-motion: reduce) {
16
- *, *::before, *::after {
16
+ *,
17
+ *::before,
18
+ *::after {
17
19
  animation-duration: 0.01ms !important;
18
20
  transition-duration: 0.01ms !important;
19
21
  }
20
22
  }
21
23
 
22
- * { box-sizing: border-box; }
24
+ * {
25
+ box-sizing: border-box;
26
+ }
23
27
 
24
28
  body {
25
29
  margin: 0;
@@ -79,16 +83,101 @@ body {
79
83
  border: 1px solid rgba(255, 255, 255, 0.12);
80
84
  }
81
85
 
86
+ .level-pill {
87
+ font-size: 12px;
88
+ padding: 6px 12px;
89
+ border-radius: 999px;
90
+ background: rgba(15, 118, 110, 0.35);
91
+ border: 1px solid rgba(153, 246, 228, 0.35);
92
+ font-variant-numeric: tabular-nums;
93
+ }
94
+
95
+ .iceberg-strip {
96
+ display: grid;
97
+ grid-template-columns: repeat(6, 1fr);
98
+ gap: 4px;
99
+ padding: 8px 20px 10px;
100
+ background: #0a1620;
101
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
102
+ }
103
+
104
+ .iceberg-seg {
105
+ text-align: center;
106
+ font-size: 10px;
107
+ padding: 6px 4px;
108
+ border-radius: 6px;
109
+ border: 1px solid rgba(255, 255, 255, 0.12);
110
+ color: #94a3b8;
111
+ opacity: 0.55;
112
+ }
113
+
114
+ .iceberg-seg.current {
115
+ opacity: 1;
116
+ color: #ecfdf5;
117
+ border-color: rgba(153, 246, 228, 0.55);
118
+ background: rgba(15, 118, 110, 0.35);
119
+ }
120
+
121
+ .iceberg-seg.target {
122
+ opacity: 0.85;
123
+ border-style: dashed;
124
+ }
125
+
126
+ .iceberg-seg.deferred {
127
+ opacity: 0.35;
128
+ border-style: dotted;
129
+ }
130
+
131
+ .climb-panel {
132
+ margin: 0 0 12px;
133
+ padding: 10px;
134
+ border-radius: 8px;
135
+ background: rgba(255, 255, 255, 0.04);
136
+ border: 1px solid var(--line);
137
+ }
138
+
139
+ .climb-panel h3 {
140
+ margin: 0 0 8px;
141
+ font-size: 12px;
142
+ text-transform: uppercase;
143
+ letter-spacing: 0.04em;
144
+ color: var(--muted);
145
+ }
146
+
147
+ .climb-panel ol {
148
+ margin: 0 0 10px;
149
+ padding-left: 18px;
150
+ font-size: 12px;
151
+ color: var(--ink);
152
+ }
153
+
154
+ .climb-panel li {
155
+ margin-bottom: 6px;
156
+ }
157
+
158
+ .climb-refresh {
159
+ width: 100%;
160
+ font-size: 12px;
161
+ padding: 6px 8px;
162
+ }
163
+
82
164
  .office-main {
83
165
  display: grid;
84
166
  grid-template-columns: 220px minmax(0, 1fr);
85
167
  gap: 0;
86
- min-height: calc(100vh - 64px);
168
+ min-height: calc(100vh - 96px);
87
169
  }
88
170
 
89
171
  @media (max-width: 900px) {
90
- .office-main { grid-template-columns: 1fr; }
91
- .station-list { border-right: 0; border-bottom: 1px solid var(--line); max-height: 180px; overflow: auto; }
172
+ .office-main {
173
+ grid-template-columns: 1fr;
174
+ }
175
+ .station-list {
176
+ border-right: 0;
177
+ border-bottom: 1px solid var(--line);
178
+ max-height: 180px;
179
+ overflow: auto;
180
+ }
92
181
  }
93
182
 
94
183
  .station-list {
@@ -130,8 +219,12 @@ body {
130
219
  cursor: pointer;
131
220
  }
132
221
 
133
- .station-list button:hover { background: rgba(255, 255, 255, 0.1); }
134
- .station-list button.done { border-color: rgba(74, 222, 128, 0.4); }
222
+ .station-list button:hover {
223
+ background: rgba(255, 255, 255, 0.1);
224
+ }
225
+ .station-list button.done {
226
+ border-color: rgba(74, 222, 128, 0.4);
227
+ }
135
228
  .station-list button .chip {
136
229
  float: right;
137
230
  font-size: 10px;
@@ -224,8 +317,14 @@ body {
224
317
  max-width: 90vw;
225
318
  }
226
319
 
227
- .status.ok { background: #dcfce7; color: #166534; }
228
- .status.error { background: #fee2e2; color: #991b1b; }
320
+ .status.ok {
321
+ background: #dcfce7;
322
+ color: #166534;
323
+ }
324
+ .status.error {
325
+ background: #fee2e2;
326
+ color: #991b1b;
327
+ }
229
328
 
230
329
  .btn {
231
330
  display: inline-flex;
@@ -240,9 +339,18 @@ body {
240
339
  text-decoration: none;
241
340
  }
242
341
 
243
- .btn.primary { background: var(--accent); color: #fff; }
244
- .btn.secondary { background: #334155; color: #f8fafc; }
245
- .btn:disabled { opacity: 0.5; cursor: not-allowed; }
342
+ .btn.primary {
343
+ background: var(--accent);
344
+ color: #fff;
345
+ }
346
+ .btn.secondary {
347
+ background: #334155;
348
+ color: #f8fafc;
349
+ }
350
+ .btn:disabled {
351
+ opacity: 0.5;
352
+ cursor: not-allowed;
353
+ }
246
354
 
247
355
  .panel {
248
356
  position: fixed;
@@ -257,7 +365,9 @@ body {
257
365
  flex-direction: column;
258
366
  }
259
367
 
260
- .panel.hidden { display: none; }
368
+ .panel.hidden {
369
+ display: none;
370
+ }
261
371
 
262
372
  .panel-head {
263
373
  display: flex;
@@ -267,7 +377,10 @@ body {
267
377
  border-bottom: 1px solid var(--line);
268
378
  }
269
379
 
270
- .panel-head h2 { margin: 0; font-size: 18px; }
380
+ .panel-head h2 {
381
+ margin: 0;
382
+ font-size: 18px;
383
+ }
271
384
 
272
385
  .panel-close {
273
386
  background: none;
@@ -317,7 +430,10 @@ body {
317
430
  padding: 10px;
318
431
  }
319
432
 
320
- .panel textarea { min-height: 90px; resize: vertical; }
433
+ .panel textarea {
434
+ min-height: 90px;
435
+ resize: vertical;
436
+ }
321
437
 
322
438
  .panel .agent-role {
323
439
  font-size: 13px;
@@ -363,7 +479,9 @@ body {
363
479
  }
364
480
 
365
481
  .modal[hidden],
366
- .modal.hidden { display: none; }
482
+ .modal.hidden {
483
+ display: none;
484
+ }
367
485
 
368
486
  .modal-card {
369
487
  background: var(--panel);
@@ -374,11 +492,19 @@ body {
374
492
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
375
493
  }
376
494
 
377
- .modal-wide { max-width: 640px; }
495
+ .modal-wide {
496
+ max-width: 640px;
497
+ }
378
498
 
379
- .modal-card h2 { margin: 0 0 8px; }
499
+ .modal-card h2 {
500
+ margin: 0 0 8px;
501
+ }
380
502
 
381
- .why { color: var(--muted); font-size: 14px; margin: 0 0 16px; }
503
+ .why {
504
+ color: var(--muted);
505
+ font-size: 14px;
506
+ margin: 0 0 16px;
507
+ }
382
508
 
383
509
  .depth-grid {
384
510
  display: grid;
@@ -395,14 +521,39 @@ body {
395
521
  font: inherit;
396
522
  }
397
523
 
398
- .depth-card:hover { border-color: var(--accent); }
399
- .depth-card.selected { border-color: var(--accent); background: var(--accent-soft); }
400
- .depth-card strong { display: block; margin-bottom: 4px; }
401
- .depth-card p { margin: 0; font-size: 13px; color: var(--muted); }
524
+ .depth-card:hover {
525
+ border-color: var(--accent);
526
+ }
527
+ .depth-card.selected {
528
+ border-color: var(--accent);
529
+ background: var(--accent-soft);
530
+ }
531
+ .depth-card strong {
532
+ display: block;
533
+ margin-bottom: 4px;
534
+ }
535
+ .depth-card p {
536
+ margin: 0;
537
+ font-size: 13px;
538
+ color: var(--muted);
539
+ }
402
540
 
403
- .review { margin: 0; display: grid; gap: 12px; }
404
- .review dt { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); font-weight: 700; }
405
- .review dd { margin: 4px 0 0; white-space: pre-wrap; }
541
+ .review {
542
+ margin: 0;
543
+ display: grid;
544
+ gap: 12px;
545
+ }
546
+ .review dt {
547
+ font-size: 11px;
548
+ text-transform: uppercase;
549
+ letter-spacing: 0.06em;
550
+ color: var(--muted);
551
+ font-weight: 700;
552
+ }
553
+ .review dd {
554
+ margin: 4px 0 0;
555
+ white-space: pre-wrap;
556
+ }
406
557
 
407
558
  .modal-actions {
408
559
  display: flex;
@@ -411,7 +562,9 @@ body {
411
562
  margin-top: 20px;
412
563
  }
413
564
 
414
- .hidden { display: none !important; }
565
+ .hidden {
566
+ display: none !important;
567
+ }
415
568
 
416
569
  code {
417
570
  font-family: ui-monospace, Menlo, Consolas, monospace;
@@ -507,8 +660,14 @@ code {
507
660
  }
508
661
 
509
662
  @media (max-width: 900px) {
510
- .office-main.studio-layout { grid-template-columns: 1fr; }
511
- .transcript-panel { max-height: 200px; border-left: 0; border-top: 1px solid rgba(255, 255, 255, 0.08); }
663
+ .office-main.studio-layout {
664
+ grid-template-columns: 1fr;
665
+ }
666
+ .transcript-panel {
667
+ max-height: 200px;
668
+ border-left: 0;
669
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
670
+ }
512
671
  }
513
672
 
514
673
  .studio-main .transcript-list {
@@ -35,7 +35,8 @@
35
35
  handoffPulse: null,
36
36
  studioSessionId: boot.activeSessionId || "",
37
37
  studioEvents: [],
38
- speechBubbles: []
38
+ speechBubbles: [],
39
+ agenticLevel: null
39
40
  };
40
41
 
41
42
  const agentRuntime = {};
@@ -44,6 +45,11 @@
44
45
  canvas: document.getElementById("office-floor"),
45
46
  projectName: document.getElementById("project-name"),
46
47
  progressPill: document.getElementById("progress-pill"),
48
+ levelPill: document.getElementById("level-pill"),
49
+ icebergStrip: document.getElementById("iceberg-strip"),
50
+ climbPanel: document.getElementById("climb-panel"),
51
+ climbList: document.getElementById("climb-list"),
52
+ climbRefresh: document.getElementById("climb-refresh"),
47
53
  sessionPill: document.getElementById("session-pill"),
48
54
  stationList: document.getElementById("station-list"),
49
55
  status: document.getElementById("status"),
@@ -188,9 +194,11 @@
188
194
  state.progress = data.progress || {};
189
195
  state.onboarding = data.onboarding || {};
190
196
  state.depth = data.onboarding?.depth || "undecided";
197
+ state.agenticLevel = data.agenticLevel || null;
191
198
  if (Array.isArray(data.agents) && data.agents.length) state.agents = data.agents;
192
199
  els.projectName.textContent = data.projectName || "your project";
193
200
  updateProgressUi();
201
+ updateAgenticLevelUi();
194
202
  renderStationList();
195
203
  if (state.depth === "undecided") showDepthModal();
196
204
  else {
@@ -260,7 +268,7 @@
260
268
  (ev) =>
261
269
  '<li><span class="tx-time">' +
262
270
  escapeHtml((ev.createdAt || "").slice(11, 19)) +
263
- '</span> <strong>' +
271
+ "</span> <strong>" +
264
272
  escapeHtml(ev.agentId || ev.fromAgentId || "session") +
265
273
  "</strong> " +
266
274
  escapeHtml(eventLabel(ev)) +
@@ -316,15 +324,7 @@
316
324
  .map((b) => {
317
325
  const left = offsetLeft + b.x * scaleX;
318
326
  const top = offsetTop + b.y * scaleY - 28;
319
- return (
320
- '<span class="speech-bubble" style="left:' +
321
- left +
322
- "px;top:" +
323
- top +
324
- 'px">' +
325
- escapeHtml(b.text) +
326
- "</span>"
327
- );
327
+ return '<span class="speech-bubble" style="left:' + left + "px;top:" + top + 'px">' + escapeHtml(b.text) + "</span>";
328
328
  })
329
329
  .join("");
330
330
  }
@@ -334,6 +334,57 @@
334
334
  if (els.progressPill) els.progressPill.textContent = pct + "% ready";
335
335
  }
336
336
 
337
+ function updateAgenticLevelUi() {
338
+ const level = state.agenticLevel;
339
+ if (!level || isStudio) return;
340
+ const current = level.currentLevel ?? 3;
341
+ const target = level.targetLevel ?? 5;
342
+ if (els.levelPill) {
343
+ els.levelPill.textContent = "L" + current + " → L" + target;
344
+ els.levelPill.setAttribute("aria-label", "Agentic engineering level " + current + ", target level " + target);
345
+ }
346
+ if (els.icebergStrip) {
347
+ els.icebergStrip.innerHTML = [3, 4, 5, 6, 7, 8]
348
+ .map((n) => {
349
+ let cls = "iceberg-seg";
350
+ if (n === current) cls += " current";
351
+ if (n === target && n !== current) cls += " target";
352
+ if (n >= 7) cls += " deferred";
353
+ return '<span class="' + cls + '">L' + n + "</span>";
354
+ })
355
+ .join("");
356
+ }
357
+ const steps = level.climbSteps || [];
358
+ if (els.climbPanel && els.climbList) {
359
+ if (current >= target || steps.length === 0) {
360
+ els.climbPanel.hidden = true;
361
+ } else {
362
+ els.climbPanel.hidden = false;
363
+ els.climbList.innerHTML = steps
364
+ .slice(0, 3)
365
+ .map((step) => "<li><strong>L" + step.level + "</strong> " + escapeHtml(step.label) + " — " + escapeHtml(step.remediation) + "</li>")
366
+ .join("");
367
+ }
368
+ }
369
+ if (level.maintainerNote && els.status && !els.status.textContent) {
370
+ setStatus("ok", level.maintainerNote);
371
+ }
372
+ }
373
+
374
+ if (els.climbRefresh) {
375
+ els.climbRefresh.addEventListener("click", async () => {
376
+ try {
377
+ const data = await api("/api/agentic-level/refresh", { method: "POST" });
378
+ state.agenticLevel = data.agenticLevel;
379
+ state.progress = data.progress;
380
+ updateAgenticLevelUi();
381
+ setStatus("ok", "Agentic level refreshed.");
382
+ } catch (error) {
383
+ setStatus("error", error.message);
384
+ }
385
+ });
386
+ }
387
+
337
388
  function spawnConfetti(x, y) {
338
389
  if (state.reducedMotion) return;
339
390
  for (let i = 0; i < 12; i += 1) {
@@ -395,17 +446,7 @@
395
446
  const cx = offsetLeft + (station.x + station.w / 2) * TILE * scaleX;
396
447
  const cy = offsetTop + station.y * TILE * scaleY - 4;
397
448
  const st = stationStatus(station);
398
- return (
399
- '<span class="nameplate ' +
400
- st +
401
- '" style="left:' +
402
- cx +
403
- "px;top:" +
404
- cy +
405
- 'px">' +
406
- escapeHtml(station.label) +
407
- "</span>"
408
- );
449
+ return '<span class="nameplate ' + st + '" style="left:' + cx + "px;top:" + cy + 'px">' + escapeHtml(station.label) + "</span>";
409
450
  })
410
451
  .join("");
411
452
  }
@@ -420,13 +461,7 @@
420
461
  ]
421
462
  .map(
422
463
  ([id, title, desc]) =>
423
- '<button type="button" class="depth-card" data-depth="' +
424
- id +
425
- '"><strong>' +
426
- escapeHtml(title) +
427
- "</strong><p>" +
428
- escapeHtml(desc) +
429
- "</p></button>"
464
+ '<button type="button" class="depth-card" data-depth="' + id + '"><strong>' + escapeHtml(title) + "</strong><p>" + escapeHtml(desc) + "</p></button>"
430
465
  )
431
466
  .join("");
432
467
  els.depthGrid.querySelectorAll("[data-depth]").forEach((btn) => {
@@ -757,9 +792,9 @@
757
792
  const req = optional ? "" : " required";
758
793
  if (type === "textarea") {
759
794
  return (
760
- "<label for=\"p-" +
795
+ '<label for="p-' +
761
796
  name +
762
- "\">" +
797
+ '">' +
763
798
  escapeHtml(label) +
764
799
  (hint ? "<span>" + escapeHtml(hint) + "</span>" : "") +
765
800
  '</label><textarea id="p-' +
@@ -776,9 +811,9 @@
776
811
  );
777
812
  }
778
813
  return (
779
- "<label for=\"p-" +
814
+ '<label for="p-' +
780
815
  name +
781
- "\">" +
816
+ '">' +
782
817
  escapeHtml(label) +
783
818
  (hint ? "<span>" + escapeHtml(hint) + "</span>" : "") +
784
819
  '</label><input id="p-' +
@@ -806,7 +841,7 @@
806
841
  field +
807
842
  '">Project briefing<span>Optional — what is unique about this project for ' +
808
843
  escapeHtml(station.label) +
809
- "?</span></label><textarea id=\"p-" +
844
+ '?</span></label><textarea id="p-' +
810
845
  field +
811
846
  '" name="' +
812
847
  field +
@@ -819,16 +854,7 @@
819
854
  const maps = {
820
855
  ide: () => {
821
856
  const opts = (boot.ideSurfaces || [])
822
- .map(
823
- (s) =>
824
- '<option value="' +
825
- s.id +
826
- '"' +
827
- (state.form.ideSurface === s.id ? " selected" : "") +
828
- ">" +
829
- escapeHtml(s.label) +
830
- "</option>"
831
- )
857
+ .map((s) => '<option value="' + s.id + '"' + (state.form.ideSurface === s.id ? " selected" : "") + ">" + escapeHtml(s.label) + "</option>")
832
858
  .join("");
833
859
  return (
834
860
  '<label for="p-ideSurface">Primary AI coding tool</label><select id="p-ideSurface" name="ideSurface" required><option value="">Choose…</option>' +
@@ -847,14 +873,10 @@
847
873
  inputField("primaryWorkflows", "Top workflows", "One per line.", "textarea", "Workflow one", false),
848
874
  access: () =>
849
875
  '<label for="p-tenantModel">Who uses the system?</label><select id="p-tenantModel" name="tenantModel">' +
850
- (boot.tenantModels || [])
851
- .map((c) => '<option value="' + c + '"' + (state.form.tenantModel === c ? " selected" : "") + ">" + c + "</option>")
852
- .join("") +
876
+ (boot.tenantModels || []).map((c) => '<option value="' + c + '"' + (state.form.tenantModel === c ? " selected" : "") + ">" + c + "</option>").join("") +
853
877
  "</select>" +
854
878
  inputField("owner", "Project owner", "Optional.", "text", "", true) +
855
- (boot.hasSupabase
856
- ? '<div class="hint-box">Supabase detected.<button type="button" id="apply-supabase-auth">Insert auth baseline</button></div>'
857
- : "") +
879
+ (boot.hasSupabase ? '<div class="hint-box">Supabase detected.<button type="button" id="apply-supabase-auth">Insert auth baseline</button></div>' : "") +
858
880
  inputField("authModel", "Authentication model", "Rules agents must preserve.", "textarea", "Describe auth boundaries.", false),
859
881
  ui: () =>
860
882
  inputField("uiPreferred", "UI should feel like…", "", "textarea", "Clear, readable, task-first.", false) +