@zigrivers/scaffold 3.30.0 → 3.31.1

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 (69) hide show
  1. package/content/guides/AUTHORING.md +8 -5
  2. package/content/guides/cli/index.html +367 -14
  3. package/content/guides/concepts/index.html +367 -14
  4. package/content/guides/dashboard/index.html +367 -14
  5. package/content/guides/index.html +368 -15
  6. package/content/guides/install/index.html +373 -20
  7. package/content/guides/install/index.md +6 -6
  8. package/content/guides/knowledge/index.html +367 -14
  9. package/content/guides/knowledge-freshness/index.html +369 -16
  10. package/content/guides/knowledge-freshness/index.md +2 -2
  11. package/content/guides/mmr/index.html +373 -20
  12. package/content/guides/multi-agent/index.html +369 -16
  13. package/content/guides/multi-agent/index.md +2 -2
  14. package/content/guides/observability/index.html +367 -14
  15. package/content/guides/pipeline/index.html +378 -37
  16. package/content/guides/pipeline/index.md +8 -8
  17. package/content/guides/review-workflow/index.html +367 -14
  18. package/dist/cli/commands/info.test.js +3 -0
  19. package/dist/cli/commands/info.test.js.map +1 -1
  20. package/dist/cli/commands/list.test.js +6 -0
  21. package/dist/cli/commands/list.test.js.map +1 -1
  22. package/dist/cli/commands/status.d.ts.map +1 -1
  23. package/dist/cli/commands/status.js +38 -4
  24. package/dist/cli/commands/status.js.map +1 -1
  25. package/dist/cli/commands/status.test.js +66 -2
  26. package/dist/cli/commands/status.test.js.map +1 -1
  27. package/dist/e2e/commands.test.js +3 -0
  28. package/dist/e2e/commands.test.js.map +1 -1
  29. package/dist/e2e/knowledge.test.js +5 -0
  30. package/dist/e2e/knowledge.test.js.map +1 -1
  31. package/dist/guides/build.d.ts +1 -1
  32. package/dist/guides/build.d.ts.map +1 -1
  33. package/dist/guides/build.js +14 -7
  34. package/dist/guides/build.js.map +1 -1
  35. package/dist/guides/build.test.js +39 -0
  36. package/dist/guides/build.test.js.map +1 -1
  37. package/dist/guides/chrome.d.ts.map +1 -1
  38. package/dist/guides/chrome.js +83 -12
  39. package/dist/guides/chrome.js.map +1 -1
  40. package/dist/guides/cli-guides.test.js +3 -0
  41. package/dist/guides/cli-guides.test.js.map +1 -1
  42. package/dist/guides/dashboard-theme.css +8 -0
  43. package/dist/guides/directives-tabs.test.js +47 -0
  44. package/dist/guides/directives-tabs.test.js.map +1 -1
  45. package/dist/guides/directives.d.ts.map +1 -1
  46. package/dist/guides/directives.js +14 -0
  47. package/dist/guides/directives.js.map +1 -1
  48. package/dist/guides/guides.css +268 -0
  49. package/dist/guides/index-page.d.ts.map +1 -1
  50. package/dist/guides/index-page.js +41 -8
  51. package/dist/guides/index-page.js.map +1 -1
  52. package/dist/guides/sanitize.d.ts.map +1 -1
  53. package/dist/guides/sanitize.js +4 -0
  54. package/dist/guides/sanitize.js.map +1 -1
  55. package/dist/guides/template.d.ts.map +1 -1
  56. package/dist/guides/template.js +7 -2
  57. package/dist/guides/template.js.map +1 -1
  58. package/dist/state/ensure-v3-migration.d.ts.map +1 -1
  59. package/dist/state/ensure-v3-migration.js +4 -2
  60. package/dist/state/ensure-v3-migration.js.map +1 -1
  61. package/dist/utils/fs.d.ts +6 -30
  62. package/dist/utils/fs.d.ts.map +1 -1
  63. package/dist/utils/fs.js +66 -58
  64. package/dist/utils/fs.js.map +1 -1
  65. package/dist/utils/fs.test.js +53 -6
  66. package/dist/utils/fs.test.js.map +1 -1
  67. package/dist/validation/index.test.js +3 -0
  68. package/dist/validation/index.test.js.map +1 -1
  69. package/package.json +2 -2
@@ -48,9 +48,13 @@
48
48
  --yellow: #d97706;
49
49
  --yellow-bg: #fffbeb;
50
50
  --yellow-border:#fde68a;
51
+ --red: #dc2626;
52
+ --red-bg: #fef2f2;
53
+ --red-border: #fecaca;
51
54
  --gray: #9ca3af;
52
55
  --gray-bg: #f3f4f6;
53
56
  --gray-border: #e5e7eb;
57
+ --scrim: rgba(15, 17, 23, 0.45);
54
58
 
55
59
  /* Semantic: Next Banner */
56
60
  --next-bg: #eef2ff;
@@ -133,9 +137,13 @@
133
137
  --yellow: #fbbf24;
134
138
  --yellow-bg: rgba(120, 53, 15, 0.25);
135
139
  --yellow-border:rgba(251, 191, 36, 0.20);
140
+ --red: #f87171;
141
+ --red-bg: rgba(127, 29, 29, 0.25);
142
+ --red-border: rgba(248, 113, 113, 0.22);
136
143
  --gray: #6b7294;
137
144
  --gray-bg: #252940;
138
145
  --gray-border: #363c58;
146
+ --scrim: rgba(0, 0, 0, 0.6);
139
147
 
140
148
  /* Semantic: Next Banner */
141
149
  --next-bg: rgba(30, 27, 75, 0.50);
@@ -1078,17 +1086,290 @@ figure.mermaid svg .marker {
1078
1086
  }
1079
1087
  figure.mermaid svg .edgeLabel,
1080
1088
  figure.mermaid svg .edgeLabel text { fill: var(--text-muted); color: var(--text-muted); }
1089
+
1090
+ /* ============================================================================
1091
+ * guides.css — component + layout styles for `scaffold guides` reference pages.
1092
+ *
1093
+ * Pairs with lib/dashboard-theme.css (the token source) and src/guides/chrome.ts
1094
+ * (the behavior). Styles the guide CHROME (.topbar, .layout, .rail, nav.toc,
1095
+ * .content) and the markdown DIRECTIVES (callouts, sev chips, filter-tables,
1096
+ * charts, tabs, citations) plus base prose typography.
1097
+ *
1098
+ * DESIGN SYSTEM: all COLORS come from dashboard-theme.css tokens, and spacing
1099
+ * uses the --sp-* scale wherever it maps. The few structural layout constants
1100
+ * (topbar height, rail/drawer width, chart label column, card min) are declared
1101
+ * as local custom properties below; a handful of sub-scale UI values (chip/bar
1102
+ * sizing, em-based inline-code padding) and the responsive breakpoint are
1103
+ * literal because no token expresses them. Both themes are covered because every
1104
+ * color is a token.
1105
+ * ==========================================================================*/
1106
+
1107
+ :root {
1108
+ --topbar-h: 52px; /* sticky topbar height; rail sticky offset keys off it */
1109
+ --rail-w: 260px; /* desktop TOC sidebar column */
1110
+ --drawer-w: 280px; /* mobile off-canvas TOC drawer */
1111
+ --card-min: 260px; /* index card min track width */
1112
+ --chart-label-w: 90px; /* chart row label column min */
1113
+ }
1114
+
1115
+ /* ── Base / reset on top of the token base in dashboard-theme.css ─────────── */
1116
+ .content a { color: var(--accent); text-decoration: none; }
1117
+ .content a:hover { text-decoration: underline; }
1118
+ .content strong { font-weight: var(--fw-semi); }
1119
+ .content hr { border: 0; border-top: 1px solid var(--border-light); margin: var(--sp-6) 0; }
1120
+
1121
+ /* Consistent keyboard focus for every interactive control (a11y). */
1122
+ .topbar button:focus-visible,
1123
+ .copy-btn:focus-visible,
1124
+ .tab-btn:focus-visible,
1125
+ .filter-input:focus-visible,
1126
+ nav.toc a:focus-visible,
1127
+ .guide-card:focus-visible,
1128
+ .content a:focus-visible {
1129
+ outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm);
1130
+ }
1131
+
1132
+ /* ── Topbar ────────────────────────────────────────────────────────────────*/
1133
+ .topbar {
1134
+ position: sticky; top: 0; z-index: 60; height: var(--topbar-h);
1135
+ display: flex; align-items: center; gap: var(--sp-3);
1136
+ padding: 0 var(--page-pad);
1137
+ background: var(--bg-card); border-bottom: 1px solid var(--border);
1138
+ }
1139
+ .topbar h1 {
1140
+ flex: 1; min-width: 0; margin: 0;
1141
+ font-size: var(--text-lg); font-weight: var(--fw-bold);
1142
+ letter-spacing: var(--ls-tight);
1143
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1144
+ }
1145
+ .topbar button {
1146
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
1147
+ border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
1148
+ line-height: 1; cursor: pointer; font-family: inherit;
1149
+ }
1150
+ .topbar button:hover { background: var(--bg-hover); border-color: var(--accent); }
1151
+ .nav-toggle { display: none; }
1152
+
1153
+ /* ── Layout: sticky sidebar TOC + reading-width content ──────────────────── */
1154
+ .layout {
1155
+ max-width: var(--max-w); margin: 0 auto;
1156
+ display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr);
1157
+ gap: var(--sp-8); padding: 0 var(--page-pad);
1158
+ }
1159
+ .rail {
1160
+ position: sticky; top: var(--topbar-h); align-self: start;
1161
+ height: calc(100vh - var(--topbar-h)); overflow-y: auto;
1162
+ padding: var(--sp-5) 0; border-right: 1px solid var(--border-light);
1163
+ }
1164
+ .content { min-width: 0; padding: var(--sp-6) 0 var(--sp-10); }
1165
+
1166
+ /* Backdrop behind the mobile drawer (toggled with the rail via chrome.ts). */
1167
+ .rail-backdrop { display: none; }
1168
+ /* In-drawer close button — hidden on desktop (the rail is a static sidebar). */
1169
+ .rail-close { display: none; }
1170
+
1171
+ /* ── Table of contents (scrollspy marks a.active) ────────────────────────── */
1172
+ nav.toc ul { list-style: none; margin: 0; padding: 0; }
1173
+ nav.toc li { margin: 0; }
1174
+ nav.toc a {
1175
+ display: block; padding: var(--sp-1) var(--sp-3); line-height: 1.35;
1176
+ color: var(--text-muted); font-size: var(--text-sm);
1177
+ text-decoration: none; border-left: 2px solid transparent;
1178
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
1179
+ }
1180
+ nav.toc a:hover { color: var(--text); background: var(--bg-hover); }
1181
+ nav.toc a.active {
1182
+ color: var(--accent); border-left-color: var(--accent);
1183
+ background: var(--accent-glow); font-weight: var(--fw-medium);
1184
+ }
1185
+ nav.toc li.toc-3 a { padding-left: var(--sp-6); font-size: var(--text-xs); }
1186
+
1187
+ /* ── Prose typography ──────────────────────────────────────────────────────*/
1188
+ .content h2 {
1189
+ font-size: var(--text-xl); letter-spacing: var(--ls-tight);
1190
+ margin: var(--sp-8) 0 var(--sp-3); padding-bottom: var(--sp-2);
1191
+ border-bottom: 1px solid var(--border-light); scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
1192
+ }
1193
+ .content h3 {
1194
+ font-size: var(--text-lg); margin: var(--sp-5) 0 var(--sp-2);
1195
+ scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
1196
+ }
1197
+ .content p { margin: var(--sp-3) 0; line-height: var(--lh-relaxed); }
1198
+ .content ul, .content ol { margin: var(--sp-3) 0; padding-left: var(--sp-6); }
1199
+ .content li { margin: var(--sp-1) 0; line-height: var(--lh-relaxed); }
1200
+ .content blockquote {
1201
+ margin: var(--sp-3) 0; padding: var(--sp-1) var(--sp-4);
1202
+ border-left: 3px solid var(--border); color: var(--text-muted);
1203
+ }
1204
+
1205
+ /* ── Inline code + code blocks (chrome.ts wraps <pre> in .code + .copy-btn) ──*/
1206
+ .content code {
1207
+ font-family: var(--font-mono); font-size: 0.85em;
1208
+ background: var(--bg-inset); padding: 0.12em 0.4em; border-radius: var(--radius-sm);
1209
+ }
1210
+ .content .code { position: relative; margin: var(--sp-3) 0; }
1211
+ .content .code pre {
1212
+ margin: 0; padding: var(--sp-3) var(--sp-4); overflow-x: auto;
1213
+ background: var(--bg-inset); border: 1px solid var(--border-light);
1214
+ border-radius: var(--radius-sm); font-family: var(--font-mono);
1215
+ font-size: var(--text-sm); line-height: var(--lh-relaxed);
1216
+ }
1217
+ .content .code pre code { background: none; padding: 0; font-size: inherit; }
1218
+ .copy-btn {
1219
+ position: absolute; top: var(--sp-1); right: var(--sp-1);
1220
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text-muted);
1221
+ border-radius: var(--radius-sm); font-size: var(--text-xs); padding: var(--sp-1) var(--sp-2);
1222
+ cursor: pointer; opacity: 0.85; font-family: inherit;
1223
+ }
1224
+ .copy-btn:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }
1225
+
1226
+ /* ── Callouts ─ (border-color BEFORE border-left-color so the accent wins) ── */
1227
+ .callout {
1228
+ margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4);
1229
+ border: 1px solid var(--border); border-left-width: 3px;
1230
+ border-radius: var(--radius-sm); background: var(--bg-card);
1231
+ }
1232
+ .callout > :first-child { margin-top: 0; }
1233
+ .callout > :last-child { margin-bottom: 0; }
1234
+ .callout-note, .callout-info { background: var(--blue-bg); border-color: var(--blue-border); border-left-color: var(--blue); }
1235
+ .callout-tip { background: var(--green-bg); border-color: var(--green-border); border-left-color: var(--green); }
1236
+ .callout-warning { background: var(--yellow-bg); border-color: var(--yellow-border); border-left-color: var(--yellow); }
1237
+ .callout-danger { background: var(--red-bg); border-color: var(--red-border); border-left-color: var(--red); }
1238
+
1239
+ /* ── Severity chips (:sev) — tight pill, sub-scale vertical padding ───────── */
1240
+ .sev {
1241
+ display: inline-block; font-size: var(--text-xs); font-weight: var(--fw-semi);
1242
+ padding: 1px var(--sp-2); border-radius: 999px; line-height: 1.5;
1243
+ border: 1px solid var(--border); background: var(--bg-inset); color: var(--text-muted);
1244
+ white-space: nowrap;
1245
+ }
1246
+ .sev-p0 { color: var(--sev-p0); border-color: var(--sev-p0); }
1247
+ .sev-p1 { color: var(--sev-p1); border-color: var(--sev-p1); }
1248
+ .sev-p2 { color: var(--sev-p2); border-color: var(--sev-p2); }
1249
+ .sev-p3 { color: var(--sev-p3); border-color: var(--sev-p3); }
1250
+ .sev-pass { color: var(--sev-pass); border-color: var(--sev-pass); }
1251
+
1252
+ /* ── Citations (:cite) — inline provenance refs ──────────────────────────── */
1253
+ .fp, .cite-advisory {
1254
+ font-family: var(--font-mono); font-size: 0.82em;
1255
+ padding: 0.05em 0.35em; border-radius: var(--radius-sm);
1256
+ background: var(--bg-inset); border: 1px solid var(--border-light);
1257
+ }
1258
+ .fp { color: var(--accent); }
1259
+ .cite-advisory { color: var(--text-faint); border-style: dashed; }
1260
+
1261
+ /* ── Tables + filter-tables ──────────────────────────────────────────────── */
1262
+ .content table { width: 100%; border-collapse: collapse; margin: var(--sp-3) 0; font-size: var(--text-sm); }
1263
+ .content th, .content td { text-align: left; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border-light); vertical-align: top; }
1264
+ .content th {
1265
+ color: var(--text-muted); font-weight: var(--fw-semi); font-size: var(--text-xs);
1266
+ text-transform: uppercase; letter-spacing: var(--ls-wide); border-bottom-color: var(--border);
1267
+ }
1268
+ .content tbody tr:hover { background: var(--bg-hover); }
1269
+ .content td code { white-space: nowrap; }
1270
+ .filter-table { margin: var(--sp-4) 0; }
1271
+ .filter-input {
1272
+ width: 100%; max-width: 320px; margin-bottom: var(--sp-2);
1273
+ padding: var(--sp-2) var(--sp-3); font-family: inherit; font-size: var(--text-sm);
1274
+ color: var(--text); background: var(--bg-card);
1275
+ border: 1px solid var(--border); border-radius: var(--radius-sm);
1276
+ }
1277
+ .filter-input:focus { border-color: var(--accent); }
1278
+
1279
+ /* ── Charts (:::chart) — label + proportional bar (fill carries inline width%) */
1280
+ .chart-block { margin: var(--sp-4) 0; }
1281
+ .chart-row {
1282
+ display: grid; grid-template-columns: minmax(var(--chart-label-w), 24%) 1fr;
1283
+ align-items: center; gap: var(--sp-3); margin: var(--sp-1) 0;
1284
+ }
1285
+ .chart-label {
1286
+ font-size: var(--text-sm); color: var(--text-muted); text-align: right;
1287
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1288
+ }
1289
+ .chart-row .chart-bar {
1290
+ height: 0.9em; min-width: 2px; background: var(--accent);
1291
+ border-radius: var(--radius-sm);
1292
+ }
1293
+
1294
+ /* ── Tabs (::::tabs / :::tab) ─────────────────────────────────────────────── */
1295
+ .tabs { margin: var(--sp-4) 0; }
1296
+ .tablist { display: flex; flex-wrap: wrap; gap: var(--sp-1); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-3); }
1297
+ .tab-btn {
1298
+ background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
1299
+ padding: var(--sp-2) var(--sp-3); color: var(--text-muted);
1300
+ font-family: inherit; font-size: var(--text-sm); font-weight: var(--fw-medium); cursor: pointer;
1301
+ }
1302
+ .tab-btn:hover { color: var(--text); }
1303
+ .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
1304
+ .tabpane { display: none; }
1305
+ .tabpane.active { display: block; }
1306
+
1307
+ /* ── Mermaid diagrams ────────────────────────────────────────────────────── */
1308
+ .content figure { margin: var(--sp-4) 0; text-align: center; }
1309
+ .content figure svg, .content > svg, .content .mermaid svg { max-width: 100%; height: auto; }
1310
+
1311
+ /* ── Index page: category card grid ──────────────────────────────────────── */
1312
+ .content .lead { color: var(--text-muted); font-size: var(--text-base); max-width: 60ch; margin-top: var(--sp-2); }
1313
+ .guide-cards {
1314
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--card-min), 100%), 1fr));
1315
+ gap: var(--sp-4); margin: var(--sp-4) 0 var(--sp-6);
1316
+ }
1317
+ .guide-card {
1318
+ display: flex; flex-direction: column; gap: var(--sp-2);
1319
+ padding: var(--sp-4); background: var(--bg-card);
1320
+ border: 1px solid var(--border); border-radius: var(--radius);
1321
+ color: inherit; text-decoration: none;
1322
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
1323
+ }
1324
+ .guide-card:hover { border-color: var(--accent); box-shadow: var(--shadow-md); text-decoration: none; }
1325
+ .guide-card-title { font-weight: var(--fw-semi); color: var(--accent); font-size: var(--text-base); }
1326
+ .guide-card-desc { color: var(--text-muted); font-size: var(--text-sm); line-height: var(--lh-normal); }
1327
+
1328
+ /* ── Responsive: TOC becomes an off-canvas drawer (chrome.ts toggles .open) ──*/
1329
+ /* 860px is literal — media queries cannot read custom properties. Revisit it if
1330
+ --topbar-h / --rail-w / --drawer-w change (the drawer sticky offsets key off them). */
1331
+ @media (max-width: 860px) {
1332
+ .nav-toggle { display: inline-flex; align-items: center; }
1333
+ .layout { grid-template-columns: 1fr; gap: 0; }
1334
+ .rail {
1335
+ position: fixed; top: var(--topbar-h); left: 0; bottom: 0; width: var(--drawer-w); z-index: 50;
1336
+ height: auto; background: var(--bg-card); border-right: 1px solid var(--border);
1337
+ padding: var(--sp-4); box-shadow: var(--shadow-lg);
1338
+ transform: translateX(-100%); transition: transform 0.2s ease, visibility 0.2s ease;
1339
+ /* Closed drawer is off-screen AND removed from tab order / pointer events. */
1340
+ visibility: hidden; pointer-events: none;
1341
+ }
1342
+ .rail.open { transform: translateX(0); visibility: visible; pointer-events: auto; }
1343
+ .rail-close {
1344
+ display: block; margin-left: auto; margin-bottom: var(--sp-2);
1345
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
1346
+ border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
1347
+ line-height: 1; cursor: pointer; font-family: inherit;
1348
+ }
1349
+ .rail-close:hover { background: var(--bg-hover); border-color: var(--accent); }
1350
+ .rail-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1351
+ .rail-backdrop {
1352
+ display: block; position: fixed; inset: var(--topbar-h) 0 0 0;
1353
+ background: var(--scrim); z-index: 49;
1354
+ opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
1355
+ }
1356
+ .rail.open ~ .rail-backdrop { opacity: 1; pointer-events: auto; }
1357
+ }
1081
1358
  </style>
1082
1359
  <script>(function(){try{var t=localStorage.getItem('guide-theme');if(!t&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches)t='dark';if(t)document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
1083
1360
  </head>
1084
1361
  <body>
1085
1362
  <header class="topbar">
1086
- <button data-action="nav" class="nav-toggle" aria-label="Toggle navigation">☰</button>
1363
+ <button data-action="nav" class="nav-toggle" aria-label="Toggle navigation"
1364
+ aria-expanded="false" aria-controls="guide-toc">☰</button>
1087
1365
  <h1>Install, Update &amp; Adopt</h1>
1088
1366
  <button data-action="theme" class="theme-toggle" aria-label="Toggle theme">◐</button>
1089
1367
  </header>
1090
1368
  <div class="layout">
1091
- <aside class="rail"><nav class="toc" aria-label="Table of contents"><ul><li class="toc-2"><a href="#overview">Overview</a></li><li class="toc-2"><a href="#installing">Installing</a></li><li class="toc-2"><a href="#keeping-current">Keeping current</a></li><li class="toc-2"><a href="#starting-fresh-vs-adopting">Starting fresh vs. adopting</a></li><li class="toc-3"><a href="#scaffold-init">scaffold init</a></li><li class="toc-3"><a href="#scaffold-adopt">scaffold adopt</a></li><li class="toc-2"><a href="#troubleshooting">Troubleshooting</a></li><li class="toc-2"><a href="#see-also">See also</a></li></ul></nav></aside>
1369
+ <aside class="rail" id="guide-toc">
1370
+ <button class="rail-close" data-action="nav" aria-label="Close navigation">✕</button>
1371
+ <nav class="toc" aria-label="Table of contents"><ul><li class="toc-2"><a href="#overview">Overview</a></li><li class="toc-2"><a href="#installing">Installing</a></li><li class="toc-2"><a href="#keeping-current">Keeping current</a></li><li class="toc-2"><a href="#starting-fresh-vs-adopting">Starting fresh vs. adopting</a></li><li class="toc-3"><a href="#scaffold-init">scaffold init</a></li><li class="toc-3"><a href="#scaffold-adopt">scaffold adopt</a></li><li class="toc-2"><a href="#troubleshooting">Troubleshooting</a></li><li class="toc-2"><a href="#see-also">See also</a></li></ul></nav>
1372
+ </aside>
1092
1373
  <main class="content"><h2 id="overview">Overview</h2>
1093
1374
  <p>Scaffold ships as one tool with two install surfaces: the <strong><code>scaffold</code> CLI</strong>
1094
1375
  (distributed on npm as <code>@zigrivers/scaffold</code> and via Homebrew) and an optional
@@ -1107,12 +1388,12 @@ system Node.</p></div>
1107
1388
  <h2 id="installing">Installing</h2>
1108
1389
  <p>Pick one of the three surfaces below. The npm and Homebrew tabs install the CLI;
1109
1390
  the plugin tab adds the optional Claude Code skills on top.</p>
1110
- <div class="tabs"><div class="tablist" role="tablist"><button class="tab-btn active" role="tab" data-tab="0">npm</button><button class="tab-btn" role="tab" data-tab="1">Homebrew</button><button class="tab-btn" role="tab" data-tab="2">Claude</button></div><div class="tabpane active" data-tab="0"><pre><code class="language-bash">npm install -g @zigrivers/scaffold
1391
+ <div class="tabs"><div class="tablist" role="tablist"><button id="tab-0-0" class="tab-btn active" role="tab" data-tab="0" aria-controls="tabpane-0-0" aria-selected="true" tabindex="0">npm</button><button id="tab-0-1" class="tab-btn" role="tab" data-tab="1" aria-controls="tabpane-0-1" aria-selected="false" tabindex="-1">Homebrew</button><button id="tab-0-2" class="tab-btn" role="tab" data-tab="2" aria-controls="tabpane-0-2" aria-selected="false" tabindex="-1">Claude Code plugin</button></div><div id="tabpane-0-0" class="tabpane active" role="tabpanel" data-tab="0" aria-labelledby="tab-0-0" tabindex="0"><pre><code class="language-bash">npm install -g @zigrivers/scaffold
1111
1392
  </code></pre><p>Verify with <code>scaffold version</code>. This is the recommended path — it uses your
1112
- system Node and updates with <code>npm update -g</code>.</p></div><div class="tabpane" data-tab="1"><pre><code class="language-bash">brew tap zigrivers/scaffold
1393
+ system Node and updates with <code>npm update -g</code>.</p></div><div id="tabpane-0-1" class="tabpane" role="tabpanel" data-tab="1" aria-labelledby="tab-0-1" tabindex="0"><pre><code class="language-bash">brew tap zigrivers/scaffold
1113
1394
  brew install scaffold
1114
1395
  </code></pre><p>The formula installs and manages Node as a dependency, so no separate Node
1115
- install is needed. Verify with <code>scaffold version</code>.</p></div><div class="tabpane" data-tab="2"><p>Inside a Claude Code session:</p><pre><code class="language-text">/plugin marketplace add zigrivers/scaffold
1396
+ install is needed. Verify with <code>scaffold version</code>.</p></div><div id="tabpane-0-2" class="tabpane" role="tabpanel" data-tab="2" aria-labelledby="tab-0-2" tabindex="0"><p>Inside a Claude Code session:</p><pre><code class="language-text">/plugin marketplace add zigrivers/scaffold
1116
1397
  /plugin install scaffold@zigrivers-scaffold
1117
1398
  </code></pre><p>This is <strong>optional</strong> and complements the CLI — it auto-activates the Scaffold
1118
1399
  Runner and pipeline-reference skills for interactive guidance. Everything the
@@ -1134,9 +1415,9 @@ anything else falls back to <code>npx @zigrivers/scaffold@latest</code>
1134
1415
  themselves and run <code>brew update &#x26;&#x26; brew upgrade scaffold</code>, or the upgrade can
1135
1416
  silently no-op against a stale tap cache (see the danger callout below).</p>
1136
1417
  <p>Run the upgrade for your channel:</p>
1137
- <div class="tabs"><div class="tablist" role="tablist"><button class="tab-btn active" role="tab" data-tab="0">npm</button><button class="tab-btn" role="tab" data-tab="1">Homebrew</button><button class="tab-btn" role="tab" data-tab="2">Claude</button></div><div class="tabpane active" data-tab="0"><pre><code class="language-bash">npm update -g @zigrivers/scaffold
1138
- </code></pre></div><div class="tabpane" data-tab="1"><pre><code class="language-bash">brew update &#x26;&#x26; brew upgrade scaffold
1139
- </code></pre></div><div class="tabpane" data-tab="2"><pre><code class="language-text">/plugin marketplace update zigrivers-scaffold
1418
+ <div class="tabs"><div class="tablist" role="tablist"><button id="tab-1-0" class="tab-btn active" role="tab" data-tab="0" aria-controls="tabpane-1-0" aria-selected="true" tabindex="0">npm</button><button id="tab-1-1" class="tab-btn" role="tab" data-tab="1" aria-controls="tabpane-1-1" aria-selected="false" tabindex="-1">Homebrew</button><button id="tab-1-2" class="tab-btn" role="tab" data-tab="2" aria-controls="tabpane-1-2" aria-selected="false" tabindex="-1">Claude Code plugin</button></div><div id="tabpane-1-0" class="tabpane active" role="tabpanel" data-tab="0" aria-labelledby="tab-1-0" tabindex="0"><pre><code class="language-bash">npm update -g @zigrivers/scaffold
1419
+ </code></pre></div><div id="tabpane-1-1" class="tabpane" role="tabpanel" data-tab="1" aria-labelledby="tab-1-1" tabindex="0"><pre><code class="language-bash">brew update &#x26;&#x26; brew upgrade scaffold
1420
+ </code></pre></div><div id="tabpane-1-2" class="tabpane" role="tabpanel" data-tab="2" aria-labelledby="tab-1-2" tabindex="0"><pre><code class="language-text">/plugin marketplace update zigrivers-scaffold
1140
1421
  </code></pre></div></div>
1141
1422
  <div class="callout callout-danger"><p><strong>Homebrew: run <code>brew update</code> <em>before</em> <code>brew upgrade</code>.</strong> <code>brew outdated</code> and
1142
1423
  <code>brew upgrade</code> both read from the local tap cache. Without a preceding
@@ -1202,6 +1483,7 @@ and the order they run in.</li>
1202
1483
  <li><a href="../multi-agent/index.md">Multi-agent workflow</a>{mode=advisory} — running the
1203
1484
  pipeline across parallel worktrees.</li>
1204
1485
  </ul></main>
1486
+ <div class="rail-backdrop" data-action="nav" aria-hidden="true"></div>
1205
1487
  </div>
1206
1488
  <script>(function(){
1207
1489
  var LS_KEY = 'guide-theme';
@@ -1220,12 +1502,69 @@ pipeline across parallel worktrees.</li>
1220
1502
  });
1221
1503
  });
1222
1504
 
1223
- // ─── Mobile nav ──────────────────────────────────────────────────────────
1505
+ // ─── Mobile nav (drawer + backdrop; aria-expanded + Escape-to-close) ──────
1506
+ function setNav(open) {
1507
+ var rail = document.querySelector('.rail');
1508
+ if (rail) rail.classList.toggle('open', open);
1509
+ var toggle = document.querySelector('.nav-toggle');
1510
+ if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1511
+ // Modal-drawer focus containment: while open, make the page content inert
1512
+ // (out of tab order + a11y tree) and move focus into the drawer; on close,
1513
+ // restore content and return focus to the toggle.
1514
+ var main = document.querySelector('.content');
1515
+ if (main) main.inert = open;
1516
+ if (open) {
1517
+ var first = rail && rail.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
1518
+ if (first) first.focus();
1519
+ else if (rail) { rail.setAttribute('tabindex', '-1'); rail.focus(); }
1520
+ } else if (toggle) {
1521
+ toggle.focus();
1522
+ }
1523
+ }
1524
+ // If the viewport grows past the mobile breakpoint while the drawer is open,
1525
+ // the rail becomes the desktop sidebar and the toggle hides — clear the open
1526
+ // state so .content doesn't stay inert with no way to close it.
1527
+ if (window.matchMedia) {
1528
+ var mq = window.matchMedia('(max-width: 860px)');
1529
+ var onMq = function() {
1530
+ if (mq.matches) return;
1531
+ var rail = document.querySelector('.rail');
1532
+ if (rail) rail.classList.remove('open');
1533
+ var toggle = document.querySelector('.nav-toggle');
1534
+ if (toggle) toggle.setAttribute('aria-expanded', 'false');
1535
+ var main = document.querySelector('.content');
1536
+ if (main) main.inert = false;
1537
+ };
1538
+ if (mq.addEventListener) mq.addEventListener('change', onMq);
1539
+ else if (mq.addListener) mq.addListener(onMq);
1540
+ }
1224
1541
  document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
1225
1542
  btn.addEventListener('click', function() {
1226
1543
  var rail = document.querySelector('.rail');
1227
- if (rail) rail.classList.toggle('open');
1544
+ setNav(!(rail && rail.classList.contains('open')));
1545
+ });
1546
+ });
1547
+ // Selecting a TOC link closes the drawer (so the now-active content isn't
1548
+ // left inert behind the panel) before the anchor navigation scrolls.
1549
+ var drawerRail = document.querySelector('.rail');
1550
+ if (drawerRail) {
1551
+ drawerRail.querySelectorAll('a').forEach(function(a) {
1552
+ a.addEventListener('click', function() {
1553
+ if (drawerRail.classList.contains('open')) setNav(false);
1554
+ });
1228
1555
  });
1556
+ }
1557
+ document.addEventListener('keydown', function(e) {
1558
+ var rail = document.querySelector('.rail');
1559
+ if (!rail || !rail.classList.contains('open')) return;
1560
+ if (e.key === 'Escape') { setNav(false); return; } // setNav restores focus to the toggle
1561
+ // Trap Tab within the open drawer (modal pattern).
1562
+ if (e.key !== 'Tab') return;
1563
+ var f = rail.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
1564
+ if (!f.length) return;
1565
+ var first = f[0], last = f[f.length - 1];
1566
+ if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
1567
+ else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
1229
1568
  });
1230
1569
 
1231
1570
  // ─── Copy buttons ─────────────────────────────────────────────────────────
@@ -1252,17 +1591,31 @@ pipeline across parallel worktrees.</li>
1252
1591
  wrapper.insertBefore(btn, pre);
1253
1592
  });
1254
1593
 
1255
- // ─── Tabs ─────────────────────────────────────────────────────────────────
1594
+ // ─── Tabs (ARIA pattern: aria-selected + roving tabindex + arrow keys) ────
1595
+ function activateTab(group, btn, focus) {
1596
+ var idx = btn.getAttribute('data-tab');
1597
+ group.querySelectorAll('.tab-btn').forEach(function(b) {
1598
+ var on = b === btn;
1599
+ b.classList.toggle('active', on);
1600
+ b.setAttribute('aria-selected', on ? 'true' : 'false');
1601
+ b.setAttribute('tabindex', on ? '0' : '-1');
1602
+ });
1603
+ group.querySelectorAll('.tabpane').forEach(function(pane) {
1604
+ pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
1605
+ });
1606
+ if (focus) btn.focus();
1607
+ }
1256
1608
  document.querySelectorAll('.tabs').forEach(function(group) {
1257
- group.querySelectorAll('.tab-btn').forEach(function(btn) {
1258
- btn.addEventListener('click', function() {
1259
- var idx = btn.getAttribute('data-tab');
1260
- group.querySelectorAll('.tab-btn').forEach(function(b) {
1261
- b.classList.toggle('active', b === btn);
1262
- });
1263
- group.querySelectorAll('.tabpane').forEach(function(pane) {
1264
- pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
1265
- });
1609
+ var btns = [].slice.call(group.querySelectorAll('.tab-btn'));
1610
+ btns.forEach(function(btn, i) {
1611
+ btn.addEventListener('click', function() { activateTab(group, btn, false); });
1612
+ btn.addEventListener('keydown', function(e) {
1613
+ var ni = -1;
1614
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') ni = (i + 1) % btns.length;
1615
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') ni = (i - 1 + btns.length) % btns.length;
1616
+ else if (e.key === 'Home') ni = 0;
1617
+ else if (e.key === 'End') ni = btns.length - 1;
1618
+ if (ni >= 0) { e.preventDefault(); activateTab(group, btns[ni], true); }
1266
1619
  });
1267
1620
  });
1268
1621
  });
@@ -33,14 +33,14 @@ Pick one of the three surfaces below. The npm and Homebrew tabs install the CLI;
33
33
  the plugin tab adds the optional Claude Code skills on top.
34
34
 
35
35
  ::::tabs
36
- :::tab{title=npm}
36
+ :::tab{title="npm"}
37
37
  ```bash
38
38
  npm install -g @zigrivers/scaffold
39
39
  ```
40
40
  Verify with `scaffold version`. This is the recommended path — it uses your
41
41
  system Node and updates with `npm update -g`.
42
42
  :::
43
- :::tab{title=Homebrew}
43
+ :::tab{title="Homebrew"}
44
44
  ```bash
45
45
  brew tap zigrivers/scaffold
46
46
  brew install scaffold
@@ -48,7 +48,7 @@ brew install scaffold
48
48
  The formula installs and manages Node as a dependency, so no separate Node
49
49
  install is needed. Verify with `scaffold version`.
50
50
  :::
51
- :::tab{title=Claude Code plugin}
51
+ :::tab{title="Claude Code plugin"}
52
52
  Inside a Claude Code session:
53
53
  ```text
54
54
  /plugin marketplace add zigrivers/scaffold
@@ -82,17 +82,17 @@ silently no-op against a stale tap cache (see the danger callout below).
82
82
  Run the upgrade for your channel:
83
83
 
84
84
  ::::tabs
85
- :::tab{title=npm}
85
+ :::tab{title="npm"}
86
86
  ```bash
87
87
  npm update -g @zigrivers/scaffold
88
88
  ```
89
89
  :::
90
- :::tab{title=Homebrew}
90
+ :::tab{title="Homebrew"}
91
91
  ```bash
92
92
  brew update && brew upgrade scaffold
93
93
  ```
94
94
  :::
95
- :::tab{title=Claude Code plugin}
95
+ :::tab{title="Claude Code plugin"}
96
96
  ```text
97
97
  /plugin marketplace update zigrivers-scaffold
98
98
  ```