From b8fc20c09d053c687149ea31c815377be2f17319 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Tue, 11 Aug 2026 08:30:11 +0200 Subject: [PATCH 1/2] docs: use --target for the documented veryfront install commands `veryfront install` reads its tool target from `--target` only; a bare positional is silently dropped and the command falls back to auto-detection, which in a fresh project writes SKILL.md. The installation and coding-agents pages both printed `veryfront install agents` while promising AGENTS.md. Adds a docs contract test that runs every `veryfront install ...` line in the published docs through the real CLI arg pipeline and asserts it selects a target, plus that the pages promising AGENTS.md document a command that actually writes it. --- docs/getting-started/installation.md | 4 +- docs/guides/coding-agents.md | 13 ++- tests/docs/cli-install-commands.test.ts | 116 ++++++++++++++++++++++++ tests/docs/guide-code-examples.test.ts | 2 +- tests/docs/guide-contracts.test.ts | 2 +- 5 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 tests/docs/cli-install-commands.test.ts diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index bc24bcd7e3..863a76bb5e 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -109,10 +109,10 @@ Use `npm create veryfront` when you want to scaffold a new project. ## Coding-agent setup Starter templates include `AGENTS.md`. For older projects, install the shared -project guide: +project guide with `--target agents`: ```bash -veryfront install agents +veryfront install --target agents ``` Then run `veryfront dev` and connect your MCP-aware coding agent to the printed diff --git a/docs/guides/coding-agents.md b/docs/guides/coding-agents.md index 7305e9c3cd..6d186b45fe 100644 --- a/docs/guides/coding-agents.md +++ b/docs/guides/coding-agents.md @@ -26,18 +26,21 @@ bootstrap step, inference setup, and when to use https://veryfront.com/docs. For older projects, add the same guide with: ```bash -veryfront install agents +veryfront install --target agents ``` Tool-specific files are still available: ```bash -veryfront install claude-code -veryfront install cursor -veryfront install copilot -veryfront install windsurf +veryfront install --target claude-code +veryfront install --target cursor +veryfront install --target copilot +veryfront install --target windsurf ``` +Running `veryfront install` without `--target` opens an interactive picker +instead, preselecting the tools it detects in the project. + Use `AGENTS.md` as the shared source of truth when multiple coding agents work in the same project. diff --git a/tests/docs/cli-install-commands.test.ts b/tests/docs/cli-install-commands.test.ts new file mode 100644 index 0000000000..980df83bde --- /dev/null +++ b/tests/docs/cli-install-commands.test.ts @@ -0,0 +1,116 @@ +/** + * Docs contract: every `veryfront install ...` command printed in the published + * docs must actually select the AI-tool target the surrounding prose promises. + * + * The install command reads its target from `--target` only. A bare positional + * (`veryfront install agents`) is silently ignored and the command falls back to + * auto-detection, which in a fresh project writes `SKILL.md` instead of the + * `AGENTS.md` the docs describe. + */ + +import "#veryfront/schemas/_test-setup.ts"; +import { assert, assertEquals } from "#veryfront/testing/assert.ts"; +import { describe, it } from "#veryfront/testing/bdd.ts"; +import { parseCliArgs } from "../../cli/shared/args.ts"; +import { parseInstallArgs } from "../../cli/commands/install/handler.ts"; +import { parseTargetFlag } from "../../cli/commands/install/install.ts"; +import { getToolById } from "../../cli/commands/install/registry.ts"; + +const DOC_DIRS = ["docs/getting-started", "docs/guides", "docs/concepts"] as const; + +interface DocumentedInstall { + file: string; + command: string; +} + +async function listDocFiles(dir: string): Promise { + const files: string[] = []; + for await (const entry of Deno.readDir(dir)) { + const path = `${dir}/${entry.name}`; + if (entry.isDirectory) { + files.push(...await listDocFiles(path)); + } else if (entry.isFile && entry.name.endsWith(".md")) { + files.push(path); + } + } + return files; +} + +/** Collect `veryfront install ...` lines from fenced shell blocks. */ +function extractInstallCommands(file: string, source: string): DocumentedInstall[] { + const found: DocumentedInstall[] = []; + let inShellFence = false; + + for (const rawLine of source.split("\n")) { + const line = rawLine.trim(); + + if (line.startsWith("```")) { + const lang = line.slice(3).trim().toLowerCase(); + inShellFence = inShellFence ? false : lang === "bash" || lang === "sh" || lang === "shell"; + continue; + } + + if (!inShellFence) continue; + if (!line.startsWith("veryfront install")) continue; + + found.push({ file, command: line }); + } + + return found; +} + +async function collectDocumentedInstalls(): Promise { + const commands: DocumentedInstall[] = []; + for (const dir of DOC_DIRS) { + for (const file of await listDocFiles(dir)) { + commands.push(...extractInstallCommands(file, await Deno.readTextFile(file))); + } + } + return commands.sort((a, b) => a.command.localeCompare(b.command)); +} + +/** Run a documented command line through the real CLI parsing pipeline. */ +function resolveTargets(command: string): string[] { + const argv = command.replace(/^veryfront\s+/, "").split(/\s+/).filter(Boolean); + const parsed = parseInstallArgs(parseCliArgs(argv)); + + assert(parsed.success, `\`${command}\` failed argument validation`); + if (parsed.data.target === undefined) return []; + + return parseTargetFlag(parsed.data.target); +} + +describe("docs: veryfront install commands", () => { + it("every documented install command selects a target non-interactively", async () => { + const documented = await collectDocumentedInstalls(); + assert(documented.length > 0, "expected the docs to document `veryfront install`"); + + const ignored = documented.filter(({ command }) => resolveTargets(command).length === 0); + + assertEquals( + ignored.map(({ file, command }) => `${file}: ${command}`), + [], + "these documented commands pass a target the CLI ignores; use `--target `", + ); + }); + + it("the pages that promise AGENTS.md document a command that writes AGENTS.md", async () => { + const pages = ["docs/getting-started/installation.md", "docs/guides/coding-agents.md"]; + + for (const page of pages) { + const source = await Deno.readTextFile(page); + assert(source.includes("AGENTS.md"), `${page} should describe AGENTS.md`); + + const files = extractInstallCommands(page, source) + .flatMap(({ command }) => resolveTargets(command)) + .map((id) => getToolById(id).file); + + assert( + files.includes("AGENTS.md"), + `${page} promises AGENTS.md but documents no install command that writes it (writes: ${ + files.join(", ") || "nothing" + })`, + ); + } + }); +}); diff --git a/tests/docs/guide-code-examples.test.ts b/tests/docs/guide-code-examples.test.ts index e0f43edbb1..ddf87de428 100644 --- a/tests/docs/guide-code-examples.test.ts +++ b/tests/docs/guide-code-examples.test.ts @@ -925,7 +925,7 @@ describe("Guide: installation.md", () => { "yarn global add veryfront", "bun add -g veryfront", "npx veryfront@latest", - "veryfront install agents", + "veryfront install --target agents", "veryfront --version", ]; diff --git a/tests/docs/guide-contracts.test.ts b/tests/docs/guide-contracts.test.ts index 20b493edce..071e63f19a 100644 --- a/tests/docs/guide-contracts.test.ts +++ b/tests/docs/guide-contracts.test.ts @@ -635,7 +635,7 @@ const GUIDE_CONTRACTS: Record = { "npm create veryfront", "npm install -g veryfront", "npx veryfront@latest", - "veryfront install agents", + "veryfront install --target agents", ], }, "getting-started/create-agent.md": { From 7357d32af756d28e18f64c6f76b7da818ffc3dc7 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Tue, 11 Aug 2026 09:06:17 +0200 Subject: [PATCH 2/2] docs: qualify the flagless install picker for non-TTY shells Review follow-up. multiSelect returns the auto-detected selections immediately when stdout is not a TTY (cli/ui/components/multi-select.ts), so `veryfront install` with no --target never prompts in CI, behind a pipe, or from a coding agent; it installs whatever detect.ts suggested, which for a project with nothing to detect is SKILL.md. That is the exact trap this PR documents, so the new sentence has to say so. Also narrows the test docstring to name the three published guide dirs it actually scans, matching guide-contracts.test.ts and guide-code-examples.test.ts. --- docs/guides/coding-agents.md | 5 ++++- tests/docs/cli-install-commands.test.ts | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/guides/coding-agents.md b/docs/guides/coding-agents.md index 6d186b45fe..69102e186d 100644 --- a/docs/guides/coding-agents.md +++ b/docs/guides/coding-agents.md @@ -39,7 +39,10 @@ veryfront install --target windsurf ``` Running `veryfront install` without `--target` opens an interactive picker -instead, preselecting the tools it detects in the project. +instead, preselecting the tools it detects in the project. Without a TTY (in CI, +behind a pipe, or from a coding agent) there is no prompt: the detected tools +are installed immediately, and a project with nothing to detect gets `SKILL.md`. +Always pass `--target` in non-interactive environments. Use `AGENTS.md` as the shared source of truth when multiple coding agents work in the same project. diff --git a/tests/docs/cli-install-commands.test.ts b/tests/docs/cli-install-commands.test.ts index 980df83bde..7a7fea7476 100644 --- a/tests/docs/cli-install-commands.test.ts +++ b/tests/docs/cli-install-commands.test.ts @@ -1,6 +1,11 @@ /** * Docs contract: every `veryfront install ...` command printed in the published - * docs must actually select the AI-tool target the surrounding prose promises. + * guide set must actually select the AI-tool target the surrounding prose + * promises. "Published" means the same three directories the sibling docs + * contracts scan (`guide-contracts.test.ts`, `guide-code-examples.test.ts`): + * getting-started, guides, concepts. Generated pages (`docs/api-reference`) and + * unpublished notes (`docs/internal`, `docs/rfcs`, `docs/evidence`) are out of + * scope — they may quote a broken invocation deliberately. * * The install command reads its target from `--target` only. A bare positional * (`veryfront install agents`) is silently ignored and the command falls back to