From ab67c31e36b18e6502b6098c6251a32793e6bb25 Mon Sep 17 00:00:00 2001 From: iammukeshm Date: Fri, 9 Oct 2026 09:27:22 +0530 Subject: [PATCH] fix(template): ship the AI guides AGENTS.md points to, with an opt-out A project created with `fsh new` / `dotnet new fsh` included AGENTS.md but not CLAUDE.md, GEMINI.md or .agents/, so the guide pointed at files that did not exist (#1428). They now ship together by default; `--agents false` (template) / `--no-agents` (CLI) leaves out all four. AGENTS.md sections that only apply to this repo (branching/CI gates, the docs-repo rule, the CLI location) are wrapped in `` via a new Markdown conditional operation, so scaffolded projects get a guide without them. Also stop shipping superpowers/ (internal specs and audits) and a worktree-style `.git` file. Closes #1428 Co-Authored-By: Claude Opus 5.5 (1M context) --- .template.config/template.json | 37 +++++++++++++++++++++++++--- AGENTS.md | 9 ++++++- src/Tools/CLI/Commands/NewCommand.cs | 19 ++++++++++---- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/.template.config/template.json b/.template.config/template.json index f98401aff2..73dfe14905 100644 --- a/.template.config/template.json +++ b/.template.config/template.json @@ -35,6 +35,12 @@ "description": "Include the React admin + dashboard client apps (clients/).", "defaultValue": "true" }, + "agents": { + "type": "parameter", + "datatype": "bool", + "description": "Include the AI coding guides (AGENTS.md, CLAUDE.md, GEMINI.md and .agents/).", + "defaultValue": "true" + }, "skipRestore": { "type": "parameter", "datatype": "bool", @@ -112,6 +118,23 @@ "underscoreForm": { "identifier": "replace", "pattern": "\\.", "replacement": "_" }, "displayForm": { "identifier": "replace", "pattern": "[._-]+", "replacement": " " } }, + "SpecialCustomOperations": { + "**/*.md": { + "operations": [ + { + "type": "conditional", + "configuration": { + "if": [" +The `fsh` CLI (Spectre.Console) lives in `src/Tools/CLI`. + + ## Tech stack | Backend | | Frontend | | @@ -70,10 +73,12 @@ dotnet run --project src/Host/FSH.Starter.DbMigrator -- list-pending **Ports:** API 7030 (https)/5030 (http) · admin 5173 · dashboard 5174 · Postgres 5432 · pgAdmin 5050 · Valkey 6379 · RustFS 9000/9001 (S3 API/console). + ## Branching & PRs Single long-lived branch: **`main`** (the default) — there is **no `develop`**. Branch from and target `main`; stable releases are cut from `v*` tags. CI is split into path-scoped **Backend CI** (`src/**`) and **Frontend CI** (`clients/**`) workflows; branch protection requires only those two gate checks — never the individual jobs, which are skipped on the other side's PRs. + ## Golden rules (do not break) 1. **Module boundaries** — a module references another module only through its `.Contracts` project, never its runtime project. Enforced by `Architecture.Tests`. @@ -85,7 +90,9 @@ Single long-lived branch: **`main`** (the default) — there is **no `develop`** 7. **Propagate `CancellationToken`** into every EF/IO call; add as `= default` on public service methods. 8. **Every command handler + paginated query handler needs a validator** (`{Name}Validator`). Enforced by `Architecture.Tests`. 9. **Frontend: pass per-call data through `mutate(arg)`**, never via state the mutation callbacks close over (execute-time race). See `frontend/shared.md`. + 10. **Docs + changelog travel with the change** — a user-facing change (feature, endpoint, config, infra, breaking change) isn't done until the **separate docs repo** (`github.com/fullstackhero/docs`, the Astro site) is updated to match **and** a changelog entry is added (`src/content/docs/changelog/`). Don't let the docs drift from the code. + ## Rules index — read the relevant file before you work diff --git a/src/Tools/CLI/Commands/NewCommand.cs b/src/Tools/CLI/Commands/NewCommand.cs index cc9d36adb3..f973bbb973 100644 --- a/src/Tools/CLI/Commands/NewCommand.cs +++ b/src/Tools/CLI/Commands/NewCommand.cs @@ -28,6 +28,11 @@ public sealed class Settings : CommandSettings [DefaultValue(false)] public bool NoFrontend { get; init; } + [Description("Exclude the AI coding guides (AGENTS.md, CLAUDE.md, GEMINI.md, .agents/).")] + [CommandOption("--no-agents")] + [DefaultValue(false)] + public bool NoAgents { get; init; } + [Description("Skip 'npm install' for the React apps after scaffolding.")] [CommandOption("--skip-install")] [DefaultValue(false)] @@ -69,6 +74,8 @@ protected override async Task ExecuteAsync(CommandContext context, Settings bool frontend = await ResolveFrontendAsync(settings, cancellationToken).ConfigureAwait(false); + bool agents = !settings.NoAgents; + string output = settings.Output ?? Path.GetFullPath(name); // 2. Check for existing directory @@ -89,7 +96,7 @@ protected override async Task ExecuteAsync(CommandContext context, Settings } // 3. Print summary - PrintSummary(name, aspire, frontend, output, settings.DryRun); + PrintSummary(name, aspire, frontend, agents, output, settings.DryRun); if (settings.DryRun) { @@ -102,7 +109,7 @@ protected override async Task ExecuteAsync(CommandContext context, Settings return 1; // 5. Scaffold project - int result = await ScaffoldProjectAsync(name, aspire, frontend, output, cancellationToken).ConfigureAwait(false); + int result = await ScaffoldProjectAsync(name, aspire, frontend, agents, output, cancellationToken).ConfigureAwait(false); if (result != 0) { AnsiConsole.MarkupLine($"[{FshConstants.ErrorColor}]Scaffolding failed. Check the output above for errors.[/]"); @@ -179,7 +186,7 @@ private static async Task ResolveFrontendAsync(Settings settings, Cancella .ShowAsync(AnsiConsole.Console, cancellationToken).ConfigureAwait(false); } - private static void PrintSummary(string name, bool aspire, bool frontend, string output, bool dryRun) + private static void PrintSummary(string name, bool aspire, bool frontend, bool agents, string output, bool dryRun) { AnsiConsole.WriteLine(); @@ -187,6 +194,7 @@ private static void PrintSummary(string name, bool aspire, bool frontend, string AnsiConsole.MarkupLine($"[bold]Creating project:[/] {name.EscapeMarkup()}{mode}"); AnsiConsole.MarkupLine($" [{FshConstants.DimColor}]Aspire:[/] {(aspire ? "yes" : "no")}"); AnsiConsole.MarkupLine($" [{FshConstants.DimColor}]Frontend:[/] {(frontend ? "yes (admin + dashboard)" : "no")}"); + AnsiConsole.MarkupLine($" [{FshConstants.DimColor}]AI guides:[/] {(agents ? "yes (AGENTS.md, CLAUDE.md, GEMINI.md, .agents/)" : "no")}"); AnsiConsole.MarkupLine($" [{FshConstants.DimColor}]Output:[/] {output.EscapeMarkup()}"); AnsiConsole.WriteLine(); } @@ -224,7 +232,7 @@ await ProcessRunner.RunAsync( } private static async Task ScaffoldProjectAsync( - string name, bool aspire, bool frontend, string output, CancellationToken cancellationToken) + string name, bool aspire, bool frontend, bool agents, string output, CancellationToken cancellationToken) { return await AnsiConsole.Status() .Spinner(Spinner.Known.Dots) @@ -233,7 +241,8 @@ private static async Task ScaffoldProjectAsync( { string aspireFlag = aspire ? "true" : "false"; string frontendFlag = frontend ? "true" : "false"; - string args = $"new {FshConstants.TemplateShortName} -n {name} -o \"{output}\" --aspire {aspireFlag} --frontend {frontendFlag} --force"; + string agentsFlag = agents ? "true" : "false"; + string args = $"new {FshConstants.TemplateShortName} -n {name} -o \"{output}\" --aspire {aspireFlag} --frontend {frontendFlag} --agents {agentsFlag} --force"; await ProcessRunner.RunAsync("dotnet", args, showOutput: false, cancellationToken: cancellationToken) .ConfigureAwait(false);