mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(skills): register cli-skill-collector in the agent-skills catalog (Integration 2/2 base-red) (#7310)
* fix(skills): register cli-skill-collector in the agent-skills catalog (#6294 shipped the dir only) * chore(skills): regenerate cli-skill-collector SKILL.md via the generator, preserving the #6294 authored workflow in the custom block * fix(skills): derive coverage totals from the id lists + align remaining count assertions (45 catalog / 21 cli) * fix(skills): SkillCoverage totals are number, not stale literals
This commit is contained in:
committed by
GitHub
parent
83cca4d20f
commit
13e312b311
1
changelog.d/fixes/register-cli-skill-collector.md
Normal file
1
changelog.d/fixes/register-cli-skill-collector.md
Normal file
@@ -0,0 +1 @@
|
||||
- **Skills**: register `cli-skill-collector` in the agent-skills catalog (types union, curated entry, CLI id list) — #6294 shipped the `skills/cli-skill-collector/` directory without the catalog registration, so it was unreachable via the API and Integration CI failed on the catalog-integrity test; counts aligned (44 API+CLI, 45 with config)
|
||||
@@ -1,7 +1,275 @@
|
||||
---
|
||||
name: cli-skill-collector
|
||||
description: "Agent workflow: detect installed CLI coding tools (Claude Code, Codex, Cursor, Copilot, Cline, Hermes, OpenCode, etc.), search GitHub for matching agent skills, and install them to the detected tools. Replaces the standalone Skill Collector Python app."
|
||||
description: "Detect installed CLI coding tools (Claude Code, Codex, Cursor, Copilot, Cline and more), search GitHub for matching agent skills, and install them to the detected tools via OmniRoute's built-in APIs."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Detect installed CLI coding tools (Claude Code, Codex, Cursor, Copilot, Cline and more), search GitHub for matching agent skills, and install them to the detected tools via OmniRoute's built-in APIs.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `autostart`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart
|
||||
```
|
||||
|
||||
### `autostart enable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart enable
|
||||
```
|
||||
|
||||
### `autostart disable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart disable
|
||||
```
|
||||
|
||||
### `autostart toggle`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart toggle
|
||||
```
|
||||
|
||||
### `autostart status`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart status
|
||||
```
|
||||
|
||||
### `config`
|
||||
|
||||
Show or update CLI tool configuration
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config
|
||||
```
|
||||
|
||||
### `config list`
|
||||
|
||||
List all CLI tools and config status
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config list
|
||||
```
|
||||
|
||||
### `config get <tool>`
|
||||
|
||||
Show current config for a tool
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config get <tool>
|
||||
```
|
||||
|
||||
### `config set <tool>`
|
||||
|
||||
Write config for a tool
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--non-interactive`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config set <tool>
|
||||
```
|
||||
|
||||
### `config validate <tool>`
|
||||
|
||||
Validate config format without writing
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config validate <tool>
|
||||
```
|
||||
|
||||
### `config opencode`
|
||||
|
||||
Generate OpenCode config (alias for
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--non-interactive`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config opencode
|
||||
```
|
||||
|
||||
### `config lang`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config lang
|
||||
```
|
||||
|
||||
### `config get`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config get
|
||||
```
|
||||
|
||||
### `config set <code>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--force`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config set <code>
|
||||
```
|
||||
|
||||
### `config list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config list
|
||||
```
|
||||
|
||||
### `env`
|
||||
|
||||
Show and manage environment variables
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env
|
||||
```
|
||||
|
||||
### `env show`
|
||||
|
||||
Show current environment variables
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env show
|
||||
```
|
||||
|
||||
### `env get <key>`
|
||||
|
||||
Get a single environment variable
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env get <key>
|
||||
```
|
||||
|
||||
### `env set <key> <value>`
|
||||
|
||||
Set an environment variable (current session only)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env set <key> <value>
|
||||
```
|
||||
|
||||
### `setup`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--password <value>`
|
||||
- `--add-provider`
|
||||
- `--provider <id>`
|
||||
- `--provider-name <name>`
|
||||
- `--api-key <value>`
|
||||
- `--default-model <model>`
|
||||
- `--provider-base-url <url>`
|
||||
- `--test-provider`
|
||||
- `--non-interactive`
|
||||
- `--list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute setup
|
||||
```
|
||||
|
||||
### `update`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--check`
|
||||
- `--apply`
|
||||
- `--changelog`
|
||||
- `--dry-run`
|
||||
- `--no-backup`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute update
|
||||
```
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Preserved curated content from #6294 (skills/cli-skill-collector authored workflow) -->
|
||||
|
||||
# /cli-skill-collector — Agent Skill Collector
|
||||
|
||||
@@ -150,3 +418,4 @@ fi
|
||||
- OmniRoute must be running locally on port 20128 (default) — see `docs/frameworks/SKILLS.md` for custom-port setups.
|
||||
- The `/api/skills/collect/*` and `/api/github-skills` endpoints require **management-scoped authentication** the same way every other `/api/skills/*` route does: a dashboard session, the loopback CLI token, or an API key with the `manage` scope (`requireManagementAuth()`). Auth is only bypassed when the server has no login/API-key requirement configured at all.
|
||||
- This replaces the standalone Skill Collector Python app — all logic is now inside OmniRoute.
|
||||
<!-- skill:custom-end -->
|
||||
|
||||
@@ -68,6 +68,7 @@ export const CLI_SKILL_IDS: readonly string[] = [
|
||||
"cli-eval",
|
||||
"cli-plugins-skills",
|
||||
"cli-setup",
|
||||
"cli-skill-collector",
|
||||
] as const;
|
||||
|
||||
// ── Module-scope cache ──────────────────────────────────────────────────────
|
||||
@@ -148,8 +149,10 @@ export function computeCoverage(): SkillCoverage {
|
||||
const configHave = catalog.filter((s) => s.category === "config" && presentIds.has(s.id)).length;
|
||||
|
||||
return {
|
||||
api: { have: apiHave, total: 23 },
|
||||
cli: { have: cliHave, total: 20 },
|
||||
// Totals derive from the id lists — hardcoded 23/20 went stale the first
|
||||
// time the catalog grew (cli-skill-collector registration, 2026-07-15).
|
||||
api: { have: apiHave, total: API_SKILL_IDS.length },
|
||||
cli: { have: cliHave, total: CLI_SKILL_IDS.length },
|
||||
config: { have: configHave, total: configTotal },
|
||||
totalSkills: apiHave + cliHave + configHave,
|
||||
generatedAt: new Date().toISOString(),
|
||||
|
||||
@@ -48,7 +48,8 @@ export type SkillArea =
|
||||
| "cli-batches"
|
||||
| "cli-eval"
|
||||
| "cli-plugins-skills"
|
||||
| "cli-setup";
|
||||
| "cli-setup"
|
||||
| "cli-skill-collector";
|
||||
|
||||
export interface AgentSkill {
|
||||
id: string; // canonical id (e.g. "omni-providers", "cli-serve")
|
||||
@@ -66,8 +67,10 @@ export interface AgentSkill {
|
||||
}
|
||||
|
||||
export interface SkillCoverage {
|
||||
api: { have: number; total: 23 };
|
||||
cli: { have: number; total: 20 };
|
||||
// Totals are derived from the catalog id lists (literal types went stale the
|
||||
// first time the catalog grew — cli-skill-collector, 2026-07-15).
|
||||
api: { have: number; total: number };
|
||||
cli: { have: number; total: number };
|
||||
config: { have: number; total: number };
|
||||
totalSkills: number; // sum
|
||||
generatedAt: string; // ISO datetime
|
||||
|
||||
@@ -428,6 +428,15 @@ export const CURATED_SKILLS: CuratedSkillEntry[] = [
|
||||
area: "cli-setup",
|
||||
icon: "build",
|
||||
},
|
||||
{
|
||||
id: "cli-skill-collector",
|
||||
name: "CLI: Agent Skill Collector",
|
||||
description:
|
||||
"Detect installed CLI coding tools (Claude Code, Codex, Cursor, Copilot, Cline and more), search GitHub for matching agent skills, and install them to the detected tools via OmniRoute's built-in APIs.",
|
||||
category: "cli",
|
||||
area: "cli-setup",
|
||||
icon: "extension",
|
||||
},
|
||||
|
||||
// ── Config Skills ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Integration tests for Agent Skills content integrity.
|
||||
*
|
||||
* Verifies:
|
||||
* 1. All 43 skill IDs from catalog have skills/{id}/ folder with SKILL.md.
|
||||
* 1. All 44 skill IDs from catalog have skills/{id}/ folder with SKILL.md.
|
||||
* 2. Zero omniroute-* folders remain (post-prune: old omniroute-* skill dirs were removed).
|
||||
* 3. 10 specific IDs have <!-- skill:custom-start --> ... <!-- skill:custom-end --> blocks:
|
||||
* 3. 12 specific IDs have <!-- skill:custom-start --> ... <!-- skill:custom-end --> blocks:
|
||||
* omni-mcp, omni-compression, cli-providers, cli-eval, omni-agents-a2a,
|
||||
* omni-combos-routing, omni-auth, omni-resilience, omni-inference, cli-serve.
|
||||
*
|
||||
@@ -22,6 +22,7 @@ const ALL_IDS = [...API_SKILL_IDS, ...CLI_SKILL_IDS, ...CONFIG_SKILL_IDS] as str
|
||||
|
||||
// IDs that must have a custom block
|
||||
const CUSTOM_BLOCK_IDS = [
|
||||
"cli-skill-collector",
|
||||
"omni-mcp",
|
||||
"omni-compression",
|
||||
"cli-providers",
|
||||
@@ -37,7 +38,7 @@ const CUSTOM_BLOCK_IDS = [
|
||||
|
||||
// ── §1: All 42 catalog IDs have skills/{id}/SKILL.md ─────────────────────────
|
||||
|
||||
test("all 43 catalog IDs have a skills/{id}/ directory", () => {
|
||||
test("all 44 catalog IDs have a skills/{id}/ directory", () => {
|
||||
const missing: string[] = [];
|
||||
for (const id of ALL_IDS) {
|
||||
const dirPath = path.join(SKILLS_DIR, id);
|
||||
@@ -48,7 +49,7 @@ test("all 43 catalog IDs have a skills/{id}/ directory", () => {
|
||||
assert.deepEqual(missing, [], `Missing skill directories: ${missing.join(", ")}`);
|
||||
});
|
||||
|
||||
test("all 43 catalog IDs have a skills/{id}/SKILL.md file", () => {
|
||||
test("all 44 catalog IDs have a skills/{id}/SKILL.md file", () => {
|
||||
const missing: string[] = [];
|
||||
for (const id of ALL_IDS) {
|
||||
const skillPath = path.join(SKILLS_DIR, id, "SKILL.md");
|
||||
@@ -113,7 +114,7 @@ for (const id of CUSTOM_BLOCK_IDS) {
|
||||
|
||||
// ── Additional integrity checks ───────────────────────────────────────────────
|
||||
|
||||
test("exactly 11 skills have custom blocks", () => {
|
||||
test("exactly 12 skills have custom blocks", () => {
|
||||
const withCustomBlocks: string[] = [];
|
||||
for (const id of ALL_IDS) {
|
||||
const skillPath = path.join(SKILLS_DIR, id, "SKILL.md");
|
||||
@@ -128,7 +129,7 @@ test("exactly 11 skills have custom blocks", () => {
|
||||
assert.deepEqual(
|
||||
withCustomBlocks.sort(),
|
||||
expectedIds,
|
||||
`Expected exactly these 11 custom-block IDs: ${expectedIds.join(", ")}\nActual: ${withCustomBlocks.join(", ")}`,
|
||||
`Expected exactly these 12 custom-block IDs: ${expectedIds.join(", ")}\nActual: ${withCustomBlocks.join(", ")}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ test("every CLI skill ID has skills/<id>/SKILL.md on disk", () => {
|
||||
assert.deepEqual(missing, [], `Missing CLI SKILL.md files: ${missing.join(", ")}`);
|
||||
});
|
||||
|
||||
test("total skill count is exactly 43 (23 API + 20 CLI)", () => {
|
||||
assert.equal(API_SKILL_IDS.length + CLI_SKILL_IDS.length, 43);
|
||||
test("total skill count is exactly 44 (23 API + 21 CLI)", () => {
|
||||
assert.equal(API_SKILL_IDS.length + CLI_SKILL_IDS.length, 44);
|
||||
});
|
||||
|
||||
// ── §2: Frontmatter validation ────────────────────────────────────────────────
|
||||
@@ -120,11 +120,11 @@ test("each SKILL.md body is at least 100 chars", () => {
|
||||
|
||||
// ── §3: MCP tool omniroute_agent_skills_list ─────────────────────────────────
|
||||
|
||||
test("MCP omniroute_agent_skills_list handler returns count 44 (43 + config)", async () => {
|
||||
test("MCP omniroute_agent_skills_list handler returns count 45 (44 + config)", async () => {
|
||||
const result = await agentSkillTools.omniroute_agent_skills_list.handler({});
|
||||
assert.equal(result.count, 44, `Expected 44 but got ${result.count}`);
|
||||
assert.equal(result.count, 45, `Expected 45 but got ${result.count}`);
|
||||
assert.ok(Array.isArray(result.skills));
|
||||
assert.equal(result.skills.length, 44);
|
||||
assert.equal(result.skills.length, 45);
|
||||
});
|
||||
|
||||
test("MCP omniroute_agent_skills_list result has all 42 IDs", async () => {
|
||||
@@ -157,9 +157,9 @@ test("A2A list-capabilities artifact content contains 42 skill IDs as table rows
|
||||
assert.ok(rows.length >= 42, `Expected at least 42 data rows but got ${rows.length}`);
|
||||
});
|
||||
|
||||
test("A2A list-capabilities metadata.totalSkills === 44 (43 + config)", async () => {
|
||||
test("A2A list-capabilities metadata.totalSkills === 45 (44 + config)", async () => {
|
||||
const result = await executeListCapabilities(stubTask);
|
||||
assert.equal(result.metadata.totalSkills, 44);
|
||||
assert.equal(result.metadata.totalSkills, 45);
|
||||
});
|
||||
|
||||
test("A2A list-capabilities artifact contains all 42 skill IDs", async () => {
|
||||
|
||||
@@ -58,11 +58,11 @@ test("each agentSkillTool has name, description, inputSchema, and handler", () =
|
||||
|
||||
// ─── omniroute_agent_skills_list ────────────────────────────────────────────
|
||||
|
||||
test("omniroute_agent_skills_list with no filters returns all 44 skills", async () => {
|
||||
test("omniroute_agent_skills_list with no filters returns all 45 skills", async () => {
|
||||
const result = await agentSkillTools.omniroute_agent_skills_list.handler({});
|
||||
assert.equal(result.count, 44, `Expected 44 but got ${result.count}`);
|
||||
assert.equal(result.count, 45, `Expected 45 but got ${result.count}`);
|
||||
assert.ok(Array.isArray(result.skills));
|
||||
assert.equal(result.skills.length, 44);
|
||||
assert.equal(result.skills.length, 45);
|
||||
});
|
||||
|
||||
test("omniroute_agent_skills_list({category:'api'}) returns exactly 23 entries", async () => {
|
||||
@@ -71,9 +71,9 @@ test("omniroute_agent_skills_list({category:'api'}) returns exactly 23 entries",
|
||||
assert.ok(result.skills.every((s: { category: string }) => s.category === "api"));
|
||||
});
|
||||
|
||||
test("omniroute_agent_skills_list({category:'cli'}) returns exactly 20 entries", async () => {
|
||||
test("omniroute_agent_skills_list({category:'cli'}) returns exactly 21 entries", async () => {
|
||||
const result = await agentSkillTools.omniroute_agent_skills_list.handler({ category: "cli" });
|
||||
assert.equal(result.count, 20, `Expected 20 cli skills but got ${result.count}`);
|
||||
assert.equal(result.count, 21, `Expected 21 cli skills but got ${result.count}`);
|
||||
assert.ok(result.skills.every((s: { category: string }) => s.category === "cli"));
|
||||
});
|
||||
|
||||
@@ -83,7 +83,7 @@ test("omniroute_agent_skills_list result includes coverage shape", async () => {
|
||||
assert.ok(typeof result.coverage.api === "object");
|
||||
assert.ok(typeof result.coverage.cli === "object");
|
||||
assert.equal(result.coverage.api.total, 23);
|
||||
assert.equal(result.coverage.cli.total, 20);
|
||||
assert.equal(result.coverage.cli.total, 21);
|
||||
assert.ok(typeof result.coverage.totalSkills === "number");
|
||||
assert.ok(typeof result.coverage.generatedAt === "string");
|
||||
});
|
||||
@@ -168,11 +168,11 @@ test("omniroute_agent_skills_coverage({}) returns coverage shape", async () => {
|
||||
assert.ok(typeof result.api === "object");
|
||||
assert.ok(typeof result.cli === "object");
|
||||
assert.equal(result.api.total, 23);
|
||||
assert.equal(result.cli.total, 20);
|
||||
assert.equal(result.cli.total, 21);
|
||||
assert.ok(typeof result.api.have === "number");
|
||||
assert.ok(typeof result.cli.have === "number");
|
||||
assert.ok(result.api.have >= 0 && result.api.have <= 23);
|
||||
assert.ok(result.cli.have >= 0 && result.cli.have <= 20);
|
||||
assert.ok(result.cli.have >= 0 && result.cli.have <= 21);
|
||||
assert.ok(typeof result.totalSkills === "number");
|
||||
assert.equal(result.totalSkills, result.api.have + result.cli.have + (result.config?.have ?? 0));
|
||||
assert.ok(typeof result.generatedAt === "string");
|
||||
|
||||
@@ -15,10 +15,10 @@ const agentSkillsConstants = await import("../../src/shared/constants/agentSkill
|
||||
|
||||
// ─── Counts ───────────────────────────────────────────────────────────────────
|
||||
|
||||
test("getCatalog() returns exactly 44 entries", () => {
|
||||
test("getCatalog() returns exactly 45 entries", () => {
|
||||
refreshCatalog();
|
||||
const catalog = getCatalog();
|
||||
assert.equal(catalog.length, 44, `Expected 44 but got ${catalog.length}`);
|
||||
assert.equal(catalog.length, 45, `Expected 45 but got ${catalog.length}`);
|
||||
});
|
||||
|
||||
test("API_SKILL_IDS has exactly 23 entries", () => {
|
||||
@@ -26,7 +26,7 @@ test("API_SKILL_IDS has exactly 23 entries", () => {
|
||||
});
|
||||
|
||||
test("CLI_SKILL_IDS has exactly 20 entries", () => {
|
||||
assert.equal(CLI_SKILL_IDS.length, 20);
|
||||
assert.equal(CLI_SKILL_IDS.length, 21);
|
||||
});
|
||||
|
||||
test("getCatalog() contains exactly 22 api skills", () => {
|
||||
@@ -34,9 +34,9 @@ test("getCatalog() contains exactly 22 api skills", () => {
|
||||
assert.equal(apiSkills.length, 23);
|
||||
});
|
||||
|
||||
test("getCatalog() contains exactly 20 cli skills", () => {
|
||||
test("getCatalog() contains exactly 21 cli skills", () => {
|
||||
const cliSkills = getCatalog().filter((s) => s.category === "cli");
|
||||
assert.equal(cliSkills.length, 20);
|
||||
assert.equal(cliSkills.length, 21);
|
||||
});
|
||||
|
||||
// ─── ID format ────────────────────────────────────────────────────────────────
|
||||
@@ -160,9 +160,9 @@ test("filterCatalog({ category: 'api' }) returns 23 api skills", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("filterCatalog({ category: 'cli' }) returns 20 cli skills", () => {
|
||||
test("filterCatalog({ category: 'cli' }) returns 21 cli skills", () => {
|
||||
const skills = filterCatalog({ category: "cli" });
|
||||
assert.equal(skills.length, 20);
|
||||
assert.equal(skills.length, 21);
|
||||
for (const s of skills) {
|
||||
assert.equal(s.category, "cli");
|
||||
}
|
||||
@@ -185,9 +185,9 @@ test("filterCatalog({ area: 'nonexistent' }) returns empty array", () => {
|
||||
assert.equal(skills.length, 0);
|
||||
});
|
||||
|
||||
test("filterCatalog({}) returns full catalog (44 entries)", () => {
|
||||
test("filterCatalog({}) returns full catalog (45 entries)", () => {
|
||||
const skills = filterCatalog({});
|
||||
assert.equal(skills.length, 44);
|
||||
assert.equal(skills.length, 45);
|
||||
});
|
||||
|
||||
// ─── refreshCatalog ───────────────────────────────────────────────────────────
|
||||
@@ -214,9 +214,9 @@ test("computeCoverage() returns valid SkillCoverage shape", () => {
|
||||
assert.ok(cov.api.have >= 0 && cov.api.have <= 23);
|
||||
|
||||
assert.ok(typeof cov.cli === "object");
|
||||
assert.equal(cov.cli.total, 20);
|
||||
assert.equal(cov.cli.total, 21);
|
||||
assert.ok(typeof cov.cli.have === "number");
|
||||
assert.ok(cov.cli.have >= 0 && cov.cli.have <= 20);
|
||||
assert.ok(cov.cli.have >= 0 && cov.cli.have <= 21);
|
||||
|
||||
assert.equal(cov.totalSkills, cov.api.have + cov.cli.have + (cov.config?.have ?? 0));
|
||||
|
||||
@@ -255,6 +255,6 @@ test("CLI_SKILL_IDS first entry is cli-serve", () => {
|
||||
assert.equal(CLI_SKILL_IDS[0], "cli-serve");
|
||||
});
|
||||
|
||||
test("CLI_SKILL_IDS last entry is cli-setup", () => {
|
||||
assert.equal(CLI_SKILL_IDS[CLI_SKILL_IDS.length - 1], "cli-setup");
|
||||
test("CLI_SKILL_IDS last entry is cli-skill-collector", () => {
|
||||
assert.equal(CLI_SKILL_IDS[CLI_SKILL_IDS.length - 1], "cli-skill-collector");
|
||||
});
|
||||
|
||||
@@ -61,11 +61,11 @@ test("dry-run (default) returns report without writing any files", async () => {
|
||||
outputDir: tmpDir,
|
||||
});
|
||||
|
||||
// All 44 skills should appear as generated (would-write) since dir is empty
|
||||
// All 45 skills should appear as generated (would-write) since dir is empty
|
||||
assert.equal(
|
||||
report.generated.length + report.unchanged.length,
|
||||
44,
|
||||
`Expected 44 total (generated+unchanged), got generated=${report.generated.length} unchanged=${report.unchanged.length}`,
|
||||
45,
|
||||
`Expected 45 total (generated+unchanged), got generated=${report.generated.length} unchanged=${report.unchanged.length}`,
|
||||
);
|
||||
assert.equal(report.errors.length, 0, `Unexpected errors: ${JSON.stringify(report.errors)}`);
|
||||
|
||||
@@ -81,7 +81,7 @@ test("dry-run (default) returns report without writing any files", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("dry-run generates report with 44 total (generated+unchanged)", async () => {
|
||||
test("dry-run generates report with 45 total (generated+unchanged)", async () => {
|
||||
const tmpDir = mkTmpDir();
|
||||
try {
|
||||
refreshCatalog();
|
||||
@@ -91,7 +91,7 @@ test("dry-run generates report with 44 total (generated+unchanged)", async () =>
|
||||
outputDir: tmpDir,
|
||||
});
|
||||
const total = report.generated.length + report.unchanged.length;
|
||||
assert.equal(total, 44);
|
||||
assert.equal(total, 45);
|
||||
} finally {
|
||||
rmTmpDir(tmpDir);
|
||||
}
|
||||
@@ -134,7 +134,7 @@ test("apply mode writes SKILL.md with valid frontmatter for omni-providers", asy
|
||||
}
|
||||
});
|
||||
|
||||
test("apply mode writes all 44 SKILL.md files when no onlyIds filter", async () => {
|
||||
test("apply mode writes all 45 SKILL.md files when no onlyIds filter", async () => {
|
||||
const tmpDir = mkTmpDir();
|
||||
try {
|
||||
refreshCatalog();
|
||||
@@ -145,7 +145,7 @@ test("apply mode writes all 44 SKILL.md files when no onlyIds filter", async ()
|
||||
});
|
||||
|
||||
assert.equal(report.errors.length, 0, `Errors: ${JSON.stringify(report.errors)}`);
|
||||
assert.equal(report.generated.length, 44);
|
||||
assert.equal(report.generated.length, 45);
|
||||
|
||||
// Verify all dirs exist
|
||||
const catalog = getCatalog();
|
||||
|
||||
@@ -101,15 +101,15 @@ test.after(() => {
|
||||
// GET /api/agent-skills
|
||||
// ═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
test("GET /api/agent-skills — returns 44 skills with count and coverage", async () => {
|
||||
test("GET /api/agent-skills — returns 45 skills with count and coverage", async () => {
|
||||
const req = makeRequest("GET", "http://localhost/api/agent-skills");
|
||||
const res = await listRoute.GET(req);
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as { skills: unknown[]; count: number; coverage: unknown };
|
||||
assert.equal(body.count, 44, `Expected 44 skills but got ${body.count}`);
|
||||
assert.equal(body.count, 45, `Expected 45 skills but got ${body.count}`);
|
||||
assert.equal(Array.isArray(body.skills), true);
|
||||
assert.equal(body.skills.length, 44);
|
||||
assert.equal(body.skills.length, 45);
|
||||
assert.ok(body.coverage !== undefined, "coverage should be present");
|
||||
});
|
||||
|
||||
@@ -123,13 +123,13 @@ test("GET /api/agent-skills?category=api — returns 23 api skills", async () =>
|
||||
assert.ok(body.skills.every((s) => s.category === "api"), "All skills should be api category");
|
||||
});
|
||||
|
||||
test("GET /api/agent-skills?category=cli — returns 20 cli skills", async () => {
|
||||
test("GET /api/agent-skills?category=cli — returns 21 cli skills", async () => {
|
||||
const req = makeRequest("GET", "http://localhost/api/agent-skills?category=cli");
|
||||
const res = await listRoute.GET(req);
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as { skills: Array<{ category: string }>; count: number };
|
||||
assert.equal(body.count, 20);
|
||||
assert.equal(body.count, 21);
|
||||
assert.ok(body.skills.every((s) => s.category === "cli"), "All skills should be cli category");
|
||||
});
|
||||
|
||||
@@ -268,7 +268,7 @@ test("GET /api/agent-skills/coverage — returns valid SkillCoverage shape", asy
|
||||
};
|
||||
|
||||
assert.equal(body.api.total, 23, "api.total must be 23");
|
||||
assert.equal(body.cli.total, 20, "cli.total must be 20");
|
||||
assert.equal(body.cli.total, 21, "cli.total must be 21");
|
||||
assert.ok(typeof body.totalSkills === "number", "totalSkills must be a number");
|
||||
assert.ok(typeof body.generatedAt === "string", "generatedAt must be a string");
|
||||
// generatedAt must be a valid ISO datetime
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Verifies:
|
||||
* - Return shape matches §3.7 contract
|
||||
* - Markdown table contains all 43 skill IDs
|
||||
* - Markdown table contains all 44 skill IDs
|
||||
* - Coverage bounds are within declared totals
|
||||
* - metadata.source === "agent-skills-catalog"
|
||||
* - metadata.generatedAt is an ISO datetime string
|
||||
@@ -31,7 +31,7 @@ test("executeListCapabilities returns shape matching §3.7 contract", async () =
|
||||
const { metadata } = result;
|
||||
assert.ok(metadata, "metadata exists");
|
||||
assert.equal(metadata.source, "agent-skills-catalog", "metadata.source matches");
|
||||
assert.equal(metadata.totalSkills, 44, "metadata.totalSkills === 44 (43 + config)");
|
||||
assert.equal(metadata.totalSkills, 45, "metadata.totalSkills === 45 (44 + config)");
|
||||
assert.ok(metadata.coverage, "metadata.coverage exists");
|
||||
assert.ok(metadata.coverage.api, "metadata.coverage.api exists");
|
||||
assert.ok(metadata.coverage.cli, "metadata.coverage.cli exists");
|
||||
@@ -39,12 +39,12 @@ test("executeListCapabilities returns shape matching §3.7 contract", async () =
|
||||
assert.equal(metadata.coverage.cli.total, 20, "cli.total === 20");
|
||||
});
|
||||
|
||||
test("executeListCapabilities markdown table contains all 43 API+CLI skill IDs", async () => {
|
||||
test("executeListCapabilities markdown table contains all 44 API+CLI skill IDs", async () => {
|
||||
const result = await executeListCapabilities(stubTask);
|
||||
const content = result.artifacts[0].content;
|
||||
|
||||
const allIds = [...API_SKILL_IDS, ...CLI_SKILL_IDS] as string[];
|
||||
assert.equal(allIds.length, 43, "API+CLI catalog declares 43 skill IDs");
|
||||
assert.equal(allIds.length, 44, "API+CLI catalog declares 44 skill IDs");
|
||||
|
||||
for (const id of allIds) {
|
||||
assert.ok(content.includes(id), `Markdown table missing skill ID: ${id}`);
|
||||
@@ -58,11 +58,11 @@ test("metadata.coverage.api.have is within [0, 23]", async () => {
|
||||
assert.ok(api.have <= 23, "api.have <= 23");
|
||||
});
|
||||
|
||||
test("metadata.coverage.cli.have is within [0, 20]", async () => {
|
||||
test("metadata.coverage.cli.have is within [0, 21]", async () => {
|
||||
const result = await executeListCapabilities(stubTask);
|
||||
const { cli } = result.metadata.coverage;
|
||||
assert.ok(cli.have >= 0, "cli.have >= 0");
|
||||
assert.ok(cli.have <= 20, "cli.have <= 20");
|
||||
assert.ok(cli.have <= 21, "cli.have <= 21");
|
||||
});
|
||||
|
||||
test("metadata.generatedAt is a valid ISO datetime", async () => {
|
||||
|
||||
Reference in New Issue
Block a user