jd-skills 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +103 -3
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -3156,7 +3156,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
3156
3156
  tree
3157
3157
  };
3158
3158
  }
3159
- var version$1 = "0.1.2";
3159
+ var version$1 = "0.1.3";
3160
3160
  const isCancelled$1 = (value) => typeof value === "symbol";
3161
3161
  async function isSourcePrivate(source) {
3162
3162
  const ownerRepo = parseOwnerRepo(source);
@@ -4558,6 +4558,58 @@ let _lastJdSearchKind = null;
4558
4558
  function getLastJdSearchKind() {
4559
4559
  return _lastJdSearchKind;
4560
4560
  }
4561
+ function skillDescriptionKey(skill) {
4562
+ return `${skill.origin}:${skill.source}:${skill.slug}:${skill.name}`;
4563
+ }
4564
+ async function fetchUpstreamDescription(skill) {
4565
+ const parts = skill.source.split("/");
4566
+ if (parts.length < 2) return null;
4567
+ const [owner, repo] = parts;
4568
+ const slug = toSkillSlug(skill.name);
4569
+ if (!slug) return null;
4570
+ try {
4571
+ const url = `${SEARCH_API_BASE}/api/download/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(slug)}`;
4572
+ const res = await fetch(url, { signal: AbortSignal.timeout(5e3) });
4573
+ if (!res.ok) return null;
4574
+ const skillMd = (await res.json()).files?.find((f) => f.path === "SKILL.md" || f.path.endsWith("/SKILL.md"));
4575
+ if (!skillMd?.contents) return null;
4576
+ const { data: fm } = parseFrontmatter(skillMd.contents);
4577
+ const desc = fm.description;
4578
+ return typeof desc === "string" && desc.trim() ? sanitizeMetadata(desc.trim()) : null;
4579
+ } catch {
4580
+ return null;
4581
+ }
4582
+ }
4583
+ function wrapDescriptionPreview(text, maxLines = 4) {
4584
+ const cols = process.stdout.columns || 80;
4585
+ const width = Math.max(24, cols - 4);
4586
+ const plain = text.replace(/\s+/g, " ").trim();
4587
+ if (!plain) return [];
4588
+ const lines = [];
4589
+ let line = "";
4590
+ let truncated = false;
4591
+ for (const word of plain.split(" ")) {
4592
+ if (!line) {
4593
+ line = word;
4594
+ continue;
4595
+ }
4596
+ if (line.length + 1 + word.length <= width) line += ` ${word}`;
4597
+ else {
4598
+ lines.push(` ${DIM$3}${line}${RESET$3}`);
4599
+ line = word;
4600
+ if (lines.length >= maxLines) {
4601
+ truncated = true;
4602
+ break;
4603
+ }
4604
+ }
4605
+ }
4606
+ if (!truncated && line && lines.length < maxLines) lines.push(` ${DIM$3}${line}${RESET$3}`);
4607
+ else if (truncated && lines.length > 0) {
4608
+ const last = lines[lines.length - 1];
4609
+ lines[lines.length - 1] = last.replace(new RegExp(`${RESET$3}$`), `…${RESET$3}`);
4610
+ }
4611
+ return lines;
4612
+ }
4561
4613
  async function searchUpstreamSkills(query) {
4562
4614
  try {
4563
4615
  const url = `${SEARCH_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=10`;
@@ -4626,6 +4678,42 @@ async function runSearchPrompt(initialQuery = "") {
4626
4678
  let debounceTimer = null;
4627
4679
  let lastRenderedLines = 0;
4628
4680
  let jdNeedsLogin = false;
4681
+ let scrollOffset = 0;
4682
+ const maxVisibleResults = 8;
4683
+ const descriptionCache = /* @__PURE__ */ new Map();
4684
+ let descriptionFetchToken = 0;
4685
+ let descriptionLoading = false;
4686
+ function getSelectedDescription() {
4687
+ const skill = results[selectedIndex];
4688
+ if (!skill) return void 0;
4689
+ if (skill.description?.trim()) return skill.description.trim();
4690
+ if (skill.jdDetail?.description?.trim()) return skill.jdDetail.description.trim();
4691
+ return descriptionCache.get(skillDescriptionKey(skill));
4692
+ }
4693
+ async function prefetchSelectedDescription() {
4694
+ const skill = results[selectedIndex];
4695
+ if (!skill || getSelectedDescription()) return;
4696
+ const key = skillDescriptionKey(skill);
4697
+ if (descriptionCache.has(key)) return;
4698
+ const token = ++descriptionFetchToken;
4699
+ descriptionLoading = true;
4700
+ render();
4701
+ const desc = skill.origin === "upstream" ? await fetchUpstreamDescription(skill) : skill.jdDetail?.description;
4702
+ if (token !== descriptionFetchToken) return;
4703
+ descriptionLoading = false;
4704
+ if (desc?.trim()) {
4705
+ descriptionCache.set(key, desc.trim());
4706
+ render();
4707
+ } else render();
4708
+ }
4709
+ function syncScrollOffset() {
4710
+ if (results.length <= maxVisibleResults) {
4711
+ scrollOffset = 0;
4712
+ return;
4713
+ }
4714
+ if (selectedIndex < scrollOffset) scrollOffset = selectedIndex;
4715
+ else if (selectedIndex >= scrollOffset + maxVisibleResults) scrollOffset = selectedIndex - maxVisibleResults + 1;
4716
+ }
4629
4717
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
4630
4718
  readline.emitKeypressEvents(process.stdin);
4631
4719
  process.stdin.resume();
@@ -4641,10 +4729,11 @@ async function runSearchPrompt(initialQuery = "") {
4641
4729
  else if (results.length === 0 && loading) lines.push(`${DIM$3}Searching...${RESET$3}`);
4642
4730
  else if (results.length === 0) lines.push(`${DIM$3}No skills found${RESET$3}`);
4643
4731
  else {
4644
- const visible = results.slice(0, 8);
4732
+ syncScrollOffset();
4733
+ const visible = results.slice(scrollOffset, scrollOffset + maxVisibleResults);
4645
4734
  for (let i = 0; i < visible.length; i++) {
4646
4735
  const skill = visible[i];
4647
- const isSelected = i === selectedIndex;
4736
+ const isSelected = scrollOffset + i === selectedIndex;
4648
4737
  const arrow = isSelected ? `${BOLD$3}>${RESET$3}` : " ";
4649
4738
  const name = isSelected ? `${BOLD$3}${skill.name}${RESET$3}` : `${TEXT$2}${skill.name}${RESET$3}`;
4650
4739
  const originBadge = skill.origin === "jd" ? ` ${YELLOW$1}[jd]${RESET$3}` : "";
@@ -4654,6 +4743,10 @@ async function runSearchPrompt(initialQuery = "") {
4654
4743
  const loadingIndicator = loading && i === 0 ? ` ${DIM$3}...${RESET$3}` : "";
4655
4744
  lines.push(` ${arrow} ${originBadge}${name}${source}${installsBadge}${loadingIndicator}`);
4656
4745
  }
4746
+ lines.push("");
4747
+ const preview = getSelectedDescription();
4748
+ if (preview) lines.push(...wrapDescriptionPreview(preview));
4749
+ else if (descriptionLoading) lines.push(` ${DIM$3}Loading description…${RESET$3}`);
4657
4750
  }
4658
4751
  if (jdNeedsLogin) {
4659
4752
  lines.push("");
@@ -4664,6 +4757,7 @@ async function runSearchPrompt(initialQuery = "") {
4664
4757
  lines.push(footerHint);
4665
4758
  for (const line of lines) process.stdout.write(line + "\n");
4666
4759
  lastRenderedLines = lines.length;
4760
+ if (results.length > 0 && !getSelectedDescription() && !descriptionLoading) prefetchSelectedDescription();
4667
4761
  }
4668
4762
  function triggerSearch(q) {
4669
4763
  if (debounceTimer) {
@@ -4674,6 +4768,7 @@ async function runSearchPrompt(initialQuery = "") {
4674
4768
  if (!isSearchableQuery(q)) {
4675
4769
  results = [];
4676
4770
  selectedIndex = 0;
4771
+ scrollOffset = 0;
4677
4772
  jdNeedsLogin = false;
4678
4773
  render();
4679
4774
  return;
@@ -4685,6 +4780,7 @@ async function runSearchPrompt(initialQuery = "") {
4685
4780
  try {
4686
4781
  results = await searchSkillsAPI(q);
4687
4782
  selectedIndex = 0;
4783
+ scrollOffset = 0;
4688
4784
  if (isJdIntranetAvailable()) jdNeedsLogin = await getJdSourcePromptState() === "auth_required" || getLastJdSearchKind() === "auth_required";
4689
4785
  else jdNeedsLogin = false;
4690
4786
  } catch {
@@ -4719,11 +4815,15 @@ async function runSearchPrompt(initialQuery = "") {
4719
4815
  }
4720
4816
  if (key.name === "up") {
4721
4817
  selectedIndex = Math.max(0, selectedIndex - 1);
4818
+ descriptionFetchToken++;
4819
+ descriptionLoading = false;
4722
4820
  render();
4723
4821
  return;
4724
4822
  }
4725
4823
  if (key.name === "down") {
4726
4824
  selectedIndex = Math.min(Math.max(0, results.length - 1), selectedIndex + 1);
4825
+ descriptionFetchToken++;
4826
+ descriptionLoading = false;
4727
4827
  render();
4728
4828
  return;
4729
4829
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jd-skills",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for the agent skills ecosystem (JD fork of vercel-labs/skills)",
5
5
  "type": "module",
6
6
  "bin": {