ai-agent-skills 3.4.0 → 3.4.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.
package/README.md CHANGED
@@ -27,6 +27,14 @@ The library is organized the way I actually work:
27
27
  If you want the broad open ecosystem, use `skills.sh`.
28
28
  If you want my shelves, use this repo.
29
29
 
30
+ ## Why This Repo Still Exists
31
+
32
+ I launched this on December 17, 2025, before `skills.sh` existed and before the ecosystem had a clear default universal installer.
33
+
34
+ Originally this repo was that universal installer. That part still works.
35
+
36
+ What makes it worth keeping now is the library itself: the shelves, the provenance, and the editorial judgment. `skills.sh` is the broad open ecosystem. This repo is the smaller personal library I actually reach for.
37
+
30
38
  ## The Two-Tier Model
31
39
 
32
40
  Every skill in the library is one of two things:
@@ -69,14 +77,18 @@ Legacy agent-specific targets still work through `--agent <name>`.
69
77
 
70
78
  ## How To Read The Library
71
79
 
72
- There are four useful views:
80
+ There are two main ways to browse it:
73
81
 
74
82
  | View | Why it exists | Start here |
75
83
  | --- | --- | --- |
76
- | Shelves | The main way to understand the library | `npx ai-agent-skills list` |
77
- | My Picks | The shortest starter stack | `npx ai-agent-skills list --collection my-picks` |
78
- | Source Repos | Provenance and publisher lineage | `npx ai-agent-skills info frontend-design` |
79
- | Terminal Browser | Browse the library as a shelf system, not a flat repo | `npx ai-agent-skills browse` |
84
+ | Shelves | The main way to understand the library: start with the kind of work, then drill into the small set of picks on that shelf. | `npx ai-agent-skills list` |
85
+ | Sources | The provenance view: see which publishers feed which shelves and branches. | `npx ai-agent-skills info frontend-design` |
86
+
87
+ Secondary surfaces still exist, but they are not the main taxonomy:
88
+
89
+ - `npx ai-agent-skills browse` for the TUI
90
+ - `npx ai-agent-skills list --collection my-picks` for a cross-shelf starter stack
91
+ - `npx ai-agent-skills curate review` for the curator cleanup queue
80
92
 
81
93
  ## Shelves
82
94
 
@@ -135,7 +147,7 @@ It adds metadata and editorial placement:
135
147
  For existing picks, `curate` is the fast loop:
136
148
 
137
149
  ```bash
138
- npx ai-agent-skills curate frontend-design --branch "UI Craft"
150
+ npx ai-agent-skills curate frontend-design --branch "Frontend (Anthropic)"
139
151
  npx ai-agent-skills curate frontend-design --why "A stronger note that matches how I actually use it."
140
152
  npx ai-agent-skills curate review
141
153
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-agent-skills",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "My curated agent skills library, with a CLI that installs from any source.",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/skills.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.4.0",
2
+ "version": "3.4.1",
3
3
  "updated": "2026-03-21T00:00:00Z",
4
4
  "total": 35,
5
5
  "workAreas": [
@@ -127,7 +127,7 @@
127
127
  "description": "Create distinctive, production-grade frontend interfaces with high design quality. Use for building web components, pages, dashboards, HTML/CSS layouts, or styling any web UI.",
128
128
  "category": "development",
129
129
  "workArea": "frontend",
130
- "branch": "UI Craft",
130
+ "branch": "Frontend (Anthropic)",
131
131
  "author": "anthropics",
132
132
  "source": "anthropics/skills",
133
133
  "license": "Apache-2.0",
@@ -930,7 +930,7 @@
930
930
  "description": "Use when the task asks for a visually strong landing page, website, app, prototype, demo, or game UI. This skill enforces restrained composition, image-led hierarchy, cohesive content structure, and tasteful motion while avoiding generic cards, weak branding, and UI clutter.",
931
931
  "category": "development",
932
932
  "workArea": "frontend",
933
- "branch": "UI",
933
+ "branch": "Frontend (OpenAI)",
934
934
  "author": "openai",
935
935
  "source": "openai/skills",
936
936
  "license": "MIT",
package/tui/index.mjs CHANGED
@@ -275,6 +275,10 @@ function fitText(text, maxLength) {
275
275
  return `${value.slice(0, maxLength - 1)}…`;
276
276
  }
277
277
 
278
+ function formatCount(count, singular, plural = `${singular}s`) {
279
+ return `${count} ${count === 1 ? singular : plural}`;
280
+ }
281
+
278
282
  function shellQuote(value) {
279
283
  const stringValue = String(value);
280
284
  if (/^[a-zA-Z0-9._:/=@-]+$/.test(stringValue)) {
@@ -419,7 +423,6 @@ function ModeTabs({rootMode, compact = false}) {
419
423
  return html`
420
424
  <${Box} marginBottom=${compact ? 0 : 1} flexWrap="wrap">
421
425
  ${[
422
- {id: 'collections', label: 'Home (h)'},
423
426
  {id: 'areas', label: 'Shelves (w)'},
424
427
  {id: 'sources', label: 'Sources (r)'},
425
428
  ].map((tab) => {
@@ -860,12 +863,12 @@ function HelpOverlay({viewport = null}) {
860
863
  subtitle="Keyboard and navigation for the library view."
861
864
  footerLines=${['? or Esc closes help']}
862
865
  >
863
- <${Text} color=${COLORS.text}>Arrow keys move between shelves, picks, and source rails.<//>
864
- <${Text} color=${COLORS.text}>Enter opens the focused shelf or the focused pick.<//>
866
+ <${Text} color=${COLORS.text}>Arrow keys move between shelves, sources, lanes, and picks.<//>
867
+ <${Text} color=${COLORS.text}>Enter opens the focused shelf, source, lane, or pick.<//>
865
868
  <${Text} color=${COLORS.text}>/ opens library search, : opens the command palette, ? closes this help.<//>
866
869
  <${Text} color=${COLORS.text}>b or Esc goes back, c opens curator actions, i opens install choices, o opens upstream, q quits.<//>
867
870
  <${Text} color=${COLORS.text}>t cycles the house themes.<//>
868
- <${Text} color=${COLORS.muted}>Home is the curator-first shelf poster. Shelves and Sources stay available when you want the taxonomy underneath.<//>
871
+ <${Text} color=${COLORS.muted}>Shelves are the default library view. Sources stay available when provenance matters more than task-first browsing.<//>
869
872
  <//>
870
873
  `;
871
874
  }
@@ -880,7 +883,7 @@ function PaletteOverlay({query, setQuery, items, selectedIndex, viewport = null}
880
883
  <${ModalShell}
881
884
  width=${viewport?.micro ? 66 : 86}
882
885
  title="Command palette"
883
- subtitle="Jump around the library and change the surface."
886
+ subtitle="Jump across shelves, sources, and curator actions."
884
887
  footerLines=${['Enter runs the command · Esc closes the palette']}
885
888
  >
886
889
  <${Box} marginTop=${1}>
@@ -1400,11 +1403,9 @@ function InstallChooser({skill, scope, agent, selectedIndex, columns, viewport =
1400
1403
  }
1401
1404
 
1402
1405
  function buildBreadcrumbs(rootMode, stack, catalog) {
1403
- const rootLabel = rootMode === 'collections'
1404
- ? 'Home'
1405
- : rootMode === 'areas'
1406
- ? 'Shelves'
1407
- : 'Source Repos';
1406
+ const rootLabel = rootMode === 'areas'
1407
+ ? 'Shelves'
1408
+ : 'Sources';
1408
1409
  const trail = ['Atlas', rootLabel];
1409
1410
 
1410
1411
  for (const entry of stack.slice(1)) {
@@ -1468,13 +1469,13 @@ function getCollectionItems(catalog) {
1468
1469
  }));
1469
1470
  }
1470
1471
 
1471
- function getHomeItems(catalog) {
1472
+ function getShelfItems(catalog) {
1472
1473
  return catalog.areas
1473
1474
  .filter((area) => area.skillCount > 0)
1474
1475
  .map((area) => ({
1475
1476
  id: area.id,
1476
1477
  title: area.title,
1477
- count: `${area.skillCount} skills`,
1478
+ count: formatCount(area.skillCount, 'skill'),
1478
1479
  description: area.description,
1479
1480
  chips: area.branches.slice(0, 2).map((branch) => branch.title),
1480
1481
  sampleLines: [
@@ -1484,8 +1485,8 @@ function getHomeItems(catalog) {
1484
1485
  .map((skill) => skill.title)
1485
1486
  .join(', ')}`,
1486
1487
  ],
1487
- footerLeft: `${area.repoCount} repos · ${area.branches.length} branches`,
1488
- footerRight: 'Enter to open',
1488
+ footerLeft: `${formatCount(area.repoCount, 'repo')} · ${formatCount(area.branches.length, 'lane')}`,
1489
+ footerRight: 'Open',
1489
1490
  }));
1490
1491
  }
1491
1492
 
@@ -1534,12 +1535,12 @@ function getSourceItems(catalog) {
1534
1535
  return catalog.sources.map((source) => ({
1535
1536
  id: source.slug,
1536
1537
  title: source.title,
1537
- count: `${source.skillCount} skills`,
1538
+ count: formatCount(source.skillCount, 'skill'),
1538
1539
  description: sourceNoteFor(source.slug, source.slug),
1539
1540
  chips: source.branches.slice(0, 2).map((branch) => `${branch.areaTitle} / ${branch.title}`),
1540
- sampleLines: source.skills.slice(0, 2).map((skill) => skill.title),
1541
- footerLeft: `${source.areaCount} areas · ${source.branchCount} branches`,
1542
- footerRight: 'Enter to open',
1541
+ sampleLines: source.skills.slice(0, 2).map((skill) => `${skill.title} · ${skill.workAreaTitle}`),
1542
+ footerLeft: `${formatCount(source.areaCount, 'shelf', 'shelves')} · ${formatCount(source.branchCount, 'lane')}`,
1543
+ footerRight: 'Open',
1543
1544
  }));
1544
1545
  }
1545
1546
 
@@ -1547,12 +1548,14 @@ function getAreaItems(area) {
1547
1548
  return area.branches.map((branch) => ({
1548
1549
  id: branch.id,
1549
1550
  title: branch.title,
1550
- count: `${branch.skillCount} skills`,
1551
- description: `This lane covers ${branch.repoCount} source repos inside ${area.title.toLowerCase()}.`,
1551
+ count: formatCount(branch.skillCount, 'pick'),
1552
+ description: branch.skillCount === 1
1553
+ ? (branch.skills[0]?.whyHere || branch.skills[0]?.description || `A focused ${area.title.toLowerCase()} lane.`)
1554
+ : `${branch.title} is one thread inside ${area.title.toLowerCase()}, shaped by ${branch.repoTitles.join(', ')}.`,
1552
1555
  chips: branch.repoTitles.slice(0, 2),
1553
1556
  sampleLines: branch.skills.slice(0, 2).map((skill) => `${skill.title} · ${skill.sourceTitle}`),
1554
- footerLeft: `${branch.repoCount} repos`,
1555
- footerRight: 'Enter to open',
1557
+ footerLeft: formatCount(branch.repoCount, 'publisher'),
1558
+ footerRight: 'Open',
1556
1559
  }));
1557
1560
  }
1558
1561
 
@@ -1560,11 +1563,13 @@ function getSourceBranchItems(source) {
1560
1563
  return source.branches.map((branch) => ({
1561
1564
  id: branch.id,
1562
1565
  title: `${branch.areaTitle} / ${branch.title}`,
1563
- count: `${branch.skillCount} skills`,
1564
- description: `${source.title} contributes this branch into the atlas.`,
1566
+ count: formatCount(branch.skillCount, 'pick'),
1567
+ description: branch.skillCount === 1
1568
+ ? (branch.skills[0]?.whyHere || branch.skills[0]?.description || `${source.title} contributes this lane into the library.`)
1569
+ : `${source.title} feeds this lane into ${branch.areaTitle}.`,
1565
1570
  sampleLines: branch.skills.slice(0, 2).map((skill) => skill.title),
1566
- footerLeft: `${branch.skillCount} skills`,
1567
- footerRight: 'Enter to open',
1571
+ footerLeft: `${branch.areaTitle} · ${formatCount(branch.skillCount, 'pick')}`,
1572
+ footerRight: 'Open',
1568
1573
  }));
1569
1574
  }
1570
1575
 
@@ -1576,7 +1581,7 @@ function getSkillItems(skills) {
1576
1581
  description: skill.whyHere || skill.description,
1577
1582
  chips: [skill.sourceTitle, skill.syncMode],
1578
1583
  footerLeft: `${skill.workAreaTitle} / ${skill.branchTitle}`,
1579
- footerRight: 'Enter to inspect',
1584
+ footerRight: 'Inspect',
1580
1585
  }));
1581
1586
  }
1582
1587
 
@@ -1588,7 +1593,7 @@ function getCollectionSkillItems(collection) {
1588
1593
  description: skill.whyHere || skill.description,
1589
1594
  chips: [skill.workAreaTitle, skill.sourceTitle],
1590
1595
  footerLeft: `${skill.branchTitle} · ${skill.syncMode}`,
1591
- footerRight: 'Enter to inspect',
1596
+ footerRight: 'Inspect',
1592
1597
  }));
1593
1598
  }
1594
1599
 
@@ -1666,11 +1671,9 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
1666
1671
  const [bootReady, setBootReady] = useState(false);
1667
1672
  const [catalog, setCatalog] = useState(initialCatalog);
1668
1673
 
1669
- const [rootMode, setRootMode] = useState('collections');
1674
+ const [rootMode, setRootMode] = useState('areas');
1670
1675
  const [stack, setStack] = useState([{type: 'home'}]);
1671
1676
  const [selectedIndex, setSelectedIndex] = useState(0);
1672
- const [homeSectionIndex, setHomeSectionIndex] = useState(0);
1673
- const [homeSelections, setHomeSelections] = useState({});
1674
1677
  const [searchMode, setSearchMode] = useState(false);
1675
1678
  const [helpOpen, setHelpOpen] = useState(false);
1676
1679
  const [paletteOpen, setPaletteOpen] = useState(false);
@@ -1868,52 +1871,6 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
1868
1871
 
1869
1872
  const breadcrumbs = buildBreadcrumbs(rootMode, stack, catalog);
1870
1873
  const currentSkillsSpec = currentSkill && agent ? getSkillsInstallSpec(currentSkill, agent) : null;
1871
- const curatedHomeSections = useMemo(() => {
1872
- const myPicks = catalog.collections.find((collection) => collection.id === 'my-picks');
1873
-
1874
- return [
1875
- {
1876
- id: 'shelves',
1877
- title: 'Shelves',
1878
- subtitle: 'Start from the work itself. Each shelf stays small enough to scan in one pass.',
1879
- kind: 'area',
1880
- mode: 'default',
1881
- items: getHomeItems(catalog),
1882
- },
1883
- {
1884
- id: 'my-picks',
1885
- title: 'My Picks',
1886
- subtitle: 'The first stack I would install on a fresh machine.',
1887
- kind: 'skill',
1888
- mode: 'skills',
1889
- items: getSkillItems((myPicks?.skills || []).slice(0, 6)),
1890
- },
1891
- {
1892
- id: 'house',
1893
- title: 'House Copies',
1894
- subtitle: 'Bundled, local, and owned here when speed and permanence matter.',
1895
- kind: 'skill',
1896
- mode: 'skills',
1897
- items: getTierSkillItems(catalog, 'house'),
1898
- },
1899
- {
1900
- id: 'upstream',
1901
- title: 'Cataloged Upstream',
1902
- subtitle: 'Picked from trusted publishers, but kept live at the source.',
1903
- kind: 'skill',
1904
- mode: 'skills',
1905
- items: getTierSkillItems(catalog, 'upstream'),
1906
- },
1907
- {
1908
- id: 'sources',
1909
- title: 'Source Repos',
1910
- subtitle: 'Browse the publishers behind the shelves when provenance matters more than task-first browsing.',
1911
- kind: 'source',
1912
- mode: 'default',
1913
- items: getSourceItems(catalog).slice(0, 6),
1914
- },
1915
- ].filter((section) => section.items.length > 0);
1916
- }, [catalog]);
1917
1874
 
1918
1875
  const installOptions = currentSkill
1919
1876
  ? agent
@@ -1966,19 +1923,13 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
1966
1923
  const paletteItems = useMemo(() => {
1967
1924
  const items = [];
1968
1925
 
1969
- items.push({id: 'go-home', label: 'Home', detail: 'Jump to the curated library home', run: () => {
1970
- setRootMode('collections');
1971
- setStack([{type: 'home'}]);
1972
- setSelectedIndex(0);
1973
- setPreviewMode(false);
1974
- }});
1975
1926
  items.push({id: 'go-areas', label: 'Shelves', detail: 'Jump to the work-area shelf view', run: () => {
1976
1927
  setRootMode('areas');
1977
1928
  setStack([{type: 'home'}]);
1978
1929
  setSelectedIndex(0);
1979
1930
  setPreviewMode(false);
1980
1931
  }});
1981
- items.push({id: 'go-sources', label: 'Source Repos', detail: 'Jump to source provenance view', run: () => {
1932
+ items.push({id: 'go-sources', label: 'Sources', detail: 'Jump to source provenance view', run: () => {
1982
1933
  setRootMode('sources');
1983
1934
  setStack([{type: 'home'}]);
1984
1935
  setSelectedIndex(0);
@@ -1999,15 +1950,6 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
1999
1950
  setThemeIndex((value) => (value + 1) % THEMES.length);
2000
1951
  }});
2001
1952
 
2002
- if (rootMode === 'collections' && current.type === 'home') {
2003
- items.push({id: 'go-shelves', label: 'Browse by Shelf', detail: 'Jump to the shelf section on the home screen', run: () => {
2004
- setHomeSectionIndex(0);
2005
- }});
2006
- items.push({id: 'go-my-picks', label: 'My Picks', detail: 'Jump to the personal starter stack', run: () => {
2007
- setHomeSectionIndex(1);
2008
- }});
2009
- }
2010
-
2011
1953
  if (stack.length > 1) {
2012
1954
  items.push({id: 'back', label: 'Back', detail: 'Move one level up in the atlas', run: () => {
2013
1955
  setStack((currentStack) => currentStack.slice(0, -1));
@@ -2050,38 +1992,6 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
2050
1992
  setPaletteIndex(0);
2051
1993
  };
2052
1994
 
2053
- const setHomeSelection = (sectionIndex, nextIndex) => {
2054
- setHomeSelections((currentSelections) => ({
2055
- ...currentSelections,
2056
- [sectionIndex]: nextIndex,
2057
- }));
2058
- };
2059
-
2060
- const openHomeItem = (section, itemIndex) => {
2061
- const item = section?.items?.[itemIndex];
2062
- if (!item) return;
2063
-
2064
- if (section.kind === 'skill') {
2065
- setStack((currentStack) => [...currentStack, {type: 'skill', skillName: item.id}]);
2066
- setPreviewMode(false);
2067
- return;
2068
- }
2069
-
2070
- if (section.kind === 'collection') {
2071
- setStack((currentStack) => [...currentStack, {type: 'collection', collectionId: item.id}]);
2072
- return;
2073
- }
2074
-
2075
- if (section.kind === 'area') {
2076
- setStack((currentStack) => [...currentStack, {type: 'area', areaId: item.id}]);
2077
- return;
2078
- }
2079
-
2080
- if (section.kind === 'source') {
2081
- setStack((currentStack) => [...currentStack, {type: 'source', sourceSlug: item.id}]);
2082
- }
2083
- };
2084
-
2085
1995
  useInput((input, key) => {
2086
1996
  if (helpOpen) {
2087
1997
  if (input === 'q' || input === '?' || key.escape) {
@@ -2489,12 +2399,6 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
2489
2399
  }
2490
2400
 
2491
2401
  if (current.type === 'home') {
2492
- if (input === 'h' || input === 'c') {
2493
- setRootMode('collections');
2494
- setSelectedIndex(0);
2495
- setHomeSectionIndex(0);
2496
- return;
2497
- }
2498
2402
  if (input === 'w') {
2499
2403
  setRootMode('areas');
2500
2404
  setSelectedIndex(0);
@@ -2506,39 +2410,6 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
2506
2410
  return;
2507
2411
  }
2508
2412
 
2509
- if (rootMode === 'collections') {
2510
- const sectionCount = curatedHomeSections.length;
2511
- const currentSection = curatedHomeSections[homeSectionIndex];
2512
- const currentItemCount = currentSection?.items?.length || 0;
2513
- const currentHomeIndex = homeSelections[homeSectionIndex] || 0;
2514
-
2515
- if (key.upArrow || input === 'k') {
2516
- setHomeSectionIndex((value) => clamp(value - 1, 0, Math.max(0, sectionCount - 1)));
2517
- return;
2518
- }
2519
-
2520
- if (key.downArrow || input === 'j') {
2521
- setHomeSectionIndex((value) => clamp(value + 1, 0, Math.max(0, sectionCount - 1)));
2522
- return;
2523
- }
2524
-
2525
- if (key.leftArrow || input === 'h') {
2526
- setHomeSelection(homeSectionIndex, clamp(currentHomeIndex - 1, 0, Math.max(0, currentItemCount - 1)));
2527
- return;
2528
- }
2529
-
2530
- if (key.rightArrow || input === 'l') {
2531
- setHomeSelection(homeSectionIndex, clamp(currentHomeIndex + 1, 0, Math.max(0, currentItemCount - 1)));
2532
- return;
2533
- }
2534
-
2535
- if (key.return) {
2536
- openHomeItem(currentSection, currentHomeIndex);
2537
- setSelectedIndex(0);
2538
- }
2539
- return;
2540
- }
2541
-
2542
2413
  const itemCount = rootMode === 'areas' ? catalog.areas.length : catalog.sources.length;
2543
2414
  const columnsPerRow = getColumnsPerRow(columns);
2544
2415
 
@@ -2664,7 +2535,7 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
2664
2535
  breadcrumbs=${breadcrumbs}
2665
2536
  title="Search the library"
2666
2537
  subtitle="Find skills by work area, branch, source repo, or title."
2667
- metaItems=${[`${catalog.total} skills`, `${catalog.collections.length} collections`, `${catalog.sources.length} source repos`, activeTheme.label]}
2538
+ metaItems=${[`${catalog.total} skills`, `${catalog.areas.length} shelves`, `${catalog.sources.length} sources`, activeTheme.label]}
2668
2539
  hint="Enter opens a skill · Esc closes search"
2669
2540
  viewport=${viewport}
2670
2541
  />
@@ -2679,110 +2550,45 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
2679
2550
  <//>
2680
2551
  `;
2681
2552
  } else if (current.type === 'home') {
2682
- if (rootMode === 'collections') {
2683
- const currentSection = curatedHomeSections[homeSectionIndex] || curatedHomeSections[0];
2684
- const currentHomeIndex = homeSelections[homeSectionIndex] || 0;
2685
- const selectedHomeItem = currentSection?.items?.[currentHomeIndex] || currentSection?.items?.[0];
2686
- const visibleSectionIndices = getVisibleHomeSectionIndices(
2687
- curatedHomeSections.length,
2688
- homeSectionIndex,
2689
- viewport
2690
- );
2691
- const renderedSectionIndices = viewport.compact
2692
- ? visibleSectionIndices
2693
- : curatedHomeSections.map((_, index) => index);
2694
- const supportingSectionIndices = renderedSectionIndices.filter((index) => index !== homeSectionIndex);
2695
-
2696
- body = html`
2697
- <${Box} flexDirection="column">
2698
- <${Header}
2699
- breadcrumbs=${breadcrumbs}
2700
- title=${LIBRARY_THESIS}
2701
- subtitle=${LIBRARY_SUPPORT}
2702
- metaItems=${[`${catalog.total} skills`, `${catalog.areas.length} shelves`, `${catalog.houseCount} house copies`, `${catalog.upstreamCount} live upstream`, `scope ${agent ? agent : (scope || 'global')}`, activeTheme.label]}
2703
- hint="Up/down changes sections · Left/right moves within a section · Enter opens · : command palette"
2704
- viewport=${viewport}
2705
- />
2706
- <${ModeTabs} rootMode=${rootMode} compact=${viewport.compact} />
2707
- ${currentSection && selectedHomeItem
2708
- ? html`
2709
- <${ShelfHero}
2710
- section=${currentSection}
2711
- selectedItem=${selectedHomeItem}
2712
- columns=${columns}
2713
- selectedIndex=${currentHomeIndex}
2714
- viewport=${viewport}
2715
- />
2716
- `
2717
- : null}
2718
- <${Text} color=${COLORS.border}>
2719
- ${viewport.micro
2720
- ? 'House copies install fast. Cataloged upstream stays live with the publisher.'
2721
- : 'House copies install fast. Cataloged upstream stays live with the publisher. Start with shelves when you want taste, not search results.'}
2722
- <//>
2723
- <${Box} marginTop=${1} flexDirection="column">
2724
- ${supportingSectionIndices.map((index) => {
2725
- const section = curatedHomeSections[index];
2726
- const sectionItemIndex = homeSelections[index] || 0;
2727
- const sectionItem = section.items[sectionItemIndex] || section.items[0];
2728
- return html`
2729
- <${Box} key=${section.id} flexDirection="column" marginBottom=${1}>
2730
- <${CompactShelfPreview}
2731
- title=${section.title}
2732
- subtitle=${section.subtitle}
2733
- active=${false}
2734
- summary=${sectionItem
2735
- ? `${section.items.length} items · focus: ${sectionItem.title}${sectionItem.description ? ` · ${compactText(sectionItem.description, viewport.micro ? 36 : 64)}` : ''}`
2736
- : `${section.items.length} items`}
2737
- compact=${viewport.compact}
2738
- />
2739
- <//>
2740
- `;
2741
- })}
2742
- <//>
2743
- <//>
2744
- `;
2745
- } else {
2746
- const homeItems = rootMode === 'areas' ? getHomeItems(catalog) : getSourceItems(catalog);
2747
- const selectedHomeItem = homeItems[selectedIndex] || homeItems[0];
2748
- const showHomeInspector = !viewport.compact && Boolean(selectedHomeItem);
2749
- body = html`
2750
- <${Box} flexDirection="column">
2751
- <${Header}
2752
- breadcrumbs=${breadcrumbs}
2753
- title=${rootMode === 'areas' ? 'Browse the shelves' : 'Browse the publishers'}
2754
- subtitle=${rootMode === 'areas'
2755
- ? 'Start with the kind of work, then drill into the small set of skills on that shelf.'
2756
- : 'Browse trusted source repos and the lanes they feed into the shelves.'}
2757
- metaItems=${[`${catalog.total} skills`, `${catalog.areas.length} shelves`, `${catalog.sources.length} publishers`, `scope ${agent ? agent : (scope || 'global')}`, activeTheme.label]}
2758
- hint="Arrow keys move · Enter drills in · / searches · : command palette"
2759
- viewport=${viewport}
2760
- />
2761
- <${ModeTabs} rootMode=${rootMode} compact=${viewport.compact} />
2762
- <${AtlasGrid}
2763
- items=${homeItems}
2764
- selectedIndex=${selectedIndex}
2765
- columns=${columns}
2766
- rows=${rows}
2767
- reservedRows=${getReservedRows('home-grid', viewport, {showInspector: showHomeInspector})}
2768
- compact=${viewport.compact}
2769
- />
2770
- ${showHomeInspector
2771
- ? html`
2772
- <${Inspector}
2773
- title=${selectedHomeItem.title}
2774
- eyebrow=${rootMode === 'areas' ? 'Work area' : 'Source repo'}
2775
- lines=${[
2776
- selectedHomeItem.description,
2777
- ...(selectedHomeItem.sampleLines || []),
2778
- ]}
2779
- footer="Enter opens the focused tile"
2780
- />
2781
- `
2782
- : null}
2783
- <//>
2784
- `;
2785
- }
2553
+ const homeItems = rootMode === 'areas' ? getShelfItems(catalog) : getSourceItems(catalog);
2554
+ const selectedHomeItem = homeItems[selectedIndex] || homeItems[0];
2555
+ const showHomeInspector = !viewport.compact && Boolean(selectedHomeItem);
2556
+ body = html`
2557
+ <${Box} flexDirection="column">
2558
+ <${Header}
2559
+ breadcrumbs=${breadcrumbs}
2560
+ title=${rootMode === 'areas' ? LIBRARY_THESIS : 'Trusted publishers'}
2561
+ subtitle=${rootMode === 'areas'
2562
+ ? 'Start with the work. Each shelf stays small enough to scan and opinionated enough to trust.'
2563
+ : 'See where the picks come from and which lanes each publisher feeds into the library.'}
2564
+ metaItems=${[`${catalog.total} skills`, `${catalog.areas.length} shelves`, `${catalog.sources.length} sources`, `scope ${agent ? agent : (scope || 'global')}`, activeTheme.label]}
2565
+ hint="Arrow keys move · Enter drills in · / searches · : command palette"
2566
+ viewport=${viewport}
2567
+ />
2568
+ <${ModeTabs} rootMode=${rootMode} compact=${viewport.compact} />
2569
+ <${AtlasGrid}
2570
+ items=${homeItems}
2571
+ selectedIndex=${selectedIndex}
2572
+ columns=${columns}
2573
+ rows=${rows}
2574
+ reservedRows=${getReservedRows('home-grid', viewport, {showInspector: showHomeInspector})}
2575
+ compact=${viewport.compact}
2576
+ />
2577
+ ${showHomeInspector
2578
+ ? html`
2579
+ <${Inspector}
2580
+ title=${selectedHomeItem.title}
2581
+ eyebrow=${rootMode === 'areas' ? 'Shelf' : 'Source'}
2582
+ lines=${[
2583
+ selectedHomeItem.description,
2584
+ ...(selectedHomeItem.sampleLines || []),
2585
+ ]}
2586
+ footer="Enter opens the focused tile"
2587
+ />
2588
+ `
2589
+ : null}
2590
+ <//>
2591
+ `;
2786
2592
  } else if (current.type === 'collection' && currentCollection) {
2787
2593
  const selectedSkill = currentCollection.skills[selectedIndex] || currentCollection.skills[0];
2788
2594
  const startHereSkills = currentCollection.skills.slice(0, 3);
@@ -3028,11 +2834,11 @@ function App({catalog: initialCatalog, scope, agent, onExit}) {
3028
2834
  : 'Enter open · b back · : commands · q quit'
3029
2835
  : current.type === 'skill'
3030
2836
  ? '/ search · : palette · b back · c curate · i install · p preview · o upstream · t theme · ? help · q quit'
3031
- : '/ search · : palette · Enter open · b back · h/w/r switch root views · t theme · ? help · q quit';
2837
+ : '/ search · : palette · Enter open · b back · w/r switch views · t theme · ? help · q quit';
3032
2838
  const footerMode = current.type === 'skill'
3033
2839
  ? 'DETAIL'
3034
2840
  : current.type === 'home'
3035
- ? rootMode.toUpperCase()
2841
+ ? rootMode === 'areas' ? 'SHELVES' : 'SOURCES'
3036
2842
  : current.type.toUpperCase();
3037
2843
  const footerDetail = currentSkill
3038
2844
  ? `${currentSkill.title} · ${activeTheme.label}`