Skip to content
Open
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
3 changes: 3 additions & 0 deletions packages/cli/src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function help() {
item(dim(' --build-model=<id> --review-model=<id> --plan-model=<id>'));
item(dim(' --codex-build-model=<id> --codex-plan-model=<id> --org=<org> --project=<n>'));
item(dim(' --preview-image=<image> --preview-command=<cmd> --preview-port=<n> --preview-readiness-path=</health> --preview-ttl-hours=<n>'));
item(dim(" --canary-bot=<your-app>[bot]"));
item(dim("Run facility <command> --help for precise usage."));
item(dim("Global platform flags: --profile <name> --json --timeout <seconds>"));
console.log("");
Expand Down Expand Up @@ -215,6 +216,7 @@ function validateLocalFlags(command, flags) {
"preview-port",
"preview-readiness-path",
"preview-ttl-hours",
"canary-bot",
"help",
]),
add: new Set(["dir", "help"]),
Expand Down Expand Up @@ -277,6 +279,7 @@ function validateLocalFlags(command, flags) {
"preview-port",
"preview-readiness-path",
"preview-ttl-hours",
"canary-bot",
]
: ["dir"];
for (const name of valueNames) {
Expand Down
34 changes: 34 additions & 0 deletions packages/cli/test/init.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,40 @@ test("init installs the method end to end", async (t) => {
assert.ok(again.stdout.includes("left untouched"), "second init should skip existing files");
});

// Regression coverage for #229: the closing checklist tells the user to
// "re-run init with --canary-bot=<your-app>[bot]", but the flag was missing
// from validateLocalFlags's allowlist, so that documented command failed
// before init() ever ran. https://github.com/theam/facility/issues/229
test("init accepts --canary-bot and renders it into the crew workflow", async (t) => {
const dir = makeTargetRepo();
t.after(() => rmSync(dir, { recursive: true, force: true }));

const result = runCli(
["init", "--yes", `--dir=${dir}`, "--provision=npm run setup", "--canary-bot=my-app[bot]"],
dir,
);
assert.equal(result.status, 0, result.stdout + result.stderr);

const crew = readFileSync(join(dir, ".github/workflows/facility-crew.yml"), "utf8");
assert.ok(crew.includes("my-app[bot]"), "custom canary bot login must be rendered");
assert.ok(
!crew.includes("facility-canary[bot]"),
"default canary bot login must not leak in once overridden",
);
});

test("init rejects a valueless --canary-bot and documents the flag in help", () => {
const dir = makeTargetRepo();
const rejected = runCli(["init", "--yes", `--dir=${dir}`, "--canary-bot"], dir);
assert.equal(rejected.status, 1);
assert.match(rejected.stderr, /--canary-bot requires a value/);
rmSync(dir, { recursive: true, force: true });

const help = runCli(["help"]);
assert.equal(help.status, 0);
assert.match(help.stdout, /--canary-bot=<your-app>\[bot\]/, "global help must document --canary-bot");
});

test("init renders every supported Anthropic authentication mode consistently", async (t) => {
const expectations = {
"api-key": "anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}",
Expand Down