diff --git a/.changeset/scaffold-agents-md-wired-dirs.md b/.changeset/scaffold-agents-md-wired-dirs.md new file mode 100644 index 00000000000..e60b6107b6c --- /dev/null +++ b/.changeset/scaffold-agents-md-wired-dirs.md @@ -0,0 +1,12 @@ +--- +'create-objectstack': patch +--- + +fix(create-objectstack): the scaffolded AGENTS.md lists only the `src/` directories the project's `objectstack.config.ts` loads + +Clause-②: no +Metadata-protocol: none + +The "Project Structure" block of the `AGENTS.md` a new project gets named `src/reports/`, `src/agents/`, `src/i18n/` and `src/handlers/`, which the scaffolded `objectstack.config.ts` never imports, so metadata a coding agent wrote there was never loaded and `npm run validate` still passed. It left out `src/skills/` and `src/picklists/`, which the config does load. The block now lists exactly the eight wired directories (objects, views, actions, flows, dashboards, apps, skills, picklists) and says that a new `src/` directory is never loaded or validated until its barrel is imported in `objectstack.config.ts`. What the config loads is unchanged. + +An existing project keeps the `AGENTS.md` it was created with; to adopt the fix, edit its Project Structure block to match the directories its own `objectstack.config.ts` imports. diff --git a/packages/create-objectstack/src/agents-project-structure.test.ts b/packages/create-objectstack/src/agents-project-structure.test.ts new file mode 100644 index 00000000000..445f8dbc170 --- /dev/null +++ b/packages/create-objectstack/src/agents-project-structure.test.ts @@ -0,0 +1,69 @@ +// Copyright (c) 2026 ObjectStack contributors. Apache-2.0 license. +// +// The scaffolded AGENTS.md is the first thing a coding agent reads in a new +// project, and its "Project Structure" block tells it where each kind of +// metadata goes. A directory that `objectstack.config.ts` does not import is +// never loaded, and `objectstack validate` neither counts nor checks what it +// holds — so a block that names such a directory sends the agent's work +// somewhere it silently does nothing, and validate still passes. This pins the +// block's `src/` directories to the barrels each bundled template's config +// imports, both ways: no directory the config never loads, none it loads left +// out. + +import { describe, it, expect } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { TEMPLATES } from './template-registry.js'; + +const templatesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'templates'); +const AGENTS_MD = fs.readFileSync(path.join(templatesDir, 'AGENTS.md'), 'utf8'); + +/** The `src/` directories the "Project Structure" tree names, in order. */ +function projectStructureDirs(markdown: string): string[] { + const section = markdown.split(/^## Project Structure$/m)[1]; + if (section === undefined) throw new Error('AGENTS.md has no "## Project Structure" section'); + const tree = /```\n([\s\S]*?)\n```/.exec(section)?.[1]; + if (tree === undefined) throw new Error('the Project Structure section has no fenced tree'); + const lines = tree.split('\n'); + const srcAt = lines.findIndex((l) => /[├└]── src\/\s*$/.test(l)); + if (srcAt === -1) throw new Error('the Project Structure tree has no src/ entry'); + const dirs: string[] = []; + for (const line of lines.slice(srcAt + 1)) { + const m = /^│\s+[├└]── ([a-z0-9_-]+)\/\s/.exec(line); + if (!m) break; + dirs.push(m[1]); + } + return dirs; +} + +/** The `./src/