diff --git a/package.json b/package.json index 1da6a31..04c1ffb 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "files": [ "dist/src/", "clis/", - "skills/webcmd-*/**", + "skills/webcmd/**", + "skill-data/**", "cli-manifest.json", "scripts/", "README.md", diff --git a/skills/smart-search/SKILL.md b/skill-data/smart-search/SKILL.md similarity index 100% rename from skills/smart-search/SKILL.md rename to skill-data/smart-search/SKILL.md diff --git a/skills/smart-search/references/sources-ai.md b/skill-data/smart-search/references/sources-ai.md similarity index 100% rename from skills/smart-search/references/sources-ai.md rename to skill-data/smart-search/references/sources-ai.md diff --git a/skills/smart-search/references/sources-info.md b/skill-data/smart-search/references/sources-info.md similarity index 100% rename from skills/smart-search/references/sources-info.md rename to skill-data/smart-search/references/sources-info.md diff --git a/skills/smart-search/references/sources-media.md b/skill-data/smart-search/references/sources-media.md similarity index 100% rename from skills/smart-search/references/sources-media.md rename to skill-data/smart-search/references/sources-media.md diff --git a/skills/smart-search/references/sources-other.md b/skill-data/smart-search/references/sources-other.md similarity index 100% rename from skills/smart-search/references/sources-other.md rename to skill-data/smart-search/references/sources-other.md diff --git a/skills/smart-search/references/sources-shopping.md b/skill-data/smart-search/references/sources-shopping.md similarity index 100% rename from skills/smart-search/references/sources-shopping.md rename to skill-data/smart-search/references/sources-shopping.md diff --git a/skills/smart-search/references/sources-social.md b/skill-data/smart-search/references/sources-social.md similarity index 100% rename from skills/smart-search/references/sources-social.md rename to skill-data/smart-search/references/sources-social.md diff --git a/skills/smart-search/references/sources-tech.md b/skill-data/smart-search/references/sources-tech.md similarity index 100% rename from skills/smart-search/references/sources-tech.md rename to skill-data/smart-search/references/sources-tech.md diff --git a/skills/smart-search/references/sources-travel.md b/skill-data/smart-search/references/sources-travel.md similarity index 100% rename from skills/smart-search/references/sources-travel.md rename to skill-data/smart-search/references/sources-travel.md diff --git a/skills/webcmd-adapter-author/SKILL.md b/skill-data/webcmd-adapter-author/SKILL.md similarity index 100% rename from skills/webcmd-adapter-author/SKILL.md rename to skill-data/webcmd-adapter-author/SKILL.md diff --git a/skills/webcmd-adapter-author/references/adapter-template.md b/skill-data/webcmd-adapter-author/references/adapter-template.md similarity index 100% rename from skills/webcmd-adapter-author/references/adapter-template.md rename to skill-data/webcmd-adapter-author/references/adapter-template.md diff --git a/skills/webcmd-adapter-author/references/api-discovery.md b/skill-data/webcmd-adapter-author/references/api-discovery.md similarity index 100% rename from skills/webcmd-adapter-author/references/api-discovery.md rename to skill-data/webcmd-adapter-author/references/api-discovery.md diff --git a/skills/webcmd-adapter-author/references/coverage-matrix.md b/skill-data/webcmd-adapter-author/references/coverage-matrix.md similarity index 100% rename from skills/webcmd-adapter-author/references/coverage-matrix.md rename to skill-data/webcmd-adapter-author/references/coverage-matrix.md diff --git a/skills/webcmd-adapter-author/references/field-conventions.md b/skill-data/webcmd-adapter-author/references/field-conventions.md similarity index 100% rename from skills/webcmd-adapter-author/references/field-conventions.md rename to skill-data/webcmd-adapter-author/references/field-conventions.md diff --git a/skills/webcmd-adapter-author/references/field-decode-playbook.md b/skill-data/webcmd-adapter-author/references/field-decode-playbook.md similarity index 100% rename from skills/webcmd-adapter-author/references/field-decode-playbook.md rename to skill-data/webcmd-adapter-author/references/field-decode-playbook.md diff --git a/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md b/skill-data/webcmd-adapter-author/references/jsdom-fixture-pattern.md similarity index 100% rename from skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md rename to skill-data/webcmd-adapter-author/references/jsdom-fixture-pattern.md diff --git a/skills/webcmd-adapter-author/references/output-design.md b/skill-data/webcmd-adapter-author/references/output-design.md similarity index 100% rename from skills/webcmd-adapter-author/references/output-design.md rename to skill-data/webcmd-adapter-author/references/output-design.md diff --git a/skills/webcmd-adapter-author/references/site-memory.md b/skill-data/webcmd-adapter-author/references/site-memory.md similarity index 100% rename from skills/webcmd-adapter-author/references/site-memory.md rename to skill-data/webcmd-adapter-author/references/site-memory.md diff --git a/skills/webcmd-adapter-author/references/site-recon.md b/skill-data/webcmd-adapter-author/references/site-recon.md similarity index 100% rename from skills/webcmd-adapter-author/references/site-recon.md rename to skill-data/webcmd-adapter-author/references/site-recon.md diff --git a/skills/webcmd-adapter-author/references/strategy-selection.md b/skill-data/webcmd-adapter-author/references/strategy-selection.md similarity index 100% rename from skills/webcmd-adapter-author/references/strategy-selection.md rename to skill-data/webcmd-adapter-author/references/strategy-selection.md diff --git a/skills/webcmd-adapter-author/references/success-rate-pitfalls.md b/skill-data/webcmd-adapter-author/references/success-rate-pitfalls.md similarity index 100% rename from skills/webcmd-adapter-author/references/success-rate-pitfalls.md rename to skill-data/webcmd-adapter-author/references/success-rate-pitfalls.md diff --git a/skills/webcmd-adapter-author/references/typed-errors.md b/skill-data/webcmd-adapter-author/references/typed-errors.md similarity index 100% rename from skills/webcmd-adapter-author/references/typed-errors.md rename to skill-data/webcmd-adapter-author/references/typed-errors.md diff --git a/skills/webcmd-autofix/SKILL.md b/skill-data/webcmd-autofix/SKILL.md similarity index 100% rename from skills/webcmd-autofix/SKILL.md rename to skill-data/webcmd-autofix/SKILL.md diff --git a/skills/webcmd-browser-sitemap/SKILL.md b/skill-data/webcmd-browser-sitemap/SKILL.md similarity index 100% rename from skills/webcmd-browser-sitemap/SKILL.md rename to skill-data/webcmd-browser-sitemap/SKILL.md diff --git a/skills/webcmd-browser/SKILL.md b/skill-data/webcmd-browser/SKILL.md similarity index 100% rename from skills/webcmd-browser/SKILL.md rename to skill-data/webcmd-browser/SKILL.md diff --git a/skills/webcmd-sitemap-author/SKILL.md b/skill-data/webcmd-sitemap-author/SKILL.md similarity index 100% rename from skills/webcmd-sitemap-author/SKILL.md rename to skill-data/webcmd-sitemap-author/SKILL.md diff --git a/skills/webcmd-sitemap-author/references/sitemap-schema.md b/skill-data/webcmd-sitemap-author/references/sitemap-schema.md similarity index 100% rename from skills/webcmd-sitemap-author/references/sitemap-schema.md rename to skill-data/webcmd-sitemap-author/references/sitemap-schema.md diff --git a/skills/webcmd-usage/SKILL.md b/skill-data/webcmd-usage/SKILL.md similarity index 100% rename from skills/webcmd-usage/SKILL.md rename to skill-data/webcmd-usage/SKILL.md diff --git a/skills/webcmd/SKILL.md b/skills/webcmd/SKILL.md new file mode 100644 index 0000000..76f137f --- /dev/null +++ b/skills/webcmd/SKILL.md @@ -0,0 +1,27 @@ +--- +name: webcmd +description: Use Webcmd when an agent needs deterministic CLI access to websites, browser sessions, desktop apps, or local tools. Prefer Webcmd adapters before raw browser control, and load current workflow instructions from the installed CLI. +allowed-tools: Bash(webcmd:*), Bash(npx webcmd:*) +--- + +# Webcmd + +This is a discovery skill. It keeps installation to one skill and points agents at version-matched instructions from the installed CLI. + +## Start Here + +```bash +webcmd skills list +webcmd skills get webcmd-usage +``` + +Use `webcmd skills get ` for specialized workflows: + +- `webcmd-browser` for ad-hoc browser control. +- `webcmd-adapter-author` for writing reusable adapters. +- `webcmd-autofix` for repairing broken adapters. +- `webcmd-browser-sitemap` for sitemap-aware browser work. +- `webcmd-sitemap-author` for capturing reusable site knowledge. +- `smart-search` for search and research routing. + +The CLI serves skill content that matches its installed version, so do not rely on copied stale instructions. diff --git a/src/cli.ts b/src/cli.ts index d93aad9..16b04ba 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -821,11 +821,21 @@ export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command const skillsCmd = program .command('skills') - .description('Read bundled Webcmd skills'); + .description('Read bundled Webcmd skills') + .action(() => { + const rows = listWebcmdSkills(); + renderOutput(rows, { + fmt: 'table', + fmtExplicit: false, + columns: ['name', 'description', 'version', 'path'], + title: 'webcmd/skills/list', + source: 'webcmd skills', + }); + }); skillsCmd .command('list') - .description('List bundled webcmd-* skills') + .description('List bundled Webcmd skills') .option('-f, --format ', 'Output format: table, json, yaml, md, csv', 'table') .action((opts) => { const rows = listWebcmdSkills(); @@ -840,7 +850,8 @@ export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command skillsCmd .command('read') - .description("Print a bundled webcmd-* skill's SKILL.md or reference file") + .alias('get') + .description("Print a bundled Webcmd skill's SKILL.md or reference file") .argument('', 'Skill name, or skill/path like webcmd-browser/references/foo.md') .argument('[path]', 'Path under the skill directory') .option('--json', 'Output a JSON envelope instead of raw markdown', false) diff --git a/src/skills.test.ts b/src/skills.test.ts index ad0192b..bc16649 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -7,11 +7,11 @@ import { listWebcmdSkills, readWebcmdSkill } from './skills.js'; function makePackageRoot(): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-skills-')); - fs.mkdirSync(path.join(root, 'skills', 'webcmd-browser', 'references'), { recursive: true }); - fs.mkdirSync(path.join(root, 'skills', 'webcmd-autofix'), { recursive: true }); - fs.mkdirSync(path.join(root, 'skills', 'smart-search'), { recursive: true }); + fs.mkdirSync(path.join(root, 'skill-data', 'webcmd-browser', 'references'), { recursive: true }); + fs.mkdirSync(path.join(root, 'skill-data', 'webcmd-autofix'), { recursive: true }); + fs.mkdirSync(path.join(root, 'skill-data', 'smart-search'), { recursive: true }); fs.writeFileSync(path.join(root, 'package.json'), '{"name":"@agentrhq/webcmd"}\n'); - fs.writeFileSync(path.join(root, 'skills', 'webcmd-browser', 'SKILL.md'), [ + fs.writeFileSync(path.join(root, 'skill-data', 'webcmd-browser', 'SKILL.md'), [ '---', 'name: webcmd-browser', 'description: Browser control skill', @@ -23,15 +23,15 @@ function makePackageRoot(): string { 'Body.', '', ].join('\n')); - fs.writeFileSync(path.join(root, 'skills', 'webcmd-browser', 'references', 'targets.md'), '# Targets\n'); - fs.writeFileSync(path.join(root, 'skills', 'webcmd-autofix', 'SKILL.md'), [ + fs.writeFileSync(path.join(root, 'skill-data', 'webcmd-browser', 'references', 'targets.md'), '# Targets\n'); + fs.writeFileSync(path.join(root, 'skill-data', 'webcmd-autofix', 'SKILL.md'), [ '---', 'name: webcmd-autofix', 'description: Fix adapters: keep scope narrow', '---', '', ].join('\n')); - fs.writeFileSync(path.join(root, 'skills', 'smart-search', 'SKILL.md'), [ + fs.writeFileSync(path.join(root, 'skill-data', 'smart-search', 'SKILL.md'), [ '---', 'name: smart-search', 'description: Search skill', @@ -42,10 +42,11 @@ function makePackageRoot(): string { } describe('webcmd skills content', () => { - it('lists only webcmd-prefixed skills', () => { + it('lists bundled skills', () => { const root = makePackageRoot(); expect(listWebcmdSkills(root).map((skill) => skill.name)).toEqual([ + 'smart-search', 'webcmd-autofix', 'webcmd-browser', ]); @@ -68,10 +69,14 @@ describe('webcmd skills content', () => { expect(readWebcmdSkill('webcmd-browser', 'references/targets.md', root).content).toBe('# Targets\n'); }); - it('rejects non-webcmd skills and path traversal', () => { + it('rejects unknown skills and path traversal', () => { const root = makePackageRoot(); - expect(() => readWebcmdSkill('smart-search', '', root)).toThrow(ArgumentError); + expect(readWebcmdSkill('smart-search', '', root)).toMatchObject({ + skill: 'smart-search', + path: 'SKILL.md', + }); + expect(() => readWebcmdSkill('not-real', '', root)).toThrow(ArgumentError); expect(() => readWebcmdSkill('webcmd-browser/../smart-search/SKILL.md', '', root)).toThrow(ArgumentError); expect(() => readWebcmdSkill('webcmd-browser', '../../package.json', root)).toThrow(ArgumentError); }); diff --git a/src/skills.ts b/src/skills.ts index 173aad8..c39dc26 100644 --- a/src/skills.ts +++ b/src/skills.ts @@ -27,7 +27,8 @@ interface SkillFrontmatter { } export function getSkillsRoot(packageRoot: string = findPackageRoot(MODULE_FILE)): string { - return path.join(packageRoot, 'skills'); + const skillDataRoot = path.join(packageRoot, 'skill-data'); + return isDirectory(skillDataRoot) ? skillDataRoot : path.join(packageRoot, 'skills'); } export function listWebcmdSkills(packageRoot?: string): WebcmdSkillInfo[] { @@ -35,7 +36,7 @@ export function listWebcmdSkills(packageRoot?: string): WebcmdSkillInfo[] { if (!fs.existsSync(skillsRoot)) return []; return fs.readdirSync(skillsRoot, { withFileTypes: true }) - .filter((entry) => entry.isDirectory() && entry.name.startsWith('webcmd-')) + .filter((entry) => entry.isDirectory()) .map((entry) => readSkillInfo(skillsRoot, entry.name)) .filter((entry): entry is WebcmdSkillInfo => entry !== null) .sort((a, b) => a.name.localeCompare(b.name)); @@ -43,9 +44,6 @@ export function listWebcmdSkills(packageRoot?: string): WebcmdSkillInfo[] { export function readWebcmdSkill(target: string, relpath = '', packageRoot?: string): WebcmdSkillReadResult { const { name, pathInSkill } = parseSkillTarget(target, relpath); - if (!name.startsWith('webcmd-')) { - throw new ArgumentError(`Unknown Webcmd skill: ${name}`, 'Run "webcmd skills list" to see available Webcmd skills.'); - } const skillsRoot = getSkillsRoot(packageRoot); const skillRoot = path.join(skillsRoot, name); @@ -60,7 +58,7 @@ export function readWebcmdSkill(target: string, relpath = '', packageRoot?: stri throw new ArgumentError(`Invalid skill path: ${relativePath}`, 'Skill paths must stay inside the selected Webcmd skill.'); } if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) { - throw new ArgumentError(`Skill file not found: ${name}/${relativePath}`, 'Run "webcmd skills list " is not supported yet; read SKILL.md or a known references/... file.'); + throw new ArgumentError(`Skill file not found: ${name}/${relativePath}`, 'Run "webcmd skills read " or read a known references/... file.'); } return {