- Fix npm test glob pattern for Node >= 21 compatibility - Add --version/-v flag to display package version - Add validate command: run 78 profile checks (files, skills, frontmatter, legacy patterns, AGENTS mapping) - Add list command: display all 13 bundled skills with descriptions - Add --dry-run/-n flag for init: preview files without copying - Add --backup/-b flag for init --force: backup existing .agent before overwrite - Add name field to workflow frontmatter (brainstorm, execute-plan, write-plan) - Expand smoke check to cover all 13 skills and new command files - Increase test coverage from 3 to 20 tests (cli, init, validate) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
80 lines
2.1 KiB
JavaScript
80 lines
2.1 KiB
JavaScript
import { readdir, readFile } from "node:fs/promises";
|
|
import { fileURLToPath } from "node:url";
|
|
import { join } from "node:path";
|
|
|
|
function getSkillsDir() {
|
|
return fileURLToPath(new URL("../../templates/.agent/skills", import.meta.url));
|
|
}
|
|
|
|
function parseFrontmatter(content) {
|
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
if (!match) return {};
|
|
|
|
const fm = {};
|
|
const lines = match[1].split("\n");
|
|
let currentKey = null;
|
|
let currentValue = "";
|
|
|
|
for (const line of lines) {
|
|
const keyMatch = line.match(/^(\w+):\s*(.*)$/);
|
|
if (keyMatch) {
|
|
if (currentKey) {
|
|
fm[currentKey] = currentValue.trim();
|
|
}
|
|
currentKey = keyMatch[1];
|
|
currentValue = keyMatch[2].replace(/^["']|["']$/g, "");
|
|
} else if (currentKey) {
|
|
currentValue += " " + line.trim();
|
|
}
|
|
}
|
|
if (currentKey) {
|
|
fm[currentKey] = currentValue.trim();
|
|
}
|
|
|
|
return fm;
|
|
}
|
|
|
|
export async function listCommand({ stdout, stderr }) {
|
|
const skillsDir = getSkillsDir();
|
|
|
|
let entries;
|
|
try {
|
|
entries = await readdir(skillsDir, { withFileTypes: true });
|
|
} catch {
|
|
stderr.write("Could not read bundled skills directory.\n");
|
|
return 1;
|
|
}
|
|
|
|
const skills = [];
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) continue;
|
|
|
|
const skillFile = join(skillsDir, entry.name, "SKILL.md");
|
|
try {
|
|
const content = await readFile(skillFile, "utf8");
|
|
const fm = parseFrontmatter(content);
|
|
skills.push({
|
|
name: fm.name || entry.name,
|
|
description: fm.description || "(no description)",
|
|
});
|
|
} catch {
|
|
skills.push({ name: entry.name, description: "(SKILL.md not found)" });
|
|
}
|
|
}
|
|
|
|
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
stdout.write(`Antigravity Superpowers — ${skills.length} skills available:\n\n`);
|
|
|
|
const maxName = Math.max(...skills.map((s) => s.name.length));
|
|
for (const skill of skills) {
|
|
const desc = skill.description.length > 80
|
|
? skill.description.slice(0, 77) + "..."
|
|
: skill.description;
|
|
stdout.write(` ${skill.name.padEnd(maxName + 2)} ${desc}\n`);
|
|
}
|
|
|
|
stdout.write("\n");
|
|
return 0;
|
|
}
|