skilld 0.9.5 → 0.9.6

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.
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk.mjs";
2
2
  import { _ as writeSections, b as sanitizeMarkdown, h as readCachedSection, y as repairMarkdown } from "./storage.mjs";
3
- import { d as getPackageRules, o as mapInsert, t as yamlEscape, u as getFilePatterns } from "./yaml.mjs";
3
+ import { o as getFilePatterns, s as getPackageRules, t as yamlEscape } from "./yaml.mjs";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join, relative } from "pathe";
6
6
  import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
@@ -8,6 +8,7 @@ import { exec, spawn, spawnSync } from "node:child_process";
8
8
  import { globby } from "globby";
9
9
  import { findDynamicImports, findStaticImports } from "mlly";
10
10
  import { createHash } from "node:crypto";
11
+ import { setTimeout } from "node:timers/promises";
11
12
  import { promisify } from "node:util";
12
13
  import { readFile } from "node:fs/promises";
13
14
  import { parseSync } from "oxc-parser";
@@ -1392,58 +1393,39 @@ const TOOL_VERBS = {
1392
1393
  search_file_content: "Searching"
1393
1394
  };
1394
1395
  function createToolProgress(log) {
1395
- const pending = /* @__PURE__ */ new Map();
1396
- let timer = null;
1397
- let lastEmitted = "";
1398
- function flush() {
1399
- const parts = [];
1400
- for (const [section, { verb, path, count }] of pending) {
1401
- const suffix = count > 1 ? ` \x1B[90m(+${count - 1})\x1B[0m` : "";
1402
- parts.push(`\x1B[90m[${section}]\x1B[0m ${verb} ${path}${suffix}`);
1403
- }
1404
- const msg = parts.join(" ");
1405
- if (msg && msg !== lastEmitted) {
1396
+ let lastMsg = "";
1397
+ let repeatCount = 0;
1398
+ function emit(msg) {
1399
+ if (msg === lastMsg) {
1400
+ repeatCount++;
1401
+ log.message(`${msg} \x1B[90m(+${repeatCount})\x1B[0m`);
1402
+ } else {
1403
+ lastMsg = msg;
1404
+ repeatCount = 0;
1406
1405
  log.message(msg);
1407
- lastEmitted = msg;
1408
1406
  }
1409
- pending.clear();
1410
- timer = null;
1411
1407
  }
1412
1408
  return ({ type, chunk, section }) => {
1413
1409
  if (type === "text") {
1414
- log.message(`${section ? `\x1B[90m[${section}]\x1B[0m ` : ""}Writing...`);
1410
+ emit(`${section ? `\x1B[90m[${section}]\x1B[0m ` : ""}Writing...`);
1415
1411
  return;
1416
1412
  }
1417
1413
  if (type !== "reasoning" || !chunk.startsWith("[")) return;
1418
- const key = section ?? "";
1419
- const match = chunk.match(/^\[(\w+)(?:,\s\w+)*(?::\s(.+))?\]$/);
1414
+ const match = chunk.match(/^\[([^:[\]]+)(?::\s(.+))?\]$/);
1420
1415
  if (!match) return;
1421
- const rawName = match[1];
1422
- const hint = match[2] ?? "";
1423
- let verb = TOOL_VERBS[rawName] ?? rawName;
1424
- let path = hint || "...";
1425
- if (rawName === "Bash" && hint) {
1426
- const searchMatch = hint.match(/skilld search\s+"([^"]+)"/);
1427
- if (searchMatch) {
1428
- verb = "skilld search:";
1429
- path = searchMatch[1];
1430
- } else path = hint.length > 60 ? `${hint.slice(0, 57)}...` : hint;
1431
- } else path = shortenPath(path);
1432
- if (rawName === "Write") {
1433
- if (timer) flush();
1416
+ const names = match[1].split(",").map((n) => n.trim());
1417
+ const hints = match[2]?.split(",").map((h) => h.trim()) ?? [];
1418
+ for (let i = 0; i < names.length; i++) {
1419
+ const rawName = names[i];
1420
+ const hint = hints[i] ?? hints[0] ?? "";
1421
+ const verb = TOOL_VERBS[rawName] ?? rawName;
1434
1422
  const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : "";
1435
- log.message(`${prefix}Writing ${path}`);
1436
- return;
1423
+ if (rawName === "Bash" && hint) {
1424
+ const searchMatch = hint.match(/skilld search\s+"([^"]+)"/);
1425
+ if (searchMatch) emit(`${prefix}Searching \x1B[36m"${searchMatch[1]}"\x1B[0m`);
1426
+ else emit(`${prefix}Running ${hint.length > 50 ? `${hint.slice(0, 47)}...` : hint}`);
1427
+ } else emit(`${prefix}${verb} \x1B[90m${shortenPath(hint || "...")}\x1B[0m`);
1437
1428
  }
1438
- const entry = mapInsert(pending, key, () => ({
1439
- verb,
1440
- path,
1441
- count: 0
1442
- }));
1443
- entry.verb = verb;
1444
- entry.path = path;
1445
- entry.count++;
1446
- if (!timer) timer = setTimeout(flush, 400);
1447
1429
  };
1448
1430
  }
1449
1431
  const CLI_DEFS = [
@@ -1622,13 +1604,16 @@ function optimizeSection(opts) {
1622
1604
  } catch {}
1623
1605
  }
1624
1606
  const raw = (existsSync(outputPath) ? readFileSync(outputPath, "utf-8") : lastWriteContent || accumulatedText).trim();
1607
+ const logsDir = join(skilldDir, "logs");
1608
+ const logName = section.toUpperCase().replace(/-/g, "_");
1609
+ if (debug || stderr && (!raw || code !== 0)) {
1610
+ mkdirSync(logsDir, { recursive: true });
1611
+ if (stderr) writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr);
1612
+ }
1625
1613
  if (debug) {
1626
- const logsDir = join(skilldDir, "logs");
1627
1614
  mkdirSync(logsDir, { recursive: true });
1628
- const logName = section.toUpperCase().replace(/-/g, "_");
1629
1615
  if (rawLines.length) writeFileSync(join(logsDir, `${logName}.jsonl`), rawLines.join("\n"));
1630
1616
  if (raw) writeFileSync(join(logsDir, `${logName}.md`), raw);
1631
- if (stderr) writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr);
1632
1617
  }
1633
1618
  if (!raw && code !== 0) {
1634
1619
  resolve({
@@ -1739,10 +1724,22 @@ async function optimizeDocs(opts) {
1739
1724
  }
1740
1725
  const skilldDir = join(skillDir, ".skilld");
1741
1726
  mkdirSync(skilldDir, { recursive: true });
1727
+ for (const entry of readdirSync(skilldDir)) {
1728
+ const entryPath = join(skilldDir, entry);
1729
+ try {
1730
+ if (lstatSync(entryPath).isSymbolicLink() && !existsSync(entryPath)) onProgress?.({
1731
+ chunk: `[warn: broken symlink .skilld/${entry}]`,
1732
+ type: "reasoning",
1733
+ text: "",
1734
+ reasoning: ""
1735
+ });
1736
+ } catch {}
1737
+ }
1742
1738
  const preExistingFiles = new Set(readdirSync(skilldDir));
1743
- const spawnResults = uncachedSections.length > 0 ? await Promise.allSettled(uncachedSections.map(({ section, prompt }) => {
1739
+ const STAGGER_MS = 3e3;
1740
+ const spawnResults = uncachedSections.length > 0 ? await Promise.allSettled(uncachedSections.map(({ section, prompt }, i) => {
1744
1741
  const outputFile = SECTION_OUTPUT_FILES[section];
1745
- return optimizeSection({
1742
+ const run = () => optimizeSection({
1746
1743
  section,
1747
1744
  prompt,
1748
1745
  outputFile,
@@ -1754,32 +1751,71 @@ async function optimizeDocs(opts) {
1754
1751
  debug,
1755
1752
  preExistingFiles
1756
1753
  });
1754
+ if (i === 0) return run();
1755
+ return setTimeout(i * STAGGER_MS).then(run);
1757
1756
  })) : [];
1758
1757
  const allResults = [...cachedResults];
1759
1758
  let totalUsage;
1760
1759
  let totalCost = 0;
1760
+ const retryQueue = [];
1761
1761
  for (let i = 0; i < spawnResults.length; i++) {
1762
1762
  const r = spawnResults[i];
1763
1763
  const { section, prompt } = uncachedSections[i];
1764
- if (r.status === "fulfilled") {
1765
- const result = r.value;
1766
- allResults.push(result);
1767
- if (result.wasOptimized && !noCache) setCache(prompt, model, section, result.content);
1768
- if (result.usage) {
1764
+ if (r.status === "fulfilled" && r.value.wasOptimized) {
1765
+ allResults.push(r.value);
1766
+ if (r.value.usage) {
1769
1767
  totalUsage = totalUsage ?? {
1770
1768
  input: 0,
1771
1769
  output: 0
1772
1770
  };
1773
- totalUsage.input += result.usage.input;
1774
- totalUsage.output += result.usage.output;
1771
+ totalUsage.input += r.value.usage.input;
1772
+ totalUsage.output += r.value.usage.output;
1775
1773
  }
1776
- if (result.cost != null) totalCost += result.cost;
1777
- } else allResults.push({
1774
+ if (r.value.cost != null) totalCost += r.value.cost;
1775
+ if (!noCache) setCache(prompt, model, section, r.value.content);
1776
+ } else retryQueue.push({
1777
+ index: i,
1778
+ section,
1779
+ prompt
1780
+ });
1781
+ }
1782
+ for (const { section, prompt } of retryQueue) {
1783
+ onProgress?.({
1784
+ chunk: `[${section}: retrying...]`,
1785
+ type: "reasoning",
1786
+ text: "",
1787
+ reasoning: "",
1788
+ section
1789
+ });
1790
+ await setTimeout(STAGGER_MS);
1791
+ const result = await optimizeSection({
1792
+ section,
1793
+ prompt,
1794
+ outputFile: SECTION_OUTPUT_FILES[section],
1795
+ skillDir,
1796
+ model,
1797
+ packageName,
1798
+ onProgress,
1799
+ timeout,
1800
+ debug,
1801
+ preExistingFiles
1802
+ }).catch((err) => ({
1778
1803
  section,
1779
1804
  content: "",
1780
1805
  wasOptimized: false,
1781
- error: String(r.reason)
1782
- });
1806
+ error: err.message
1807
+ }));
1808
+ allResults.push(result);
1809
+ if (result.wasOptimized && !noCache) setCache(prompt, model, section, result.content);
1810
+ if (result.usage) {
1811
+ totalUsage = totalUsage ?? {
1812
+ input: 0,
1813
+ output: 0
1814
+ };
1815
+ totalUsage.input += result.usage.input;
1816
+ totalUsage.output += result.usage.output;
1817
+ }
1818
+ if (result.cost != null) totalCost += result.cost;
1783
1819
  }
1784
1820
  if (version) {
1785
1821
  const sectionFiles = allResults.filter((r) => r.wasOptimized && r.content).map((r) => ({