Upgrade CLI to v0.2.0: add validate, list commands, --version, --dry-run, --backup flags

- 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>
This commit is contained in:
2026-03-19 01:25:28 +07:00
parent 5325629a81
commit 9f1d6eb880
12 changed files with 627 additions and 15 deletions

View File

@@ -1,4 +1,4 @@
import { mkdtemp, mkdir, rm, access } from "node:fs/promises";
import { mkdtemp, mkdir, readdir, rm, access, writeFile } from "node:fs/promises";
import { constants as fsConstants } from "node:fs";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
@@ -77,3 +77,96 @@ test("init replaces .agent with --force", async () => {
await rm(projectDir, { recursive: true, force: true });
}
});
test("init with -f short flag works", async () => {
const projectDir = await createTempProject("agsp-short-f-");
try {
await mkdir(join(projectDir, ".agent"), { recursive: true });
const result = runCli(["init", "-f"], projectDir);
assert.equal(result.status, 0);
assert.match(result.stdout, /Initialized/);
} finally {
await rm(projectDir, { recursive: true, force: true });
}
});
test("init with target directory argument", async () => {
const parentDir = await createTempProject("agsp-parent-");
const targetDir = join(parentDir, "myproject");
await mkdir(targetDir);
try {
const result = runCli(["init", targetDir], parentDir);
assert.equal(result.status, 0);
const hasAgent = await pathExists(join(targetDir, ".agent", "AGENTS.md"));
assert.equal(hasAgent, true);
} finally {
await rm(parentDir, { recursive: true, force: true });
}
});
test("init fails when target directory does not exist", async () => {
const result = runCli(["init", "/tmp/nonexistent-dir-agsp-12345"]);
assert.equal(result.status, 1);
assert.match(result.stderr, /does not exist/i);
});
test("init with unknown option fails", async () => {
const projectDir = await createTempProject("agsp-unknown-");
try {
const result = runCli(["init", "--bogus"], projectDir);
assert.equal(result.status, 1);
assert.match(result.stderr, /Unknown option/);
} finally {
await rm(projectDir, { recursive: true, force: true });
}
});
test("init --dry-run previews files without copying", async () => {
const projectDir = await createTempProject("agsp-dryrun-");
try {
const result = runCli(["init", "--dry-run"], projectDir);
assert.equal(result.status, 0);
assert.match(result.stdout, /Dry run/);
assert.match(result.stdout, /AGENTS\.md/);
const hasAgent = await pathExists(join(projectDir, ".agent"));
assert.equal(hasAgent, false, ".agent should not be created during dry run");
} finally {
await rm(projectDir, { recursive: true, force: true });
}
});
test("init --force --backup creates backup before replacing", async () => {
const projectDir = await createTempProject("agsp-backup-");
try {
// Create initial .agent with a custom file
await mkdir(join(projectDir, ".agent"), { recursive: true });
await writeFile(join(projectDir, ".agent", "custom.txt"), "my custom config");
const result = runCli(["init", "--force", "--backup"], projectDir);
assert.equal(result.status, 0);
assert.match(result.stdout, /Backed up/);
// New .agent should exist
const hasAgent = await pathExists(join(projectDir, ".agent", "AGENTS.md"));
assert.equal(hasAgent, true);
// Backup dir should exist
const entries = await readdir(projectDir);
const backupDirs = entries.filter((e) => e.startsWith(".agent-backup-"));
assert.equal(backupDirs.length, 1, "Should have exactly one backup directory");
// Backup should contain the custom file
const hasCustom = await pathExists(join(projectDir, backupDirs[0], "custom.txt"));
assert.equal(hasCustom, true, "Backup should preserve custom files");
} finally {
await rm(projectDir, { recursive: true, force: true });
}
});