skilld 0.9.4 → 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.
- package/dist/_chunks/detect-imports.mjs +116 -73
- package/dist/_chunks/detect-imports.mjs.map +1 -1
- package/dist/_chunks/npm.mjs +19 -2
- package/dist/_chunks/npm.mjs.map +1 -1
- package/dist/_chunks/utils.d.mts +2 -0
- package/dist/_chunks/utils.d.mts.map +1 -1
- package/dist/_chunks/yaml.mjs +10 -22
- package/dist/_chunks/yaml.mjs.map +1 -1
- package/dist/agent/index.d.mts +1 -1
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/cli.mjs +9 -5
- package/dist/cli.mjs.map +1 -1
- package/dist/sources/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -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 { o as
|
|
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";
|
|
@@ -558,7 +559,7 @@ function budgetScale(sectionCount) {
|
|
|
558
559
|
if (sectionCount === 3) return .7;
|
|
559
560
|
return .6;
|
|
560
561
|
}
|
|
561
|
-
function apiChangesSection({ packageName, version, hasReleases, hasChangelog, hasIssues, hasDiscussions, features, enabledSectionCount }) {
|
|
562
|
+
function apiChangesSection({ packageName, version, hasReleases, hasChangelog, hasDocs, hasIssues, hasDiscussions, features, enabledSectionCount }) {
|
|
562
563
|
const [, major, minor] = version?.match(/^(\d+)\.(\d+)/) ?? [];
|
|
563
564
|
const searchHints = [];
|
|
564
565
|
if (features?.search !== false) {
|
|
@@ -587,11 +588,11 @@ function apiChangesSection({ packageName, version, hasReleases, hasChangelog, ha
|
|
|
587
588
|
});
|
|
588
589
|
if (hasChangelog) referenceWeights.push({
|
|
589
590
|
name: "Changelog",
|
|
590
|
-
path: `./.skilld
|
|
591
|
+
path: `./.skilld/${hasChangelog}`,
|
|
591
592
|
score: 9,
|
|
592
593
|
useFor: "Features/Breaking Changes sections per version"
|
|
593
594
|
});
|
|
594
|
-
referenceWeights.push({
|
|
595
|
+
if (hasDocs) referenceWeights.push({
|
|
595
596
|
name: "Docs",
|
|
596
597
|
path: "./.skilld/docs/",
|
|
597
598
|
score: 4,
|
|
@@ -646,15 +647,16 @@ Each item: ⚠️ (breaking/deprecated) or ✨ (new) + API name + what changed +
|
|
|
646
647
|
].filter(Boolean)
|
|
647
648
|
};
|
|
648
649
|
}
|
|
649
|
-
function bestPracticesSection({ packageName, hasIssues, hasDiscussions, hasReleases, hasChangelog, features, enabledSectionCount }) {
|
|
650
|
+
function bestPracticesSection({ packageName, hasIssues, hasDiscussions, hasReleases, hasChangelog, hasDocs, features, enabledSectionCount }) {
|
|
650
651
|
const searchHints = [];
|
|
651
652
|
if (features?.search !== false) searchHints.push(`\`npx -y skilld search "recommended" -p ${packageName}\``, `\`npx -y skilld search "avoid" -p ${packageName}\``);
|
|
652
|
-
const referenceWeights = [
|
|
653
|
+
const referenceWeights = [];
|
|
654
|
+
if (hasDocs) referenceWeights.push({
|
|
653
655
|
name: "Docs",
|
|
654
656
|
path: "./.skilld/docs/",
|
|
655
657
|
score: 9,
|
|
656
658
|
useFor: "Primary source — recommended patterns, configuration, idiomatic usage"
|
|
657
|
-
}
|
|
659
|
+
});
|
|
658
660
|
if (hasDiscussions) referenceWeights.push({
|
|
659
661
|
name: "Discussions",
|
|
660
662
|
path: "./.skilld/discussions/_INDEX.md",
|
|
@@ -675,7 +677,7 @@ function bestPracticesSection({ packageName, hasIssues, hasDiscussions, hasRelea
|
|
|
675
677
|
});
|
|
676
678
|
if (hasChangelog) referenceWeights.push({
|
|
677
679
|
name: "Changelog",
|
|
678
|
-
path: `./.skilld
|
|
680
|
+
path: `./.skilld/${hasChangelog}`,
|
|
679
681
|
score: 3,
|
|
680
682
|
useFor: "Only for new patterns introduced in recent versions"
|
|
681
683
|
});
|
|
@@ -723,13 +725,14 @@ Content addressing the user's instructions above, using concise examples and sou
|
|
|
723
725
|
rules: [`- **Custom section "${heading}":** MAX ${maxLines(50, 80, enabledSectionCount)} lines, use \`## ${heading}\` heading`]
|
|
724
726
|
};
|
|
725
727
|
}
|
|
726
|
-
function apiSection({ hasReleases, hasChangelog, hasIssues, hasDiscussions, enabledSectionCount }) {
|
|
727
|
-
const referenceWeights = [
|
|
728
|
+
function apiSection({ hasReleases, hasChangelog, hasDocs, hasIssues, hasDiscussions, enabledSectionCount }) {
|
|
729
|
+
const referenceWeights = [];
|
|
730
|
+
if (hasDocs) referenceWeights.push({
|
|
728
731
|
name: "Docs",
|
|
729
732
|
path: "./.skilld/docs/",
|
|
730
733
|
score: 10,
|
|
731
734
|
useFor: "Primary source — scan all doc pages for export names"
|
|
732
|
-
}
|
|
735
|
+
});
|
|
733
736
|
if (hasReleases) referenceWeights.push({
|
|
734
737
|
name: "Releases",
|
|
735
738
|
path: "./.skilld/releases/_INDEX.md",
|
|
@@ -738,7 +741,7 @@ function apiSection({ hasReleases, hasChangelog, hasIssues, hasDiscussions, enab
|
|
|
738
741
|
});
|
|
739
742
|
if (hasChangelog) referenceWeights.push({
|
|
740
743
|
name: "Changelog",
|
|
741
|
-
path: `./.skilld
|
|
744
|
+
path: `./.skilld/${hasChangelog}`,
|
|
742
745
|
score: 5,
|
|
743
746
|
useFor: "New APIs added in recent versions"
|
|
744
747
|
});
|
|
@@ -815,7 +818,7 @@ function generateImportantBlock({ packageName, hasIssues, hasDiscussions, hasRel
|
|
|
815
818
|
const rows = [["Docs", hasShippedDocs ? `\`${skillDir}/.skilld/pkg/docs/\` or \`${skillDir}/.skilld/pkg/README.md\`` : docsType === "llms.txt" ? `\`${skillDir}/.skilld/docs/llms.txt\`` : docsType === "readme" ? `\`${skillDir}/.skilld/pkg/README.md\`` : `\`${skillDir}/.skilld/docs/\``], ["Package", `\`${skillDir}/.skilld/pkg/\``]];
|
|
816
819
|
if (hasIssues) rows.push(["Issues", `\`${skillDir}/.skilld/issues/\``]);
|
|
817
820
|
if (hasDiscussions) rows.push(["Discussions", `\`${skillDir}/.skilld/discussions/\``]);
|
|
818
|
-
if (hasChangelog) rows.push(["Changelog", `\`${skillDir}/.skilld
|
|
821
|
+
if (hasChangelog) rows.push(["Changelog", `\`${skillDir}/.skilld/${hasChangelog}\``]);
|
|
819
822
|
if (hasReleases) rows.push(["Releases", `\`${skillDir}/.skilld/releases/\``]);
|
|
820
823
|
const table = [
|
|
821
824
|
"| Resource | Path |",
|
|
@@ -890,15 +893,18 @@ function buildSectionPrompt(opts) {
|
|
|
890
893
|
hasDiscussions,
|
|
891
894
|
hasReleases,
|
|
892
895
|
hasChangelog,
|
|
896
|
+
hasDocs: !!opts.docFiles?.some((f) => f.includes("/docs/")),
|
|
893
897
|
features: opts.features,
|
|
894
898
|
enabledSectionCount: opts.enabledSectionCount
|
|
895
899
|
}, customPrompt);
|
|
896
900
|
if (!sectionDef) return "";
|
|
897
901
|
const outputFile = SECTION_OUTPUT_FILES[section];
|
|
902
|
+
const packageRules = getPackageRules(packageName);
|
|
898
903
|
const rules = [
|
|
899
904
|
...sectionDef.rules ?? [],
|
|
900
905
|
"- Link to exact source file where you found info",
|
|
901
|
-
"- TypeScript only
|
|
906
|
+
"- TypeScript only",
|
|
907
|
+
...packageRules.map((r) => `- ${r}`),
|
|
902
908
|
"- Imperative voice (\"Use X\" not \"You should use X\")",
|
|
903
909
|
`- **NEVER fetch external URLs.** All information is in the local \`./.skilld/\` directory. Use Read, Glob${opts.features?.search !== false ? ", and `skilld search`" : ""} only.`,
|
|
904
910
|
"- **Do NOT use Task tool or spawn subagents.** Work directly.",
|
|
@@ -1342,7 +1348,7 @@ function buildArgs(model, skillDir, symlinkDirs) {
|
|
|
1342
1348
|
"-m",
|
|
1343
1349
|
model,
|
|
1344
1350
|
"--allowed-tools",
|
|
1345
|
-
"read_file,write_file,glob_tool",
|
|
1351
|
+
"read_file,write_file,glob_tool,list_directory,search_file_content",
|
|
1346
1352
|
"--include-directories",
|
|
1347
1353
|
skillDir,
|
|
1348
1354
|
...symlinkDirs.flatMap((d) => ["--include-directories", d])
|
|
@@ -1382,61 +1388,44 @@ const TOOL_VERBS = {
|
|
|
1382
1388
|
Bash: "Running",
|
|
1383
1389
|
read_file: "Reading",
|
|
1384
1390
|
glob_tool: "Searching",
|
|
1385
|
-
write_file: "Writing"
|
|
1391
|
+
write_file: "Writing",
|
|
1392
|
+
list_directory: "Listing",
|
|
1393
|
+
search_file_content: "Searching"
|
|
1386
1394
|
};
|
|
1387
1395
|
function createToolProgress(log) {
|
|
1388
|
-
|
|
1389
|
-
let
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
const msg = parts.join(" ");
|
|
1398
|
-
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;
|
|
1399
1405
|
log.message(msg);
|
|
1400
|
-
lastEmitted = msg;
|
|
1401
1406
|
}
|
|
1402
|
-
pending.clear();
|
|
1403
|
-
timer = null;
|
|
1404
1407
|
}
|
|
1405
1408
|
return ({ type, chunk, section }) => {
|
|
1406
1409
|
if (type === "text") {
|
|
1407
|
-
|
|
1410
|
+
emit(`${section ? `\x1B[90m[${section}]\x1B[0m ` : ""}Writing...`);
|
|
1408
1411
|
return;
|
|
1409
1412
|
}
|
|
1410
1413
|
if (type !== "reasoning" || !chunk.startsWith("[")) return;
|
|
1411
|
-
const
|
|
1412
|
-
const match = chunk.match(/^\[(\w+)(?:,\s\w+)*(?::\s(.+))?\]$/);
|
|
1414
|
+
const match = chunk.match(/^\[([^:[\]]+)(?::\s(.+))?\]$/);
|
|
1413
1415
|
if (!match) return;
|
|
1414
|
-
const
|
|
1415
|
-
const
|
|
1416
|
-
let
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
const
|
|
1420
|
-
if (searchMatch) {
|
|
1421
|
-
verb = "skilld search:";
|
|
1422
|
-
path = searchMatch[1];
|
|
1423
|
-
} else path = hint.length > 60 ? `${hint.slice(0, 57)}...` : hint;
|
|
1424
|
-
} else path = shortenPath(path);
|
|
1425
|
-
if (rawName === "Write") {
|
|
1426
|
-
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;
|
|
1427
1422
|
const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : "";
|
|
1428
|
-
|
|
1429
|
-
|
|
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`);
|
|
1430
1428
|
}
|
|
1431
|
-
const entry = mapInsert(pending, key, () => ({
|
|
1432
|
-
verb,
|
|
1433
|
-
path,
|
|
1434
|
-
count: 0
|
|
1435
|
-
}));
|
|
1436
|
-
entry.verb = verb;
|
|
1437
|
-
entry.path = path;
|
|
1438
|
-
entry.count++;
|
|
1439
|
-
if (!timer) timer = setTimeout(flush, 400);
|
|
1440
1429
|
};
|
|
1441
1430
|
}
|
|
1442
1431
|
const CLI_DEFS = [
|
|
@@ -1615,13 +1604,16 @@ function optimizeSection(opts) {
|
|
|
1615
1604
|
} catch {}
|
|
1616
1605
|
}
|
|
1617
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
|
+
}
|
|
1618
1613
|
if (debug) {
|
|
1619
|
-
const logsDir = join(skilldDir, "logs");
|
|
1620
1614
|
mkdirSync(logsDir, { recursive: true });
|
|
1621
|
-
const logName = section.toUpperCase().replace(/-/g, "_");
|
|
1622
1615
|
if (rawLines.length) writeFileSync(join(logsDir, `${logName}.jsonl`), rawLines.join("\n"));
|
|
1623
1616
|
if (raw) writeFileSync(join(logsDir, `${logName}.md`), raw);
|
|
1624
|
-
if (stderr) writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr);
|
|
1625
1617
|
}
|
|
1626
1618
|
if (!raw && code !== 0) {
|
|
1627
1619
|
resolve({
|
|
@@ -1732,10 +1724,22 @@ async function optimizeDocs(opts) {
|
|
|
1732
1724
|
}
|
|
1733
1725
|
const skilldDir = join(skillDir, ".skilld");
|
|
1734
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
|
+
}
|
|
1735
1738
|
const preExistingFiles = new Set(readdirSync(skilldDir));
|
|
1736
|
-
const
|
|
1739
|
+
const STAGGER_MS = 3e3;
|
|
1740
|
+
const spawnResults = uncachedSections.length > 0 ? await Promise.allSettled(uncachedSections.map(({ section, prompt }, i) => {
|
|
1737
1741
|
const outputFile = SECTION_OUTPUT_FILES[section];
|
|
1738
|
-
|
|
1742
|
+
const run = () => optimizeSection({
|
|
1739
1743
|
section,
|
|
1740
1744
|
prompt,
|
|
1741
1745
|
outputFile,
|
|
@@ -1747,32 +1751,71 @@ async function optimizeDocs(opts) {
|
|
|
1747
1751
|
debug,
|
|
1748
1752
|
preExistingFiles
|
|
1749
1753
|
});
|
|
1754
|
+
if (i === 0) return run();
|
|
1755
|
+
return setTimeout(i * STAGGER_MS).then(run);
|
|
1750
1756
|
})) : [];
|
|
1751
1757
|
const allResults = [...cachedResults];
|
|
1752
1758
|
let totalUsage;
|
|
1753
1759
|
let totalCost = 0;
|
|
1760
|
+
const retryQueue = [];
|
|
1754
1761
|
for (let i = 0; i < spawnResults.length; i++) {
|
|
1755
1762
|
const r = spawnResults[i];
|
|
1756
1763
|
const { section, prompt } = uncachedSections[i];
|
|
1757
|
-
if (r.status === "fulfilled") {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
if (result.wasOptimized && !noCache) setCache(prompt, model, section, result.content);
|
|
1761
|
-
if (result.usage) {
|
|
1764
|
+
if (r.status === "fulfilled" && r.value.wasOptimized) {
|
|
1765
|
+
allResults.push(r.value);
|
|
1766
|
+
if (r.value.usage) {
|
|
1762
1767
|
totalUsage = totalUsage ?? {
|
|
1763
1768
|
input: 0,
|
|
1764
1769
|
output: 0
|
|
1765
1770
|
};
|
|
1766
|
-
totalUsage.input +=
|
|
1767
|
-
totalUsage.output +=
|
|
1771
|
+
totalUsage.input += r.value.usage.input;
|
|
1772
|
+
totalUsage.output += r.value.usage.output;
|
|
1768
1773
|
}
|
|
1769
|
-
if (
|
|
1770
|
-
|
|
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) => ({
|
|
1771
1803
|
section,
|
|
1772
1804
|
content: "",
|
|
1773
1805
|
wasOptimized: false,
|
|
1774
|
-
error:
|
|
1775
|
-
});
|
|
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;
|
|
1776
1819
|
}
|
|
1777
1820
|
if (version) {
|
|
1778
1821
|
const sectionFiles = allResults.filter((r) => r.wasOptimized && r.content).map((r) => ({
|