yourskills 0.3.1 → 0.4.2

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 (2) hide show
  1. package/dist/main.js +297 -40
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -909,6 +909,24 @@ function SearchInput({
909
909
  });
910
910
  }
911
911
 
912
+ // src/ui/viewport.ts
913
+ import { useWindowSize as useWindowSize2 } from "ink";
914
+ var MIN_ROWS = 3;
915
+ var MAX_ROWS = 20;
916
+ function useViewport({
917
+ cursor,
918
+ total,
919
+ chrome
920
+ }) {
921
+ const { rows } = useWindowSize2();
922
+ const size = Math.max(MIN_ROWS, Math.min(MAX_ROWS, rows - chrome));
923
+ const first = Math.max(0, Math.min(cursor - Math.floor(size / 2), total - size));
924
+ return { first, size };
925
+ }
926
+ function position(cursor, total) {
927
+ return `${total > 0 ? cursor + 1 : 0}/${total}`;
928
+ }
929
+
912
930
  // src/ui/command.tsx
913
931
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
914
932
  function filterCommands(groups, query) {
@@ -923,17 +941,40 @@ function filterCommands(groups, query) {
923
941
  function flattenCommands(groups) {
924
942
  return groups.flatMap((group) => group.items);
925
943
  }
944
+ function windowGroups(groups, first, count) {
945
+ const out = [];
946
+ let index = 0;
947
+ for (const group of groups) {
948
+ const items = group.items.filter((_, i) => {
949
+ const row = index + i;
950
+ return row >= first && row < first + count;
951
+ });
952
+ index += group.items.length;
953
+ if (items.length > 0)
954
+ out.push({ group: group.group, items });
955
+ }
956
+ return out;
957
+ }
926
958
  var POINTER = 2;
927
959
  var GAP2 = 2;
960
+ var LABEL_CAP = 22;
961
+ function valueColumn(items) {
962
+ return Math.min(items.reduce((wide, item) => Math.max(wide, item.label.length), 0), LABEL_CAP);
963
+ }
928
964
  function CommandRow({
929
965
  item,
930
966
  width,
967
+ column,
931
968
  active
932
969
  }) {
933
970
  const hint = item.hint ?? "";
934
971
  const room = width - POINTER - (hint ? hint.length + GAP2 : 0);
935
972
  const label = clip(item.label, Math.max(room, 0));
936
- const gap = Math.max(width - POINTER - label.length - hint.length, hint ? 1 : 0);
973
+ const shows = item.shows ?? "";
974
+ const pad = Math.max(column - label.length, 0) + GAP2;
975
+ const value = shows && label.length + pad + shows.length <= room ? shows : "";
976
+ const left = label.length + (value ? pad + value.length : 0);
977
+ const gap = Math.max(width - POINTER - left - hint.length, hint ? 1 : 0);
937
978
  return /* @__PURE__ */ jsxs5(Text4, {
938
979
  children: [
939
980
  /* @__PURE__ */ jsx5(Text4, {
@@ -945,6 +986,9 @@ function CommandRow({
945
986
  color: active ? tone.primary : undefined,
946
987
  children: label
947
988
  }),
989
+ value ? /* @__PURE__ */ jsx5(Text4, {
990
+ children: `${" ".repeat(pad)}${value}`
991
+ }) : null,
948
992
  /* @__PURE__ */ jsx5(Text4, {
949
993
  dimColor: true,
950
994
  children: `${" ".repeat(gap)}${hint}`
@@ -957,6 +1001,7 @@ function CommandPalette({
957
1001
  query,
958
1002
  cursor,
959
1003
  width,
1004
+ chrome = 11,
960
1005
  placeholder = "type a command or search",
961
1006
  emptyMessage = "No matching commands."
962
1007
  }) {
@@ -964,6 +1009,13 @@ function CommandPalette({
964
1009
  const rows = flattenCommands(visible);
965
1010
  const current = rows[cursor];
966
1011
  const interior = width - FRAME_CHROME;
1012
+ const column = valueColumn(rows);
1013
+ const { first, size } = useViewport({
1014
+ cursor,
1015
+ total: rows.length,
1016
+ chrome: chrome + Math.max(0, visible.length * 2 - 1)
1017
+ });
1018
+ const paged = windowGroups(visible, first, size);
967
1019
  return /* @__PURE__ */ jsxs5(Box3, {
968
1020
  flexDirection: "column",
969
1021
  children: [
@@ -985,7 +1037,7 @@ function CommandPalette({
985
1037
  rows.length === 0 ? /* @__PURE__ */ jsx5(Text4, {
986
1038
  dimColor: true,
987
1039
  children: emptyMessage
988
- }) : visible.map((group, i) => /* @__PURE__ */ jsxs5(Box3, {
1040
+ }) : paged.map((group, i) => /* @__PURE__ */ jsxs5(Box3, {
989
1041
  flexDirection: "column",
990
1042
  children: [
991
1043
  i > 0 ? /* @__PURE__ */ jsx5(Divider, {
@@ -998,6 +1050,7 @@ function CommandPalette({
998
1050
  group.items.map((item) => /* @__PURE__ */ jsx5(CommandRow, {
999
1051
  item,
1000
1052
  width: interior,
1053
+ column,
1001
1054
  active: item.value === current?.value
1002
1055
  }, item.value))
1003
1056
  ]
@@ -1471,7 +1524,7 @@ function catalogReport(title, skills) {
1471
1524
  };
1472
1525
  }
1473
1526
  // src/catalog/screens/browser.tsx
1474
- import { Box as Box9, Text as Text11, useApp as useApp5, useInput as useInput5, useWindowSize as useWindowSize2 } from "ink";
1527
+ import { Box as Box9, Text as Text11, useApp as useApp5, useInput as useInput5 } from "ink";
1475
1528
  import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo2, useState as useState7 } from "react";
1476
1529
 
1477
1530
  // src/manifest/manifest.ts
@@ -1583,7 +1636,8 @@ import { createRequire } from "node:module";
1583
1636
  import { dirname as dirname3, join as join5 } from "node:path";
1584
1637
  function skillsAddArgv(location, opts = {}) {
1585
1638
  const path = location.path.replace(/^\/+|\/+$/g, "");
1586
- const spec = `${location.source}${path ? `/${path}` : ""}#${location.ref}`;
1639
+ const at = location.ref ? `#${location.ref}` : "";
1640
+ const spec = `${location.source}${path ? `/${path}` : ""}${at}`;
1587
1641
  const argv = ["add", spec, "--yes"];
1588
1642
  if (opts.scope !== "project")
1589
1643
  argv.push("--global");
@@ -1662,7 +1716,7 @@ function manifestEntryFor(location, reached, bundle) {
1662
1716
  return {
1663
1717
  name: location.skillName,
1664
1718
  externalId: location.externalId,
1665
- ref: location.ref,
1719
+ ref: location.ref ?? "",
1666
1720
  source: location.source,
1667
1721
  path,
1668
1722
  ...bundle ? { bundle } : {},
@@ -1893,7 +1947,7 @@ async function adoptSkills(client, where, plan) {
1893
1947
  if (event.kind === "installed")
1894
1948
  outcome.adopted.push({
1895
1949
  name: event.location.skillName,
1896
- ref: event.location.ref
1950
+ ref: event.location.ref ?? ""
1897
1951
  });
1898
1952
  else if (event.kind === "failed")
1899
1953
  outcome.failed.push({ name: event.name, message: event.message });
@@ -1932,7 +1986,7 @@ function importReport(nearest, plan, outcome) {
1932
1986
  lines.push({
1933
1987
  mark: "ok",
1934
1988
  text: row.name.padEnd(width),
1935
- note: `adopted at ${row.ref.slice(0, 7)}`
1989
+ note: row.ref ? `adopted at ${row.ref.slice(0, 7)}` : "adopted, unpinned"
1936
1990
  });
1937
1991
  for (const name of outcome.pending)
1938
1992
  lines.push({
@@ -1988,7 +2042,8 @@ import { useEffect as useEffect4, useState as useState5 } from "react";
1988
2042
 
1989
2043
  // src/install/lines.ts
1990
2044
  function where(location) {
1991
- return `${location.source}/${location.path}@${location.ref.slice(0, 8)}`;
2045
+ const at = location.ref ? `@${location.ref.slice(0, 8)}` : " (unpinned)";
2046
+ return `${location.source}/${location.path}${at}`;
1992
2047
  }
1993
2048
  function put(lines, name, progress, deprecated = false) {
1994
2049
  const at = lines.findIndex((l) => l.name === name);
@@ -2763,9 +2818,6 @@ function runFor(command, where, connect) {
2763
2818
  // src/catalog/screens/browser.tsx
2764
2819
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2765
2820
  var TABS = ["bundles", "skills"];
2766
- function pageSize(rows) {
2767
- return Math.max(3, Math.min(20, rows - 11));
2768
- }
2769
2821
  function matches(row, query) {
2770
2822
  if (query.length === 0)
2771
2823
  return true;
@@ -2803,7 +2855,6 @@ function BrowserScreen({
2803
2855
  }) {
2804
2856
  const { exit } = useApp5();
2805
2857
  const width = useFrameWidth();
2806
- const { rows: terminalRows } = useWindowSize2();
2807
2858
  const [catalog, setCatalog] = useState7(null);
2808
2859
  const [tab, setTab] = useState7(initialTab);
2809
2860
  const [query, setQuery] = useState7(initialQuery);
@@ -2846,6 +2897,11 @@ function BrowserScreen({
2846
2897
  setCursor((c) => Math.max(0, Math.min(c, visible.length - 1)));
2847
2898
  }, [visible.length]);
2848
2899
  const selected = picked[tab];
2900
+ const { first, size } = useViewport({
2901
+ cursor,
2902
+ total: visible.length,
2903
+ chrome: 11
2904
+ });
2849
2905
  const start = useCallback2(() => {
2850
2906
  const names = selected.size > 0 ? [...selected] : visible[cursor] ? [visible[cursor].name] : [];
2851
2907
  if (names.length === 0)
@@ -2861,9 +2917,9 @@ function BrowserScreen({
2861
2917
  } else if (key.downArrow) {
2862
2918
  setCursor((c) => Math.min(visible.length - 1, c + 1));
2863
2919
  } else if (key.pageUp) {
2864
- setCursor((c) => Math.max(0, c - pageSize(terminalRows)));
2920
+ setCursor((c) => Math.max(0, c - size));
2865
2921
  } else if (key.pageDown) {
2866
- setCursor((c) => Math.min(visible.length - 1, c + pageSize(terminalRows)));
2922
+ setCursor((c) => Math.min(visible.length - 1, c + size));
2867
2923
  } else if (key.tab) {
2868
2924
  setTab((t) => t === "bundles" ? "skills" : "bundles");
2869
2925
  setCursor(0);
@@ -2921,9 +2977,7 @@ function BrowserScreen({
2921
2977
  })
2922
2978
  });
2923
2979
  }
2924
- const size = pageSize(terminalRows);
2925
- const first = Math.max(0, Math.min(cursor - Math.floor(size / 2), visible.length - size));
2926
- const page = visible.slice(Math.max(0, first), Math.max(0, first) + size);
2980
+ const page = visible.slice(first, first + size);
2927
2981
  const names = nameWidth(visible.length > 0 ? visible : catalog[tab]);
2928
2982
  return /* @__PURE__ */ jsx13(Frame, {
2929
2983
  title: "yourskills",
@@ -2960,7 +3014,7 @@ function BrowserScreen({
2960
3014
  width: width - 2,
2961
3015
  names,
2962
3016
  selectable: true,
2963
- cursor: Math.max(0, first) + i === cursor,
3017
+ cursor: first + i === cursor,
2964
3018
  selected: selected.has(row.name)
2965
3019
  }, row.name)),
2966
3020
  /* @__PURE__ */ jsx13(Divider, {
@@ -2970,7 +3024,7 @@ function BrowserScreen({
2970
3024
  children: [
2971
3025
  /* @__PURE__ */ jsx13(Text11, {
2972
3026
  dimColor: true,
2973
- children: `${visible.length > 0 ? cursor + 1 : 0}/${visible.length}`
3027
+ children: position(cursor, visible.length)
2974
3028
  }),
2975
3029
  selected.size > 0 ? /* @__PURE__ */ jsx13(Text11, {
2976
3030
  color: tone.notice,
@@ -5754,7 +5808,7 @@ import { realpathSync } from "node:fs";
5754
5808
  // package.json
5755
5809
  var package_default = {
5756
5810
  name: "yourskills",
5757
- version: "0.3.1",
5811
+ version: "0.4.2",
5758
5812
  description: "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
5759
5813
  license: "UNLICENSED",
5760
5814
  homepage: "https://yourskills.store",
@@ -6934,6 +6988,7 @@ function noSuchPass(id, passes) {
6934
6988
  };
6935
6989
  }
6936
6990
  // src/commands/facts.ts
6991
+ var NO_FEED = {};
6937
6992
  function count2(n, one, many = `${one}s`) {
6938
6993
  return `${n} ${n === 1 ? one : many}`;
6939
6994
  }
@@ -7026,6 +7081,7 @@ var TABLE = {
7026
7081
  value: "catalog.bundles",
7027
7082
  label: "browse bundles",
7028
7083
  hint: "browse",
7084
+ shows: (facts) => facts.feed.bundles === undefined ? "" : count2(facts.feed.bundles, "bundle"),
7029
7085
  when: (facts) => facts.signedIn,
7030
7086
  opens: { kind: "browse" }
7031
7087
  }
@@ -7132,6 +7188,7 @@ var TABLE = {
7132
7188
  value: "account.org",
7133
7189
  label: "switch org",
7134
7190
  hint: "org <name>",
7191
+ shows: (facts) => facts.feed.organizations === undefined ? "" : facts.feed.organizations <= 1 ? "only this one" : count2(facts.feed.organizations - 1, "other"),
7135
7192
  when: (facts) => facts.signedIn,
7136
7193
  says: (facts) => facts.organization ? `switch this machine out of ${facts.organization}` : "choose which organization this machine installs from"
7137
7194
  }
@@ -7156,6 +7213,7 @@ var TABLE = {
7156
7213
  value: "catalog.skills",
7157
7214
  label: "browse skills",
7158
7215
  hint: "list",
7216
+ shows: (facts) => facts.feed.skills === undefined ? "" : count2(facts.feed.skills, "skill"),
7159
7217
  when: (facts) => facts.signedIn,
7160
7218
  opens: { kind: "list" }
7161
7219
  }
@@ -7333,6 +7391,7 @@ var TABLE = {
7333
7391
  value: "installed.update",
7334
7392
  label: "update all",
7335
7393
  hint: "update --all",
7394
+ shows: (facts) => updateScope(facts.bundles, facts.loose),
7336
7395
  when: (facts) => facts.skills > 0 && facts.signedIn,
7337
7396
  opens: { kind: "update", all: true }
7338
7397
  }
@@ -7422,6 +7481,7 @@ var TABLE = {
7422
7481
  value: "installed.list",
7423
7482
  label: "list installed",
7424
7483
  hint: "installed",
7484
+ shows: (facts) => count2(facts.skills, "skill"),
7425
7485
  when: (facts) => facts.skills > 0,
7426
7486
  opens: { kind: "installed" }
7427
7487
  }
@@ -7451,6 +7511,7 @@ var TABLE = {
7451
7511
  value: "installed.outdated",
7452
7512
  label: "outdated",
7453
7513
  hint: "outdated",
7514
+ shows: (facts) => facts.feed.outdated === undefined ? "" : facts.feed.outdated === 0 ? "all current" : `${facts.feed.outdated} behind`,
7454
7515
  when: (facts) => facts.skills > 0 && facts.signedIn,
7455
7516
  opens: { kind: "outdated" }
7456
7517
  }
@@ -7485,6 +7546,7 @@ var TABLE = {
7485
7546
  value: "catalog.pending",
7486
7547
  label: "waiting for a review",
7487
7548
  hint: "pending",
7549
+ shows: (facts) => facts.feed.pending === undefined ? "" : facts.feed.pending === 0 ? "none open" : `${facts.feed.pending} open`,
7488
7550
  when: (facts) => facts.signedIn,
7489
7551
  opens: { kind: "pending" }
7490
7552
  }
@@ -7591,20 +7653,21 @@ var TABLE = {
7591
7653
  value: "setup.scope",
7592
7654
  label: "scope",
7593
7655
  hint: "config set scope project",
7594
- opens: { kind: "config", action: "get", key: "scope" },
7656
+ shows: (facts) => facts.scope,
7595
7657
  says: (facts) => `where installs land: ${facts.scope} right now`
7596
7658
  },
7597
7659
  {
7598
7660
  value: "setup.agents",
7599
7661
  label: "agents",
7600
7662
  hint: "config set agents <name>",
7601
- opens: { kind: "config", action: "get", key: "agents" },
7602
- says: () => "which agents a skill is written for when it is installed"
7663
+ shows: (facts) => facts.agents.length > 0 ? count2(facts.agents.length, "agent") : "unset",
7664
+ says: (facts) => facts.agents.length > 0 ? `which agents an install writes for: ${facts.agents.join(", ")}` : "which agents an install writes for; nothing pinned, so `skills` looks"
7603
7665
  },
7604
7666
  {
7605
7667
  value: "setup.explain",
7606
7668
  label: "explain",
7607
7669
  hint: "config get --explain",
7670
+ shows: (facts) => `${count2(facts.settings, "setting")} set`,
7608
7671
  opens: { kind: "config", action: "get", explain: true }
7609
7672
  }
7610
7673
  ],
@@ -7682,6 +7745,7 @@ var TABLE = {
7682
7745
  value: "setup.upgrade",
7683
7746
  label: "upgrade",
7684
7747
  hint: "upgrade",
7748
+ shows: (facts) => facts.feed.upgrade === undefined ? "" : facts.feed.upgrade === null ? "up to date" : `${facts.feed.upgrade} is out`,
7685
7749
  opens: { kind: "upgrade" }
7686
7750
  }
7687
7751
  ],
@@ -7778,6 +7842,9 @@ function refuseCommand(command) {
7778
7842
  code: EXIT.usage
7779
7843
  });
7780
7844
  }
7845
+ function doorShows(door, facts) {
7846
+ return door.shows?.(facts) ?? "";
7847
+ }
7781
7848
  function doorSays(door, facts) {
7782
7849
  if (door.opens === undefined)
7783
7850
  return door.says(facts);
@@ -8079,13 +8146,19 @@ var PALETTE = [
8079
8146
  ]
8080
8147
  }
8081
8148
  ];
8082
- function item(door) {
8083
- return { value: door.value, label: door.label, hint: door.hint };
8149
+ function item(door, facts) {
8150
+ const shows = doorShows(door, facts);
8151
+ return {
8152
+ value: door.value,
8153
+ label: door.label,
8154
+ hint: door.hint,
8155
+ ...shows ? { shows } : {}
8156
+ };
8084
8157
  }
8085
8158
  function paletteGroups(facts) {
8086
8159
  const groups = [];
8087
8160
  for (const place of PALETTE) {
8088
- const items = place.doors.map((value) => DOORS[value]).filter((door) => door.when?.(facts) ?? true).map(item);
8161
+ const items = place.doors.map((value) => DOORS[value]).filter((door) => door.when?.(facts) ?? true).map((door) => item(door, facts));
8089
8162
  if (items.length > 0)
8090
8163
  groups.push({ group: place.group, items });
8091
8164
  }
@@ -8113,13 +8186,59 @@ async function paletteFacts(scope, server) {
8113
8186
  manifest: nearest.path,
8114
8187
  skills,
8115
8188
  bundles: Object.keys(nearest.manifest.bundles).length,
8116
- loose: looseSkills(nearest.manifest).length
8189
+ loose: looseSkills(nearest.manifest).length,
8190
+ settings: CONFIG_KEYS.filter((key) => configValue(config, key) !== UNSET).length,
8191
+ feed: NO_FEED
8117
8192
  };
8118
8193
  }
8119
8194
  // src/shell/screens/home.tsx
8120
8195
  import { Box as Box14, Text as Text16, useApp as useApp7, useInput as useInput8 } from "ink";
8121
8196
  import { useCallback as useCallback3, useEffect as useEffect9, useMemo as useMemo4, useRef, useState as useState11 } from "react";
8122
8197
 
8198
+ // src/shell/feed.ts
8199
+ async function feedFacts(client, where, server) {
8200
+ const installed = Object.keys(where.manifest.skills).length > 0;
8201
+ const [skills, bundles, passes, drift, orgs] = await Promise.allSettled([
8202
+ client.skills.list.query({}),
8203
+ client.bundles.list.query({}),
8204
+ client.mirror.passes.query(),
8205
+ installed ? driftNow(client, where) : Promise.resolve(null),
8206
+ organizationChoices(server)
8207
+ ]);
8208
+ return {
8209
+ ...skills.status === "fulfilled" ? { skills: skills.value.length } : {},
8210
+ ...bundles.status === "fulfilled" ? { bundles: bundles.value.length } : {},
8211
+ ...passes.status === "fulfilled" ? { pending: passes.value.length } : {},
8212
+ ...drift.status === "fulfilled" && drift.value ? { outdated: drift.value.moved.length } : {},
8213
+ ...orgs.status === "fulfilled" ? { organizations: orgs.value.length } : {}
8214
+ };
8215
+ }
8216
+
8217
+ // src/shell/probe.ts
8218
+ var importProbe = async (where) => {
8219
+ const found = await listInstalled(where.scope);
8220
+ const recorded = new Set(Object.keys(where.manifest.skills));
8221
+ const news = found.filter((skill) => !recorded.has(skill.name)).length;
8222
+ if (found.length === 0)
8223
+ return "nothing on this machine to adopt";
8224
+ return news === 0 ? `${count2(found.length, "skill")} on this machine, all recorded here already` : `${count2(found.length, "skill")} on this machine, ${news} not recorded here`;
8225
+ };
8226
+ var doctorProbe = async (where, server) => {
8227
+ const checks = await diagnose(where, {
8228
+ local: true,
8229
+ ...server ? { server } : {}
8230
+ });
8231
+ const bad = checks.filter((check) => !check.ok);
8232
+ return bad.length === 0 ? `${count2(checks.length, "local check")}, all passing` : `${bad.length} of ${checks.length} local checks failing: ${bad.map((check) => check.name).join(", ")}`;
8233
+ };
8234
+ var PROBES = {
8235
+ "setup.import": importProbe,
8236
+ "setup.doctor": doctorProbe
8237
+ };
8238
+ function probeFor(value) {
8239
+ return value ? PROBES[value] : undefined;
8240
+ }
8241
+
8123
8242
  // src/args.ts
8124
8243
  function configCommand2(rest, explain) {
8125
8244
  const [action, key, ...value] = rest;
@@ -8449,13 +8568,14 @@ function PickScreen({
8449
8568
  verb,
8450
8569
  single = false,
8451
8570
  marked,
8571
+ preset,
8452
8572
  onPick,
8453
8573
  onBack
8454
8574
  }) {
8455
8575
  const width = useFrameWidth();
8456
8576
  const [query, setQuery] = useState9("");
8457
8577
  const [cursor, setCursor] = useState9(0);
8458
- const [picked, setPicked] = useState9(new Set);
8578
+ const [picked, setPicked] = useState9(() => new Set(preset ?? []));
8459
8579
  const visible = useMemo3(() => {
8460
8580
  if (query.length === 0)
8461
8581
  return rows;
@@ -8465,11 +8585,20 @@ function PickScreen({
8465
8585
  useEffect8(() => {
8466
8586
  setCursor((c) => Math.max(0, Math.min(c, visible.length - 1)));
8467
8587
  }, [visible.length]);
8588
+ const { first, size } = useViewport({
8589
+ cursor,
8590
+ total: visible.length,
8591
+ chrome: 9
8592
+ });
8468
8593
  useInput6((input, key) => {
8469
8594
  if (key.upArrow) {
8470
8595
  setCursor((c) => Math.max(0, c - 1));
8471
8596
  } else if (key.downArrow) {
8472
8597
  setCursor((c) => Math.min(visible.length - 1, c + 1));
8598
+ } else if (key.pageUp) {
8599
+ setCursor((c) => Math.max(0, c - size));
8600
+ } else if (key.pageDown) {
8601
+ setCursor((c) => Math.min(visible.length - 1, c + size));
8473
8602
  } else if (key.return) {
8474
8603
  const names = picked.size > 0 ? [...picked] : visible[cursor] ? [visible[cursor].name] : [];
8475
8604
  if (names.length > 0)
@@ -8500,6 +8629,7 @@ function PickScreen({
8500
8629
  }
8501
8630
  });
8502
8631
  const names = nameWidth(rows);
8632
+ const page = visible.slice(first, first + size);
8503
8633
  return /* @__PURE__ */ jsx16(Frame, {
8504
8634
  title,
8505
8635
  width,
@@ -8527,12 +8657,12 @@ function PickScreen({
8527
8657
  }) : visible.length === 0 ? /* @__PURE__ */ jsx16(Text14, {
8528
8658
  dimColor: true,
8529
8659
  children: `Nothing matches "${query}".`
8530
- }) : visible.map((row, i) => /* @__PURE__ */ jsx16(SkillRow, {
8660
+ }) : page.map((row, i) => /* @__PURE__ */ jsx16(SkillRow, {
8531
8661
  row,
8532
8662
  width: width - 2,
8533
8663
  names,
8534
8664
  selectable: true,
8535
- cursor: i === cursor,
8665
+ cursor: first + i === cursor,
8536
8666
  selected: single ? row.name === marked : picked.has(row.name)
8537
8667
  }, row.name)),
8538
8668
  /* @__PURE__ */ jsx16(Divider, {
@@ -8540,6 +8670,10 @@ function PickScreen({
8540
8670
  }),
8541
8671
  /* @__PURE__ */ jsxs14(Box12, {
8542
8672
  children: [
8673
+ /* @__PURE__ */ jsx16(Text14, {
8674
+ dimColor: true,
8675
+ children: `${position(cursor, visible.length)} `
8676
+ }),
8543
8677
  picked.size > 0 && !single ? /* @__PURE__ */ jsx16(Text14, {
8544
8678
  color: tone.notice,
8545
8679
  children: `${mark.selected} ${picked.size} marked `
@@ -8663,11 +8797,30 @@ function failure(title, error) {
8663
8797
  json: { error: message }
8664
8798
  };
8665
8799
  }
8800
+ var HEAD_FLOOR = 14;
8801
+ var AGENTS_FLOOR = 9;
8802
+ function agentsIn(agents, room) {
8803
+ if (agents.length === 0)
8804
+ return "agents unset";
8805
+ const full = agents.join(",");
8806
+ if (full.length <= room)
8807
+ return full;
8808
+ for (let keep = agents.length - 1;keep > 0; keep--) {
8809
+ const text = `${agents.slice(0, keep).join(",")} +${agents.length - keep}`;
8810
+ if (text.length <= room)
8811
+ return text;
8812
+ }
8813
+ return `${agents.length} agents`;
8814
+ }
8666
8815
  function AccountLine({ facts, width }) {
8667
- const agents = facts.agents.length > 0 ? facts.agents.join(",") : "agents unset";
8668
- const right = `${agents} ${facts.scope} ${facts.skills} installed`;
8669
8816
  const head = facts.signedIn ? facts.email ?? "signed in" : "not signed in";
8670
- const room = Math.max(width - FRAME_CHROME - right.length - 2, 12);
8817
+ const stat = `${facts.scope} ${facts.skills} installed`;
8818
+ const inner = width - FRAME_CHROME;
8819
+ const spent = Math.min(head.length, 24) + 2 + stat.length + 4;
8820
+ const forAgents = inner - spent;
8821
+ const agents = forAgents >= AGENTS_FLOOR ? agentsIn(facts.agents, forAgents) : "";
8822
+ const right = agents ? `${agents} ${stat}` : stat;
8823
+ const room = Math.max(inner - right.length - 2, HEAD_FLOOR);
8671
8824
  const tail = facts.signedIn ? ` ${hostOf(facts.server)}` : " — the catalog needs a session";
8672
8825
  const org = facts.signedIn && facts.organization ? ` ${facts.organization}` : "";
8673
8826
  const spare = room - 2 - head.length;
@@ -8699,7 +8852,7 @@ function AccountLine({ facts, width }) {
8699
8852
  }),
8700
8853
  /* @__PURE__ */ jsx18(Text16, {
8701
8854
  dimColor: true,
8702
- children: right
8855
+ children: clip(right, inner - room)
8703
8856
  })
8704
8857
  ]
8705
8858
  });
@@ -8715,7 +8868,9 @@ function StaleLine({ stale, width }) {
8715
8868
  });
8716
8869
  }
8717
8870
  var RUN = "run";
8871
+ var REST_MS = 250;
8718
8872
  var ASK_NPM = () => upgradeCheck();
8873
+ var ASK_REGISTRY = feedFacts;
8719
8874
  var NO_EXIT_CODE = () => {
8720
8875
  return;
8721
8876
  };
@@ -8731,19 +8886,20 @@ function HomeScreen({
8731
8886
  onWelcomed = async () => {
8732
8887
  return;
8733
8888
  },
8734
- checkUpgrade = ASK_NPM
8889
+ checkUpgrade = ASK_NPM,
8890
+ loadFeed = ASK_REGISTRY,
8891
+ probes: probeSet = probeFor
8735
8892
  }) {
8736
8893
  const { exit } = useApp7();
8737
8894
  const width = useFrameWidth();
8738
8895
  const [facts, setFacts] = useState11(initial);
8739
8896
  const [where, setWhere] = useState11(initialWhere);
8740
- const groups = useMemo4(() => paletteGroups(facts), [facts]);
8741
8897
  const [query, setQuery] = useState11("");
8742
8898
  const [cursor, setCursor] = useState11(0);
8743
8899
  const [route, setRoute] = useState11(null);
8744
8900
  const [welcome, setWelcome] = useState11(initial.firstRun);
8745
8901
  const [wizard, setWizard] = useState11(false);
8746
- const [stale, setStale] = useState11(null);
8902
+ const [stale, setStale] = useState11(undefined);
8747
8903
  useEffect9(() => {
8748
8904
  let live = true;
8749
8905
  checkUpgrade().then((found) => {
@@ -8756,6 +8912,17 @@ function HomeScreen({
8756
8912
  live = false;
8757
8913
  };
8758
8914
  }, [checkUpgrade]);
8915
+ const [feed, setFeed] = useState11(NO_FEED);
8916
+ const [probes, setProbes] = useState11({});
8917
+ const [probing, setProbing] = useState11(null);
8918
+ const shown = useMemo4(() => ({
8919
+ ...facts,
8920
+ feed: {
8921
+ ...feed,
8922
+ ...stale === undefined ? {} : { upgrade: stale ? stale.latest : null }
8923
+ }
8924
+ }), [facts, feed, stale]);
8925
+ const groups = useMemo4(() => paletteGroups(shown), [shown]);
8759
8926
  const typed = useMemo4(() => typedCommand(query), [query]);
8760
8927
  const visible = useMemo4(() => {
8761
8928
  const filtered = filterCommands(groups, query);
@@ -8778,6 +8945,20 @@ function HomeScreen({
8778
8945
  held.current ??= await openClient();
8779
8946
  return held.current;
8780
8947
  }, [openClient]);
8948
+ useEffect9(() => {
8949
+ if (!facts.signedIn)
8950
+ return;
8951
+ let live = true;
8952
+ connect().then((client) => loadFeed(client, where, server)).then((found) => {
8953
+ if (live)
8954
+ setFeed(found);
8955
+ }).catch(() => {
8956
+ return;
8957
+ });
8958
+ return () => {
8959
+ live = false;
8960
+ };
8961
+ }, [facts.signedIn, connect, loadFeed, where, server]);
8781
8962
  const checkSession = useCallback3(async () => facts.signedIn, [facts.signedIn]);
8782
8963
  const answering = useCallback3((title, working, load) => ({
8783
8964
  kind: "report",
@@ -8873,6 +9054,52 @@ function HomeScreen({
8873
9054
  verb: "remove",
8874
9055
  command: (names) => ({ kind: "remove", names })
8875
9056
  };
9057
+ case "setup.scope":
9058
+ return {
9059
+ kind: "pick",
9060
+ title: "scope",
9061
+ rows: [
9062
+ {
9063
+ name: "global",
9064
+ description: "installs land in your home directory, where every project sees them"
9065
+ },
9066
+ {
9067
+ name: "project",
9068
+ description: "installs land in this project's yourskills.json, and travel with the repo"
9069
+ }
9070
+ ],
9071
+ empty: "",
9072
+ verb: "set",
9073
+ single: true,
9074
+ marked: facts.scope,
9075
+ command: (names) => ({
9076
+ kind: "config",
9077
+ action: "set",
9078
+ key: "scope",
9079
+ value: names[0] ?? ""
9080
+ })
9081
+ };
9082
+ case "setup.agents": {
9083
+ const valid = await knownAgents() ?? [];
9084
+ const onDisk = new Set(facts.agentsOnDisk);
9085
+ return {
9086
+ kind: "pick",
9087
+ title: "agents",
9088
+ rows: valid.map((name) => ({
9089
+ name,
9090
+ description: onDisk.has(name) ? "on this machine" : ""
9091
+ })),
9092
+ empty: "Could not ask the `skills` CLI which agent names it accepts. Run `yourskills doctor`.",
9093
+ verb: "set",
9094
+ preset: facts.agents,
9095
+ command: (names) => ({
9096
+ kind: "config",
9097
+ action: "set",
9098
+ key: "agents",
9099
+ value: names.join(",")
9100
+ })
9101
+ };
9102
+ }
8876
9103
  case "account.org": {
8877
9104
  const orgs = await organizationChoices(server);
8878
9105
  const active = orgs.find((org) => org.active)?.name;
@@ -8891,7 +9118,7 @@ function HomeScreen({
8891
9118
  };
8892
9119
  }
8893
9120
  }
8894
- }, [where, server]);
9121
+ }, [where, server, facts]);
8895
9122
  const routeForDoor = useCallback3(async (door) => door.opens === undefined ? ownRoute(door.value) : routeForCommand(door.opens), [ownRoute, routeForCommand]);
8896
9123
  const open = useCallback3((pending, title) => {
8897
9124
  pending.then((next) => {
@@ -8908,6 +9135,29 @@ function HomeScreen({
8908
9135
  open(routeForCommand(parsed.command), parsed.text);
8909
9136
  }, [open, routeForCommand]);
8910
9137
  const current = rows[cursor];
9138
+ useEffect9(() => {
9139
+ const value = current?.value;
9140
+ const probe = probeSet(value);
9141
+ if (!value || !probe || probes[value] !== undefined)
9142
+ return;
9143
+ let live = true;
9144
+ const timer = setTimeout(() => {
9145
+ setProbing(value);
9146
+ probe(where, server).then((text) => {
9147
+ if (live)
9148
+ setProbes((was) => ({ ...was, [value]: text }));
9149
+ }).catch(() => {
9150
+ return;
9151
+ }).finally(() => {
9152
+ if (live)
9153
+ setProbing(null);
9154
+ });
9155
+ }, REST_MS);
9156
+ return () => {
9157
+ live = false;
9158
+ clearTimeout(timer);
9159
+ };
9160
+ }, [current?.value, probes, probeSet, where, server]);
8911
9161
  const activate = useCallback3(() => {
8912
9162
  const item = rows[cursor];
8913
9163
  if (!item)
@@ -8934,6 +9184,7 @@ function HomeScreen({
8934
9184
  setRoute(null);
8935
9185
  setQuery("");
8936
9186
  setCursor(0);
9187
+ setProbes({});
8937
9188
  reload().then(setFacts).catch(() => {
8938
9189
  return;
8939
9190
  });
@@ -9049,6 +9300,7 @@ function HomeScreen({
9049
9300
  verb: picked.verb,
9050
9301
  ...picked.single ? { single: true } : {},
9051
9302
  ...picked.marked ? { marked: picked.marked } : {},
9303
+ ...picked.preset ? { preset: picked.preset } : {},
9052
9304
  onPick: (names) => {
9053
9305
  open(routeForCommand(picked.command(names)), picked.title);
9054
9306
  },
@@ -9081,14 +9333,15 @@ function HomeScreen({
9081
9333
  });
9082
9334
  }
9083
9335
  const highlighted = current ? doorFor(current.value) : undefined;
9084
- const description = current?.value === RUN && runnable(typed) ? say(typed.command, facts) : highlighted ? doorSays(highlighted, facts) : typed ? say(typed.command, facts) : "";
9336
+ const description = current?.value === RUN && runnable(typed) ? say(typed.command, shown) : highlighted ? doorSays(highlighted, shown) : typed ? say(typed.command, shown) : "";
9337
+ const found = current ? probes[current.value] ?? (probing === current.value ? "looking..." : "") : "";
9085
9338
  const empty = typed?.command.kind === "error" && typed.command.next ? `${mark.fail} ${typed.command.message} try: ${typed.command.next.replace(/^yourskills /, "")}` : "No matching commands.";
9086
9339
  return /* @__PURE__ */ jsxs16(Frame, {
9087
9340
  title: "yourskills",
9088
9341
  width,
9089
9342
  children: [
9090
9343
  /* @__PURE__ */ jsx18(AccountLine, {
9091
- facts,
9344
+ facts: shown,
9092
9345
  width
9093
9346
  }),
9094
9347
  stale ? /* @__PURE__ */ jsx18(StaleLine, {
@@ -9112,6 +9365,10 @@ function HomeScreen({
9112
9365
  dimColor: true,
9113
9366
  children: clip(description, width - FRAME_CHROME)
9114
9367
  }),
9368
+ /* @__PURE__ */ jsx18(Text16, {
9369
+ dimColor: true,
9370
+ children: found ? clip(` ${found}`, width - FRAME_CHROME) : " "
9371
+ }),
9115
9372
  /* @__PURE__ */ jsx18(Hints, {
9116
9373
  hints: [
9117
9374
  ["^v", "move"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yourskills",
3
- "version": "0.3.1",
3
+ "version": "0.4.2",
4
4
  "description": "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://yourskills.store",