skilld 0.9.1 → 0.9.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.
package/README.md CHANGED
@@ -158,6 +158,7 @@ skilld config
158
158
  | `skilld remove` | Remove installed skills |
159
159
  | `skilld uninstall` | Remove all skilld data |
160
160
  | `skilld cache` | Cache management (clean expired LLM cache entries) |
161
+ | `skilld eject <pkg>` | Eject skill as portable directory (no symlinks) |
161
162
 
162
163
  ### Eject
163
164
 
@@ -165,10 +166,16 @@ Export a skill as a portable, self-contained directory with references copied as
165
166
 
166
167
  ```bash
167
168
  # Eject to the default skill directory
168
- skilld add vue --eject
169
+ skilld eject vue
170
+
171
+ # Custom skill directory name
172
+ skilld eject vue --name vue
169
173
 
170
174
  # Eject to a custom path
171
- skilld add vue --eject ./skills/vue/
175
+ skilld eject vue --out ./skills/vue/
176
+
177
+ # Only collect releases/issues/discussions since a date
178
+ skilld eject vue --from 2025-07-01
172
179
  ```
173
180
 
174
181
  The ejected skill contains `SKILL.md` plus a `references/` directory with docs, issues, and releases as real files. Share it via `skilld add owner/repo` — consumers get fully functional skills with no LLM cost.
@@ -182,7 +189,9 @@ The ejected skill contains `SKILL.md` plus a `references/` directory with docs,
182
189
  | `--yes` | `-y` | `false` | Skip prompts, use defaults |
183
190
  | `--force` | `-f` | `false` | Ignore all caches, re-fetch docs and regenerate |
184
191
  | `--model` | `-m` | config default | LLM model for skill generation (sonnet, haiku, opus, etc.) |
185
- | `--eject` | `-e` | | Eject skill with references as real files (optional path override) |
192
+ | `--name` | `-n` | | Custom skill directory name (eject only) |
193
+ | `--out` | `-o` | | Output directory path override (eject only) |
194
+ | `--from` | | | Collect releases/issues/discussions from this date (YYYY-MM-DD, eject only) |
186
195
  | `--debug` | | `false` | Save raw LLM output to logs/ for each section |
187
196
 
188
197
  ## The Landscape
@@ -162,10 +162,11 @@ function truncateBody$1(body, limit) {
162
162
  if (lastParagraph > lastSafeEnd * .6) return `${slice.slice(0, lastParagraph)}\n\n...`;
163
163
  return `${slice}...`;
164
164
  }
165
- function fetchIssuesByState(owner, repo, state, count, releasedAt) {
165
+ function fetchIssuesByState(owner, repo, state, count, releasedAt, fromDate) {
166
166
  const fetchCount = Math.min(count * 3, 100);
167
167
  let datePart = "";
168
- if (state === "closed") if (releasedAt) {
168
+ if (fromDate) datePart = state === "closed" ? `+closed:>=${fromDate}` : `+created:>=${fromDate}`;
169
+ else if (state === "closed") if (releasedAt) {
169
170
  const date = new Date(releasedAt);
170
171
  date.setMonth(date.getMonth() + 6);
171
172
  datePart = `+closed:<=${isoDate(date.toISOString())}`;
@@ -256,13 +257,13 @@ function detectResolvedVersion(comments) {
256
257
  }
257
258
  }
258
259
  }
259
- async function fetchGitHubIssues(owner, repo, limit = 30, releasedAt) {
260
+ async function fetchGitHubIssues(owner, repo, limit = 30, releasedAt, fromDate) {
260
261
  if (!isGhAvailable()) return [];
261
262
  const openCount = Math.ceil(limit * .75);
262
263
  const closedCount = limit - openCount;
263
264
  try {
264
- const open = fetchIssuesByState(owner, repo, "open", Math.min(openCount * 2, 100), releasedAt);
265
- const closed = fetchIssuesByState(owner, repo, "closed", Math.min(closedCount * 2, 50), releasedAt);
265
+ const open = fetchIssuesByState(owner, repo, "open", Math.min(openCount * 2, 100), releasedAt, fromDate);
266
+ const closed = fetchIssuesByState(owner, repo, "closed", Math.min(closedCount * 2, 50), releasedAt, fromDate);
266
267
  const selected = applyTypeQuotas([...open, ...closed], limit);
267
268
  enrichWithComments(owner, repo, selected);
268
269
  return selected;
@@ -473,16 +474,21 @@ async function fetchAllReleases(owner, repo) {
473
474
  }
474
475
  return fetchReleasesViaUngh(owner, repo);
475
476
  }
476
- function selectReleases(releases, packageName, installedVersion) {
477
+ function selectReleases(releases, packageName, installedVersion, fromDate) {
477
478
  const hasMonorepoTags = packageName && releases.some((r) => tagMatchesPackage(r.tag, packageName));
478
479
  const installedSv = installedVersion ? parseSemver(installedVersion) : null;
479
480
  const installedIsPrerelease = installedVersion ? isPrerelease(installedVersion) : false;
480
- return releases.filter((r) => {
481
+ const fromTs = fromDate ? new Date(fromDate).getTime() : null;
482
+ const sorted = releases.filter((r) => {
481
483
  const ver = extractVersion(r.tag, hasMonorepoTags ? packageName : void 0);
482
484
  if (!ver) return false;
483
485
  const sv = parseSemver(ver);
484
486
  if (!sv) return false;
485
487
  if (hasMonorepoTags && packageName && !tagMatchesPackage(r.tag, packageName)) return false;
488
+ if (fromTs) {
489
+ const pubDate = r.publishedAt || r.createdAt;
490
+ if (pubDate && new Date(pubDate).getTime() < fromTs) return false;
491
+ }
486
492
  if (r.prerelease) {
487
493
  if (!installedIsPrerelease || !installedSv) return false;
488
494
  return sv.major === installedSv.major && sv.minor === installedSv.minor;
@@ -494,7 +500,8 @@ function selectReleases(releases, packageName, installedVersion) {
494
500
  const verB = extractVersion(b.tag, hasMonorepoTags ? packageName : void 0);
495
501
  if (!verA || !verB) return 0;
496
502
  return compareSemver(parseSemver(verB), parseSemver(verA));
497
- }).slice(0, 20);
503
+ });
504
+ return fromDate ? sorted : sorted.slice(0, 20);
498
505
  }
499
506
  function formatRelease(release, packageName) {
500
507
  const date = isoDate(release.publishedAt || release.createdAt);
@@ -572,8 +579,8 @@ async function fetchChangelog(owner, repo, ref) {
572
579
  }
573
580
  return null;
574
581
  }
575
- async function fetchReleaseNotes(owner, repo, installedVersion, gitRef, packageName) {
576
- const selected = selectReleases(await fetchAllReleases(owner, repo), packageName, installedVersion);
582
+ async function fetchReleaseNotes(owner, repo, installedVersion, gitRef, packageName, fromDate) {
583
+ const selected = selectReleases(await fetchAllReleases(owner, repo), packageName, installedVersion, fromDate);
577
584
  if (selected.length > 0) {
578
585
  if (isChangelogRedirectPattern(selected)) {
579
586
  const changelog = await fetchChangelog(owner, repo, gitRef || selected[0].tag);
@@ -713,9 +720,9 @@ function truncateBody(body, limit) {
713
720
  function scoreComment(c) {
714
721
  return (c.isMaintainer ? 3 : 1) * (hasCodeBlock(c.body) ? 2 : 1) * (1 + c.reactions);
715
722
  }
716
- async function fetchGitHubDiscussions(owner, repo, limit = 20, releasedAt) {
723
+ async function fetchGitHubDiscussions(owner, repo, limit = 20, releasedAt, fromDate) {
717
724
  if (!isGhAvailable()) return [];
718
- if (releasedAt) {
725
+ if (!fromDate && releasedAt) {
719
726
  const cutoff = new Date(releasedAt);
720
727
  cutoff.setMonth(cutoff.getMonth() + 6);
721
728
  if (cutoff < /* @__PURE__ */ new Date()) return [];
@@ -737,10 +744,11 @@ async function fetchGitHubDiscussions(owner, repo, limit = 20, releasedAt) {
737
744
  if (!result) return [];
738
745
  const nodes = JSON.parse(result)?.data?.repository?.discussions?.nodes;
739
746
  if (!Array.isArray(nodes)) return [];
747
+ const fromTs = fromDate ? new Date(fromDate).getTime() : null;
740
748
  return nodes.filter((d) => d.author && !BOT_USERS.has(d.author.login)).filter((d) => {
741
749
  const cat = (d.category?.name || "").toLowerCase();
742
750
  return !LOW_VALUE_CATEGORIES.has(cat);
743
- }).map((d) => {
751
+ }).filter((d) => !fromTs || new Date(d.createdAt).getTime() >= fromTs).map((d) => {
744
752
  let answer;
745
753
  if (d.answer?.body) {
746
754
  const isMaintainer = [