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
21 changes: 12 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@

- Skills live in `skills/<skill-name>/SKILL.md`.
- If a request matches a skill, read and follow that skill before implementing changes.
- `skills/rebrand/SKILL.md` — Rebrand the boilerplate into a new project using a new site title and description.
- `skills/hidden-admin-auth/SKILL.md` — Configure the private section as a hidden admin utility and rotate hardcoded default credentials safely.
- `skills/migrate-design-system-to-shadcn/SKILL.md` — Migrate UI from Chakra to shadcn/ui and fully remove Chakra dependencies.
- `skills/add-form-manager/SKILL.md` — Introduce React Hook Form + Zod with typed, reusable form validation patterns.
- `skills/playwright-migration/SKILL.md` — Migrate the E2E stack from Cypress to Playwright in a controlled, test-first rollout.
- `skills/policy-guide/SKILL.md` — Maintain the combined policy-writing guide page and keep routes/tests/docs aligned.
- `skills/add-redirect/SKILL.md` — Add or update URL redirects with status-code guidance and E2E coverage.
- `skills/migrate-api-to-tanstack-query/SKILL.md` — Migrate REST client usage to TanStack Query with caching and staged rollout.
- `skills/migrate-api-to-graphql-client/SKILL.md` — Migrate REST client usage to a GraphQL client path with incremental parity checks.
<!-- SKILLS_INDEX_START -->
- `skills/add-form-manager/SKILL.md` — Introduce React Hook Form + Zod for form state, validation, and typed submission flows.
- `skills/add-redirect/SKILL.md` — Add or update URL redirects in the server redirect registry, with status-code rationale and E2E coverage.
- `skills/hidden-admin-auth/SKILL.md` — Configure the private route as a hidden admin utility. Moves credentials to env vars; operator updates .env using .envTemplate instructions. No credentials passed to the skill.
- `skills/migrate-api-to-graphql-client/SKILL.md` — Migrate the REST client path to a GraphQL client architecture with typed operations and incremental rollout.
- `skills/migrate-api-to-tanstack-query/SKILL.md` — Migrate the current REST API usage to TanStack Query with server-state caching, invalidation, and progressive rollout.
- `skills/migrate-design-system-to-shadcn/SKILL.md` — Migrate the client UI layer from Chakra UI to shadcn/ui and fully remove Chakra dependencies.
- `skills/playwright-migration/SKILL.md` — Migrate this repository from Cypress E2E testing to Playwright with minimal disruption and clear validation evidence.
- `skills/policy-guide/SKILL.md` — Update the combined policy-writing guide page and keep linked routes, metadata, tests, and docs in sync.
- `skills/rebrand/SKILL.md` — Use this immediately after cloning the boilerplate to convert it into a new project identity. Trigger whenever a developer provides a new site title/description and wants boilerplate branding removed from centralized metadata (`PageMeta`), docs, and shared public/private header branding.
<!-- SKILLS_INDEX_END -->

## Development Setup

Expand All @@ -30,6 +32,7 @@
- `npm run dev` — development server (Express + Vite HMR)
- `npm run lint` / `npm run lint:fix` — ESLint
- `npm run type-check` — TypeScript type verification
- `npm run gen:skills-index` — regenerate synchronized skill references in README/AGENTS
- `npm run build` — production build
- `npm run test` — Full suite: lint + type-check + E2E
- `npm run test:e2e` — Cypress E2E tests (dev server must be running)
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added `scripts/generate-skills-index.mjs` and `npm run gen:skills-index` to keep README and AGENTS skills references synchronized.
- Added shared `LoadingFallback` and `BackHomeCta` UI components to reduce repeated loading/CTA markup.
- Added `skills/migrate-design-system-to-shadcn/SKILL.md` to guide full Chakra-to-shadcn migration with explicit removal criteria.
- Added `skills/add-form-manager/SKILL.md` to standardize React Hook Form + Zod adoption, starting with login-form migration guidance.
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ For script implementation details (like i18n validation), see [scripts/README.md
- [AGENTS.md](AGENTS.md) — Project operating instructions and coding expectations
- [.cursor/rules/](.cursor/rules/) — Enforced rule files for React, Redux, and server patterns
- Skills:
- [skills/rebrand/SKILL.md](skills/rebrand/SKILL.md)
- [skills/hidden-admin-auth/SKILL.md](skills/hidden-admin-auth/SKILL.md)
- [skills/playwright-migration/SKILL.md](skills/playwright-migration/SKILL.md)
- [skills/policy-guide/SKILL.md](skills/policy-guide/SKILL.md)
- [skills/add-redirect/SKILL.md](skills/add-redirect/SKILL.md)
<!-- SKILLS_INDEX_START -->
- [skills/add-form-manager/SKILL.md](skills/add-form-manager/SKILL.md) — Introduce React Hook Form + Zod for form state, validation, and typed submission flows.
- [skills/add-redirect/SKILL.md](skills/add-redirect/SKILL.md) — Add or update URL redirects in the server redirect registry, with status-code rationale and E2E coverage.
- [skills/hidden-admin-auth/SKILL.md](skills/hidden-admin-auth/SKILL.md) — Configure the private route as a hidden admin utility. Moves credentials to env vars; operator updates .env using .envTemplate instructions. No credentials passed to the skill.
- [skills/migrate-api-to-graphql-client/SKILL.md](skills/migrate-api-to-graphql-client/SKILL.md) — Migrate the REST client path to a GraphQL client architecture with typed operations and incremental rollout.
- [skills/migrate-api-to-tanstack-query/SKILL.md](skills/migrate-api-to-tanstack-query/SKILL.md) — Migrate the current REST API usage to TanStack Query with server-state caching, invalidation, and progressive rollout.
- [skills/migrate-design-system-to-shadcn/SKILL.md](skills/migrate-design-system-to-shadcn/SKILL.md) — Migrate the client UI layer from Chakra UI to shadcn/ui and fully remove Chakra dependencies.
- [skills/playwright-migration/SKILL.md](skills/playwright-migration/SKILL.md) — Migrate this repository from Cypress E2E testing to Playwright with minimal disruption and clear validation evidence.
- [skills/policy-guide/SKILL.md](skills/policy-guide/SKILL.md) — Update the combined policy-writing guide page and keep linked routes, metadata, tests, and docs in sync.
- [skills/rebrand/SKILL.md](skills/rebrand/SKILL.md) — Use this immediately after cloning the boilerplate to convert it into a new project identity. Trigger whenever a developer provides a new site title/description and wants boilerplate branding removed from centralized metadata (`PageMeta`), docs, and shared public/private header branding.
<!-- SKILLS_INDEX_END -->

### Architecture and Implementation Docs

Expand Down
7 changes: 7 additions & 0 deletions docs/SCRIPTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ Validates i18n locale files (optional; only when fully translating all locales).

**When to use:** Before committing when changing UI strings, if using option 3. See [scripts/README.md](../scripts/README.md) and [docs/I18N.md](I18N.md) for details.

### `npm run gen:skills-index`
Regenerates the skills index blocks in `AGENTS.md` and `README.md`.
- Reads each `skills/*/SKILL.md` frontmatter description
- Rebuilds the marked skills sections in both files

**When to use:** After adding/removing/updating skills so references stay synchronized.

## Common Workflows

### Starting a new feature
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"build": "tsc -b && vite build",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"gen:skills-index": "node ./scripts/generate-skills-index.mjs",
"check:i18n": "node ./scripts/check-i18n-keys.mjs",
"preview": "vite preview",
"start": "NODE_ENV=production tsx src/server/main.ts",
Expand Down
16 changes: 16 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,19 @@ Part of the [i18n system](../docs/I18N.md). Validates that i18n locale files sta
On success, prints a confirmation. On failure, prints which keys are missing or extra and exits with code 1.

**When to run:** Only if using option 3 (full translation). Before committing when you add or change UI strings.

## generate-skills-index.mjs

Invoked via `npm run gen:skills-index`.

Synchronizes skill references in:

- `AGENTS.md`
- `README.md`

It reads all `skills/*/SKILL.md` files, extracts each frontmatter `description`, and rewrites the sections between:

- `<!-- SKILLS_INDEX_START -->`
- `<!-- SKILLS_INDEX_END -->`

**When to run:** After adding/removing/updating skills or editing a skill description.
90 changes: 90 additions & 0 deletions scripts/generate-skills-index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const workspaceRoot = path.resolve(__dirname, '..');
const skillsRoot = path.join(workspaceRoot, 'skills');

const SKILLS_START = '<!-- SKILLS_INDEX_START -->';
const SKILLS_END = '<!-- SKILLS_INDEX_END -->';

const parseFrontmatter = (content) => {
const match = content.match(/^---\n([\s\S]*?)\n---\n/);
if (!match) {
return {};
}

const fields = {};
const lines = match[1].split('\n');
for (const line of lines) {
const separator = line.indexOf(':');
if (separator < 0) {
continue;
}
const key = line.slice(0, separator).trim();
const value = line.slice(separator + 1).trim();
fields[key] = value;
}

return fields;
};

const collectSkills = () => {
const entries = fs.readdirSync(skillsRoot, { withFileTypes: true });
const skills = [];

for (const entry of entries) {
if (!entry.isDirectory()) {
continue;
}

const relativePath = `skills/${entry.name}/SKILL.md`;
const absolutePath = path.join(workspaceRoot, relativePath);
if (!fs.existsSync(absolutePath)) {
continue;
}

const content = fs.readFileSync(absolutePath, 'utf8');
const frontmatter = parseFrontmatter(content);
const description = typeof frontmatter.description === 'string' && frontmatter.description.length > 0
? frontmatter.description
: 'No description provided.';

skills.push({
path: relativePath,
description,
});
}

return skills.sort((a, b) => a.path.localeCompare(b.path));
};

const replaceBetweenMarkers = (content, replacement) => {
const startIndex = content.indexOf(SKILLS_START);
const endIndex = content.indexOf(SKILLS_END);

if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) {
throw new Error(`Missing or invalid skills markers (${SKILLS_START} / ${SKILLS_END}).`);
}

const before = content.slice(0, startIndex + SKILLS_START.length);
const after = content.slice(endIndex);
return `${before}\n${replacement}\n${after}`;
};

const skills = collectSkills();
const agentsLines = skills.map((skill) => `- \`${skill.path}\` — ${skill.description}`).join('\n');
const readmeLines = skills.map((skill) => ` - [${skill.path}](${skill.path}) — ${skill.description}`).join('\n');

const agentsPath = path.join(workspaceRoot, 'AGENTS.md');
const readmePath = path.join(workspaceRoot, 'README.md');

const agentsContent = fs.readFileSync(agentsPath, 'utf8');
const readmeContent = fs.readFileSync(readmePath, 'utf8');

fs.writeFileSync(agentsPath, replaceBetweenMarkers(agentsContent, agentsLines), 'utf8');
fs.writeFileSync(readmePath, replaceBetweenMarkers(readmeContent, readmeLines), 'utf8');

console.log(`Updated skills index in AGENTS.md and README.md with ${skills.length} skills.`);