@poncho-ai/cli 0.6.0 → 0.6.2
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/.turbo/turbo-build.log +7 -7
- package/dist/chunk-2TLKQG7R.js +5360 -0
- package/dist/chunk-3FY4LP2E.js +4981 -0
- package/dist/chunk-3WQANPEG.js +5086 -0
- package/dist/chunk-44DXWF6D.js +5450 -0
- package/dist/chunk-62G3MI43.js +5316 -0
- package/dist/chunk-6J2JICGH.js +5135 -0
- package/dist/chunk-6KLC6MWK.js +5357 -0
- package/dist/chunk-6LG2DUWF.js +5181 -0
- package/dist/chunk-ASAXSYEZ.js +5179 -0
- package/dist/chunk-B5B5LAR2.js +5181 -0
- package/dist/chunk-C7T4EJNQ.js +4934 -0
- package/dist/chunk-EPG7ZYDE.js +5452 -0
- package/dist/chunk-FMRRGTJX.js +5041 -0
- package/dist/chunk-HHMFEU26.js +5451 -0
- package/dist/chunk-JRJY6LUC.js +5178 -0
- package/dist/chunk-O7SJY7YQ.js +5177 -0
- package/dist/chunk-PYP4SKOI.js +5125 -0
- package/dist/chunk-VECWMU7E.js +5276 -0
- package/dist/chunk-XEBDWQI6.js +5178 -0
- package/dist/chunk-YW2D7Z22.js +5360 -0
- package/dist/chunk-YZXMEO2T.js +5177 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +11 -1
- package/dist/run-interactive-ink-6EJ6Z5HE.js +494 -0
- package/dist/run-interactive-ink-72BHZB7Q.js +494 -0
- package/dist/run-interactive-ink-BNRIM52Y.js +494 -0
- package/dist/run-interactive-ink-BZNBOELJ.js +494 -0
- package/dist/run-interactive-ink-GODBXZF3.js +494 -0
- package/dist/run-interactive-ink-J4AISGNQ.js +494 -0
- package/dist/run-interactive-ink-K75SE2J2.js +494 -0
- package/dist/run-interactive-ink-M2XKKPIJ.js +494 -0
- package/dist/run-interactive-ink-MITWAF7L.js +494 -0
- package/dist/run-interactive-ink-NR5BRFUF.js +494 -0
- package/dist/run-interactive-ink-OGNG6UYE.js +494 -0
- package/dist/run-interactive-ink-P3VNJEXK.js +494 -0
- package/dist/run-interactive-ink-PHLW5YWV.js +494 -0
- package/dist/run-interactive-ink-PVU3XABN.js +494 -0
- package/dist/run-interactive-ink-SLSK7BY5.js +494 -0
- package/dist/run-interactive-ink-TRPYQYHG.js +494 -0
- package/dist/run-interactive-ink-U2RPRBIR.js +494 -0
- package/dist/run-interactive-ink-U2WTGZJ3.js +494 -0
- package/dist/run-interactive-ink-UHBFYNNB.js +494 -0
- package/dist/run-interactive-ink-XQDUN6OS.js +494 -0
- package/dist/run-interactive-ink-XUHSJCGH.js +494 -0
- package/package.json +1 -1
- package/src/index.ts +322 -27
- package/src/web-ui.ts +350 -33
- package/test/cli.test.ts +232 -1
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import {
|
|
5
5
|
createServer,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type Server,
|
|
8
8
|
type ServerResponse,
|
|
9
9
|
} from "node:http";
|
|
10
|
-
import { dirname, relative, resolve } from "node:path";
|
|
10
|
+
import { basename, dirname, normalize, relative, resolve } from "node:path";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import {
|
|
@@ -328,13 +328,29 @@ poncho tools
|
|
|
328
328
|
Install skills from a local path or remote repository, then verify discovery:
|
|
329
329
|
|
|
330
330
|
\`\`\`bash
|
|
331
|
-
# Install skills
|
|
332
|
-
poncho add <repo-or-path>
|
|
331
|
+
# Install all skills from a source package/repo
|
|
332
|
+
poncho skills add <repo-or-path>
|
|
333
|
+
|
|
334
|
+
# Install one specific skill path from a source
|
|
335
|
+
poncho skills add <repo-or-path> <relative-skill-path>
|
|
336
|
+
|
|
337
|
+
# Remove all installed skills from a source
|
|
338
|
+
poncho skills remove <repo-or-path>
|
|
339
|
+
|
|
340
|
+
# Remove one installed skill path from a source
|
|
341
|
+
poncho skills remove <repo-or-path> <relative-skill-path>
|
|
342
|
+
|
|
343
|
+
# List installed skills
|
|
344
|
+
poncho skills list
|
|
333
345
|
|
|
334
346
|
# Verify loaded tools
|
|
335
347
|
poncho tools
|
|
336
348
|
\`\`\`
|
|
337
349
|
|
|
350
|
+
\`poncho skills add\` copies discovered skill directories (folders that contain \`SKILL.md\`) into \`skills/<source>/...\`.
|
|
351
|
+
If a destination folder already exists, the command fails instead of overwriting files.
|
|
352
|
+
\`poncho add\` and \`poncho remove\` remain available as aliases.
|
|
353
|
+
|
|
338
354
|
After adding skills, run \`poncho dev\` or \`poncho run --interactive\` and ask the agent to use them.
|
|
339
355
|
|
|
340
356
|
## Configure MCP Servers (Remote)
|
|
@@ -385,6 +401,16 @@ Pattern format is strict slash-only:
|
|
|
385
401
|
- MCP: \`server/tool\`, \`server/*\`
|
|
386
402
|
- Scripts: relative paths such as \`./scripts/file.ts\`, \`./scripts/*\`, \`./tools/deploy.ts\`
|
|
387
403
|
|
|
404
|
+
Skill authoring guardrails:
|
|
405
|
+
|
|
406
|
+
- Every \`SKILL.md\` must include YAML frontmatter between \`---\` markers.
|
|
407
|
+
- Include at least \`name\` (required for discovery) and \`description\`.
|
|
408
|
+
- Put tool intent in frontmatter using \`allowed-tools\` and \`approval-required\`.
|
|
409
|
+
- \`approval-required\` is stricter than allowed access:
|
|
410
|
+
- MCP entries in \`approval-required\` must also appear in \`allowed-tools\`.
|
|
411
|
+
- Script entries outside \`./scripts/\` must also appear in \`allowed-tools\`.
|
|
412
|
+
- Keep MCP server connection details in \`poncho.config.js\`, not in \`SKILL.md\`.
|
|
413
|
+
|
|
388
414
|
## Configuration
|
|
389
415
|
|
|
390
416
|
Core files:
|
|
@@ -520,7 +546,9 @@ const copyIfExists = async (sourcePath: string, destinationPath: string): Promis
|
|
|
520
546
|
return;
|
|
521
547
|
}
|
|
522
548
|
await mkdir(dirname(destinationPath), { recursive: true });
|
|
523
|
-
|
|
549
|
+
// Build outputs should contain materialized files, not symlinks to paths
|
|
550
|
+
// that may not exist inside deployment artifacts (e.g. .agents/skills/*).
|
|
551
|
+
await cp(sourcePath, destinationPath, { recursive: true, dereference: true });
|
|
524
552
|
};
|
|
525
553
|
|
|
526
554
|
const resolveCliEntrypoint = async (): Promise<string> => {
|
|
@@ -1798,48 +1826,278 @@ const resolveSkillRoot = (
|
|
|
1798
1826
|
}
|
|
1799
1827
|
};
|
|
1800
1828
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1829
|
+
const normalizeSkillSourceName = (value: string): string => {
|
|
1830
|
+
const normalized = value
|
|
1831
|
+
.trim()
|
|
1832
|
+
.replace(/\\/g, "/")
|
|
1833
|
+
.replace(/^@/, "")
|
|
1834
|
+
.replace(/[\/\s]+/g, "-")
|
|
1835
|
+
.replace(/[^a-zA-Z0-9._-]/g, "-")
|
|
1836
|
+
.replace(/-+/g, "-")
|
|
1837
|
+
.replace(/^-|-$/g, "");
|
|
1838
|
+
return normalized.length > 0 ? normalized : "skills";
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1841
|
+
const collectSkillManifests = async (dir: string, depth = 2): Promise<string[]> => {
|
|
1842
|
+
const manifests: string[] = [];
|
|
1843
|
+
const localManifest = resolve(dir, "SKILL.md");
|
|
1806
1844
|
try {
|
|
1807
|
-
await access(
|
|
1808
|
-
|
|
1845
|
+
await access(localManifest);
|
|
1846
|
+
manifests.push(localManifest);
|
|
1809
1847
|
} catch {
|
|
1810
1848
|
// Not found at this level — look one level deeper (e.g. skills/<name>/SKILL.md)
|
|
1811
1849
|
}
|
|
1812
|
-
|
|
1850
|
+
|
|
1851
|
+
if (depth <= 0) return manifests;
|
|
1852
|
+
|
|
1813
1853
|
try {
|
|
1814
|
-
const { readdir } = await import("node:fs/promises");
|
|
1815
1854
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1816
1855
|
for (const entry of entries) {
|
|
1817
|
-
if (entry.
|
|
1818
|
-
|
|
1819
|
-
|
|
1856
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1857
|
+
|
|
1858
|
+
let isDir = entry.isDirectory();
|
|
1859
|
+
// Dirent reports symlinks separately; resolve target type via stat()
|
|
1860
|
+
if (!isDir && entry.isSymbolicLink()) {
|
|
1861
|
+
try {
|
|
1862
|
+
const s = await stat(resolve(dir, entry.name));
|
|
1863
|
+
isDir = s.isDirectory();
|
|
1864
|
+
} catch {
|
|
1865
|
+
continue; // broken symlink — skip
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
if (isDir) {
|
|
1870
|
+
manifests.push(...(await collectSkillManifests(resolve(dir, entry.name), depth - 1)));
|
|
1820
1871
|
}
|
|
1821
1872
|
}
|
|
1822
1873
|
} catch {
|
|
1823
1874
|
// ignore read errors
|
|
1824
1875
|
}
|
|
1825
|
-
|
|
1876
|
+
|
|
1877
|
+
return manifests;
|
|
1826
1878
|
};
|
|
1827
1879
|
|
|
1828
1880
|
const validateSkillPackage = async (
|
|
1829
1881
|
workingDir: string,
|
|
1830
1882
|
packageNameOrPath: string,
|
|
1831
|
-
): Promise<
|
|
1883
|
+
): Promise<{ skillRoot: string; manifests: string[] }> => {
|
|
1832
1884
|
const skillRoot = resolveSkillRoot(workingDir, packageNameOrPath);
|
|
1833
|
-
const
|
|
1834
|
-
if (
|
|
1885
|
+
const manifests = await collectSkillManifests(skillRoot);
|
|
1886
|
+
if (manifests.length === 0) {
|
|
1835
1887
|
throw new Error(`Skill validation failed: no SKILL.md found in ${skillRoot}`);
|
|
1836
1888
|
}
|
|
1889
|
+
return { skillRoot, manifests };
|
|
1837
1890
|
};
|
|
1838
1891
|
|
|
1839
|
-
|
|
1892
|
+
const selectSkillManifests = async (
|
|
1893
|
+
skillRoot: string,
|
|
1894
|
+
manifests: string[],
|
|
1895
|
+
relativeSkillPath?: string,
|
|
1896
|
+
): Promise<string[]> => {
|
|
1897
|
+
if (!relativeSkillPath) return manifests;
|
|
1898
|
+
|
|
1899
|
+
const normalized = normalize(relativeSkillPath);
|
|
1900
|
+
if (normalized.startsWith("..") || normalized.startsWith("/")) {
|
|
1901
|
+
throw new Error(`Invalid skill path "${relativeSkillPath}": path must be within package root.`);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
const candidate = resolve(skillRoot, normalized);
|
|
1905
|
+
const relativeToRoot = relative(skillRoot, candidate).split("\\").join("/");
|
|
1906
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot.startsWith("/")) {
|
|
1907
|
+
throw new Error(`Invalid skill path "${relativeSkillPath}": path escapes package root.`);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
const candidateAsFile = candidate.toLowerCase().endsWith("skill.md")
|
|
1911
|
+
? candidate
|
|
1912
|
+
: resolve(candidate, "SKILL.md");
|
|
1913
|
+
if (!existsSync(candidateAsFile)) {
|
|
1914
|
+
throw new Error(
|
|
1915
|
+
`Skill path "${relativeSkillPath}" does not point to a directory (or file) containing SKILL.md.`,
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const selected = manifests.filter((manifest) => resolve(manifest) === resolve(candidateAsFile));
|
|
1920
|
+
if (selected.length === 0) {
|
|
1921
|
+
throw new Error(`Skill path "${relativeSkillPath}" was not discovered as a valid skill manifest.`);
|
|
1922
|
+
}
|
|
1923
|
+
return selected;
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
const copySkillsIntoProject = async (
|
|
1927
|
+
workingDir: string,
|
|
1928
|
+
manifests: string[],
|
|
1929
|
+
sourceName: string,
|
|
1930
|
+
): Promise<string[]> => {
|
|
1931
|
+
const skillsDir = resolve(workingDir, "skills", normalizeSkillSourceName(sourceName));
|
|
1932
|
+
await mkdir(skillsDir, { recursive: true });
|
|
1933
|
+
|
|
1934
|
+
const destinations = new Map<string, string>();
|
|
1935
|
+
for (const manifest of manifests) {
|
|
1936
|
+
const sourceSkillDir = dirname(manifest);
|
|
1937
|
+
const skillFolderName = basename(sourceSkillDir);
|
|
1938
|
+
if (destinations.has(skillFolderName)) {
|
|
1939
|
+
throw new Error(
|
|
1940
|
+
`Skill copy failed: multiple skill directories map to "skills/${skillFolderName}" (${destinations.get(skillFolderName)} and ${sourceSkillDir}).`,
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
destinations.set(skillFolderName, sourceSkillDir);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
const copied: string[] = [];
|
|
1947
|
+
for (const [skillFolderName, sourceSkillDir] of destinations.entries()) {
|
|
1948
|
+
const destinationSkillDir = resolve(skillsDir, skillFolderName);
|
|
1949
|
+
if (existsSync(destinationSkillDir)) {
|
|
1950
|
+
throw new Error(
|
|
1951
|
+
`Skill copy failed: destination already exists at ${destinationSkillDir}. Remove or rename it and try again.`,
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
await cp(sourceSkillDir, destinationSkillDir, {
|
|
1955
|
+
recursive: true,
|
|
1956
|
+
dereference: true,
|
|
1957
|
+
force: false,
|
|
1958
|
+
errorOnExist: true,
|
|
1959
|
+
});
|
|
1960
|
+
copied.push(relative(workingDir, destinationSkillDir).split("\\").join("/"));
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
return copied.sort();
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
export const copySkillsFromPackage = async (
|
|
1967
|
+
workingDir: string,
|
|
1968
|
+
packageNameOrPath: string,
|
|
1969
|
+
options?: { path?: string },
|
|
1970
|
+
): Promise<string[]> => {
|
|
1971
|
+
const { skillRoot, manifests } = await validateSkillPackage(workingDir, packageNameOrPath);
|
|
1972
|
+
const selected = await selectSkillManifests(skillRoot, manifests, options?.path);
|
|
1973
|
+
const sourceName = resolveInstalledPackageName(packageNameOrPath) ?? basename(skillRoot);
|
|
1974
|
+
return await copySkillsIntoProject(workingDir, selected, sourceName);
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
export const addSkill = async (
|
|
1978
|
+
workingDir: string,
|
|
1979
|
+
packageNameOrPath: string,
|
|
1980
|
+
options?: { path?: string },
|
|
1981
|
+
): Promise<void> => {
|
|
1840
1982
|
await runInstallCommand(workingDir, packageNameOrPath);
|
|
1841
|
-
await
|
|
1842
|
-
process.stdout.write(
|
|
1983
|
+
const copiedSkills = await copySkillsFromPackage(workingDir, packageNameOrPath, options);
|
|
1984
|
+
process.stdout.write(
|
|
1985
|
+
`Added ${copiedSkills.length} skill${copiedSkills.length === 1 ? "" : "s"} from ${packageNameOrPath}:\n`,
|
|
1986
|
+
);
|
|
1987
|
+
for (const copied of copiedSkills) {
|
|
1988
|
+
process.stdout.write(`- ${copied}\n`);
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
|
|
1992
|
+
const getSkillFolderNames = (manifests: string[]): string[] => {
|
|
1993
|
+
const names = new Set<string>();
|
|
1994
|
+
for (const manifest of manifests) {
|
|
1995
|
+
names.add(basename(dirname(manifest)));
|
|
1996
|
+
}
|
|
1997
|
+
return Array.from(names).sort();
|
|
1998
|
+
};
|
|
1999
|
+
|
|
2000
|
+
export const removeSkillsFromPackage = async (
|
|
2001
|
+
workingDir: string,
|
|
2002
|
+
packageNameOrPath: string,
|
|
2003
|
+
options?: { path?: string },
|
|
2004
|
+
): Promise<{ removed: string[]; missing: string[] }> => {
|
|
2005
|
+
const { skillRoot, manifests } = await validateSkillPackage(workingDir, packageNameOrPath);
|
|
2006
|
+
const selected = await selectSkillManifests(skillRoot, manifests, options?.path);
|
|
2007
|
+
const skillsDir = resolve(workingDir, "skills");
|
|
2008
|
+
const sourceName = normalizeSkillSourceName(
|
|
2009
|
+
resolveInstalledPackageName(packageNameOrPath) ?? basename(skillRoot),
|
|
2010
|
+
);
|
|
2011
|
+
const sourceSkillsDir = resolve(skillsDir, sourceName);
|
|
2012
|
+
const skillNames = getSkillFolderNames(selected);
|
|
2013
|
+
|
|
2014
|
+
const removed: string[] = [];
|
|
2015
|
+
const missing: string[] = [];
|
|
2016
|
+
|
|
2017
|
+
if (!options?.path && existsSync(sourceSkillsDir)) {
|
|
2018
|
+
await rm(sourceSkillsDir, { recursive: true, force: false });
|
|
2019
|
+
removed.push(`skills/${sourceName}`);
|
|
2020
|
+
return { removed, missing };
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
for (const skillName of skillNames) {
|
|
2024
|
+
const destinationSkillDir = resolve(sourceSkillsDir, skillName);
|
|
2025
|
+
const normalized = relative(skillsDir, destinationSkillDir).split("\\").join("/");
|
|
2026
|
+
if (normalized.startsWith("..") || normalized.startsWith("/")) {
|
|
2027
|
+
throw new Error(`Refusing to remove path outside skills directory: ${destinationSkillDir}`);
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
if (!existsSync(destinationSkillDir)) {
|
|
2031
|
+
missing.push(`skills/${sourceName}/${skillName}`);
|
|
2032
|
+
continue;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
await rm(destinationSkillDir, { recursive: true, force: false });
|
|
2036
|
+
removed.push(`skills/${sourceName}/${skillName}`);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
return { removed, missing };
|
|
2040
|
+
};
|
|
2041
|
+
|
|
2042
|
+
export const removeSkillPackage = async (
|
|
2043
|
+
workingDir: string,
|
|
2044
|
+
packageNameOrPath: string,
|
|
2045
|
+
options?: { path?: string },
|
|
2046
|
+
): Promise<void> => {
|
|
2047
|
+
const result = await removeSkillsFromPackage(workingDir, packageNameOrPath, options);
|
|
2048
|
+
process.stdout.write(
|
|
2049
|
+
`Removed ${result.removed.length} skill${result.removed.length === 1 ? "" : "s"} from ${packageNameOrPath}:\n`,
|
|
2050
|
+
);
|
|
2051
|
+
for (const removed of result.removed) {
|
|
2052
|
+
process.stdout.write(`- ${removed}\n`);
|
|
2053
|
+
}
|
|
2054
|
+
if (result.missing.length > 0) {
|
|
2055
|
+
process.stdout.write(
|
|
2056
|
+
`Skipped ${result.missing.length} missing skill${result.missing.length === 1 ? "" : "s"}:\n`,
|
|
2057
|
+
);
|
|
2058
|
+
for (const missing of result.missing) {
|
|
2059
|
+
process.stdout.write(`- ${missing}\n`);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
|
|
2064
|
+
export const listInstalledSkills = async (
|
|
2065
|
+
workingDir: string,
|
|
2066
|
+
sourceName?: string,
|
|
2067
|
+
): Promise<string[]> => {
|
|
2068
|
+
const skillsRoot = resolve(workingDir, "skills");
|
|
2069
|
+
const resolvedSourceName = sourceName
|
|
2070
|
+
? resolveInstalledPackageName(sourceName) ?? sourceName
|
|
2071
|
+
: undefined;
|
|
2072
|
+
const targetRoot = sourceName
|
|
2073
|
+
? resolve(skillsRoot, normalizeSkillSourceName(resolvedSourceName ?? sourceName))
|
|
2074
|
+
: skillsRoot;
|
|
2075
|
+
if (!existsSync(targetRoot)) {
|
|
2076
|
+
return [];
|
|
2077
|
+
}
|
|
2078
|
+
const manifests = await collectSkillManifests(targetRoot, sourceName ? 1 : 2);
|
|
2079
|
+
return manifests
|
|
2080
|
+
.map((manifest) => relative(workingDir, dirname(manifest)).split("\\").join("/"))
|
|
2081
|
+
.sort();
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
export const listSkills = async (workingDir: string, sourceName?: string): Promise<void> => {
|
|
2085
|
+
const skills = await listInstalledSkills(workingDir, sourceName);
|
|
2086
|
+
if (skills.length === 0) {
|
|
2087
|
+
process.stdout.write("No installed skills found.\n");
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
const resolvedSourceName = sourceName
|
|
2091
|
+
? resolveInstalledPackageName(sourceName) ?? sourceName
|
|
2092
|
+
: undefined;
|
|
2093
|
+
process.stdout.write(
|
|
2094
|
+
sourceName
|
|
2095
|
+
? `Installed skills for ${normalizeSkillSourceName(resolvedSourceName ?? sourceName)}:\n`
|
|
2096
|
+
: "Installed skills:\n",
|
|
2097
|
+
);
|
|
2098
|
+
for (const skill of skills) {
|
|
2099
|
+
process.stdout.write(`- ${skill}\n`);
|
|
2100
|
+
}
|
|
1843
2101
|
};
|
|
1844
2102
|
|
|
1845
2103
|
export const runTests = async (
|
|
@@ -2390,12 +2648,49 @@ export const buildCli = (): Command => {
|
|
|
2390
2648
|
await listTools(process.cwd());
|
|
2391
2649
|
});
|
|
2392
2650
|
|
|
2651
|
+
const skillsCommand = program.command("skills").description("Manage installed skills");
|
|
2652
|
+
skillsCommand
|
|
2653
|
+
.command("add")
|
|
2654
|
+
.argument("<source>", "skill package name/path")
|
|
2655
|
+
.argument("[skillPath]", "optional path to one specific skill within source")
|
|
2656
|
+
.description("Install and copy skills into ./skills/<source>/...")
|
|
2657
|
+
.action(async (source: string, skillPath?: string) => {
|
|
2658
|
+
await addSkill(process.cwd(), source, { path: skillPath });
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
skillsCommand
|
|
2662
|
+
.command("remove")
|
|
2663
|
+
.argument("<source>", "skill package name/path")
|
|
2664
|
+
.argument("[skillPath]", "optional path to one specific skill within source")
|
|
2665
|
+
.description("Remove installed skills from ./skills/<source>/...")
|
|
2666
|
+
.action(async (source: string, skillPath?: string) => {
|
|
2667
|
+
await removeSkillPackage(process.cwd(), source, { path: skillPath });
|
|
2668
|
+
});
|
|
2669
|
+
|
|
2670
|
+
skillsCommand
|
|
2671
|
+
.command("list")
|
|
2672
|
+
.argument("[source]", "optional source package/folder")
|
|
2673
|
+
.description("List installed skills")
|
|
2674
|
+
.action(async (source?: string) => {
|
|
2675
|
+
await listSkills(process.cwd(), source);
|
|
2676
|
+
});
|
|
2677
|
+
|
|
2393
2678
|
program
|
|
2394
2679
|
.command("add")
|
|
2395
2680
|
.argument("<packageOrPath>", "skill package name/path")
|
|
2396
|
-
.
|
|
2397
|
-
.
|
|
2398
|
-
|
|
2681
|
+
.option("--path <relativePath>", "only copy a specific skill path from the package")
|
|
2682
|
+
.description("Alias for `poncho skills add <source> [skillPath]`")
|
|
2683
|
+
.action(async (packageOrPath: string, options: { path?: string }) => {
|
|
2684
|
+
await addSkill(process.cwd(), packageOrPath, { path: options.path });
|
|
2685
|
+
});
|
|
2686
|
+
|
|
2687
|
+
program
|
|
2688
|
+
.command("remove")
|
|
2689
|
+
.argument("<packageOrPath>", "skill package name/path")
|
|
2690
|
+
.option("--path <relativePath>", "only remove a specific skill path from the package")
|
|
2691
|
+
.description("Alias for `poncho skills remove <source> [skillPath]`")
|
|
2692
|
+
.action(async (packageOrPath: string, options: { path?: string }) => {
|
|
2693
|
+
await removeSkillPackage(process.cwd(), packageOrPath, { path: options.path });
|
|
2399
2694
|
});
|
|
2400
2695
|
|
|
2401
2696
|
program
|