@hasna/skills 0.1.14 → 0.1.15
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/bin/index.js +1195 -220
- package/bin/mcp.js +512 -101
- package/dist/index.d.ts +2 -1
- package/dist/index.js +375 -105
- package/dist/lib/installer.d.ts +11 -2
- package/dist/lib/registry.d.ts +12 -0
- package/dist/lib/scheduler.d.ts +47 -0
- package/package.json +1 -1
- package/skills/_common/index.ts +4 -0
- package/skills/_common/vision.ts +374 -0
- package/skills/skill-colorextract/SKILL.md +35 -0
- package/skills/skill-colorextract/bun.lock +102 -0
- package/skills/skill-colorextract/package.json +13 -0
- package/skills/skill-colorextract/src/index.ts +405 -0
- package/skills/skill-siteanalyze/SKILL.md +25 -0
- package/skills/skill-siteanalyze/package.json +13 -0
- package/skills/skill-siteanalyze/src/index.ts +592 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/lib/registry.ts
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { homedir } from "os";
|
|
3
6
|
var CATEGORIES = [
|
|
4
7
|
"Development Tools",
|
|
5
8
|
"Business & Marketing",
|
|
@@ -1036,6 +1039,20 @@ var SKILLS = [
|
|
|
1036
1039
|
category: "Design & Branding",
|
|
1037
1040
|
tags: ["testimonials", "graphics", "social-proof", "marketing"]
|
|
1038
1041
|
},
|
|
1042
|
+
{
|
|
1043
|
+
name: "colorextract",
|
|
1044
|
+
displayName: "Color Extract",
|
|
1045
|
+
description: "Extract complete color palettes from screenshots and images using Claude Vision. Outputs open-styles compatible profiles.",
|
|
1046
|
+
category: "Design & Branding",
|
|
1047
|
+
tags: ["colors", "palette", "design", "vision", "screenshot", "extract", "open-styles"]
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "siteanalyze",
|
|
1051
|
+
displayName: "Site Analyze",
|
|
1052
|
+
description: "Analyze any website's design system \u2014 detects shadcn/ui, Tailwind, extracts colors, typography, and components via Playwright + Claude Vision.",
|
|
1053
|
+
category: "Design & Branding",
|
|
1054
|
+
tags: ["design", "shadcn", "tailwind", "colors", "typography", "playwright", "analysis", "open-styles"]
|
|
1055
|
+
},
|
|
1039
1056
|
{
|
|
1040
1057
|
name: "browse",
|
|
1041
1058
|
displayName: "Browse",
|
|
@@ -1436,8 +1453,91 @@ var SKILLS = [
|
|
|
1436
1453
|
tags: ["seating", "chart", "events", "venues"]
|
|
1437
1454
|
}
|
|
1438
1455
|
];
|
|
1456
|
+
function parseSkillMdFrontmatter(content) {
|
|
1457
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1458
|
+
if (!match)
|
|
1459
|
+
return null;
|
|
1460
|
+
const result = {};
|
|
1461
|
+
for (const line of match[1].split(`
|
|
1462
|
+
`)) {
|
|
1463
|
+
const colon = line.indexOf(":");
|
|
1464
|
+
if (colon === -1)
|
|
1465
|
+
continue;
|
|
1466
|
+
const key = line.slice(0, colon).trim();
|
|
1467
|
+
const value = line.slice(colon + 1).trim();
|
|
1468
|
+
if (!key || !value)
|
|
1469
|
+
continue;
|
|
1470
|
+
if (key === "name")
|
|
1471
|
+
result.name = value;
|
|
1472
|
+
else if (key === "description")
|
|
1473
|
+
result.description = value;
|
|
1474
|
+
else if (key === "displayName" || key === "display_name")
|
|
1475
|
+
result.displayName = value;
|
|
1476
|
+
else if (key === "category")
|
|
1477
|
+
result.category = value;
|
|
1478
|
+
else if (key === "tags") {
|
|
1479
|
+
result.tags = value.replace(/[\[\]]/g, "").split(",").map((t) => t.trim()).filter(Boolean);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
1483
|
+
}
|
|
1484
|
+
function discoverSkillsInDir(dir) {
|
|
1485
|
+
if (!existsSync(dir))
|
|
1486
|
+
return [];
|
|
1487
|
+
const result = [];
|
|
1488
|
+
try {
|
|
1489
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
1490
|
+
for (const entry of entries) {
|
|
1491
|
+
if (!entry.isDirectory())
|
|
1492
|
+
continue;
|
|
1493
|
+
const skillMdPath = join(dir, entry.name, "SKILL.md");
|
|
1494
|
+
if (!existsSync(skillMdPath))
|
|
1495
|
+
continue;
|
|
1496
|
+
let content;
|
|
1497
|
+
try {
|
|
1498
|
+
content = readFileSync(skillMdPath, "utf-8");
|
|
1499
|
+
} catch {
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
const fm = parseSkillMdFrontmatter(content);
|
|
1503
|
+
if (!fm?.name)
|
|
1504
|
+
continue;
|
|
1505
|
+
const name = fm.name.replace(/^skill-/, "");
|
|
1506
|
+
result.push({
|
|
1507
|
+
name,
|
|
1508
|
+
displayName: fm.displayName || name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
1509
|
+
description: fm.description || "",
|
|
1510
|
+
category: fm.category || "Development Tools",
|
|
1511
|
+
tags: fm.tags || [],
|
|
1512
|
+
source: "custom"
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
} catch {}
|
|
1516
|
+
return result;
|
|
1517
|
+
}
|
|
1518
|
+
var _registryCache = null;
|
|
1519
|
+
var _registryCacheTime = 0;
|
|
1520
|
+
var REGISTRY_CACHE_TTL = 5000;
|
|
1521
|
+
function loadRegistry(cwd) {
|
|
1522
|
+
const now = Date.now();
|
|
1523
|
+
if (_registryCache && now - _registryCacheTime < REGISTRY_CACHE_TTL) {
|
|
1524
|
+
return _registryCache;
|
|
1525
|
+
}
|
|
1526
|
+
const official = SKILLS.map((s) => ({ ...s, source: "official" }));
|
|
1527
|
+
const globalCustom = discoverSkillsInDir(join(homedir(), ".skills"));
|
|
1528
|
+
const projectCustom = discoverSkillsInDir(join(cwd || process.cwd(), ".skills", "custom-skills"));
|
|
1529
|
+
const customNames = new Set([...globalCustom, ...projectCustom].map((s) => s.name));
|
|
1530
|
+
const filtered = official.filter((s) => !customNames.has(s.name));
|
|
1531
|
+
_registryCache = [...filtered, ...globalCustom, ...projectCustom];
|
|
1532
|
+
_registryCacheTime = now;
|
|
1533
|
+
return _registryCache;
|
|
1534
|
+
}
|
|
1535
|
+
function clearRegistryCache() {
|
|
1536
|
+
_registryCache = null;
|
|
1537
|
+
_registryCacheTime = 0;
|
|
1538
|
+
}
|
|
1439
1539
|
function getSkillsByCategory(category) {
|
|
1440
|
-
return
|
|
1540
|
+
return loadRegistry().filter((s) => s.category === category);
|
|
1441
1541
|
}
|
|
1442
1542
|
function editDistance(a, b) {
|
|
1443
1543
|
if (a === b)
|
|
@@ -1483,7 +1583,7 @@ function searchSkills(query) {
|
|
|
1483
1583
|
if (words.length === 0)
|
|
1484
1584
|
return [];
|
|
1485
1585
|
const scored = [];
|
|
1486
|
-
for (const skill of
|
|
1586
|
+
for (const skill of loadRegistry()) {
|
|
1487
1587
|
const nameLower = skill.name.toLowerCase();
|
|
1488
1588
|
const displayNameLower = skill.displayName.toLowerCase();
|
|
1489
1589
|
const descriptionLower = skill.description.toLowerCase();
|
|
@@ -1519,15 +1619,15 @@ function searchSkills(query) {
|
|
|
1519
1619
|
return scored.map((s) => s.skill);
|
|
1520
1620
|
}
|
|
1521
1621
|
function getSkill(name) {
|
|
1522
|
-
return
|
|
1622
|
+
return loadRegistry().find((s) => s.name === name);
|
|
1523
1623
|
}
|
|
1524
1624
|
function getSkillsByTag(tag) {
|
|
1525
1625
|
const needle = tag.toLowerCase();
|
|
1526
|
-
return
|
|
1626
|
+
return loadRegistry().filter((s) => s.tags.some((t) => t.toLowerCase().includes(needle)));
|
|
1527
1627
|
}
|
|
1528
1628
|
function getAllTags() {
|
|
1529
1629
|
const tagSet = new Set;
|
|
1530
|
-
for (const skill of
|
|
1630
|
+
for (const skill of loadRegistry()) {
|
|
1531
1631
|
for (const tag of skill.tags) {
|
|
1532
1632
|
tagSet.add(tag.toLowerCase());
|
|
1533
1633
|
}
|
|
@@ -1546,13 +1646,13 @@ function levenshtein(a, b) {
|
|
|
1546
1646
|
}
|
|
1547
1647
|
function findSimilarSkills(query, maxResults = 3) {
|
|
1548
1648
|
const q = query.toLowerCase();
|
|
1549
|
-
const scored =
|
|
1649
|
+
const scored = loadRegistry().map((s) => ({ name: s.name, dist: levenshtein(q, s.name.toLowerCase()) })).filter((s) => s.dist <= Math.max(3, Math.floor(q.length / 2))).sort((a, b) => a.dist - b.dist);
|
|
1550
1650
|
return scored.slice(0, maxResults).map((s) => s.name);
|
|
1551
1651
|
}
|
|
1552
1652
|
// src/lib/installer.ts
|
|
1553
|
-
import { existsSync, cpSync, mkdirSync, writeFileSync, rmSync, readdirSync, statSync, readFileSync, accessSync, constants } from "fs";
|
|
1554
|
-
import { join, dirname } from "path";
|
|
1555
|
-
import { homedir } from "os";
|
|
1653
|
+
import { existsSync as existsSync2, cpSync, mkdirSync, writeFileSync, rmSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync2, accessSync, constants } from "fs";
|
|
1654
|
+
import { join as join2, dirname } from "path";
|
|
1655
|
+
import { homedir as homedir2 } from "os";
|
|
1556
1656
|
import { fileURLToPath } from "url";
|
|
1557
1657
|
|
|
1558
1658
|
// src/lib/utils.ts
|
|
@@ -1565,36 +1665,36 @@ var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
|
1565
1665
|
function findSkillsDir() {
|
|
1566
1666
|
let dir = __dirname2;
|
|
1567
1667
|
for (let i = 0;i < 5; i++) {
|
|
1568
|
-
const candidate =
|
|
1569
|
-
if (
|
|
1668
|
+
const candidate = join2(dir, "skills");
|
|
1669
|
+
if (existsSync2(candidate) && !dir.includes(".skills")) {
|
|
1570
1670
|
return candidate;
|
|
1571
1671
|
}
|
|
1572
1672
|
dir = dirname(dir);
|
|
1573
1673
|
}
|
|
1574
|
-
return
|
|
1674
|
+
return join2(__dirname2, "..", "skills");
|
|
1575
1675
|
}
|
|
1576
1676
|
var SKILLS_DIR = findSkillsDir();
|
|
1577
1677
|
function getSkillPath(name) {
|
|
1578
1678
|
const skillName = normalizeSkillName(name);
|
|
1579
|
-
return
|
|
1679
|
+
return join2(SKILLS_DIR, skillName);
|
|
1580
1680
|
}
|
|
1581
1681
|
function skillExists(name) {
|
|
1582
|
-
return
|
|
1682
|
+
return existsSync2(getSkillPath(name));
|
|
1583
1683
|
}
|
|
1584
1684
|
function installSkill(name, options = {}) {
|
|
1585
1685
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
1586
1686
|
const skillName = normalizeSkillName(name);
|
|
1587
1687
|
const sourcePath = getSkillPath(name);
|
|
1588
|
-
const destDir =
|
|
1589
|
-
const destPath =
|
|
1590
|
-
if (!
|
|
1688
|
+
const destDir = join2(targetDir, ".skills");
|
|
1689
|
+
const destPath = join2(destDir, skillName);
|
|
1690
|
+
if (!existsSync2(sourcePath)) {
|
|
1591
1691
|
return {
|
|
1592
1692
|
skill: name,
|
|
1593
1693
|
success: false,
|
|
1594
1694
|
error: `Skill '${name}' not found`
|
|
1595
1695
|
};
|
|
1596
1696
|
}
|
|
1597
|
-
if (
|
|
1697
|
+
if (existsSync2(destPath) && !overwrite) {
|
|
1598
1698
|
return {
|
|
1599
1699
|
skill: name,
|
|
1600
1700
|
success: false,
|
|
@@ -1603,10 +1703,10 @@ function installSkill(name, options = {}) {
|
|
|
1603
1703
|
};
|
|
1604
1704
|
}
|
|
1605
1705
|
try {
|
|
1606
|
-
if (!
|
|
1706
|
+
if (!existsSync2(destDir)) {
|
|
1607
1707
|
mkdirSync(destDir, { recursive: true });
|
|
1608
1708
|
}
|
|
1609
|
-
if (
|
|
1709
|
+
if (existsSync2(destPath) && overwrite) {
|
|
1610
1710
|
rmSync(destPath, { recursive: true, force: true });
|
|
1611
1711
|
}
|
|
1612
1712
|
cpSync(sourcePath, destPath, {
|
|
@@ -1645,10 +1745,10 @@ function installSkills(names, options = {}) {
|
|
|
1645
1745
|
return names.map((name) => installSkill(name, options));
|
|
1646
1746
|
}
|
|
1647
1747
|
function updateSkillsIndex(skillsDir) {
|
|
1648
|
-
const indexPath =
|
|
1748
|
+
const indexPath = join2(skillsDir, "index.ts");
|
|
1649
1749
|
const meta = loadMeta(skillsDir);
|
|
1650
1750
|
const disabledSet = new Set(meta.disabled || []);
|
|
1651
|
-
const skills =
|
|
1751
|
+
const skills = readdirSync2(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
|
|
1652
1752
|
const exports = skills.map((s) => {
|
|
1653
1753
|
const name = s.replace("skill-", "").replace(/-/g, "_");
|
|
1654
1754
|
return `export * as ${name} from './${s}/src/index.js';`;
|
|
@@ -1664,13 +1764,13 @@ ${exports}
|
|
|
1664
1764
|
writeFileSync(indexPath, content);
|
|
1665
1765
|
}
|
|
1666
1766
|
function getMetaPath(skillsDir) {
|
|
1667
|
-
return
|
|
1767
|
+
return join2(skillsDir, ".meta.json");
|
|
1668
1768
|
}
|
|
1669
1769
|
function loadMeta(skillsDir) {
|
|
1670
1770
|
const metaPath = getMetaPath(skillsDir);
|
|
1671
|
-
if (
|
|
1771
|
+
if (existsSync2(metaPath)) {
|
|
1672
1772
|
try {
|
|
1673
|
-
return JSON.parse(
|
|
1773
|
+
return JSON.parse(readFileSync2(metaPath, "utf-8"));
|
|
1674
1774
|
} catch {}
|
|
1675
1775
|
}
|
|
1676
1776
|
return { skills: {} };
|
|
@@ -1683,9 +1783,9 @@ function recordInstall(skillsDir, name) {
|
|
|
1683
1783
|
const skillName = normalizeSkillName(name);
|
|
1684
1784
|
let version = "unknown";
|
|
1685
1785
|
try {
|
|
1686
|
-
const pkgPath =
|
|
1687
|
-
if (
|
|
1688
|
-
const pkg = JSON.parse(
|
|
1786
|
+
const pkgPath = join2(skillsDir, skillName, "package.json");
|
|
1787
|
+
if (existsSync2(pkgPath)) {
|
|
1788
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
1689
1789
|
version = pkg.version || "unknown";
|
|
1690
1790
|
}
|
|
1691
1791
|
} catch {}
|
|
@@ -1698,12 +1798,12 @@ function recordRemove(skillsDir, name) {
|
|
|
1698
1798
|
saveMeta(skillsDir, meta);
|
|
1699
1799
|
}
|
|
1700
1800
|
function getInstallMeta(targetDir = process.cwd()) {
|
|
1701
|
-
return loadMeta(
|
|
1801
|
+
return loadMeta(join2(targetDir, ".skills"));
|
|
1702
1802
|
}
|
|
1703
1803
|
function disableSkill(name, targetDir = process.cwd()) {
|
|
1704
|
-
const skillsDir =
|
|
1804
|
+
const skillsDir = join2(targetDir, ".skills");
|
|
1705
1805
|
const skillName = normalizeSkillName(name);
|
|
1706
|
-
if (!
|
|
1806
|
+
if (!existsSync2(join2(skillsDir, skillName)))
|
|
1707
1807
|
return false;
|
|
1708
1808
|
const meta = loadMeta(skillsDir);
|
|
1709
1809
|
const disabled = new Set(meta.disabled || []);
|
|
@@ -1716,7 +1816,7 @@ function disableSkill(name, targetDir = process.cwd()) {
|
|
|
1716
1816
|
return true;
|
|
1717
1817
|
}
|
|
1718
1818
|
function enableSkill(name, targetDir = process.cwd()) {
|
|
1719
|
-
const skillsDir =
|
|
1819
|
+
const skillsDir = join2(targetDir, ".skills");
|
|
1720
1820
|
const meta = loadMeta(skillsDir);
|
|
1721
1821
|
const disabled = new Set(meta.disabled || []);
|
|
1722
1822
|
if (!disabled.has(name))
|
|
@@ -1728,24 +1828,24 @@ function enableSkill(name, targetDir = process.cwd()) {
|
|
|
1728
1828
|
return true;
|
|
1729
1829
|
}
|
|
1730
1830
|
function getDisabledSkills(targetDir = process.cwd()) {
|
|
1731
|
-
const meta = loadMeta(
|
|
1831
|
+
const meta = loadMeta(join2(targetDir, ".skills"));
|
|
1732
1832
|
return meta.disabled || [];
|
|
1733
1833
|
}
|
|
1734
1834
|
function getInstalledSkills(targetDir = process.cwd()) {
|
|
1735
|
-
const skillsDir =
|
|
1736
|
-
if (!
|
|
1835
|
+
const skillsDir = join2(targetDir, ".skills");
|
|
1836
|
+
if (!existsSync2(skillsDir)) {
|
|
1737
1837
|
return [];
|
|
1738
1838
|
}
|
|
1739
|
-
return
|
|
1740
|
-
const fullPath =
|
|
1839
|
+
return readdirSync2(skillsDir).filter((f) => {
|
|
1840
|
+
const fullPath = join2(skillsDir, f);
|
|
1741
1841
|
return f.startsWith("skill-") && statSync(fullPath).isDirectory();
|
|
1742
1842
|
}).map((f) => f.replace("skill-", ""));
|
|
1743
1843
|
}
|
|
1744
1844
|
function removeSkill(name, targetDir = process.cwd()) {
|
|
1745
1845
|
const skillName = normalizeSkillName(name);
|
|
1746
|
-
const skillsDir =
|
|
1747
|
-
const skillPath =
|
|
1748
|
-
if (!
|
|
1846
|
+
const skillsDir = join2(targetDir, ".skills");
|
|
1847
|
+
const skillPath = join2(skillsDir, skillName);
|
|
1848
|
+
if (!existsSync2(skillPath)) {
|
|
1749
1849
|
return false;
|
|
1750
1850
|
}
|
|
1751
1851
|
rmSync(skillPath, { recursive: true, force: true });
|
|
@@ -1753,29 +1853,40 @@ function removeSkill(name, targetDir = process.cwd()) {
|
|
|
1753
1853
|
recordRemove(skillsDir, name);
|
|
1754
1854
|
return true;
|
|
1755
1855
|
}
|
|
1756
|
-
var AGENT_TARGETS = ["claude", "codex", "gemini"];
|
|
1856
|
+
var AGENT_TARGETS = ["claude", "codex", "gemini", "pi", "opencode"];
|
|
1857
|
+
var AGENT_LABELS = {
|
|
1858
|
+
claude: "Claude Code",
|
|
1859
|
+
codex: "Codex CLI",
|
|
1860
|
+
gemini: "Gemini CLI",
|
|
1861
|
+
pi: "pi.dev",
|
|
1862
|
+
opencode: "OpenCode"
|
|
1863
|
+
};
|
|
1757
1864
|
function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1865
|
+
const base = projectDir || process.cwd();
|
|
1866
|
+
switch (agent) {
|
|
1867
|
+
case "pi":
|
|
1868
|
+
return scope === "project" ? join2(base, ".pi", "skills") : join2(homedir2(), ".pi", "agent", "skills");
|
|
1869
|
+
case "opencode":
|
|
1870
|
+
return scope === "project" ? join2(base, ".opencode", "skills") : join2(homedir2(), ".opencode", "skills");
|
|
1871
|
+
default:
|
|
1872
|
+
return scope === "project" ? join2(base, `.${agent}`, "skills") : join2(homedir2(), `.${agent}`, "skills");
|
|
1761
1873
|
}
|
|
1762
|
-
return join(homedir(), agentDir, "skills");
|
|
1763
1874
|
}
|
|
1764
1875
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
1765
1876
|
const skillName = normalizeSkillName(name);
|
|
1766
|
-
return
|
|
1877
|
+
return join2(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
1767
1878
|
}
|
|
1768
1879
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
1769
1880
|
const { agent, scope = "global", projectDir } = options;
|
|
1770
1881
|
const skillName = normalizeSkillName(name);
|
|
1771
1882
|
const sourcePath = getSkillPath(name);
|
|
1772
|
-
if (!
|
|
1883
|
+
if (!existsSync2(sourcePath)) {
|
|
1773
1884
|
return { skill: name, success: false, error: `Skill '${name}' not found` };
|
|
1774
1885
|
}
|
|
1775
1886
|
let skillMdContent = null;
|
|
1776
|
-
const skillMdPath =
|
|
1777
|
-
if (
|
|
1778
|
-
skillMdContent =
|
|
1887
|
+
const skillMdPath = join2(sourcePath, "SKILL.md");
|
|
1888
|
+
if (existsSync2(skillMdPath)) {
|
|
1889
|
+
skillMdContent = readFileSync2(skillMdPath, "utf-8");
|
|
1779
1890
|
} else if (generateSkillMd) {
|
|
1780
1891
|
skillMdContent = generateSkillMd(name);
|
|
1781
1892
|
}
|
|
@@ -1784,17 +1895,12 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
1784
1895
|
}
|
|
1785
1896
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
1786
1897
|
if (scope === "global") {
|
|
1787
|
-
const agentBaseDir =
|
|
1788
|
-
if (!
|
|
1789
|
-
const agentLabels = {
|
|
1790
|
-
claude: "Claude Code",
|
|
1791
|
-
codex: "Codex CLI",
|
|
1792
|
-
gemini: "Gemini CLI"
|
|
1793
|
-
};
|
|
1898
|
+
const agentBaseDir = agent === "pi" ? join2(homedir2(), ".pi", "agent") : join2(homedir2(), `.${agent}`);
|
|
1899
|
+
if (!existsSync2(agentBaseDir)) {
|
|
1794
1900
|
return {
|
|
1795
1901
|
skill: name,
|
|
1796
1902
|
success: false,
|
|
1797
|
-
error: `Agent directory ${agentBaseDir} does not exist. Is ${
|
|
1903
|
+
error: `Agent directory ${agentBaseDir} does not exist. Is ${AGENT_LABELS[agent]} installed?`
|
|
1798
1904
|
};
|
|
1799
1905
|
}
|
|
1800
1906
|
try {
|
|
@@ -1809,7 +1915,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
1809
1915
|
}
|
|
1810
1916
|
try {
|
|
1811
1917
|
mkdirSync(destDir, { recursive: true });
|
|
1812
|
-
writeFileSync(
|
|
1918
|
+
writeFileSync(join2(destDir, "SKILL.md"), skillMdContent);
|
|
1813
1919
|
return { skill: name, success: true, path: destDir };
|
|
1814
1920
|
} catch (error) {
|
|
1815
1921
|
return {
|
|
@@ -1822,23 +1928,23 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
1822
1928
|
function removeSkillForAgent(name, options) {
|
|
1823
1929
|
const { agent, scope = "global", projectDir } = options;
|
|
1824
1930
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
1825
|
-
if (!
|
|
1931
|
+
if (!existsSync2(destDir)) {
|
|
1826
1932
|
return false;
|
|
1827
1933
|
}
|
|
1828
1934
|
rmSync(destDir, { recursive: true, force: true });
|
|
1829
1935
|
return true;
|
|
1830
1936
|
}
|
|
1831
1937
|
// src/lib/skillinfo.ts
|
|
1832
|
-
import { existsSync as
|
|
1833
|
-
import { join as
|
|
1938
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
1939
|
+
import { join as join3 } from "path";
|
|
1834
1940
|
function getSkillDocs(name) {
|
|
1835
1941
|
const skillPath = getSkillPath(name);
|
|
1836
|
-
if (!
|
|
1942
|
+
if (!existsSync3(skillPath))
|
|
1837
1943
|
return null;
|
|
1838
1944
|
return {
|
|
1839
|
-
skillMd: readIfExists(
|
|
1840
|
-
readme: readIfExists(
|
|
1841
|
-
claudeMd: readIfExists(
|
|
1945
|
+
skillMd: readIfExists(join3(skillPath, "SKILL.md")),
|
|
1946
|
+
readme: readIfExists(join3(skillPath, "README.md")),
|
|
1947
|
+
claudeMd: readIfExists(join3(skillPath, "CLAUDE.md"))
|
|
1842
1948
|
};
|
|
1843
1949
|
}
|
|
1844
1950
|
function getSkillBestDoc(name) {
|
|
@@ -1849,11 +1955,11 @@ function getSkillBestDoc(name) {
|
|
|
1849
1955
|
}
|
|
1850
1956
|
function getSkillRequirements(name) {
|
|
1851
1957
|
const skillPath = getSkillPath(name);
|
|
1852
|
-
if (!
|
|
1958
|
+
if (!existsSync3(skillPath))
|
|
1853
1959
|
return null;
|
|
1854
1960
|
const texts = [];
|
|
1855
1961
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
1856
|
-
const content = readIfExists(
|
|
1962
|
+
const content = readIfExists(join3(skillPath, file));
|
|
1857
1963
|
if (content)
|
|
1858
1964
|
texts.push(content);
|
|
1859
1965
|
}
|
|
@@ -1880,10 +1986,10 @@ function getSkillRequirements(name) {
|
|
|
1880
1986
|
}
|
|
1881
1987
|
let cliCommand = null;
|
|
1882
1988
|
let dependencies = {};
|
|
1883
|
-
const pkgPath =
|
|
1884
|
-
if (
|
|
1989
|
+
const pkgPath = join3(skillPath, "package.json");
|
|
1990
|
+
if (existsSync3(pkgPath)) {
|
|
1885
1991
|
try {
|
|
1886
|
-
const pkg = JSON.parse(
|
|
1992
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1887
1993
|
if (pkg.bin) {
|
|
1888
1994
|
const binKeys = Object.keys(pkg.bin);
|
|
1889
1995
|
if (binKeys.length > 0)
|
|
@@ -1903,25 +2009,25 @@ async function runSkill(name, args, options = {}) {
|
|
|
1903
2009
|
const skillName = normalizeSkillName(name);
|
|
1904
2010
|
let skillPath;
|
|
1905
2011
|
if (options.installed) {
|
|
1906
|
-
skillPath =
|
|
2012
|
+
skillPath = join3(process.cwd(), ".skills", skillName);
|
|
1907
2013
|
} else {
|
|
1908
|
-
const installedPath =
|
|
1909
|
-
if (
|
|
2014
|
+
const installedPath = join3(process.cwd(), ".skills", skillName);
|
|
2015
|
+
if (existsSync3(installedPath)) {
|
|
1910
2016
|
skillPath = installedPath;
|
|
1911
2017
|
} else {
|
|
1912
2018
|
skillPath = getSkillPath(name);
|
|
1913
2019
|
}
|
|
1914
2020
|
}
|
|
1915
|
-
if (!
|
|
2021
|
+
if (!existsSync3(skillPath)) {
|
|
1916
2022
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
1917
2023
|
}
|
|
1918
|
-
const pkgPath =
|
|
1919
|
-
if (!
|
|
2024
|
+
const pkgPath = join3(skillPath, "package.json");
|
|
2025
|
+
if (!existsSync3(pkgPath)) {
|
|
1920
2026
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
1921
2027
|
}
|
|
1922
2028
|
let entryPoint;
|
|
1923
2029
|
try {
|
|
1924
|
-
const pkg = JSON.parse(
|
|
2030
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1925
2031
|
if (pkg.bin) {
|
|
1926
2032
|
const binValues = Object.values(pkg.bin);
|
|
1927
2033
|
entryPoint = binValues[0];
|
|
@@ -1935,12 +2041,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
1935
2041
|
} catch {
|
|
1936
2042
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
1937
2043
|
}
|
|
1938
|
-
const entryPath =
|
|
1939
|
-
if (!
|
|
2044
|
+
const entryPath = join3(skillPath, entryPoint);
|
|
2045
|
+
if (!existsSync3(entryPath)) {
|
|
1940
2046
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
1941
2047
|
}
|
|
1942
|
-
const nodeModules =
|
|
1943
|
-
if (!
|
|
2048
|
+
const nodeModules = join3(skillPath, "node_modules");
|
|
2049
|
+
if (!existsSync3(nodeModules)) {
|
|
1944
2050
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
1945
2051
|
cwd: skillPath,
|
|
1946
2052
|
stdout: "pipe",
|
|
@@ -1958,17 +2064,17 @@ async function runSkill(name, args, options = {}) {
|
|
|
1958
2064
|
return { exitCode };
|
|
1959
2065
|
}
|
|
1960
2066
|
function generateEnvExample(targetDir = process.cwd()) {
|
|
1961
|
-
const skillsDir =
|
|
1962
|
-
if (!
|
|
2067
|
+
const skillsDir = join3(targetDir, ".skills");
|
|
2068
|
+
if (!existsSync3(skillsDir))
|
|
1963
2069
|
return "";
|
|
1964
|
-
const dirs =
|
|
2070
|
+
const dirs = readdirSync3(skillsDir).filter((f) => f.startsWith("skill-") && existsSync3(join3(skillsDir, f, "package.json")));
|
|
1965
2071
|
const envMap = new Map;
|
|
1966
2072
|
for (const dir of dirs) {
|
|
1967
2073
|
const skillName = dir.replace("skill-", "");
|
|
1968
|
-
const skillPath =
|
|
2074
|
+
const skillPath = join3(skillsDir, dir);
|
|
1969
2075
|
const texts = [];
|
|
1970
2076
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example"]) {
|
|
1971
|
-
const content = readIfExists(
|
|
2077
|
+
const content = readIfExists(join3(skillPath, file));
|
|
1972
2078
|
if (content)
|
|
1973
2079
|
texts.push(content);
|
|
1974
2080
|
}
|
|
@@ -2013,7 +2119,7 @@ function generateSkillMd(name) {
|
|
|
2013
2119
|
if (!meta)
|
|
2014
2120
|
return null;
|
|
2015
2121
|
const skillPath = getSkillPath(name);
|
|
2016
|
-
if (!
|
|
2122
|
+
if (!existsSync3(skillPath))
|
|
2017
2123
|
return null;
|
|
2018
2124
|
const frontmatter = [
|
|
2019
2125
|
"---",
|
|
@@ -2022,13 +2128,13 @@ function generateSkillMd(name) {
|
|
|
2022
2128
|
"---"
|
|
2023
2129
|
].join(`
|
|
2024
2130
|
`);
|
|
2025
|
-
const readme = readIfExists(
|
|
2026
|
-
const claudeMd = readIfExists(
|
|
2131
|
+
const readme = readIfExists(join3(skillPath, "README.md"));
|
|
2132
|
+
const claudeMd = readIfExists(join3(skillPath, "CLAUDE.md"));
|
|
2027
2133
|
let cliCommand = null;
|
|
2028
|
-
const pkgPath =
|
|
2029
|
-
if (
|
|
2134
|
+
const pkgPath = join3(skillPath, "package.json");
|
|
2135
|
+
if (existsSync3(pkgPath)) {
|
|
2030
2136
|
try {
|
|
2031
|
-
const pkg = JSON.parse(
|
|
2137
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
2032
2138
|
if (pkg.bin) {
|
|
2033
2139
|
const binKeys = Object.keys(pkg.bin);
|
|
2034
2140
|
if (binKeys.length > 0)
|
|
@@ -2103,16 +2209,16 @@ function extractEnvVars(text) {
|
|
|
2103
2209
|
}
|
|
2104
2210
|
function readIfExists(path) {
|
|
2105
2211
|
try {
|
|
2106
|
-
if (
|
|
2107
|
-
return
|
|
2212
|
+
if (existsSync3(path)) {
|
|
2213
|
+
return readFileSync3(path, "utf-8");
|
|
2108
2214
|
}
|
|
2109
2215
|
} catch {}
|
|
2110
2216
|
return null;
|
|
2111
2217
|
}
|
|
2112
2218
|
// src/lib/config.ts
|
|
2113
|
-
import { existsSync as
|
|
2114
|
-
import { join as
|
|
2115
|
-
import { homedir as
|
|
2219
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
2220
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
2221
|
+
import { homedir as homedir3 } from "os";
|
|
2116
2222
|
var VALID_KEYS = {
|
|
2117
2223
|
defaultAgent: ["claude", "codex", "gemini", "all"],
|
|
2118
2224
|
defaultScope: ["global", "project"],
|
|
@@ -2120,15 +2226,15 @@ var VALID_KEYS = {
|
|
|
2120
2226
|
};
|
|
2121
2227
|
function getConfigPath(scope) {
|
|
2122
2228
|
if (scope === "global") {
|
|
2123
|
-
return
|
|
2229
|
+
return join4(homedir3(), ".skillsrc");
|
|
2124
2230
|
}
|
|
2125
|
-
return
|
|
2231
|
+
return join4(process.cwd(), "skills.config.json");
|
|
2126
2232
|
}
|
|
2127
2233
|
function readConfigFile(path) {
|
|
2128
|
-
if (!
|
|
2234
|
+
if (!existsSync4(path))
|
|
2129
2235
|
return {};
|
|
2130
2236
|
try {
|
|
2131
|
-
const raw =
|
|
2237
|
+
const raw = readFileSync4(path, "utf-8");
|
|
2132
2238
|
const parsed = JSON.parse(raw);
|
|
2133
2239
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
2134
2240
|
return {};
|
|
@@ -2159,9 +2265,9 @@ function saveConfig(key, value, scope = "project") {
|
|
|
2159
2265
|
}
|
|
2160
2266
|
const filePath = getConfigPath(scope);
|
|
2161
2267
|
let existing = {};
|
|
2162
|
-
if (
|
|
2268
|
+
if (existsSync4(filePath)) {
|
|
2163
2269
|
try {
|
|
2164
|
-
existing = JSON.parse(
|
|
2270
|
+
existing = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
2165
2271
|
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
2166
2272
|
existing = {};
|
|
2167
2273
|
}
|
|
@@ -2170,7 +2276,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
2170
2276
|
}
|
|
2171
2277
|
} else {
|
|
2172
2278
|
const dir = dirname2(filePath);
|
|
2173
|
-
if (!
|
|
2279
|
+
if (!existsSync4(dir)) {
|
|
2174
2280
|
mkdirSync2(dir, { recursive: true });
|
|
2175
2281
|
}
|
|
2176
2282
|
}
|
|
@@ -2178,14 +2284,174 @@ function saveConfig(key, value, scope = "project") {
|
|
|
2178
2284
|
writeFileSync2(filePath, JSON.stringify(existing, null, 2) + `
|
|
2179
2285
|
`);
|
|
2180
2286
|
}
|
|
2287
|
+
// src/lib/scheduler.ts
|
|
2288
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
2289
|
+
import { join as join5 } from "path";
|
|
2290
|
+
function getSchedulesPath(targetDir = process.cwd()) {
|
|
2291
|
+
return join5(targetDir, ".skills", "schedules.json");
|
|
2292
|
+
}
|
|
2293
|
+
function loadSchedules(targetDir = process.cwd()) {
|
|
2294
|
+
const path = getSchedulesPath(targetDir);
|
|
2295
|
+
if (existsSync5(path)) {
|
|
2296
|
+
try {
|
|
2297
|
+
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
2298
|
+
} catch {}
|
|
2299
|
+
}
|
|
2300
|
+
return { version: 1, schedules: [] };
|
|
2301
|
+
}
|
|
2302
|
+
function saveSchedules(data, targetDir = process.cwd()) {
|
|
2303
|
+
const path = getSchedulesPath(targetDir);
|
|
2304
|
+
const dir = join5(targetDir, ".skills");
|
|
2305
|
+
if (!existsSync5(dir))
|
|
2306
|
+
mkdirSync3(dir, { recursive: true });
|
|
2307
|
+
writeFileSync3(path, JSON.stringify(data, null, 2));
|
|
2308
|
+
}
|
|
2309
|
+
function validateCron(expr) {
|
|
2310
|
+
const fields = expr.trim().split(/\s+/);
|
|
2311
|
+
if (fields.length !== 5) {
|
|
2312
|
+
return { valid: false, error: `Expected 5 fields, got ${fields.length}. Format: "minute hour day-of-month month day-of-week"` };
|
|
2313
|
+
}
|
|
2314
|
+
return { valid: true };
|
|
2315
|
+
}
|
|
2316
|
+
function getNextRun(cron, from = new Date) {
|
|
2317
|
+
const { valid } = validateCron(cron);
|
|
2318
|
+
if (!valid)
|
|
2319
|
+
return null;
|
|
2320
|
+
const [minuteF, hourF, domF, monthF, dowF] = cron.trim().split(/\s+/);
|
|
2321
|
+
function parseField(f, min, max) {
|
|
2322
|
+
if (f === "*")
|
|
2323
|
+
return Array.from({ length: max - min + 1 }, (_, i) => i + min);
|
|
2324
|
+
if (f.startsWith("*/")) {
|
|
2325
|
+
const step = parseInt(f.slice(2));
|
|
2326
|
+
if (isNaN(step))
|
|
2327
|
+
return [];
|
|
2328
|
+
const vals = [];
|
|
2329
|
+
for (let i = min;i <= max; i += step)
|
|
2330
|
+
vals.push(i);
|
|
2331
|
+
return vals;
|
|
2332
|
+
}
|
|
2333
|
+
return f.split(",").flatMap((part) => {
|
|
2334
|
+
if (part.includes("-")) {
|
|
2335
|
+
const [lo, hi] = part.split("-").map(Number);
|
|
2336
|
+
return Array.from({ length: hi - lo + 1 }, (_, i) => i + lo);
|
|
2337
|
+
}
|
|
2338
|
+
const n = parseInt(part);
|
|
2339
|
+
return isNaN(n) ? [] : [n];
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
const minutes = parseField(minuteF, 0, 59);
|
|
2343
|
+
const hours = parseField(hourF, 0, 23);
|
|
2344
|
+
const doms = parseField(domF, 1, 31);
|
|
2345
|
+
const months = parseField(monthF, 1, 12);
|
|
2346
|
+
const dows = parseField(dowF, 0, 6);
|
|
2347
|
+
const candidate = new Date(from);
|
|
2348
|
+
candidate.setSeconds(0, 0);
|
|
2349
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
2350
|
+
const limit = new Date(from);
|
|
2351
|
+
limit.setFullYear(limit.getFullYear() + 1);
|
|
2352
|
+
while (candidate < limit) {
|
|
2353
|
+
const month = candidate.getMonth() + 1;
|
|
2354
|
+
const dom = candidate.getDate();
|
|
2355
|
+
const dow = candidate.getDay();
|
|
2356
|
+
const hour = candidate.getHours();
|
|
2357
|
+
const minute = candidate.getMinutes();
|
|
2358
|
+
if (!months.includes(month)) {
|
|
2359
|
+
candidate.setMonth(candidate.getMonth() + 1, 1);
|
|
2360
|
+
candidate.setHours(0, 0, 0, 0);
|
|
2361
|
+
continue;
|
|
2362
|
+
}
|
|
2363
|
+
if (!doms.includes(dom) || !dows.includes(dow)) {
|
|
2364
|
+
candidate.setDate(candidate.getDate() + 1);
|
|
2365
|
+
candidate.setHours(0, 0, 0, 0);
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
if (!hours.includes(hour)) {
|
|
2369
|
+
candidate.setHours(candidate.getHours() + 1, 0, 0, 0);
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
if (!minutes.includes(minute)) {
|
|
2373
|
+
candidate.setMinutes(candidate.getMinutes() + 1, 0, 0);
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
return new Date(candidate);
|
|
2377
|
+
}
|
|
2378
|
+
return null;
|
|
2379
|
+
}
|
|
2380
|
+
function addSchedule(skill, cron, options = {}) {
|
|
2381
|
+
const { valid, error } = validateCron(cron);
|
|
2382
|
+
if (!valid)
|
|
2383
|
+
return { schedule: null, error };
|
|
2384
|
+
const data = loadSchedules(options.targetDir);
|
|
2385
|
+
const id = `${skill}-${Date.now()}`;
|
|
2386
|
+
const now = new Date;
|
|
2387
|
+
const nextRun = getNextRun(cron, now);
|
|
2388
|
+
const schedule = {
|
|
2389
|
+
id,
|
|
2390
|
+
name: options.name || `${skill} (${cron})`,
|
|
2391
|
+
skill,
|
|
2392
|
+
cron,
|
|
2393
|
+
args: options.args,
|
|
2394
|
+
enabled: true,
|
|
2395
|
+
createdAt: now.toISOString(),
|
|
2396
|
+
nextRun: nextRun?.toISOString()
|
|
2397
|
+
};
|
|
2398
|
+
data.schedules.push(schedule);
|
|
2399
|
+
saveSchedules(data, options.targetDir);
|
|
2400
|
+
return { schedule };
|
|
2401
|
+
}
|
|
2402
|
+
function listSchedules(targetDir) {
|
|
2403
|
+
return loadSchedules(targetDir).schedules;
|
|
2404
|
+
}
|
|
2405
|
+
function removeSchedule(idOrName, targetDir) {
|
|
2406
|
+
const data = loadSchedules(targetDir);
|
|
2407
|
+
const before = data.schedules.length;
|
|
2408
|
+
data.schedules = data.schedules.filter((s) => s.id !== idOrName && s.name !== idOrName);
|
|
2409
|
+
if (data.schedules.length === before)
|
|
2410
|
+
return false;
|
|
2411
|
+
saveSchedules(data, targetDir);
|
|
2412
|
+
return true;
|
|
2413
|
+
}
|
|
2414
|
+
function setScheduleEnabled(idOrName, enabled, targetDir) {
|
|
2415
|
+
const data = loadSchedules(targetDir);
|
|
2416
|
+
const schedule = data.schedules.find((s) => s.id === idOrName || s.name === idOrName);
|
|
2417
|
+
if (!schedule)
|
|
2418
|
+
return false;
|
|
2419
|
+
schedule.enabled = enabled;
|
|
2420
|
+
if (enabled) {
|
|
2421
|
+
schedule.nextRun = getNextRun(schedule.cron)?.toISOString();
|
|
2422
|
+
}
|
|
2423
|
+
saveSchedules(data, targetDir);
|
|
2424
|
+
return true;
|
|
2425
|
+
}
|
|
2426
|
+
function getDueSchedules(targetDir) {
|
|
2427
|
+
const now = new Date;
|
|
2428
|
+
return listSchedules(targetDir).filter((s) => s.enabled && s.nextRun && new Date(s.nextRun) <= now);
|
|
2429
|
+
}
|
|
2430
|
+
function recordScheduleRun(id, status, targetDir) {
|
|
2431
|
+
const data = loadSchedules(targetDir);
|
|
2432
|
+
const schedule = data.schedules.find((s) => s.id === id);
|
|
2433
|
+
if (!schedule)
|
|
2434
|
+
return;
|
|
2435
|
+
const now = new Date;
|
|
2436
|
+
schedule.lastRun = now.toISOString();
|
|
2437
|
+
schedule.lastRunStatus = status;
|
|
2438
|
+
schedule.nextRun = getNextRun(schedule.cron, now)?.toISOString();
|
|
2439
|
+
saveSchedules(data, targetDir);
|
|
2440
|
+
}
|
|
2181
2441
|
export {
|
|
2442
|
+
validateCron,
|
|
2182
2443
|
skillExists,
|
|
2444
|
+
setScheduleEnabled,
|
|
2183
2445
|
searchSkills,
|
|
2184
2446
|
saveConfig,
|
|
2185
2447
|
runSkill,
|
|
2186
2448
|
removeSkillForAgent,
|
|
2187
2449
|
removeSkill,
|
|
2450
|
+
removeSchedule,
|
|
2451
|
+
recordScheduleRun,
|
|
2452
|
+
loadRegistry,
|
|
2188
2453
|
loadConfig,
|
|
2454
|
+
listSchedules,
|
|
2189
2455
|
installSkills,
|
|
2190
2456
|
installSkillForAgent,
|
|
2191
2457
|
installSkill,
|
|
@@ -2196,8 +2462,10 @@ export {
|
|
|
2196
2462
|
getSkillDocs,
|
|
2197
2463
|
getSkillBestDoc,
|
|
2198
2464
|
getSkill,
|
|
2465
|
+
getNextRun,
|
|
2199
2466
|
getInstalledSkills,
|
|
2200
2467
|
getInstallMeta,
|
|
2468
|
+
getDueSchedules,
|
|
2201
2469
|
getDisabledSkills,
|
|
2202
2470
|
getConfigPath,
|
|
2203
2471
|
getAllTags,
|
|
@@ -2208,6 +2476,8 @@ export {
|
|
|
2208
2476
|
findSimilarSkills,
|
|
2209
2477
|
enableSkill,
|
|
2210
2478
|
disableSkill,
|
|
2479
|
+
clearRegistryCache,
|
|
2480
|
+
addSchedule,
|
|
2211
2481
|
SKILLS,
|
|
2212
2482
|
CATEGORIES,
|
|
2213
2483
|
AGENT_TARGETS
|