Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-skill-rule-extra-args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stainless-code/codemap": patch
---

Reject unexpected arguments on `codemap skill` and `codemap rule` instead of silently printing bundled content.
32 changes: 32 additions & 0 deletions src/cli/cmd-skill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,38 @@ import {
checkConsumerPointers,
EXPECTED_POINTER_VERSION,
} from "../application/agent-content";
import { parseAgentContentRest } from "./cmd-skill";

describe("parseAgentContentRest", () => {
it("returns help on --help / -h", () => {
expect(parseAgentContentRest(["skill", "--help"]).kind).toBe("help");
expect(parseAgentContentRest(["rule", "-h"]).kind).toBe("help");
});

it("runs with no extra arguments", () => {
expect(parseAgentContentRest(["skill"])).toEqual({
kind: "run",
verb: "skill",
});
expect(parseAgentContentRest(["rule"])).toEqual({
kind: "run",
verb: "rule",
});
});

it("errors on unexpected flags or positionals", () => {
const skillJson = parseAgentContentRest(["skill", "--json"]);
expect(skillJson.kind).toBe("error");
if (skillJson.kind === "error") {
expect(skillJson.message).toContain("unexpected argument");
}
const ruleExtra = parseAgentContentRest(["rule", "extra"]);
expect(ruleExtra.kind).toBe("error");
if (ruleExtra.kind === "error") {
expect(ruleExtra.message).toContain("unexpected argument");
}
});
});

describe("agent content fetch surfaces", () => {
it("assembles the skill from section files", () => {
Expand Down
31 changes: 31 additions & 0 deletions src/cli/cmd-skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,37 @@ import type { AgentContentKind } from "../application/agent-content";
*/
export type { AgentContentKind };

export type AgentContentRest =
| { kind: "help"; verb: AgentContentKind }
| { kind: "run"; verb: AgentContentKind }
| { kind: "error"; message: string };

/** Parse `codemap skill` / `codemap rule` argv after bootstrap strips global flags. */
export function parseAgentContentRest(rest: string[]): AgentContentRest {
const verb = rest[0];
if (verb !== "skill" && verb !== "rule") {
throw new Error(
`parseAgentContentRest: expected first token skill|rule, got ${String(verb)}`,
);
}
const args = rest.slice(1);
if (args.length === 0) return { kind: "run", verb };
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
return { kind: "help", verb };
}
const bad = args.find((a) => a !== "--help" && a !== "-h");
if (bad !== undefined) {
return {
kind: "error",
message: `codemap ${verb}: unexpected argument "${bad}". Run \`codemap ${verb} --help\` for usage.`,
};
}
return {
kind: "error",
message: `codemap ${verb}: unexpected extra arguments. Run \`codemap ${verb} --help\` for usage.`,
};
}

export function printAgentContentCmdHelp(kind: AgentContentKind): void {
const verb = kind;
const source = `templates/agent-content/${kind}/*.md (assembled)`;
Expand Down
19 changes: 13 additions & 6 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,21 @@ Copies bundled agent templates into .agents/ under the project root.
}

if (rest[0] === "skill" || rest[0] === "rule") {
const kind = rest[0];
const { printAgentContentCmdHelp, runAgentContentCmd } =
await import("./cmd-skill.js");
if (rest.includes("--help") || rest.includes("-h")) {
printAgentContentCmdHelp(kind);
const {
parseAgentContentRest,
printAgentContentCmdHelp,
runAgentContentCmd,
} = await import("./cmd-skill.js");
const parsed = parseAgentContentRest(rest);
if (parsed.kind === "help") {
printAgentContentCmdHelp(parsed.verb);
return;
}
runAgentContentCmd(kind);
if (parsed.kind === "error") {
console.error(parsed.message);
process.exit(1);
}
runAgentContentCmd(parsed.verb);
return;
}

Expand Down
Loading