diff --git a/.env b/.env index dbf0a5eb177..6330f63e9ba 100644 --- a/.env +++ b/.env @@ -1,6 +1,6 @@ # Production Build -BUILD_GRID_VERSION=35.1.0-beta.20260315.2039 -BUILD_CHARTS_VERSION=13.1.0-beta.20260315 +BUILD_GRID_VERSION=35.1.0-beta.20260316.108 +BUILD_CHARTS_VERSION=13.1.0-beta.20260316 ENV=local NX_BATCH_MODE=true NX_ADD_PLUGINS=false diff --git a/.gitignore b/.gitignore index c4d2845583d..9bdba2b235f 100644 --- a/.gitignore +++ b/.gitignore @@ -143,5 +143,4 @@ documentation/ag-grid-docs/test-results/.last-run.json **/WARP.md **/modular-mcp.json !.rulesync/.aiignore -.rulesync/skills/rulesync /.junie/ diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml index 65a65c8407d..c227d967f2b 100644 --- a/.idea/jsLibraryMappings.xml +++ b/.idea/jsLibraryMappings.xml @@ -8,7 +8,6 @@ - @@ -19,10 +18,12 @@ + + \ No newline at end of file diff --git a/.rulesync/README.md b/.rulesync/README.md index 264df094809..fa2d2022163 100644 --- a/.rulesync/README.md +++ b/.rulesync/README.md @@ -34,7 +34,6 @@ Quick-reference for all AI agent commands, skills, sub-agents, and rules availab | Skill | 🟢 `dev-server` | `/dev-server` | Start dev server, check build status | | Skill | 🔵 `git-conventions` | `/git-conventions` | Branch, commit, and PR naming conventions | | Skill | 🟢 `technology-stack` | `/technology-stack` | Architecture constraints and zero-dependency rules | -| Agent | 🔵 `code-reviewer` | Auto (after edits) | Quality, security, and maintainability review | ## Testing and Quality @@ -46,12 +45,21 @@ Quick-reference for all AI agent commands, skills, sub-agents, and rules availab | Command | 🟢 `/manual-test` | `/manual-test ` | Create a manual test project from a docs example or plunker URL | | Agent | 🔵 `playwright-expert` | Auto | Playwright test architecture and debugging | +## Documentation and Examples + +| Type | Name | Invoke | What it does | +| ----- | ------------------ | ---------------- | -------------------------------------------------------- | +| Skill | 🔵 `example` | `/example` | AG Charts/Grid/Studio example conventions and patterns | +| Skill | 🔵 `website-astro` | `/website-astro` | Astro page patterns, content collections, and components | +| Skill | 🔵 `website-css` | `/website-css` | CSS architecture, design tokens, and styling patterns | + ## Planning and Analysis | Type | Name | Invoke | What it does | | ----- | ------------------------------- | ------------------------------------ | ------------------------------------------------ | | Skill | 🔵 `plan-review` | `/plan-review` (user) | Review plans for completeness and correctness | | Skill | 🔵 `plan-implementation-review` | `/plan-implementation-review` (user) | Review plan execution, identify delivery gaps | +| Skill | 🔵 `jira` | `/jira` | Create, estimate, or analyse JIRA tickets | | Agent | 🔵 `nx-expert` | Auto | Nx monorepo configuration and build optimisation | ## Prompt Hygiene @@ -59,6 +67,7 @@ Quick-reference for all AI agent commands, skills, sub-agents, and rules availab | Type | Name | Invoke | What it does | | ----- | --------------------- | -------------------------- | ------------------------------------------------ | | Skill | 🔵 `validate-prompts` | `/validate-prompts` (user) | Validate prompt file references for path hygiene | +| Skill | 🔵 `rulesync` | `/rulesync` | Configure AI/agentic tooling via `.rulesync/` | ## Memory @@ -76,12 +85,13 @@ Quick-reference for all AI agent commands, skills, sub-agents, and rules availab ## Git and Branch Management -| Type | Name | Invoke | What it does | -| ----- | ----------------------- | ---------------------------- | ---------------------------------------- | -| Skill | 🔵 `sync-ag-shared` | `/sync-ag-shared` (user) | Sync ag-shared subrepo across AG repos | -| Skill | 🔵 `git-worktree-clean` | `/git-worktree-clean` (user) | Hard-reset worktree to `origin/latest` | -| Skill | 🔵 `git-split` | `/git-split` (user) | Split large files preserving git history | -| Skill | 🔵 `pr-split` | `/pr-split` (user) | Split a branch into stacked PRs | +| Type | Name | Invoke | What it does | +| ----- | ----------------------- | ---------------------------- | -------------------------------------------- | +| Skill | 🔵 `sync-ag-shared` | `/sync-ag-shared` (user) | Sync ag-shared subrepo across AG repos | +| Skill | 🔵 `git-worktree-clean` | `/git-worktree-clean` (user) | Hard-reset worktree to `origin/latest` | +| Skill | 🔵 `git-split` | `/git-split` (user) | Split large files preserving git history | +| Skill | 🔵 `pr-split` | `/pr-split` (user) | Split a branch into stacked PRs | +| Skill | 🔵 `ag-shared-sync-log` | `/ag-shared-sync-log` (user) | Generate migration log for ag-shared changes | --- @@ -123,14 +133,17 @@ Skills load on-demand when invoked. All skills are invoked via `/skill-name`. Al | Skill | Description | | ------------------------------- | ----------------------------------------------------------- | +| 🔵 `ag-shared-sync-log` | Generate migration log entries for ag-shared changes | | 🔵 `batch-lint-cleanup` | Auto-fix ESLint violations by rule | | 🔵 `code-cleanup` | Remove bloat, duplication; improve clarity | | 🔵 `code-fixup` | Fix build and lint errors across a package | | 🟢 `dev-server` | Start dev server, check build status | +| 🔵 `example` | AG Charts/Grid/Studio example conventions and patterns | | 🔵 `git-bisect` | Find the commit that introduced a regression | | 🔵 `git-conventions` | Branch, commit, and PR naming conventions | | 🔵 `git-split` | Split large files preserving git history | | 🔵 `git-worktree-clean` | Hard-reset worktree to `origin/latest` | +| 🔵 `jira` | Create, estimate, or analyse JIRA tickets | | 🔵 `plan-implementation-review` | Review plan execution, identify delivery gaps | | 🔵 `plan-review` | Review plans for completeness and correctness | | 🔵 `pr-create` | Commit, push, and open a PR | @@ -138,9 +151,12 @@ Skills load on-demand when invoked. All skills are invoked via `/skill-name`. Al | 🔵 `pr-split` | Split a branch into stacked PRs | | 🔵 `recall` | Load branch context, browse project memories | | 🔵 `remember` | Save branch context or project learnings as memory | +| 🔵 `rulesync` | Configure AI/agentic tooling via `.rulesync/` | | 🔵 `sync-ag-shared` | Sync ag-shared subrepo changes across AG repos | | 🟢 `technology-stack` | Architecture constraints and zero-dependency requirements | | 🔵 `validate-prompts` | Validate prompt file references for consistency and hygiene | +| 🔵 `website-astro` | Astro page patterns, content collections, and components | +| 🔵 `website-css` | CSS architecture, design tokens, and styling patterns | --- @@ -148,8 +164,7 @@ Skills load on-demand when invoked. All skills are invoked via `/skill-name`. Al Sub-agents are spawned automatically when the AI determines a task matches their speciality. They cannot be invoked directly. -| Agent | Description | -| ---------------------- | ------------------------------------------------------- | -| 🔵 `code-reviewer` | Reviews code for quality, security, and maintainability | -| 🔵 `nx-expert` | Nx monorepo configuration and build optimisation | -| 🔵 `playwright-expert` | Playwright E2E test architecture and debugging | +| Agent | Description | +| ---------------------- | ------------------------------------------------ | +| 🔵 `nx-expert` | Nx monorepo configuration and build optimisation | +| 🔵 `playwright-expert` | Playwright E2E test architecture and debugging | diff --git a/.rulesync/mcp.json b/.rulesync/mcp.json deleted file mode 100644 index c9c44db88a8..00000000000 --- a/.rulesync/mcp.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "mcpServers": { - "sequential-thinking": { - "command": "yarn", - "args": ["run", "--silent", "mcp-server-sequential-thinking"], - "env": {} - } - } -} diff --git a/.rulesync/mcp.json b/.rulesync/mcp.json new file mode 120000 index 00000000000..457e6f80079 --- /dev/null +++ b/.rulesync/mcp.json @@ -0,0 +1 @@ +../external/ag-shared/prompts/.mcp.json \ No newline at end of file diff --git a/.rulesync/skills/ag-shared-sync-log b/.rulesync/skills/ag-shared-sync-log new file mode 120000 index 00000000000..c15b2b9eca9 --- /dev/null +++ b/.rulesync/skills/ag-shared-sync-log @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/ag-shared-sync-log/ \ No newline at end of file diff --git a/.rulesync/skills/example b/.rulesync/skills/example new file mode 120000 index 00000000000..97e07b5c6ca --- /dev/null +++ b/.rulesync/skills/example @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/example/ \ No newline at end of file diff --git a/.rulesync/skills/jira b/.rulesync/skills/jira new file mode 120000 index 00000000000..19fa44a6a17 --- /dev/null +++ b/.rulesync/skills/jira @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/jira/ \ No newline at end of file diff --git a/.rulesync/skills/rulesync b/.rulesync/skills/rulesync new file mode 120000 index 00000000000..92c0b40700c --- /dev/null +++ b/.rulesync/skills/rulesync @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/rulesync/ \ No newline at end of file diff --git a/.rulesync/skills/website-astro b/.rulesync/skills/website-astro new file mode 120000 index 00000000000..8139604e7d5 --- /dev/null +++ b/.rulesync/skills/website-astro @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/website-astro/ \ No newline at end of file diff --git a/.rulesync/skills/website-css b/.rulesync/skills/website-css new file mode 120000 index 00000000000..79826f585c7 --- /dev/null +++ b/.rulesync/skills/website-css @@ -0,0 +1 @@ +../../external/ag-shared/prompts/skills/website-css/ \ No newline at end of file diff --git a/.rulesync/subagents/code-reviewer.md b/.rulesync/subagents/code-reviewer.md deleted file mode 120000 index d5a26b894a4..00000000000 --- a/.rulesync/subagents/code-reviewer.md +++ /dev/null @@ -1 +0,0 @@ -../../external/ag-shared/prompts/agents/code-reviewer.md \ No newline at end of file diff --git a/.snyk b/.snyk index 6c3c934c592..3dbb3d10846 100644 --- a/.snyk +++ b/.snyk @@ -2,6 +2,52 @@ version: v1.25.1 # ignores vulnerabilities until expiry date; change duration by modifying expiry date ignore: + SNYK-JS-UNDICI-15518070: + - rulesync@7.0.0 > fastmcp@3.31.0 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:18:19.780Z + SNYK-JS-UNDICI-15518068: + - rulesync@7.0.0 > fastmcp@3.31.0 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:18:17.019Z + SNYK-JS-UNDICI-15518064: + - rulesync@7.0.0 > fastmcp@3.31.0 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:18:13.556Z + SNYK-JS-FLATTED-15518041: + - stylelint@16.25.0 > file-entry-cache@10.1.4 > flat-cache@6.1.18 > flatted@3.3.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:31.450Z + - eslint@9.39.1 > file-entry-cache@8.0.0 > flat-cache@4.0.1 > flatted@3.3.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:27.919Z + SNYK-JS-FILETYPE-15609896: + - rulesync@7.0.0 > fastmcp@3.31.0 > file-type@21.3.0: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:14.433Z + SNYK-JS-EXPRESSRATELIMIT-15440710: + - rulesync@7.0.0 > @modelcontextprotocol/sdk@1.26.0 > express-rate-limit@8.2.1: + reason: >- + Downstream dependency of mcp - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:06:58.808Z + - '@modelcontextprotocol/server-sequential-thinking@2025.12.18 > @modelcontextprotocol/sdk@1.26.0 > express-rate-limit@8.2.1': + reason: >- + Downstream dependency of mcp - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:06:57.894Z SNYK-JS-SVGO-15423912: - cssnano@7.1.2 > cssnano-preset-default@7.0.10 > postcss-svgo@7.1.0 > svgo@4.0.0: reason: >- diff --git a/AGENTS.md b/AGENTS.md index 2161df1996b..844a0245171 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ This file provides guidance to AI Agents when working with code in this reposito ### Quick Reference - **Main branch:** `latest` -- **Format:** `yarn nx format` (run before commits) +- **Format:** `yarn nx format --sort-root-tsconfig-paths=false` (run before commits) - **Type-check:** `yarn nx build:types ` (run before commits) - **Lint:** `yarn nx lint ` (run before commits) - **Build:** `yarn nx build ` @@ -27,7 +27,7 @@ This file provides guidance to AI Agents when working with code in this reposito - **Main constraint:** Community and enterprise runtime bundles stay dependency-free beyond AG Grid code. - **Default branch:** Target `latest`; follow release/JIRA naming conventions below for topic branches. - **Build monitoring:** Check `node_modules/.cache/ag-watch-status.json` to monitor watch state (`yarn nx dev`) and build health (see [Development Server Guide](.rulesync/rules/dev-server.md)). -- **Formatting:** Run `yarn nx format` from the repo root before proposing commits. +- **Formatting:** Run `yarn nx format --sort-root-tsconfig-paths=false` from the repo root before proposing commits. - **Typechecking:** Run `yarn nx build:types ` from the repo root before proposing commits. - **Linting:** Run `yarn nx lint ` from the repo root before proposing commits. - **Baseline verification:** Expect to run `yarn nx test ag-grid-community`, `yarn nx test ag-grid-enterprise`, and `yarn nx e2e ag-grid-docs` after meaningful grid changes. @@ -68,7 +68,7 @@ For detailed information about preferred technologies and architectural constrai - `yarn install` – install dependencies after cloning or when the Yarn lockfile changes. - `./external/ag-shared/scripts/install-for-cloud/install-for-cloud.sh` – install dependencies and tooling in a remote environment - use this in preference to `yarn install` to ensure all global tools are installed. - `yarn nx clean` – purge all dist folders when switching branches or before packaging releases. -- `yarn nx format` – format repo files; run from the project root before committing. +- `yarn nx format --sort-root-tsconfig-paths=false` – format repo files; run from the project root before committing. - `yarn nx build ` – compile a specific package after code edits. - `yarn nx build:types ` – regenerate declaration files when touching exported APIs. - `yarn nx build:package ` – create ESM/CJS bundles to validate publishable output. @@ -147,7 +147,7 @@ For code quality guidelines, see [Code Quality Guide](.rulesync/rules/code-quali Essential practices: -- Run `yarn nx format` before committing +- Run `yarn nx format --sort-root-tsconfig-paths=false` before committing - Self-review your changes before proposing commits - Ensure tests exercise real implementations, not test helpers diff --git a/community-modules/locale/package.json b/community-modules/locale/package.json index f7ebb8ca0aa..10c23da8bbd 100644 --- a/community-modules/locale/package.json +++ b/community-modules/locale/package.json @@ -1,6 +1,6 @@ { "name": "@ag-grid-community/locale", - "version": "35.1.0-beta.20260315.2039", + "version": "35.1.0-beta.20260316.108", "description": "Localisation Module for AG Grid, providing translations in 31 languages.", "main": "./dist/package/main.cjs.js", "types": "./dist/types/src/main.d.ts", diff --git a/community-modules/locale/src/ar-EG.ts b/community-modules/locale/src/ar-EG.ts index d3a313e1f05..d0fcb0b2de3 100644 --- a/community-modules/locale/src/ar-EG.ts +++ b/community-modules/locale/src/ar-EG.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_EG = { // Filter Buttons applyFilter: 'تطبيق', + applyColumnToolPanel: 'تطبيق', + deferMode: 'Defer mode', resetFilter: 'إعادة تعيين', clearFilter: 'مسح', cancelFilter: 'إلغاء', + cancelColumnToolPanel: 'إلغاء', // Filter Titles textFilter: 'فلتر النص', diff --git a/community-modules/locale/src/bg-BG.ts b/community-modules/locale/src/bg-BG.ts index 60d65c48caf..954b19e8c3c 100644 --- a/community-modules/locale/src/bg-BG.ts +++ b/community-modules/locale/src/bg-BG.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_BG = { // Filter Buttons applyFilter: 'Приложи', + applyColumnToolPanel: 'Приложи', + deferMode: 'Defer mode', resetFilter: 'Нулиране', clearFilter: 'Изчисти', cancelFilter: 'Отказ', + cancelColumnToolPanel: 'Отказ', // Filter Titles textFilter: 'Филтър за текст', diff --git a/community-modules/locale/src/cs-CZ.ts b/community-modules/locale/src/cs-CZ.ts index a3e9db632f7..74458a0479f 100644 --- a/community-modules/locale/src/cs-CZ.ts +++ b/community-modules/locale/src/cs-CZ.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_CZ = { // Filter Buttons applyFilter: 'Použít', + applyColumnToolPanel: 'Použít', + deferMode: 'Defer mode', resetFilter: 'Obnovit', clearFilter: 'Vymazat', cancelFilter: 'Zrušit', + cancelColumnToolPanel: 'Zrušit', // Filter Titles textFilter: 'Textový filtr', diff --git a/community-modules/locale/src/da-DK.ts b/community-modules/locale/src/da-DK.ts index 8f9d1f5df04..869ba27f590 100644 --- a/community-modules/locale/src/da-DK.ts +++ b/community-modules/locale/src/da-DK.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_DK = { // Filter Buttons applyFilter: 'Anvend', + applyColumnToolPanel: 'Anvend', + deferMode: 'Defer mode', resetFilter: 'Nulstil', clearFilter: 'Ryd', cancelFilter: 'Annuller', + cancelColumnToolPanel: 'Annuller', // Filter Titles textFilter: 'Tekstfilter', diff --git a/community-modules/locale/src/de-DE.ts b/community-modules/locale/src/de-DE.ts index 619764a7bc7..d94d0bb79b5 100644 --- a/community-modules/locale/src/de-DE.ts +++ b/community-modules/locale/src/de-DE.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_DE = { // Filter Buttons applyFilter: 'Anwenden', + applyColumnToolPanel: 'Anwenden', + deferMode: 'Defer mode', resetFilter: 'Zurücksetzen', clearFilter: 'Löschen', cancelFilter: 'Abbrechen', + cancelColumnToolPanel: 'Abbrechen', // Filter Titles textFilter: 'Textfilter', diff --git a/community-modules/locale/src/el-GR.ts b/community-modules/locale/src/el-GR.ts index 4bd3c5d476f..059d703bdfe 100644 --- a/community-modules/locale/src/el-GR.ts +++ b/community-modules/locale/src/el-GR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_GR = { // Filter Buttons applyFilter: 'Εφαρμογή', + applyColumnToolPanel: 'Εφαρμογή', + deferMode: 'Defer mode', resetFilter: 'Επαναφορά', clearFilter: 'Εκκαθάριση', cancelFilter: 'Ακύρωση', + cancelColumnToolPanel: 'Ακύρωση', // Filter Titles textFilter: 'Φίλτρο Κειμένου', diff --git a/community-modules/locale/src/en-US.ts b/community-modules/locale/src/en-US.ts index c55ba889da7..523b3cd0ecc 100644 --- a/community-modules/locale/src/en-US.ts +++ b/community-modules/locale/src/en-US.ts @@ -65,9 +65,12 @@ export const AG_GRID_LOCALE_EN = { // Filter Buttons applyFilter: 'Apply', + applyColumnToolPanel: 'Apply', + deferMode: 'Defer mode', resetFilter: 'Reset', clearFilter: 'Clear', cancelFilter: 'Cancel', + cancelColumnToolPanel: 'Cancel', // Filter Titles textFilter: 'Text Filter', diff --git a/community-modules/locale/src/es-ES.ts b/community-modules/locale/src/es-ES.ts index de285bb6ea7..ecd8705698f 100644 --- a/community-modules/locale/src/es-ES.ts +++ b/community-modules/locale/src/es-ES.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_ES = { // Filter Buttons applyFilter: 'Aplicar', + applyColumnToolPanel: 'Aplicar', + deferMode: 'Defer mode', resetFilter: 'Reiniciar', clearFilter: 'Borrar', cancelFilter: 'Cancelar', + cancelColumnToolPanel: 'Cancelar', // Filter Titles textFilter: 'Filtro de Texto', diff --git a/community-modules/locale/src/fa-IR.ts b/community-modules/locale/src/fa-IR.ts index b6a7535c935..5622e3b77d2 100644 --- a/community-modules/locale/src/fa-IR.ts +++ b/community-modules/locale/src/fa-IR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_IR = { // Filter Buttons applyFilter: 'اعمال', + applyColumnToolPanel: 'اعمال', + deferMode: 'Defer mode', resetFilter: 'بازنشانی', clearFilter: 'پاک کردن', cancelFilter: 'لغو', + cancelColumnToolPanel: 'لغو', // Filter Titles textFilter: 'فیلتر متنی', diff --git a/community-modules/locale/src/fi-FI.ts b/community-modules/locale/src/fi-FI.ts index 5eb88efed45..f81ea00d0d5 100644 --- a/community-modules/locale/src/fi-FI.ts +++ b/community-modules/locale/src/fi-FI.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_FI = { // Filter Buttons applyFilter: 'Käytä', + applyColumnToolPanel: 'Käytä', + deferMode: 'Defer mode', resetFilter: 'Nollaa', clearFilter: 'Tyhjennä', cancelFilter: 'Peruuta', + cancelColumnToolPanel: 'Peruuta', // Filter Titles textFilter: 'Tekstisuodatin', diff --git a/community-modules/locale/src/fr-FR.ts b/community-modules/locale/src/fr-FR.ts index fe21dcbd70f..dc12f0c653d 100644 --- a/community-modules/locale/src/fr-FR.ts +++ b/community-modules/locale/src/fr-FR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_FR = { // Filter Buttons applyFilter: 'Appliquer', + applyColumnToolPanel: 'Appliquer', + deferMode: 'Defer mode', resetFilter: 'Réinitialiser', clearFilter: 'Effacer', cancelFilter: 'Annuler', + cancelColumnToolPanel: 'Annuler', // Filter Titles textFilter: 'Filtre de texte', diff --git a/community-modules/locale/src/he-IL.ts b/community-modules/locale/src/he-IL.ts index 151cea837b2..a3a11b5326d 100644 --- a/community-modules/locale/src/he-IL.ts +++ b/community-modules/locale/src/he-IL.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_IL = { // Filter Buttons applyFilter: 'החל', + applyColumnToolPanel: 'החל', + deferMode: 'Defer mode', resetFilter: 'אפס', clearFilter: 'נקה', cancelFilter: 'בטל', + cancelColumnToolPanel: 'בטל', // Filter Titles textFilter: 'מסנן טקסט', diff --git a/community-modules/locale/src/hr-HR.ts b/community-modules/locale/src/hr-HR.ts index 14c0f68b72c..9fda8d73db8 100644 --- a/community-modules/locale/src/hr-HR.ts +++ b/community-modules/locale/src/hr-HR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_HR = { // Filter Buttons applyFilter: 'Primijeni', + applyColumnToolPanel: 'Primijeni', + deferMode: 'Defer mode', resetFilter: 'Poništi', clearFilter: 'Očisti', cancelFilter: 'Odustani', + cancelColumnToolPanel: 'Odustani', // Filter Titles textFilter: 'Tekstualni filter', diff --git a/community-modules/locale/src/hu-HU.ts b/community-modules/locale/src/hu-HU.ts index 17faff71813..48c096620d4 100644 --- a/community-modules/locale/src/hu-HU.ts +++ b/community-modules/locale/src/hu-HU.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_HU = { // Filter Buttons applyFilter: 'Alkalmaz', + applyColumnToolPanel: 'Alkalmaz', + deferMode: 'Defer mode', resetFilter: 'Visszaállítás', clearFilter: 'Törlés', cancelFilter: 'Mégse', + cancelColumnToolPanel: 'Mégse', // Filter Titles textFilter: 'Szövegszűrő', diff --git a/community-modules/locale/src/it-IT.ts b/community-modules/locale/src/it-IT.ts index 05869a628f4..b50d2a1b048 100644 --- a/community-modules/locale/src/it-IT.ts +++ b/community-modules/locale/src/it-IT.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_IT = { // Filter Buttons applyFilter: 'Applica', + applyColumnToolPanel: 'Applica', + deferMode: 'Defer mode', resetFilter: 'Reimposta', clearFilter: 'Pulisci', cancelFilter: 'Annulla', + cancelColumnToolPanel: 'Annulla', // Filter Titles textFilter: 'Filtro di testo', diff --git a/community-modules/locale/src/ja-JP.ts b/community-modules/locale/src/ja-JP.ts index 0d8c9f538ed..65ea89b8b4f 100644 --- a/community-modules/locale/src/ja-JP.ts +++ b/community-modules/locale/src/ja-JP.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_JP = { // Filter Buttons applyFilter: '適用', + applyColumnToolPanel: '適用', + deferMode: 'Defer mode', resetFilter: 'リセット', clearFilter: 'クリア', cancelFilter: 'キャンセル', + cancelColumnToolPanel: 'キャンセル', // Filter Titles textFilter: 'テキストフィルター', diff --git a/community-modules/locale/src/ko-KR.ts b/community-modules/locale/src/ko-KR.ts index fab4c529082..6f16b32c16f 100644 --- a/community-modules/locale/src/ko-KR.ts +++ b/community-modules/locale/src/ko-KR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_KR = { // Filter Buttons applyFilter: '적용', + applyColumnToolPanel: '적용', + deferMode: 'Defer mode', resetFilter: '재설정', clearFilter: '지우기', cancelFilter: '취소', + cancelColumnToolPanel: '취소', // Filter Titles textFilter: '텍스트 필터', diff --git a/community-modules/locale/src/nb-NO.ts b/community-modules/locale/src/nb-NO.ts index 16da5224b9d..e16aa13deb5 100644 --- a/community-modules/locale/src/nb-NO.ts +++ b/community-modules/locale/src/nb-NO.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_NO = { // Filter Buttons applyFilter: 'Bruk', + applyColumnToolPanel: 'Bruk', + deferMode: 'Defer mode', resetFilter: 'Tilbakestill', clearFilter: 'Tøm', cancelFilter: 'Avbryt', + cancelColumnToolPanel: 'Avbryt', // Filter Titles textFilter: 'Tekstfilter', diff --git a/community-modules/locale/src/nl-NL.ts b/community-modules/locale/src/nl-NL.ts index e4a2412b10d..8bb49522b95 100644 --- a/community-modules/locale/src/nl-NL.ts +++ b/community-modules/locale/src/nl-NL.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_NL = { // Filter Buttons applyFilter: 'Toepassen', + applyColumnToolPanel: 'Toepassen', + deferMode: 'Defer mode', resetFilter: 'Resetten', clearFilter: 'Wissen', cancelFilter: 'Annuleren', + cancelColumnToolPanel: 'Annuleren', // Filter Titles textFilter: 'Tekstfilter', diff --git a/community-modules/locale/src/pl-PL.ts b/community-modules/locale/src/pl-PL.ts index 5876e3b9709..35719892611 100644 --- a/community-modules/locale/src/pl-PL.ts +++ b/community-modules/locale/src/pl-PL.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_PL = { // Filter Buttons applyFilter: 'Zastosuj', + applyColumnToolPanel: 'Zastosuj', + deferMode: 'Defer mode', resetFilter: 'Resetuj', clearFilter: 'Wyczyść', cancelFilter: 'Anuluj', + cancelColumnToolPanel: 'Anuluj', // Filter Titles textFilter: 'Filtr tekstu', diff --git a/community-modules/locale/src/pt-BR.ts b/community-modules/locale/src/pt-BR.ts index 1398fbfea30..00c66a3c465 100644 --- a/community-modules/locale/src/pt-BR.ts +++ b/community-modules/locale/src/pt-BR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_BR = { // Filter Buttons applyFilter: 'Aplicar', + applyColumnToolPanel: 'Aplicar', + deferMode: 'Defer mode', resetFilter: 'Redefinir', clearFilter: 'Limpar', cancelFilter: 'Cancelar', + cancelColumnToolPanel: 'Cancelar', // Filter Titles textFilter: 'Filtro de Texto', diff --git a/community-modules/locale/src/pt-PT.ts b/community-modules/locale/src/pt-PT.ts index fa28e8fad23..10fdf6ecdd5 100644 --- a/community-modules/locale/src/pt-PT.ts +++ b/community-modules/locale/src/pt-PT.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_PT = { // Filter Buttons applyFilter: 'Aplicar', + applyColumnToolPanel: 'Aplicar', + deferMode: 'Defer mode', resetFilter: 'Redefinir', clearFilter: 'Limpar', cancelFilter: 'Cancelar', + cancelColumnToolPanel: 'Cancelar', // Filter Titles textFilter: 'Filtro de Texto', diff --git a/community-modules/locale/src/ro-RO.ts b/community-modules/locale/src/ro-RO.ts index ec615ef7c6c..d7d519e1324 100644 --- a/community-modules/locale/src/ro-RO.ts +++ b/community-modules/locale/src/ro-RO.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_RO = { // Filter Buttons applyFilter: 'Aplică', + applyColumnToolPanel: 'Aplică', + deferMode: 'Defer mode', resetFilter: 'Resetează', clearFilter: 'Curăță', cancelFilter: 'Anulează', + cancelColumnToolPanel: 'Anulează', // Filter Titles textFilter: 'Filtru text', diff --git a/community-modules/locale/src/sk-SK.ts b/community-modules/locale/src/sk-SK.ts index 07255634690..89b6469e30a 100644 --- a/community-modules/locale/src/sk-SK.ts +++ b/community-modules/locale/src/sk-SK.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_SK = { // Filter Buttons applyFilter: 'Použiť', + applyColumnToolPanel: 'Použiť', + deferMode: 'Defer mode', resetFilter: 'Resetovať', clearFilter: 'Vyčistiť', cancelFilter: 'Zrušiť', + cancelColumnToolPanel: 'Zrušiť', // Filter Titles textFilter: 'Textový filter', diff --git a/community-modules/locale/src/sv-SE.ts b/community-modules/locale/src/sv-SE.ts index 8bf1a96eb53..0bae00b89cb 100644 --- a/community-modules/locale/src/sv-SE.ts +++ b/community-modules/locale/src/sv-SE.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_SE = { // Filter Buttons applyFilter: 'Verkställ', + applyColumnToolPanel: 'Verkställ', + deferMode: 'Defer mode', resetFilter: 'Återställ', clearFilter: 'Rensa', cancelFilter: 'Avbryt', + cancelColumnToolPanel: 'Avbryt', // Filter Titles textFilter: 'Textfilter', diff --git a/community-modules/locale/src/tr-TR.ts b/community-modules/locale/src/tr-TR.ts index cd409258218..0dafce9cbc9 100644 --- a/community-modules/locale/src/tr-TR.ts +++ b/community-modules/locale/src/tr-TR.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_TR = { // Filter Buttons applyFilter: 'Uygula', + applyColumnToolPanel: 'Uygula', + deferMode: 'Defer mode', resetFilter: 'Sıfırla', clearFilter: 'Temizle', cancelFilter: 'İptal', + cancelColumnToolPanel: 'İptal', // Filter Titles textFilter: 'Metin Filtresi', diff --git a/community-modules/locale/src/uk-UA.ts b/community-modules/locale/src/uk-UA.ts index e03467f8d48..0e4f21b53d3 100644 --- a/community-modules/locale/src/uk-UA.ts +++ b/community-modules/locale/src/uk-UA.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_UA = { // Filter Buttons applyFilter: 'Застосувати', + applyColumnToolPanel: 'Застосувати', + deferMode: 'Defer mode', resetFilter: 'Скинути', clearFilter: 'Очистити', cancelFilter: 'Скасувати', + cancelColumnToolPanel: 'Скасувати', // Filter Titles textFilter: 'Текстовий фільтр', diff --git a/community-modules/locale/src/ur-PK.ts b/community-modules/locale/src/ur-PK.ts index 7574ef4ea89..bca12db5672 100644 --- a/community-modules/locale/src/ur-PK.ts +++ b/community-modules/locale/src/ur-PK.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_PK = { // Filter Buttons applyFilter: 'لاگو کریں', + applyColumnToolPanel: 'لاگو کریں', + deferMode: 'Defer mode', resetFilter: 'ری سیٹ کریں', clearFilter: 'صاف کریں', cancelFilter: 'منسوخ کریں', + cancelColumnToolPanel: 'منسوخ کریں', // Filter Titles textFilter: 'متن فلٹر', diff --git a/community-modules/locale/src/vi-VN.ts b/community-modules/locale/src/vi-VN.ts index f04d534087f..96ad5664341 100644 --- a/community-modules/locale/src/vi-VN.ts +++ b/community-modules/locale/src/vi-VN.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_VN = { // Filter Buttons applyFilter: 'Áp dụng', + applyColumnToolPanel: 'Áp dụng', + deferMode: 'Defer mode', resetFilter: 'Đặt lại', clearFilter: 'Xóa', cancelFilter: 'Hủy', + cancelColumnToolPanel: 'Hủy', // Filter Titles textFilter: 'Bộ lọc văn bản', diff --git a/community-modules/locale/src/zh-CN.ts b/community-modules/locale/src/zh-CN.ts index 0a81442d11b..a236848c806 100644 --- a/community-modules/locale/src/zh-CN.ts +++ b/community-modules/locale/src/zh-CN.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_CN = { // Filter Buttons applyFilter: '应用', + applyColumnToolPanel: '应用', + deferMode: 'Defer mode', resetFilter: '重置', clearFilter: '清除', cancelFilter: '取消', + cancelColumnToolPanel: '取消', // Filter Titles textFilter: '文本过滤器', diff --git a/community-modules/locale/src/zh-HK.ts b/community-modules/locale/src/zh-HK.ts index 48195073252..69332932021 100644 --- a/community-modules/locale/src/zh-HK.ts +++ b/community-modules/locale/src/zh-HK.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_HK = { // Filter Buttons applyFilter: '應用', + applyColumnToolPanel: '應用', + deferMode: 'Defer mode', resetFilter: '重置', clearFilter: '清除', cancelFilter: '取消', + cancelColumnToolPanel: '取消', // Filter Titles textFilter: '文本篩選', diff --git a/community-modules/locale/src/zh-TW.ts b/community-modules/locale/src/zh-TW.ts index 5db4e444025..42d81580774 100644 --- a/community-modules/locale/src/zh-TW.ts +++ b/community-modules/locale/src/zh-TW.ts @@ -74,9 +74,12 @@ export const AG_GRID_LOCALE_TW = { // Filter Buttons applyFilter: '應用', + applyColumnToolPanel: '應用', + deferMode: 'Defer mode', resetFilter: '重置', clearFilter: '清除', cancelFilter: '取消', + cancelColumnToolPanel: '取消', // Filter Titles textFilter: '文字篩選', diff --git a/community-modules/styles/.snyk b/community-modules/styles/.snyk index 2fa22367df8..9e3df0db142 100644 --- a/community-modules/styles/.snyk +++ b/community-modules/styles/.snyk @@ -2,6 +2,23 @@ version: v1.25.1 ignore: + SNYK-JS-TAR-15456201: + - '@vusion/webfonts-generator@0.8.0 > ttf2woff2@4.0.5 > node-gyp@9.4.1 > make-fetch-happen@10.2.1 > cacache@16.1.3 > tar@6.2.1': + reason: >- + not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:55.759Z + - '@vusion/webfonts-generator@0.8.0 > ttf2woff2@4.0.5 > node-gyp@9.4.1 > tar@6.2.1': + reason: >- + not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:55.749Z + SNYK-JS-FLATTED-15518041: + - stylelint@14.9.1 > file-entry-cache@6.0.1 > flat-cache@3.2.0 > flatted@3.3.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:07:31.454Z SNYK-JS-UNDERSCORE-15369786: - '@vusion/webfonts-generator@0.8.0 > underscore@1.13.7': reason: >- diff --git a/community-modules/styles/package.json b/community-modules/styles/package.json index a01e6d0881b..887d6b5b8cf 100644 --- a/community-modules/styles/package.json +++ b/community-modules/styles/package.json @@ -1,6 +1,6 @@ { "name": "@ag-grid-community/styles", - "version": "35.1.0-beta.20260315.2039", + "version": "35.1.0-beta.20260316.108", "description": "AG Grid Styles and Themes", "main": "_index.scss", "files": [ diff --git a/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss b/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss index a8f553a85bf..769ffd2e49a 100644 --- a/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss +++ b/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss @@ -36,6 +36,33 @@ } } + .ag-column-panel-buttons { + display: flex; + flex-wrap: wrap; + gap: var(--ag-widget-vertical-spacing) var(--ag-widget-horizontal-spacing); + justify-content: flex-end; + overflow: hidden; + padding: var(--ag-widget-container-vertical-padding) var(--ag-widget-container-horizontal-padding); + border-top: var(--ag-borders-secondary) var(--ag-secondary-border-color); + } + + .ag-column-panel-defer-mode-toggle { + display: flex; + align-items: center; + gap: var(--ag-widget-horizontal-spacing); + padding: var(--ag-widget-container-vertical-padding) var(--ag-widget-container-horizontal-padding); + border-top: var(--ag-borders-secondary) var(--ag-secondary-border-color); + } + + .ag-column-panel-defer-mode-select { + display: flex; + align-items: center; + } + + .ag-column-panel-buttons-button { + line-height: 1.5; + } + .ag-column-group-icons, .ag-column-select-header-icon { color: var(--ag-secondary-foreground-color); diff --git a/documentation/ag-grid-docs/.snyk b/documentation/ag-grid-docs/.snyk index 1d586449c96..62d97bcd7b8 100644 --- a/documentation/ag-grid-docs/.snyk +++ b/documentation/ag-grid-docs/.snyk @@ -2,6 +2,24 @@ version: v1.25.1 # ignores vulnerabilities until expiry date; change duration by modifying expiry date ignore: + SNYK-JS-UNDICI-15518068: + - cheerio@1.1.2 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:17:16.076Z + SNYK-JS-UNDICI-15518070: + - cheerio@1.1.2 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:17:09.159Z + SNYK-JS-UNDICI-15518064: + - cheerio@1.1.2 > undici@6.21.3: + reason: >- + Used in build & dev - not included in final production build + expires: 2026-06-08T00:00:00.000Z + created: 2026-03-16T10:12:38.485Z SNYK-JS-SVGO-15423912: - astro@5.16.6 > svgo@4.0.0: reason: >- diff --git a/documentation/ag-grid-docs/package.json b/documentation/ag-grid-docs/package.json index 7a42c02adbd..1f3ecdf6a23 100644 --- a/documentation/ag-grid-docs/package.json +++ b/documentation/ag-grid-docs/package.json @@ -2,7 +2,7 @@ "name": "ag-grid-docs", "description": "Documentation for AG Grid", "type": "module", - "version": "35.1.0-beta.20260315.2039", + "version": "35.1.0-beta.20260316.108", "repository": { "type": "git", "url": "https://github.com/ag-grid/ag-grid.git" @@ -53,17 +53,17 @@ "@types/react": "^18.2.47", "@types/react-dom": "^18.2.18", "@pqina/flip": "^1.8.4", - "ag-charts-angular": "13.1.0-beta.20260315", - "ag-charts-community": "13.1.0-beta.20260315", - "ag-charts-enterprise": "13.1.0-beta.20260315", - "ag-charts-types": "13.1.0-beta.20260315", - "ag-charts-react": "13.1.0-beta.20260315", - "ag-charts-vue3": "13.1.0-beta.20260315", - "ag-grid-angular": "35.1.0-beta.20260315.2039", - "ag-grid-community": "35.1.0-beta.20260315.2039", - "ag-grid-enterprise": "35.1.0-beta.20260315.2039", - "ag-grid-react": "35.1.0-beta.20260315.2039", - "ag-grid-vue3": "35.1.0-beta.20260315.2039", + "ag-charts-angular": "13.1.0-beta.20260316", + "ag-charts-community": "13.1.0-beta.20260316", + "ag-charts-enterprise": "13.1.0-beta.20260316", + "ag-charts-types": "13.1.0-beta.20260316", + "ag-charts-react": "13.1.0-beta.20260316", + "ag-charts-vue3": "13.1.0-beta.20260316", + "ag-grid-angular": "35.1.0-beta.20260316.108", + "ag-grid-community": "35.1.0-beta.20260316.108", + "ag-grid-enterprise": "35.1.0-beta.20260316.108", + "ag-grid-react": "35.1.0-beta.20260316.108", + "ag-grid-vue3": "35.1.0-beta.20260316.108", "algoliasearch": "^4.18.0", "astro": "5.16.6", "cheerio": "^1.0.0", diff --git a/documentation/ag-grid-docs/src/content/api-documentation/column-properties/properties.json b/documentation/ag-grid-docs/src/content/api-documentation/column-properties/properties.json index ebe99bfda22..41a511a7cb8 100644 --- a/documentation/ag-grid-docs/src/content/api-documentation/column-properties/properties.json +++ b/documentation/ag-grid-docs/src/content/api-documentation/column-properties/properties.json @@ -442,6 +442,20 @@ } }, "editable": {}, + "groupRowEditable": { + "description": "Works like `editable`, but is evaluated only for group rows. Requires `RowGroupingEditModule`.", + "more": { + "name": "Editing Group Row Cells", + "url": "./grouping-edit/" + } + }, + "groupRowValueSetter": { + "description": "Runs after a group row value changes so custom code can push edits down to descendant rows. Requires `RowGroupingEditModule`.", + "more": { + "name": "Editing Group Row Cells", + "url": "./grouping-edit/" + } + }, "valueSetter": { "description": "Function or [expression](./cell-expressions/#column-definition-expressions). Custom function to modify your data based off the new value for saving. Return `true` if the data changed.", "more": { diff --git a/documentation/ag-grid-docs/src/content/api-documentation/row-object/properties.json b/documentation/ag-grid-docs/src/content/api-documentation/row-object/properties.json index 25c9a8bbbb0..ad968b3a61a 100644 --- a/documentation/ag-grid-docs/src/content/api-documentation/row-object/properties.json +++ b/documentation/ag-grid-docs/src/content/api-documentation/row-object/properties.json @@ -132,6 +132,7 @@ "allChildrenCount": {}, "leafGroup": {}, "sibling": {}, + "primaryRow": {}, "depthFirstSearch": {}, "getAggregatedChildren": { "more": { diff --git a/documentation/ag-grid-docs/src/content/docs/example-demos.spec.ts b/documentation/ag-grid-docs/src/content/docs/example-demos.spec.ts index de0db1f9ddc..f5930d78484 100644 --- a/documentation/ag-grid-docs/src/content/docs/example-demos.spec.ts +++ b/documentation/ag-grid-docs/src/content/docs/example-demos.spec.ts @@ -22,7 +22,7 @@ test.describe(`Demo Examples`, async () => { await page.waitForTimeout(1000); await page.locator('button').filter({ hasText: 'Rows, 22 Cols' }).click(); - await page.getByText('1,000 Rows, 22 Cols').click(); + await page.getByRole('option', { name: '1,000 Rows, 22 Cols' }).click(); await page.locator('button').filter({ hasText: 'Quartz' }).click(); await page.getByText('Balham').click(); await page.getByRole('textbox', { name: 'Filter:' }).click(); diff --git a/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/group-editable-totals-custom/main.ts b/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/group-editable-totals-custom/main.ts index 165ad59e96e..60543447303 100644 --- a/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/group-editable-totals-custom/main.ts +++ b/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/group-editable-totals-custom/main.ts @@ -7,7 +7,7 @@ import { ValidationModule, createGrid, } from 'ag-grid-community'; -import { RowGroupingModule, SetFilterModule } from 'ag-grid-enterprise'; +import { RowGroupingEditModule, RowGroupingModule, SetFilterModule } from 'ag-grid-enterprise'; import { getData } from './data'; @@ -23,6 +23,7 @@ interface SalesRecord { ModuleRegistry.registerModules([ RowGroupingModule, + RowGroupingEditModule, ClientSideRowModelModule, NumberFilterModule, SetFilterModule, diff --git a/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/pivot-editable-totals-custom/main.ts b/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/pivot-editable-totals-custom/main.ts index 1d1ce2593c9..8b06755d487 100644 --- a/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/pivot-editable-totals-custom/main.ts +++ b/documentation/ag-grid-docs/src/content/docs/grouping-edit/_examples/pivot-editable-totals-custom/main.ts @@ -7,7 +7,13 @@ import { ValidationModule, createGrid, } from 'ag-grid-community'; -import { ColumnsToolPanelModule, PivotModule, RowGroupingModule, SideBarModule } from 'ag-grid-enterprise'; +import { + ColumnsToolPanelModule, + PivotModule, + RowGroupingEditModule, + RowGroupingModule, + SideBarModule, +} from 'ag-grid-enterprise'; import { getData } from './data'; @@ -23,6 +29,7 @@ let gridApi: GridApi; ModuleRegistry.registerModules([ RowGroupingModule, + RowGroupingEditModule, ClientSideRowModelModule, NumberFilterModule, TextEditorModule, diff --git a/documentation/ag-grid-docs/src/content/docs/grouping-edit/index.mdoc b/documentation/ag-grid-docs/src/content/docs/grouping-edit/index.mdoc index 001d1ff08ca..94399e5292f 100644 --- a/documentation/ag-grid-docs/src/content/docs/grouping-edit/index.mdoc +++ b/documentation/ag-grid-docs/src/content/docs/grouping-edit/index.mdoc @@ -27,6 +27,10 @@ connecting the grid with a store. ## Editing Group Row Cells +{% note %} +`groupRowEditable` and `groupRowValueSetter` require the `RowGroupingEditModule` to be registered. +{% /note %} + Set `groupRowEditable` on any column that should accept edits on group nodes. Provide either a boolean or a callback; callbacks only run for nodes where `rowNode.group === true`, while leaf rows continue to honour `editable`. diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/example.spec.ts b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/example.spec.ts new file mode 100644 index 00000000000..ed79767b830 --- /dev/null +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/example.spec.ts @@ -0,0 +1,11 @@ +import { clickAllButtons, ensureGridReady, test, waitForGridContent } from '@utils/grid/test-utils'; + +test.agExample(import.meta, () => { + test.eachFramework('Example', async ({ page }) => { + // PLACEHOLDER - MINIMAL TEST TO ENSURE GRID LOADS WITHOUT ERRORS + await ensureGridReady(page); + await waitForGridContent(page); + await clickAllButtons(page); + // END PLACEHOLDER + }); +}); diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/fakeServer.ts b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/fakeServer.ts new file mode 100644 index 00000000000..2c27d527e0d --- /dev/null +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/fakeServer.ts @@ -0,0 +1,254 @@ +import type { IServerSideDatasource, IServerSideGetRowsRequest } from 'ag-grid-community'; + +type ServerResponse = { + success: boolean; + rows: IOlympicData[]; + lastRow: number; + pivotResultFields?: string[]; +}; + +export function createServerSideDatasource(server: { + getData: (request: IServerSideGetRowsRequest) => ServerResponse; +}): IServerSideDatasource { + return { + getRows: (params) => { + console.log('server request', { + rowGroups: params.request.rowGroupCols?.map((col) => col.id) ?? [], + groupKeys: params.request.groupKeys ?? [], + pivots: params.request.pivotCols?.map((col) => col.id) ?? [], + values: params.request.valueCols?.map((col) => `${col.id}:${col.aggFunc ?? 'sum'}`) ?? [], + sortModel: params.request.sortModel ?? [], + }); + const response = server.getData(params.request); + + setTimeout(() => { + if (response.success) { + params.success({ + rowData: response.rows, + rowCount: response.lastRow, + pivotResultFields: response.pivotResultFields, + }); + } else { + params.fail(); + } + }, Math.random() * 1000); + }, + }; +} + +export function createFakeServer(allData: IOlympicData[]) { + const matchesPivotKey = (row: IOlympicData, pivotColIds: string[], pivotKey: string[]) => { + for (let i = 0; i < pivotColIds.length; i++) { + if (String((row as any)[pivotColIds[i]]) !== pivotKey[i]) { + return false; + } + } + return true; + }; + + const createPivotField = (pivotKey: string[], valueColId: string) => `${pivotKey.join('_')}_${valueColId}`; + + const toNumber = (value: unknown): number | null => { + if (typeof value === 'number' && Number.isFinite(value)) { + return value; + } + if (typeof value === 'string') { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : null; + } + return null; + }; + + const aggregateValues = (rows: IOlympicData[], field: string, aggFunc: string): number | string | null => { + const values = rows.map((row) => (row as any)[field]); + + switch (aggFunc) { + case 'sum': { + return values.reduce((sum, value) => sum + (toNumber(value) ?? 0), 0); + } + case 'min': { + let min: number | null = null; + for (const value of values) { + const num = toNumber(value); + if (num == null) { + continue; + } + min = min == null ? num : Math.min(min, num); + } + return min; + } + case 'max': { + let max: number | null = null; + for (const value of values) { + const num = toNumber(value); + if (num == null) { + continue; + } + max = max == null ? num : Math.max(max, num); + } + return max; + } + case 'avg': { + let sum = 0; + let count = 0; + for (const value of values) { + const num = toNumber(value); + if (num == null) { + continue; + } + sum += num; + count++; + } + return count > 0 ? sum / count : null; + } + case 'count': { + return values.length; + } + case 'first': { + return values.length ? (values[0] as any) ?? null : null; + } + case 'last': { + return values.length ? (values[values.length - 1] as any) ?? null : null; + } + default: + return values.reduce((sum, value) => sum + (toNumber(value) ?? 0), 0); + } + }; + + const sortRows = (rows: IOlympicData[], sortModel: IServerSideGetRowsRequest['sortModel']) => { + if (!sortModel?.length) { + return rows; + } + + return [...rows].sort((a: any, b: any) => { + for (const sort of sortModel) { + const left = a[sort.colId]; + const right = b[sort.colId]; + + if (left === right) { + continue; + } + + const compare = left > right ? 1 : -1; + return sort.sort === 'asc' ? compare : -compare; + } + return 0; + }); + }; + + const compareKeys = (left: string, right: string): number => { + const leftNum = toNumber(left); + const rightNum = toNumber(right); + if (leftNum != null && rightNum != null) { + return leftNum - rightNum; + } + return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }); + }; + + return { + getData: (request: IServerSideGetRowsRequest): ServerResponse => { + let rows = allData; + const { rowGroupCols = [], pivotCols = [], groupKeys = [], valueCols = [], sortModel = [] } = request; + + for (let i = 0; i < groupKeys.length; i++) { + const key = groupKeys[i]; + const rowGroupCol = rowGroupCols[i]; + if (!rowGroupCol) { + continue; + } + rows = rows.filter((row) => String((row as any)[rowGroupCol.id]) === String(key)); + } + + const isGrouping = rowGroupCols.length > groupKeys.length; + let resultRows: IOlympicData[]; + let pivotResultFields: string[] | undefined; + + const hasPivot = pivotCols.length > 0 && valueCols.length > 0; + const pivotColIds = pivotCols.map((col) => col.id); + const pivotKeys = hasPivot + ? Array.from(new Set(rows.map((row) => pivotColIds.map((id) => String((row as any)[id])).join('|')))) + .map((key) => key.split('|')) + .sort((a, b) => { + const len = Math.max(a.length, b.length); + for (let i = 0; i < len; i++) { + const cmp = compareKeys(a[i] ?? '', b[i] ?? ''); + if (cmp !== 0) { + return cmp; + } + } + return 0; + }) + : []; + + const buildPivotedRow = (targetRows: IOlympicData[], base: any = {}) => { + const result: any = { ...base }; + for (const pivotKey of pivotKeys) { + const matchingRows = targetRows.filter((row) => matchesPivotKey(row, pivotColIds, pivotKey)); + for (const valueCol of valueCols) { + const field = createPivotField(pivotKey, valueCol.id); + result[field] = aggregateValues(matchingRows, valueCol.id, valueCol.aggFunc!); + } + } + return result as IOlympicData; + }; + + if (isGrouping) { + const rowGroupCol = rowGroupCols[groupKeys.length]; + const groupField = rowGroupCol.id; + const grouped = new Map(); + + for (const row of rows) { + const key = String((row as any)[groupField]); + const bucket = grouped.get(key); + if (bucket) { + bucket.push(row); + } else { + grouped.set(key, [row]); + } + } + + resultRows = Array.from(grouped.entries()) + .sort(([leftKey, leftRows], [rightKey, rightRows]) => { + if (groupField === 'country') { + const countDiff = rightRows.length - leftRows.length; + if (countDiff !== 0) { + return countDiff; + } + } + return compareKeys(leftKey, rightKey); + }) + .map(([key, groupRows]) => { + if (hasPivot) { + return buildPivotedRow(groupRows, { [groupField]: key, childCount: groupRows.length }); + } + + const groupRow: any = { [groupField]: key, childCount: groupRows.length }; + for (const valueCol of valueCols) { + groupRow[valueCol.id] = aggregateValues(groupRows, valueCol.id, valueCol.aggFunc!); + } + return groupRow as IOlympicData; + }); + } else { + resultRows = hasPivot ? [buildPivotedRow(rows)] : [...rows]; + } + + if (hasPivot) { + pivotResultFields = pivotKeys.flatMap((pivotKey) => + valueCols.map((valueCol) => createPivotField(pivotKey, valueCol.id)) + ); + } + + const sortedRows = sortRows(resultRows, sortModel); + const start = request.startRow ?? 0; + const end = request.endRow ?? sortedRows.length; + const requestedRows = sortedRows.slice(start, end); + + return { + success: true, + rows: requestedRows, + lastRow: sortedRows.length, + pivotResultFields, + }; + }, + }; +} diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/index.html b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/index.html new file mode 100644 index 00000000000..ab3ab34e55a --- /dev/null +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/index.html @@ -0,0 +1,3 @@ +
+
+
diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/main.ts b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/main.ts new file mode 100644 index 00000000000..8856db31fe5 --- /dev/null +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/main.ts @@ -0,0 +1,112 @@ +import type { ColDef, GridApi, GridOptions } from 'ag-grid-community'; +import { ModuleRegistry, ValidationModule, createGrid } from 'ag-grid-community'; +import { + ColumnMenuModule, + ColumnsToolPanelModule, + ContextMenuModule, + PivotModule, + RowGroupingPanelModule, + ServerSideRowModelModule, +} from 'ag-grid-enterprise'; + +import { createFakeServer, createServerSideDatasource } from './fakeServer'; + +ModuleRegistry.registerModules([ + ColumnsToolPanelModule, + ColumnMenuModule, + ContextMenuModule, + PivotModule, + RowGroupingPanelModule, + ServerSideRowModelModule, + ...(process.env.NODE_ENV !== 'production' ? [ValidationModule] : []), +]); + +const columnDefs: ColDef[] = [ + { + field: 'athlete', + minWidth: 200, + enableRowGroup: true, + enablePivot: true, + }, + { + field: 'age', + enableValue: true, + }, + { + field: 'country', + minWidth: 200, + enableRowGroup: true, + enablePivot: true, + rowGroupIndex: 1, + }, + { + field: 'year', + enableRowGroup: true, + enablePivot: true, + pivotIndex: 1, + }, + { + field: 'date', + minWidth: 180, + enableRowGroup: true, + enablePivot: true, + }, + { + field: 'sport', + minWidth: 200, + enableRowGroup: true, + enablePivot: true, + rowGroupIndex: 2, + }, + { field: 'gold', hide: true, enableValue: true }, + { field: 'silver', hide: true, enableValue: true, aggFunc: 'sum' }, + { field: 'bronze', hide: true, enableValue: true, aggFunc: 'sum' }, + { headerName: 'Total', field: 'total', enableValue: true }, +]; + +let gridApi: GridApi; + +const gridOptions: GridOptions = { + columnDefs, + defaultColDef: { + flex: 1, + minWidth: 150, + }, + autoGroupColumnDef: { + minWidth: 250, + }, + pivotMode: true, + rowModelType: 'serverSide', + rowGroupPanelShow: 'always', + pivotPanelShow: 'always', + sideBar: { + toolPanels: [ + { + id: 'columns', + labelDefault: 'Columns', + labelKey: 'columns', + iconKey: 'columns', + toolPanel: 'agColumnsToolPanel', + toolPanelParams: { + deferApply: true, + }, + }, + ], + defaultToolPanel: 'columns', + }, + getChildCount: (data: any) => (typeof data?.childCount === 'number' ? data.childCount : undefined), +}; + +// setup the grid after the page has finished loading +document.addEventListener('DOMContentLoaded', function () { + const gridDiv = document.querySelector('#myGrid')!; + gridApi = createGrid(gridDiv, gridOptions); + + fetch('https://www.ag-grid.com/example-assets/olympic-winners.json') + .then((response) => response.json()) + .then((data: IOlympicData[]) => { + const fakeServer = createFakeServer(data); + const datasource = createServerSideDatasource(fakeServer); + gridApi!.setGridOption('serverSideDatasource', datasource); + }); +}); diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/styles.css b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/styles.css new file mode 100644 index 00000000000..072f3617e55 --- /dev/null +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/_examples/deferred-apply-mode/styles.css @@ -0,0 +1,10 @@ +.example-wrapper { + display: flex; + flex-direction: column; + height: 100%; +} + +#myGrid { + flex: 1 1 0px; + width: 100%; +} diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc index 8fffb610c1a..6afabab7a03 100644 --- a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc +++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc @@ -24,6 +24,43 @@ Selecting columns means different things depending on whether the grid is in piv - **Pivot Mode Off**: When pivot mode is off, selecting a column toggles the visibility of the column. A selected column is visible and an unselected column is hidden. With `allowDragFromColumnsToolPanel=true`, you can drag a column from the tool panel onto the grid and it will become visible. - **Pivot Mode On**: When pivot mode is on, selecting a column will trigger the column to be either aggregated, grouped or pivoted depending on what is allowed for that column. +## Deferred Apply Mode + +You can configure the Columns Tool Panel to stage changes and require an explicit **Apply** action before they are committed. + +```{% frameworkTransform=true %} +const gridOptions = { + sideBar: { + toolPanels: [ + { + id: 'columns', + labelDefault: 'Columns', + labelKey: 'columns', + iconKey: 'columns', + toolPanel: 'agColumnsToolPanel', + toolPanelParams: { + deferApply: true, + }, + }, + ], + defaultToolPanel: 'columns', + }, +}; +``` + +When `deferApply=true`: + +- Changes made in the Columns Tool Panel are staged as pending changes. +- The Columns Tool Panel shows **Apply** and **Cancel** actions. +- **Apply** commits pending changes. +- **Cancel** discards pending changes. + +Changes made outside the Columns Tool Panel (for example via other grid entry points) continue to apply immediately. + +The example below uses the Server-Side Row Model. + +{% gridExampleRunner title="Deferred Apply Mode" name="deferred-apply-mode" exampleHeight=830 /%} + ## Columns Tool Panel Sections The Columns Tool Panel is split into different sections as described from the top: diff --git a/documentation/ag-grid-docs/src/content/module-mappings/modules.json b/documentation/ag-grid-docs/src/content/module-mappings/modules.json index 750a1cdab4b..757916ebe77 100644 --- a/documentation/ag-grid-docs/src/content/module-mappings/modules.json +++ b/documentation/ag-grid-docs/src/content/module-mappings/modules.json @@ -301,6 +301,12 @@ "path": "grouping-single-group-column/#inherit-row-grouped-columns-filters", "name": "Group Filter", "isEnterprise": true + }, + { + "moduleName": "RowGroupingEditModule", + "name": "Editing and Updating Group Row Cells", + "path": "grouping-edit", + "isEnterprise": true } ] }, diff --git a/documentation/update-algolia-indices/package.json b/documentation/update-algolia-indices/package.json index 2f38940c7ae..16a665039e5 100644 --- a/documentation/update-algolia-indices/package.json +++ b/documentation/update-algolia-indices/package.json @@ -1,6 +1,6 @@ { "name": "update-algolia-indices", - "version": "35.1.0-beta.20260315.2039", + "version": "35.1.0-beta.20260316.108", "description": "Update algolia indices", "main": "src/index.ts", "type": "module", diff --git a/external/ag-shared/.gitrepo b/external/ag-shared/.gitrepo index 9c8781b4b8d..b6355b8f527 100644 --- a/external/ag-shared/.gitrepo +++ b/external/ag-shared/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/ag-grid/ag-shared.git branch = latest - commit = 9197faeb19da5832bcc42c6e104a27d3a265000e - parent = 6ecd838c6d676842733cd0ca5d215b751d2379b7 + commit = 8ba8e8e436eed4992181a97843b045a0c5249aef + parent = d0d058059e38acc51bd4484e3861c57d6cada8b7 method = rebase cmdver = 0.4.9 diff --git a/external/ag-shared/docs/SYNC-LOG.md b/external/ag-shared/docs/SYNC-LOG.md new file mode 100644 index 00000000000..666fc8c9a8d --- /dev/null +++ b/external/ag-shared/docs/SYNC-LOG.md @@ -0,0 +1,93 @@ +# ag-shared Sync Log + +Migration log for `external/ag-shared/` changes. Each entry documents what changed and what companion actions consuming repos need to perform when syncing. + +Newest entries first. Generated by `/ag-shared-sync-log`. + +--- + +## 2026-03-12 -- Context optimisation, plunker multi-product, symlinked repo support + +**Branch:** `latest` +**Commits:** `8bc421d714..e0195dbbcd` (3 commits) + +### Changes + +- **[prompts/skills]** Migrated 4 large auto-loading rules (`docs-pages`, `docs-checklist`, `examples`, `examples-framework-patterns`) from ~90KB monoliths to ~4KB slim pointer rules that load full content via skills on demand +- **[prompts/skills]** Made `plunker` skill multi-product with auto-detection — added `ag-grid-guide.md` and `ag-studio-guide.md` +- **[prompts/skills]** Migrated `examples-guide.md` (635 lines) and `framework-patterns.md` (719 lines) into `example/ag-charts/` skill subdirectory +- **[prompts/skills]** Added `context: fork` to 12 skills for context isolation +- **[prompts/skills]** Enhanced `pr-create` skill with symlinked repo scanning (Step 2: companion branch/PR creation for `external/` repos) +- **[prompts/patches]** Added `rulesync+7.0.0.patch` to pass through `context` field to `.claude/` output +- **[scripts/setup-prompts]** Added branch mismatch detection for symlinked repos with stash/checkout/unstash workflow + +### Migration Actions + +- [ ] Replace large `.rulesync/rules/` files (`docs-pages.md`, `docs-checklist.md`, `examples.md`, `examples-framework-patterns.md`) with slim pointer versions +- [ ] Run `./external/ag-shared/scripts/setup-prompts/setup-prompts.sh` +- [ ] Run `./external/ag-shared/scripts/setup-prompts/verify-rulesync.sh` + +--- + +## 2026-03-11 -- Prompts audit, guide-to-skill migrations, monolith decomposition + +**Branch:** `latest` +**Commits:** `9cf1f336db..6d2a38a7ec` (2 commits) + +### Changes + +- **[prompts/skills]** Created `website-css` skill (5 files: SKILL.md + colour-palette, dark-mode, design-tokens, utility-classes) — replaces monolithic guide +- **[prompts/skills]** Created `website-astro` skill (4 files: SKILL.md + content-collections, page-patterns, shared-components) — replaces monolithic guide +- **[prompts/skills]** Decomposed `plan-review` skill into SKILL.md + 5 sub-docs (agent-prompts-quick, agent-prompts-thorough, discovered-work, external-tools, output-format) +- **[prompts/skills]** Decomposed `plan-implementation-review` skill into SKILL.md + 3 sub-docs (agent-prompts, discovered-work, output-format) +- **[prompts/guides]** Slimmed `website-css.md` and `website-astro-pages.md` guides (content moved to skills) +- **[prompts/agents]** Deleted `code-reviewer.md` agent (unused) +- **[prompts/commands]** Removed `analyze-jira-issue.md`, `docs-create.md`, `sonar-fix.md` commands (migrated to skills) + +### Migration Actions + +- [ ] Remove `.rulesync/commands/analyze-jira-issue.md`, `docs-create.md`, `sonar-fix.md` symlinks (targets deleted) +- [ ] Add `.rulesync/skills/website-css` directory symlink → `../../external/ag-shared/prompts/skills/website-css/` +- [ ] Add `.rulesync/skills/website-astro` directory symlink → `../../external/ag-shared/prompts/skills/website-astro/` +- [ ] Add `.rulesync/skills/docs-create` directory symlink → `../../external/ag-shared/prompts/skills/docs-create/` +- [ ] Add `.rulesync/skills/sonar-fix` directory symlink → `../../external/ag-shared/prompts/skills/sonar-fix/` +- [ ] Run `./external/ag-shared/scripts/setup-prompts/setup-prompts.sh` +- [ ] Run `./external/ag-shared/scripts/setup-prompts/verify-rulesync.sh` + +--- + +## 2026-03-09 -- Rulesync skill, worktree hooks, skill optimisations + +**Branch:** `latest` +**Commits:** `c2255312a5..1d93d98522` (8 commits) + +### Changes + +- **[prompts/skills]** Added `rulesync` skill to replace the `rulesync` guide — comprehensive configuration reference for `.rulesync/` directory management +- **[prompts/guides]** Removed `rulesync.md` guide (superseded by the `rulesync` skill) +- **[prompts/skills]** Optimised `dev-server` skill — added multi-repo URL table, troubleshooting section, removed redundant `_dev-server-core.md` partial +- **[prompts/skills]** Optimised `plunker` skill — extracted CSS into `assets/ag-example-styles.css`, slimmed guide, improved description +- **[prompts/skills]** Enhanced `git-conventions` skill — expanded description for better auto-triggering, added PR base branch guidance +- **[prompts/skills]** Enhanced `pr-create` skill — improved PR creation guidelines +- **[prompts/config]** Added `WorktreeCreate` and `WorktreeRemove` hooks to `.claude-settings.json` for automated worktree lifecycle management +- **[scripts/setup-prompts]** Updated `verify-rulesync.sh` to include files in skill subdirectories (e.g. `assets/`) in the expected inventory +- **[scripts]** Added `scripts/setup-worktree/claude-worktree-create.sh` and `claude-worktree-remove.sh` for Claude Code worktree lifecycle + +### Migration Actions + +- [ ] Remove `.rulesync/rules/rulesync.md` file symlink (target guide was deleted) +- [ ] Add `.rulesync/skills/rulesync` directory symlink -> `../../external/ag-shared/prompts/skills/rulesync/` +- [ ] Run `./external/ag-shared/scripts/setup-prompts/setup-prompts.sh` +- [ ] Run `./external/ag-shared/scripts/setup-prompts/verify-rulesync.sh` + +### Post-Migration Verification + +- [ ] Confirm `/rulesync` skill is available (type `/rulesync help` in Claude Code) +- [ ] Confirm the old rulesync rule no longer loads (check `.claude/rules/` for stale `rulesync.md`) +- [ ] Test worktree creation: run `/worktree ` in Claude Code and verify the hook creates the worktree under `~/.worktrees//` +- [ ] Test worktree removal: exit the worktree session and verify cleanup runs without errors +- [ ] Verify `dev-server` skill still triggers correctly (`/dev-server help`) + +### Notes + +The `rulesync` guide-to-skill migration is a structural change: consuming repos that had a `.rulesync/rules/rulesync.md` file symlink pointing to `../../external/ag-shared/prompts/guides/rulesync.md` must remove it (the target no longer exists) and add a directory symlink at `.rulesync/skills/rulesync` instead. The new worktree hook scripts (`scripts/setup-worktree/`) are referenced from `.claude-settings.json` and do not need `.rulesync/` symlinks. diff --git a/external/ag-shared/package.json b/external/ag-shared/package.json index 92ac9cb016a..8422aa12aeb 100644 --- a/external/ag-shared/package.json +++ b/external/ag-shared/package.json @@ -6,21 +6,21 @@ "types": "./dist/types/src/main.d.ts", "exports": { ".": { + "types": "./dist/types/src/main.d.ts", "import": "./dist/package/src/main.js", "require": "./dist/package/src/main.js", - "types": "./dist/types/src/main.d.ts", "default": "./dist/package/src/main.js" }, "./plugin-utils": { + "types": "./dist/types/scripts/plugin-utils/index.d.ts", "import": "./dist/package/scripts/plugin-utils/index.js", "require": "./dist/package/scripts/plugin-utils/index.js", - "types": "./dist/types/scripts/plugin-utils/index.d.ts", "default": "./dist/package/scripts/plugin-utils/index.js" }, "./processor": { + "types": "./dist/types/scripts/junit-processor/index.d.ts", "import": "./dist/package/scripts/junit-processor/src/processor.js", "require": "./dist/package/scripts/junit-processor/src/processor.js", - "types": "./dist/types/scripts/junit-processor/index.d.ts", "default": "./dist/package/scripts/junit-processor/src/processor.js" } }, diff --git a/external/ag-shared/prompts/.claude-settings.json b/external/ag-shared/prompts/.claude-settings.json index 3f04cacdf6a..688e7c372d9 100644 --- a/external/ag-shared/prompts/.claude-settings.json +++ b/external/ag-shared/prompts/.claude-settings.json @@ -41,11 +41,43 @@ "WebFetch(domain:github.com)", "WebFetch(domain:localhost)", "WebFetch(domain:www.npmjs.com)", + "mcp__atlassian__atlassianUserInfo", + "mcp__atlassian__fetch", + "mcp__atlassian__getAccessibleAtlassianResources", + "mcp__atlassian__getIssueLinkTypes", + "mcp__atlassian__getJiraIssue", + "mcp__atlassian__getJiraIssueRemoteIssueLinks", + "mcp__atlassian__getJiraIssueTypeMetaWithFields", + "mcp__atlassian__getJiraProjectIssueTypesMetadata", + "mcp__atlassian__getTransitionsForJiraIssue", + "mcp__atlassian__getVisibleJiraProjects", + "mcp__atlassian__lookupJiraAccountId", + "mcp__atlassian__search", + "mcp__atlassian__searchJiraIssuesUsingJql", + "mcp__claude_ai_Atlassian__atlassianUserInfo", + "mcp__claude_ai_Atlassian__fetch", + "mcp__claude_ai_Atlassian__getAccessibleAtlassianResources", + "mcp__claude_ai_Atlassian__getIssueLinkTypes", + "mcp__claude_ai_Atlassian__getJiraIssue", + "mcp__claude_ai_Atlassian__getJiraIssueRemoteIssueLinks", + "mcp__claude_ai_Atlassian__getJiraIssueTypeMetaWithFields", + "mcp__claude_ai_Atlassian__getJiraProjectIssueTypesMetadata", + "mcp__claude_ai_Atlassian__getTransitionsForJiraIssue", + "mcp__claude_ai_Atlassian__getVisibleJiraProjects", + "mcp__claude_ai_Atlassian__lookupJiraAccountId", + "mcp__claude_ai_Atlassian__search", + "mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql", "mcp__ide__getDiagnostics", "mcp__sequential-thinking__sequentialthinking" ], "deny": [] }, + "env": { + "ENABLE_TOOL_SEARCH": "true" + }, + "enabledPlugins": { + "skill-creator@claude-plugins-official": true + }, "hooks": { "PostToolUse": [ { @@ -69,6 +101,31 @@ { "type": "command", "command": "if [ -n \"$CLAUDE_ENV_FILE\" ]; then echo 'export NX_DAEMON=false' >> \"$CLAUDE_ENV_FILE\"; fi" + }, + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/external/ag-shared/scripts/install-for-cloud/setup-terminal-title.sh" + } + ] + } + ], + "WorktreeCreate": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/external/ag-shared/scripts/setup-worktree/claude-worktree-create.sh", + "timeout": 300 + } + ] + } + ], + "WorktreeRemove": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/external/ag-shared/scripts/setup-worktree/claude-worktree-remove.sh" } ] } diff --git a/external/ag-shared/prompts/.mcp.json b/external/ag-shared/prompts/.mcp.json index c8de2ca8ed8..bd9a351731c 100644 --- a/external/ag-shared/prompts/.mcp.json +++ b/external/ag-shared/prompts/.mcp.json @@ -1,5 +1,9 @@ { "mcpServers": { + "atlassian": { + "type": "http", + "url": "https://mcp.atlassian.com/v1/mcp" + }, "sequential-thinking": { "type": "stdio", "command": "yarn", diff --git a/external/ag-shared/prompts/agents/code-reviewer.md b/external/ag-shared/prompts/agents/code-reviewer.md deleted file mode 100644 index f55dc48f1b7..00000000000 --- a/external/ag-shared/prompts/agents/code-reviewer.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: code-reviewer -targets: ['*'] -description: 'Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code.' -claudecode: - model: opus - tools: - - Read - - Grep - - Glob - - Bash - - Write - - Edit - - MultiEdit ---- - -You are a senior code reviewer ensuring high standards of code quality and security. - -When invoked: - -1. Run `git diff` or use `gh pr diff` to see recent changes -2. Focus on modified files -3. Begin review immediately - -Review checklist: - -- Code is simple and readable -- Functions and variables are well-named -- No duplicated code -- Proper error handling -- No exposed secrets or API keys -- Input validation implemented -- Good test coverage -- Performance considerations addressed - -Provide feedback organized by priority: - -- Critical issues (must fix) -- Warnings (should fix) -- Suggestions (consider improving) - -Include specific examples of how to fix issues. diff --git a/external/ag-shared/prompts/guides/_docs-review-integration.md b/external/ag-shared/prompts/guides/_docs-review-integration.md index 70a71a91b18..fccf5c4312a 100644 --- a/external/ag-shared/prompts/guides/_docs-review-integration.md +++ b/external/ag-shared/prompts/guides/_docs-review-integration.md @@ -1,3 +1,7 @@ + + # Docs Review Integration Guide This guide covers how to integrate the shared docs review methodology into any AG product repo (ag-grid, ag-studio, etc.). diff --git a/external/ag-shared/prompts/guides/rulesync.md b/external/ag-shared/prompts/guides/rulesync.md deleted file mode 100644 index ec2951d0682..00000000000 --- a/external/ag-shared/prompts/guides/rulesync.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -globs: - [ - '.rulesync/**/*', - 'external/ag-shared/prompts/**/*', - 'external/prompts/**/*', - '**/AGENTS.md', - '**/.agent/**/*', - '**/.agents/**/*', - '**/.ai/**/*', - '**/.augment/rules/**/*', - '**/CLAUDE.md', - '**/.claude/**/*', - '**/.clinerules/**/*', - '**/.codex/**/*', - '**/.cursor/**/*', - '**/GEMINI.md', - '**/.gemini/**/*', - '**/.github/copilot-instructions.md', - '**/.github/instructions/**/*', - '**/.github/prompts/**/*', - '**/.github/agents/**/*', - '**/.github/skills/**/*', - '**/.junie/**/*', - '**/.kilocode/**/*', - '**/.kiro/steering/**/*', - '**/.opencode/**/*', - '**/QWEN.md', - '**/.qwen/**/*', - '**/.roo/**/*', - '**/.warp/**/*', - '**/WARP.md', - ] -alwaysApply: false ---- - -# Rulesync Configuration Guide - -This guide covers setting up and maintaining the `.rulesync/` directory structure for AI agentic tooling across AG Grid and AG Charts repositories. - -## Overview - -Rulesync generates tool-specific configuration files (e.g., `.claude/`, `.cursor/`) from a unified source in `.rulesync/`. This enables consistent AI agent behaviour across different tools while maintaining a single source of truth. - -## Rulesync Configuration Reference - -See [Rulesync Configuration](https://github.com/dyoshikawa/rulesync?tab=readme-ov-file#configuration) for relevant documentation. - -## Directory Structure - -``` -.rulesync/ -├── .aiignore # Files to exclude from AI context -├── mcp.json # MCP server configuration (symlink) -├── commands/ # Slash commands (symlinks) -├── rules/ # Context rules and guides (symlinks + repo-specific) -├── subagents/ # Agent definitions (symlinks) -└── skills/ # Complex workflow skills (symlinks) -``` - -## Content Sources - -Files in `.rulesync/` are either simple files, or symlinks to files in other locations: - -| Source | Purpose | Examples | -| ----------------------------- | --------------------------------------------------------------- | ----------------------------------- | -| `.rulesync/` | Repo-specific rules + symlink-based inclusion of shared content | `ag-charts.md`, `ag-grid.md` | -| `external/ag-shared/prompts/` | Cross-repo shared content | `code-reviewer.md` | -| `external/prompts/` | Repo-private shared content | `spruce-example.md`, `visual-qa.md` | - -## Frontmatter Requirements - -All rulesync-managed files require YAML frontmatter with `targets`: - -### Commands - -```yaml ---- -targets: ['*'] -description: 'Brief description of what this command does' ---- -``` - -### Agents/Subagents - -```yaml ---- -targets: ['*'] -name: agent-name -description: 'Brief description (quote if contains colons)' -claudecode: - model: opus|sonnet - tools: - - Read - - Grep - - Bash ---- -``` - -### Rules/Guides - -```yaml ---- -globs: ['pattern1', 'pattern2'] -alwaysApply: true|false ---- -``` - -**Choosing globs**: Match file paths where the guidance applies: - -| Rule Type | Glob Pattern Example | When Loaded | -|-----------|---------------------|-------------| -| Domain-specific | `['**/series/**/*.ts']` | Working with series code | -| Script/tool | `['**/setup-prompts/**/*', '**/patches/rulesync*']` | Working with setup scripts or patches | -| Test guidance | `['**/*.test.ts', '**/*.spec.ts']` | Working with test files | -| Always needed | `alwaysApply: true` | Every conversation | - -**Tips**: -- Use specific patterns to avoid loading irrelevant context -- Combine multiple patterns for related file types -- Use `alwaysApply: true` sparingly (core project rules only) - -### Skills - -```yaml ---- -targets: ['*'] -name: skill-name -description: 'Brief description of the skill' -context: fork # Optional: fork context for complex workflows ---- -``` - -## YAML Gotchas - -**Quote descriptions containing colons:** - -```yaml -# BAD - YAML sees "Context:" as a mapping key -description: Examples: Context: The user... - -# GOOD - Quoted string handles colons correctly -description: "Examples: Context: The user..." -``` - -## Symlink Patterns - -Create symlinks from `.rulesync/` to source files: - -```bash -# From .rulesync/commands/ -ln -s ../../external/ag-shared/prompts/commands/git/bisect.md git-bisect.md -ln -s ../../external/prompts/commands/spruce-example.md spruce-example.md - -# From .rulesync/subagents/ -ln -s ../../external/ag-shared/prompts/agents/code-reviewer.md code-reviewer.md -ln -s ../../external/prompts/agents/visual-qa.md visual-qa.md - -# From .rulesync/rules/ -ln -s ../../external/ag-shared/prompts/guides/code-quality.md code-quality.md - -# From .rulesync/skills/ (NOTE: skills use directory symlinks, not file symlinks) -ln -s ../../external/prompts/skills/spruce-example/ spruce-example -``` - -## Verification - -Run the verification script after changes: - -```bash -./external/ag-shared/scripts/setup-prompts/verify-rulesync.sh -``` - -This checks: - -- All frontmatter is valid -- All symlinks resolve correctly -- Generated output matches expected inventory - -## Adding New Content - -- For repo-specific content that can be public, add directly in `.rulesync/` in the appropriate sub-folder. -- For repo-specific content that is private, add in `external/prompts/` in the appropriate sub-folder, and symlink to `.rulesync/` in the appropriate sub-folder. -- For shared content, add in `external/ag-shared/prompts/` in the appropriate sub-folder, and symlink to `.rulesync/` in the appropriate sub-folder. - -## Troubleshooting - -### YAML Parse Errors - -- Check for unquoted colons in description fields -- Ensure proper indentation (2 spaces) -- Validate with `npx yaml-lint ` - -### Missing Files in Output - -- Verify symlink targets exist -- Check frontmatter has required `targets` field -- Ensure file extension is `.md` - -### Verification Failures - -- Run `rulesync generate -t claudecode` manually to see detailed errors -- Check temp directory output for comparison diff --git a/external/ag-shared/prompts/guides/website-astro-pages.md b/external/ag-shared/prompts/guides/website-astro-pages.md index a14e984055b..11907179ce0 100644 --- a/external/ag-shared/prompts/guides/website-astro-pages.md +++ b/external/ag-shared/prompts/guides/website-astro-pages.md @@ -1,6 +1,6 @@ --- targets: ['*'] -description: 'Astro page creation patterns, layout props, content collections, and code conventions for AG product websites' +description: 'Astro page conventions for AG product websites — loads /website-astro skill for details' globs: [ '**/src/pages/**/*.astro', @@ -8,327 +8,15 @@ globs: ] --- -# Website Astro Pages Guide +# Website Astro Page Conventions -This guide covers Astro page creation patterns, layout props, content collections, and code conventions for AG product websites. +When editing `.astro` files for AG product websites, follow these conventions: -## Project Overview +1. **Import Layout** for standard pages: `import Layout from '@layouts/Layout.astro';` +2. **Page file path = URL**: `pages/pricing.astro` → `/pricing` +3. **Use content collections** for data: `import { getEntry } from 'astro:content';` +4. **Hydrate React components** with `client:load` (or `client:idle` / `client:visible`) +5. **Import order**: Astro → external → shared (`@ag-website-shared/*`) → local → styles +6. **Naming**: pages kebab-case, components PascalCase, styles `.module.scss`, CSS classes camelCase -- **Framework**: Astro 5 with React 18 for interactive components -- **Styling**: SCSS with CSS Modules + shared design system -- **Package Manager**: Yarn -- **Monorepo**: Nx-managed -- **Shared Components**: `external/ag-website-shared/src/components/` - -## Directory Structure - -``` -packages//src/ -├── pages/ # Astro page routes (*.astro files) -├── layouts/ -│ └── Layout.astro # Main page layout wrapper -├── components/ # Local React & Astro components -├── content/ # Content collections (data, docs) -├── pages-styles/ # Page-specific SCSS modules -├── stores/ # Nanostores (state management) -└── utils/ # Utility functions - -external/ag-website-shared/src/ -├── components/ # Shared components across AG products -│ ├── license-pricing/ # Pricing page components -│ ├── changelog/ # Changelog/pipeline components -│ ├── community/ # Community page components -│ ├── whats-new/ # Release notes components -│ ├── landing-pages/ # Landing page components -│ ├── footer/ # Footer component -│ ├── site-header/ # Site header component -│ └── ... # Many more shared components -└── design-system/ # Design tokens and base styles -``` - -## Creating Standard Astro Pages - -### Page Location - -All pages go in `packages//src/pages/`. The file path determines the URL: - -| File Path | URL | -|-----------|-----| -| `pages/index.astro` | `/` | -| `pages/pricing.astro` | `/pricing` | -| `pages/community.astro` | `/community` | -| `pages/community/events.astro` | `/community/events` | - -### Pattern 1: Full Custom Page with Layout - -Use this for pages that need custom content and styling. - -```astro ---- -import Layout from '@layouts/Layout.astro'; -import styles from '@pages-styles/my-page.module.scss'; -import { urlWithBaseUrl } from '@utils/urlWithBaseUrl'; - -// Optional: Fetch data from content collections -import { getEntry, type CollectionEntry } from 'astro:content'; -const { data: navData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; ---- - - -
-

My Page

-

Content goes here

-
-
-``` - -### Pattern 2: Wrapper Page (Delegates to Shared Component) - -Use this when a shared component handles all the page logic. - -```astro ---- -import { getEntry, type CollectionEntry } from 'astro:content'; -import WhatsNew from '@ag-website-shared/components/whats-new/pages/whats-new.astro'; - -// Note: version entry keys are product-specific (e.g. 'ag-grid-versions', 'ag-charts-versions') -const { data: versionsData } = (await getEntry('versions', '-versions')) as CollectionEntry<'versions'>; -const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; ---- - - - -``` - -### Pattern 3: Minimal Wrapper (Simplest) - -For pages that just render a shared component with no data. - -```astro ---- -import Home from '@ag-website-shared/components/community/pages/home.astro'; ---- - - -``` - -### Pattern 4: Page with React Components - -Use this for interactive pages with client-side functionality. - -```astro ---- -import { LicensePricing } from '@ag-website-shared/components/license-pricing/LicensePricing'; -import Layout from '@layouts/Layout.astro'; ---- - - - - -``` - -### Pattern 5: Page with Docs Navigation - -Use this for pages that should show the documentation sidebar. - -```astro ---- -import { getEntry, type CollectionEntry } from 'astro:content'; -import Layout from '@layouts/Layout.astro'; -import { getFrameworkFromPath } from '@components/docs/utils/urlPaths'; -import { Pipeline } from '@ag-website-shared/components/changelog/Pipeline'; -import { DocsNavFromLocalStorage } from '@ag-website-shared/components/docs-navigation/DocsNavFromLocalStorage'; -import styles from '@ag-website-shared/components/changelog/changelog.module.scss'; -import classnames from 'classnames'; - -const path = Astro.url.pathname; -const framework = getFrameworkFromPath(path); -const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; ---- - - -
- - -
-

Pipeline

- - -
-
-
-``` - -### Pattern 6: Standalone Page (No Layout) - -For special pages like demos that need full control. - -```astro ---- -import HeroDashboard from '@components/hero-dashboard/HeroDashboard.astro'; -import '@pages-styles/scratchpad.scss'; -import '@pages-styles/example-controls.css'; ---- - - - - - <Product Name> - - - - - -``` - -## Layout Component Props - -The `Layout.astro` component accepts these props: - -| Prop | Type | Default | Description | -|------|------|---------|-------------| -| `title` | string | required | Page title (shown in browser tab) | -| `description` | string | metadata default | SEO meta description | -| `showSearchBar` | boolean | undefined | Show search in header | -| `showDocsNav` | boolean | undefined | Show docs navigation toggle | -| `hideHeader` | boolean | false | Hide the site header | -| `hideFooter` | boolean | false | Hide the site footer | - -## Content Collections - -Fetch data from content collections using Astro's `getEntry`: - -```astro ---- -import { getEntry, type CollectionEntry } from 'astro:content'; - -// Navigation data -const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; -const { data: apiNavData } = (await getEntry('apiNav', 'nav')) as CollectionEntry<'apiNav'>; - -// Version data (entry key is product-specific, e.g. 'ag-grid-versions', 'ag-charts-versions') -const { data: versionsData } = (await getEntry('versions', '-versions')) as CollectionEntry<'versions'>; - -// Footer data -const { data: footerItems } = (await getEntry('footer', 'footer')) as CollectionEntry<'footer'>; - -// Metadata -const { data: metadata } = (await getEntry('metadata', 'metadata')) as CollectionEntry<'metadata'>; ---- -``` - -## Available Shared Components - -Key components from `@ag-website-shared/components/`: - -| Component | Import Path | Purpose | -|-----------|-------------|---------| -| `LicensePricing` | `license-pricing/LicensePricing` | Pricing page | -| `Pipeline` | `changelog/Pipeline` | Development pipeline | -| `WhatsNew` | `whats-new/pages/whats-new.astro` | Release notes | -| `DocsNavFromLocalStorage` | `docs-navigation/DocsNavFromLocalStorage` | Docs sidebar | -| `FrameworkTextAnimation` | `framework-text-animation/FrameworkTextAnimation` | Animated framework text | -| `LandingPageFWSelector` | `landing-pages/LandingPageFWSelector` | Framework selector | -| `Footer` | `footer/Footer` | Site footer | -| `SiteHeader` | `site-header/SiteHeader.astro` | Site header | - -## Utility Functions - -```typescript -// URL utilities -import { urlWithBaseUrl } from '@utils/urlWithBaseUrl'; -import { urlWithPrefix } from '@utils/urlWithPrefix'; - -// Add base URL to paths (base URL is product-specific) -urlWithBaseUrl('/example') // → '//example' (with configured base) - -// Add framework prefix -urlWithPrefix({ framework: 'react', url: './quick-start' }) // → '/react/quick-start' - -// Framework detection -import { getFrameworkFromPath } from '@components/docs/utils/urlPaths'; -const framework = getFrameworkFromPath(Astro.url.pathname); -``` - -## React Component Hydration - -When using React components in Astro pages, add hydration directives: - -| Directive | When to Use | -|-----------|-------------| -| `client:load` | Needs immediate interactivity (most common) | -| `client:idle` | Can wait until browser is idle | -| `client:visible` | Only when scrolled into view | -| (none) | Static content only, no JavaScript | - -```astro - - - - - - - - -``` - -## Path Aliases - -| Alias | Path | -|-------|------| -| `@components/*` | `src/components/*` | -| `@layouts/*` | `src/layouts/*` | -| `@pages-styles/*` | `src/pages-styles/*` | -| `@stores/*` | `src/stores/*` | -| `@utils/*` | `src/utils/*` | -| `@constants` | `src/constants.ts` | -| `@ag-website-shared/*` | `external/ag-website-shared/src/*` | - -## Code Style - -### Import Order - -1. Astro imports (`astro:content`) -2. External packages -3. Shared components (`@ag-website-shared/*`) -4. Local components/utils (`@components/*`, `@utils/*`) -5. Styles - -### Naming Conventions - -| Type | Convention | Example | -|------|------------|---------| -| Astro pages | kebab-case | `license-pricing.astro` | -| Components | PascalCase | `MyComponent.tsx` | -| Style modules | kebab-case | `my-page.module.scss` | -| CSS classes | camelCase | `.pageContainer` | - -## Common Tasks - -### Add a New Standard Page - -1. Create `src/pages/my-page.astro` -2. Import Layout and any needed components -3. Use standard design system classes where possible -4. Create `src/pages-styles/my-page.module.scss` only if custom styles needed - -### Use a Shared Component - -1. Check `external/ag-website-shared/src/components/` for available components -2. Import with `@ag-website-shared/components/...` -3. Add `client:load` if it's a React component needing interactivity +For full reference (6 page patterns, component catalog, content collection recipes), load the `/website-astro` skill. diff --git a/external/ag-shared/prompts/guides/website-css.md b/external/ag-shared/prompts/guides/website-css.md index 547b6db0ce1..7d6d0f4b5e2 100644 --- a/external/ag-shared/prompts/guides/website-css.md +++ b/external/ag-shared/prompts/guides/website-css.md @@ -1,6 +1,6 @@ --- targets: ['*'] -description: 'CSS architecture, design system, design tokens, utility classes, and styling patterns for AG product websites' +description: 'CSS styling conventions for AG product websites — loads /website-css skill for details' globs: [ '**/src/pages-styles/**/*.scss', @@ -10,481 +10,15 @@ globs: ] --- -# Website CSS & Styling Guide +# Website CSS Conventions -This guide covers the CSS architecture, design system, design tokens, utility classes, and styling patterns used by AG product websites. +When editing SCSS/CSS files for AG product websites, follow these rules: -## Design System +1. **Always import the design system:** `@use 'design-system' as *;` +2. **Use semantic variables** (`--color-bg-primary`) — never raw palette variables or hex values +3. **Dark mode uses `data-dark-mode`:** `#{$selector-darkmode} &` in SCSS. Never `prefers-color-scheme` +4. **Prefer standard utility classes** (`.layout-grid`, `.text-xl`, `.text-secondary`) over custom styles +5. **SCSS modules for components:** `.module.scss` files +6. **Test both light and dark modes** -### Location - -The website's design system is defined in a shared external package: - -``` -external/ag-website-shared/src/design-system/ -``` - -### File Structure - -| File | Purpose | -|------|---------| -| `_root.scss` | All CSS custom properties (colours, typography, layout, shadows) | -| `core/_variables.scss` | SCSS variables (spacing, selectors, transitions) | -| `core/_breakpoints.scss` | Responsive breakpoint values | -| `_layout.scss` | Layout utility classes | -| `_typography.scss` | Typography utility classes | -| `_color.scss` | Colour utility classes | -| `_interactions.scss` | Interactive state classes | -| `_base.scss` | Base element styles | - -### Using the Design System - -Always import the design system at the top of SCSS files: - -```scss -@use 'design-system' as *; -``` - -## CSS Custom Properties - -### How Variables Are Organised - -The design system uses CSS custom properties (variables) organised into semantic categories: - -```scss -:root { - // Abstract colours (raw palette) - --color-gray-50: #f9fafb; - --color-gray-100: #f2f4f7; - // ... through gray-950 - - --color-brand-50: #f4f8ff; - --color-brand-100: #e5effd; - // ... through brand-950 - - // Semantic colours (use these in components) - --color-bg-primary: var(--color-white); - --color-fg-primary: var(--color-gray-900); - --color-border-primary: var(--color-gray-300); -} -``` - -### Colour Palette Reference - -#### Grey Scale - -| Variable | Light Mode | Hex | -| ------------------ | ---------- | --------- | -| `--color-gray-25` | Lightest | `#fcfcfd` | -| `--color-gray-50` | | `#f9fafb` | -| `--color-gray-100` | | `#f2f4f7` | -| `--color-gray-200` | | `#eaecf0` | -| `--color-gray-300` | | `#d0d5dd` | -| `--color-gray-400` | | `#98a2b3` | -| `--color-gray-500` | | `#667085` | -| `--color-gray-600` | | `#475467` | -| `--color-gray-700` | | `#344054` | -| `--color-gray-800` | | `#182230` | -| `--color-gray-900` | | `#101828` | -| `--color-gray-950` | Darkest | `#0c111d` | - -#### Brand Colours (Blue) - -| Variable | Hex | -| ------------------- | --------- | -| `--color-brand-50` | `#f4f8ff` | -| `--color-brand-100` | `#e5effd` | -| `--color-brand-200` | `#d4e3f8` | -| `--color-brand-300` | `#a9c5ec` | -| `--color-brand-400` | `#3d7acd` | -| `--color-brand-500` | `#0e4491` | -| `--color-brand-600` | `#0042a1` | -| `--color-brand-700` | `#00388f` | -| `--color-brand-800` | `#002e7e` | -| `--color-brand-900` | `#00246c` | -| `--color-brand-950` | `#001a5a` | - -#### Warning Colours (Orange/Yellow) - -| Variable | Hex | -| --------------------- | --------- | -| `--color-warning-50` | `#fffaeb` | -| `--color-warning-100` | `#fef0c7` | -| `--color-warning-200` | `#fedf89` | -| `--color-warning-300` | `#fec84b` | -| `--color-warning-400` | `#fdb022` | -| `--color-warning-500` | `#f79009` | -| `--color-warning-600` | `#dc6803` | -| `--color-warning-700` | `#b54708` | -| `--color-warning-800` | `#93370d` | -| `--color-warning-900` | `#7a2e0e` | -| `--color-warning-950` | `#4e1d09` | - -#### Special Colours - -| Variable | Hex | Usage | -| ------------------ | ------------------------------------ | --------------------- | -| `--color-success` | `#28a745` (light) / `#64ea82` (dark) | Success states | -| `--color-positive` | `#28a745` | Positive indicators | -| `--color-negative` | `#dc3545` | Error/negative states | - -## Standard CSS Utility Classes - -**Always prefer using standard design system classes over custom styles.** These classes are globally available and ensure consistency across the site. - -### Layout Classes - -Use these for page structure and grid layouts: - -| Class | Purpose | -|-------|---------| -| `.layout-grid` | Flexbox grid container with standard gap and max-width | -| `.layout-page-max-width` | Full width constrained to max page width | -| `.layout-max-width-small` | Narrower content width with horizontal padding | - -**Column Classes (4-column grid):** -- `.column-1-4`, `.column-2-4`, `.column-3-4`, `.column-4-4` - -**Column Classes (6-column grid):** -- `.column-1-6` through `.column-6-6` - -**Column Classes (12-column grid):** -- `.column-1-12` through `.column-12-12` - -```astro -
-
Main content
-
Sidebar
-
-``` - -### Typography Classes - -| Class | Font Size | Use For | -|-------|-----------|---------| -| `.text-2xs` | 10px | Fine print | -| `.text-xs` | 12px | Captions, labels | -| `.text-sm` | 14px | Secondary text | -| `.text-base` | 16px | Body text (default) | -| `.text-lg` | 20px | Subheadings | -| `.text-xl` | 24px | Section headings | -| `.text-2xl` | 32px | Page headings | -| `.text-3xl` | 40px | Hero headings | - -**Weight Classes:** -- `.text-regular` (400) -- `.text-semibold` (600) -- `.text-bold` (700) - -**Other:** -- `.text-monospace` - Monospace font family - -```astro -

Page Title

-

Body content here.

-code example -``` - -### Colour Classes - -| Class | Purpose | -|-------|---------| -| `.text-secondary` | Secondary foreground colour | -| `.text-tertiary` | Tertiary foreground colour | - -### Interaction Classes - -| Class | Purpose | -|-------|---------| -| `.collapse` | Hidden when not `.show` | -| `.collapsing` | Animating collapse transition | -| `.no-transitions` | Disable all transitions | -| `.no-overflow-anchor` | Prevent scroll anchoring | - -### Example: Using Standard Classes - -```astro -
-

Welcome

-

- Introduction paragraph with secondary styling. -

- -
-
-

Left Column

-
-
-

Right Column

-
-
-
-``` - -## Dark Mode - -### How Dark Mode Works - -Dark mode is triggered by the `data-dark-mode="true"` attribute on the `` element: - -```scss -html[data-dark-mode='true'] { - --color-bg-primary: color-mix(in srgb, var(--color-gray-800), var(--color-gray-900) 50%); - --color-fg-primary: var(--color-white); - // ... other overrides -} -``` - -### Dark Mode in SCSS Modules - -Use the `$selector-darkmode` SCSS variable for dark mode overrides in component styles: - -```scss -.myElement { - background-color: var(--color-bg-primary); - - #{$selector-darkmode} & { - background-color: var(--color-bg-secondary); - } -} -``` - -### Key Dark Mode Colours - -| Semantic Variable | Light Mode | Dark Mode | -| -------------------------- | ---------- | ---------------------------------------- | -| `--color-bg-primary` | `#ffffff` | Mix of `#182230` + `#101828` | -| `--color-bg-secondary` | `#f9fafb` | `#344054` | -| `--color-bg-tertiary` | `#f2f4f7` | `#182230` | -| `--color-fg-primary` | `#101828` | `#ffffff` | -| `--color-fg-secondary` | `#344054` | `#d0d5dd` | -| `--color-border-primary` | `#d0d5dd` | `#344054` | -| `--color-border-secondary` | `#eaecf0` | Mix of `#344054` + bg-primary | -| `--color-link` | `#0e4491` | `#a9c5ec` | - -### Detecting Dark Mode in JavaScript - -```typescript -// Check data attribute (preferred) -const isDark = document.documentElement.getAttribute('data-dark-mode') === 'true'; - -// Or check for dark mode class (fallback) -const isDark = document.documentElement.classList.contains('dark'); -``` - -### Creating Theme-Aware Components - -Use CSS custom properties that react to `data-dark-mode`: - -```css -/* Define variables for both modes */ -:root { - --my-component-bg: #ffffff; - --my-component-text: #101828; -} - -[data-dark-mode='true'] { - --my-component-bg: #182230; - --my-component-text: #d0d5dd; -} - -/* Use variables in component */ -.my-component { - background: var(--my-component-bg); - color: var(--my-component-text); -} -``` - -This approach ensures instant theme switching without JavaScript re-rendering. - -## Semantic Colour Categories - -### Background Colours (`--color-bg-*`) - -- `--color-bg-primary`: Main content background -- `--color-bg-secondary`: Secondary/elevated surfaces -- `--color-bg-tertiary`: Subtle backgrounds -- `--color-bg-toolbar`: Toolbar backgrounds -- `--color-bg-code`: Code block backgrounds - -### Foreground/Text Colours (`--color-fg-*`) - -- `--color-fg-primary`: Primary text -- `--color-fg-secondary`: Secondary/muted text -- `--color-fg-tertiary`: Subtle text -- `--color-fg-disabled`: Disabled state text - -### Border Colours (`--color-border-*`) - -- `--color-border-primary`: Primary borders -- `--color-border-secondary`: Subtle borders -- `--color-border-tertiary`: Very subtle borders - -### Link Colours (`--color-link*`) - -- `--color-link`: Default link colour -- `--color-link-hover`: Link hover state - -## Design Tokens Reference - -### Spacing (SCSS variables from `core/_variables.scss`) - -| Variable | Value | -|----------|-------| -| `$spacing-size-1` | 4px | -| `$spacing-size-2` | 8px | -| `$spacing-size-3` | 12px | -| `$spacing-size-4` | 16px | -| `$spacing-size-5` | 20px | -| `$spacing-size-6` | 24px | -| `$spacing-size-8` | 32px | -| `$spacing-size-10` | 40px | -| `$spacing-size-12` | 48px | -| `$spacing-size-16` | 64px | -| `$spacing-size-20` | 80px | -| `$spacing-size-24` | 96px | - -### Breakpoints (SCSS variables from `core/_breakpoints.scss`) - -| Variable | Value | Use For | -|----------|-------|---------| -| `$breakpoint-hero-small` | 620px | Small hero layouts | -| `$breakpoint-hero-large` | 1020px | Large hero layouts | -| `$breakpoint-landing-page-medium` | 1020px | Landing pages | -| `$breakpoint-docs-nav-medium` | 1052px | Docs navigation | -| `$breakpoint-pricing-small` | 620px | Pricing page | -| `$breakpoint-pricing-medium` | 820px | Pricing page | -| `$breakpoint-pricing-large` | 1260px | Pricing page | - -### Typography (CSS variables from `_root.scss`) - -| Variable | Value | -|----------|-------| -| `--text-fs-2xs` | 10px | -| `--text-fs-xs` | 12px | -| `--text-fs-sm` | 14px | -| `--text-fs-base` | 16px | -| `--text-fs-lg` | 20px | -| `--text-fs-xl` | 24px | -| `--text-fs-2xl` | 32px | -| `--text-fs-3xl` | 40px | -| `--text-lh-tight` | 1.2 | -| `--text-regular` | 400 | -| `--text-semibold` | 600 | -| `--text-bold` | 700 | - -### Layout (CSS variables from `_root.scss`) - -| Variable | Description | -|----------|-------------| -| `--layout-gap` | Grid gap (32px) | -| `--layout-max-width` | Max page width (1800px) | -| `--layout-max-width-small` | Narrow content width (1240px) | -| `--layout-horizontal-margins` | Side margins | - -### Border Radius - -- `--radius-xs` (4px), `--radius-sm` (6px), `--radius-md` (8px), `--radius-lg` (10px), `--radius-xl` (12px), `--radius-2xl` (16px) - -### Shadows - -- `--shadow-xs`, `--shadow-sm`, `--shadow-md`, `--shadow-lg`, `--shadow-xl`, `--shadow-2xl` - -## Creating Custom Page Styles - -Only create custom styles when standard classes don't meet your needs. Create SCSS modules in `packages//src/pages-styles/`: - -```scss -// my-page.module.scss -@use 'design-system' as *; - -.heroSection { - padding-top: $spacing-size-16; - background-color: var(--color-bg-site-header); - - // Dark mode support - #{$selector-darkmode} & { - background-color: var(--color-bg-secondary); - } - - // Responsive - @media screen and (min-width: $breakpoint-hero-large) { - padding-top: $spacing-size-24; - } -} -``` - -## Using Styles in Astro - -```astro ---- -import styles from '@pages-styles/my-page.module.scss'; -import classnames from 'classnames'; ---- - -
-
-

Title

-
-
-``` - -## Component Styling Patterns - -### Using SCSS Modules - -The website uses CSS/SCSS modules for component styling: - -```scss -// MyComponent.module.scss -.container { - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - color: var(--color-fg-primary); -} -``` - -## Best Practices - -### DO: - -- Use semantic variables (`--color-bg-primary`) not raw colours (`--color-gray-50`) -- Define component-specific variables that reference design system variables -- Use `[data-dark-mode="true"]` selector or `#{$selector-darkmode}` for dark mode overrides -- Test components in both light and dark modes -- Prefer standard design system utility classes over custom styles - -### DON'T: - -- Hardcode hex colours directly in components -- Use `prefers-color-scheme` media query (the site uses explicit `data-dark-mode`) -- Assume light mode is the default without testing dark mode - -## Adding New Theme-Aware Styles - -When creating new components or features that need to support both themes: - -1. **Define CSS variables** in a ` + + +
+ + + + +``` + +**WITH controls:** + +```html + + + AG Grid Example - Demo Name + + + + + + + +
+
+ +
+
+
+ + + + +``` + +**Key points:** + +- Include `` to prevent indexing +- The grid container **must** have an explicit `style="height: 500px"` (or similar) — without it the grid collapses to zero height +- Apply a theme class to the container: `ag-theme-quartz` (default), `ag-theme-alpine`, or `ag-theme-balham` +- Use a **specific version** (e.g., `@33.0.0`) with optional cache-busting timestamp (`?t=1768428202375`) +- Generate timestamp with: `date +%s%3N` +- Do NOT add `

`, `

`, or other decorative elements + +For Enterprise features, use the enterprise CDN URL: + +```html + +``` + +### Enterprise vs Community + +Use `ag-grid-enterprise` CDN URL if the example uses any enterprise-only feature (e.g., row grouping, server-side row model, tree data, master/detail, range selection, integrated charts, status bar, sidebar, etc.). + +### main.js + +```javascript +const gridOptions = { + rowData: getData(), // or inline array + columnDefs: [ + { field: 'make' }, + { field: 'model' }, + { field: 'price' }, + ], + defaultColDef: { + flex: 1, + }, +}; + +const gridElement = document.getElementById('myGrid'); +agGrid.createGrid(gridElement, gridOptions); +``` + +**Key points:** + +- The UMD global is `agGrid` — use `agGrid.createGrid(element, options)` +- Do NOT use `new Grid()` (deprecated) or `agGrid.Grid` — use `agGrid.createGrid()` +- The first argument must be a DOM element, not a string selector + +### ag-example-styles.css + +Copy the CSS file directly from the skill assets — do not write it by hand: + +```bash +cp "/assets/ag-example-styles.css" "$PLNKR_DIR/ag-example-styles.css" +``` + +This file includes both the base control styles (buttons, inputs, etc.) and the vanilla framework styles needed for proper layout. + +### Controls + +If your example needs interactive controls, wrap them in `

` with `
` for each row. The grid `
` sits **outside** the controls div as a sibling. Use `gap-left`, `gap-right`, `push-left`, `push-right` classes for layout. + +### package.json + +```json +{ + "name": "ag-grid-example", + "dependencies": { + "ag-grid-community": "latest" + } +} +``` + +For Enterprise, use `"ag-grid-enterprise": "latest"` instead. + +### CDN URLs + +**Staging (DEFAULT for testing):** + +Use staging by default unless the user specifies a version. Add a cache-busting timestamp. + +- Community: `https://grid-staging.ag-grid.com/dev/ag-grid-community/dist/ag-grid-community.min.js?t={timestamp}` +- Enterprise: `https://grid-staging.ag-grid.com/dev/ag-grid-enterprise/dist/ag-grid-enterprise.min.js?t={timestamp}` + +Generate timestamp with: `date +%s%3N` + +**Versioned (for reproduction/sharing):** + +- Community: `https://cdn.jsdelivr.net/npm/ag-grid-community@33.0.0/dist/ag-grid-community.min.js` +- Enterprise: `https://cdn.jsdelivr.net/npm/ag-grid-enterprise@33.0.0/dist/ag-grid-enterprise.min.js` + +### data.js (Optional Separate Data File) + +Create a `data.js` when data is large enough to warrant separation (roughly >20 rows or >30 lines). Define a `getData()` function: + +```javascript +function getData() { + return [ + { make: 'Toyota', model: 'Celica', price: 35000 }, + { make: 'Ford', model: 'Mondeo', price: 32000 }, + // ... + ]; +} +``` + +In `index.html`, add `` **before** ``. In `main.js`, call `getData()`. For small datasets, inline the data directly. + +### Integrated Charts (AG Charts inside AG Grid) + +When the grid example uses integrated charts (e.g., chart ranges, pivot charts), you must load AG Charts Enterprise **before** AG Grid Enterprise: + +```html + + +``` + +Load order matters — AG Charts Enterprise must be loaded first so AG Grid can detect and use it. + +### Common Issues + +| Issue | Cause | Fix | +|-------|-------|-----| +| Grid collapses to zero height | Missing explicit height on container | Add `style="height: 500px"` to `#myGrid` | +| Grid doesn't render | Wrong CDN URL or missing global | Check script src and use `agGrid.createGrid()` | +| `new Grid()` error | Using deprecated API | Use `agGrid.createGrid(element, options)` | +| No theme styling | Missing theme class | Add `class="ag-theme-quartz"` to grid container | +| Styling issues | Missing example styles | Copy `ag-example-styles.css` from assets | +| Controls break grid layout | Grid div nested inside controls div | `
` must be a **sibling** outside `
` | +| Integrated charts not working | Wrong script load order | Load `ag-charts-enterprise` **before** `ag-grid-enterprise` | +| Container not found | Using string selector | Use `document.getElementById('myGrid')` | diff --git a/external/ag-shared/prompts/skills/plunker/ag-studio-guide.md b/external/ag-shared/prompts/skills/plunker/ag-studio-guide.md new file mode 100644 index 00000000000..e0c2fc9517b --- /dev/null +++ b/external/ag-shared/prompts/skills/plunker/ag-studio-guide.md @@ -0,0 +1,5 @@ +## AG Studio Example Structure + +> Placeholder — to be populated with Studio-specific plunker construction patterns. + +See `ag-charts-guide.md` for the reference implementation of a product guide. diff --git a/external/ag-shared/prompts/skills/plunker/assets/ag-example-styles.css b/external/ag-shared/prompts/skills/plunker/assets/ag-example-styles.css new file mode 100644 index 00000000000..37bbe4b3020 --- /dev/null +++ b/external/ag-shared/prompts/skills/plunker/assets/ag-example-styles.css @@ -0,0 +1,262 @@ +/** + * Styles for control elements in examples, not required for the examples' functionality + */ +:root { + --main-fg: #101828; + --main-bg: #fff; + + --chart-bg: #fff; + --chart-border: #d0d5dd; + + --button-fg: #212529; + --button-bg: transparent; + --button-border: #d0d5dd; + --button-hover-bg: rgba(0, 0, 0, 0.1); + + --input-accent: #0e4491; + --input-focus-border: #3d7acd; + --range-track-bg: #efefef; + + --row-gap: 6px; + + --select-chevron: url('data:image/svg+xml;utf8,'); + --checkbox-tick-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17L4 12'/%3E%3C/svg%3E"); +} + +:root[data-dark-mode='true'] { + --main-fg: #fff; + --main-bg: #141d2c; + + --chart-bg: #192232; + --chart-border: #344054; + + --button-fg: #f8f9fa; + --button-bg: transparent; + --button-border: rgba(255, 255, 255, 0.2); + --button-hover-bg: #2a343e; + + --input-accent: #a9c5ec; + --input-focus-border: #3d7acd; + --range-track-bg: #4a5465; + + --select-chevron: url('data:image/svg+xml;utf8,'); + --checkbox-tick-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232a343e' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17L4 12'/%3E%3C/svg%3E"); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +:root, +body { + height: 100%; + width: 100%; + margin: 0; + overflow: hidden; +} + +/* Hide codesandbox highlighter */ +body > #highlighter { + display: none; +} + +.example-controls { + display: flex; + flex-direction: column; + flex-wrap: wrap; +} + +.example-controls *, +.example-controls *::before, +.example-controls *::after { + margin: 0; + font-family: -apple-system, 'system-ui', sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 17px; + letter-spacing: 0.01em; + color: var(--main-fg); +} + +.example-controls :where(button, textarea, select, input[type='submit'], input[type='text'], input[type='number']) { + appearance: none; + display: inline-block; + height: 36px; + padding: 5px 14px 7px; + white-space: nowrap; + border-radius: 6px; + color: var(--button-fg); + background-color: var(--button-bg); + border: 1px solid var(--button-border); + box-shadow: 0 0 0 0 transparent; + transition: + background-color 0.25s ease-in-out, + border-color 0.25s ease-in-out, + box-shadow 0.25s ease-in-out; + align-self: flex-start; +} + +.example-controls :where(button, select, input[type='submit']) { + cursor: pointer; +} + +.example-controls select { + appearance: none; + padding-right: 32px; + padding-left: 14px; + background: no-repeat center right 4px var(--select-chevron); +} + +.example-controls textarea { + height: auto; + padding: 7px 14px; +} + +.example-controls pre, +.example-controls code { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; +} + +.example-controls input { + appearance: none; +} + +.example-controls input[type='checkbox'], +.example-controls input[type='radio'] { + border: 1px solid var(--button-border); + cursor: pointer; +} + +.example-controls input[type='radio'] { + width: 20px; + height: 20px; + border-radius: 50%; +} + +.example-controls input[type='radio']:checked { + border-width: 0; + box-shadow: inset 0 0 0 6px var(--input-accent); +} + +.example-controls input[type='radio']:checked:focus-visible { + box-shadow: + inset 0 0 0 2px var(--input-focus-border), + inset 0 0 0 3px var(--main-bg), + inset 0 0 0 6px var(--input-accent); +} + +.example-controls input[type='checkbox'] { + width: 24px; + height: 24px; + border-radius: 6px; + cursor: pointer; +} + +.example-controls input[type='checkbox']:checked { + background: var(--input-accent) no-repeat center/14px var(--checkbox-tick-icon); + border-color: var(--input-accent); +} + +.example-controls input[type='range'] { + appearance: none; + min-width: 160px; + border-radius: 8px; + cursor: pointer; + overflow: hidden; /* slider progress trick */ + background: var(--range-track-bg); +} + +.example-controls input[type='range']::-webkit-slider-runnable-track { + appearance: none; + height: 16px; + background: var(--range-track-bg); +} + +.example-controls input[type='range']::-moz-range-track { + appearance: none; + height: 16px; + background: var(--range-track-bg); +} + +.example-controls input[type='range']::-webkit-slider-thumb { + appearance: none; + height: 16px; + width: 16px; + background-color: var(--main-bg); + border-radius: 50%; + border: 2px solid var(--input-accent); + box-shadow: -1007px 0 0 1000px var(--input-accent); /* slider progress trick */ +} + +.example-controls input[type='range']::-moz-range-thumb { + appearance: none; + height: 16px; + width: 16px; + background-color: var(--main-bg); + border-radius: 50%; + border: 2px solid var(--input-accent); + box-shadow: -1007px 0 0 1000px var(--input-accent); /* slider progress trick */ +} + +.example-controls :is(button, input[type='submit'], select):hover { + background-color: var(--button-hover-bg); +} + +.example-controls :is(button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible) { + border-color: var(--input-focus-border); + box-shadow: + inset 0 0 0 1px var(--input-focus-border), + inset 0 0 0 2px var(--main-bg); + outline: none; +} + +.controls-row { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--row-gap); + font-variant: tabular-nums; +} + +.controls-row + .controls-row { + margin-top: var(--row-gap); +} + +.controls-row.center { + justify-content: center; +} + +.controls-row .push-right { + margin-left: auto; +} + +.controls-row .push-left { + margin-right: auto; +} + +.controls-row .gap-right { + margin-right: calc(var(--row-gap) * 6); +} + +.controls-row .gap-left { + margin-left: calc(var(--row-gap) * 6); +} + +body { + display: flex; + flex-direction: column; + height: 100%; + gap: 8px; +} + +div:has(> .ag-charts-wrapper) { + padding: 1rem; + height: 100%; + border-radius: 8px; + background-color: var(--chart-bg); + border: 1px solid var(--chart-border); + overflow: hidden; + transform: translate3d(0, 0, 0); +} diff --git a/external/ag-shared/prompts/skills/pr-create/SKILL.md b/external/ag-shared/prompts/skills/pr-create/SKILL.md index 4a329589b59..c375b2fb79b 100644 --- a/external/ag-shared/prompts/skills/pr-create/SKILL.md +++ b/external/ag-shared/prompts/skills/pr-create/SKILL.md @@ -43,7 +43,55 @@ Determine: - **Whether there are uncommitted changes** (staged, unstaged, or untracked). - **Whether there are unpushed commits** on this branch. -### STEP 2: Identify Base Branch +### STEP 2: Check Symlinked Repos + +Scan the `external/` directory for symlinked directories that resolve to separate git repos with changes. These need their own branches and PRs before the outer repo's PR is created. + +1. Identify symlinked repo candidates: + ```bash + for dir in external/*/; do + [ -L "${dir%/}" ] && [ -d "$(readlink -f "${dir%/}")/.git" ] && echo "${dir%/}" + done + ``` + Skip any directory that is NOT a symlink (e.g., `external/ag-shared` is a real directory tracked in the outer repo — ignore it). + +2. For each symlinked repo found, check for uncommitted or unpushed changes: + ```bash + RESOLVED_PATH="$(readlink -f "")" + git -C "$RESOLVED_PATH" status --porcelain + git -C "$RESOLVED_PATH" log --oneline @{upstream}..HEAD 2>/dev/null + ``` + If there are no uncommitted changes AND no unpushed commits, skip that repo silently. + +3. For each symlinked repo WITH changes, create a matching branch, commit, push, and open a PR: + - Use the same branch name as the outer repo (`CURRENT_BRANCH` or the topic branch name determined in STEP 4) for traceability. + - If the repo is not already on that branch, create and switch to it: + ```bash + git -C "$RESOLVED_PATH" checkout -b 2>/dev/null || git -C "$RESOLVED_PATH" checkout + ``` + - Stage and commit changes with a message referencing the outer repo's work: + ```bash + git -C "$RESOLVED_PATH" add -A + git -C "$RESOLVED_PATH" commit -m "$(cat <<'EOF' + Update for : + EOF + )" + ``` + - Push and create a PR: + ```bash + git -C "$RESOLVED_PATH" push -u origin + cd "$RESOLVED_PATH" && gh pr create --title "" --body "$(cat <<'EOF' + Companion PR for changes in <outer-repo-name>. + EOF + )" + ``` + - Record each created PR URL in `SYMLINKED_REPO_PRS` for the final report. + +4. If no symlinked repos have changes, proceed to the next step without comment. + +**Note:** This step may execute before the outer repo's topic branch is fully determined (STEP 4). If a topic branch has not yet been created, defer symlinked repo processing until after STEP 4 and execute it between STEP 4 and STEP 5. The key requirement is that symlinked repo PRs are created BEFORE the outer repo's PR (STEP 7). + +### STEP 3: Identify Base Branch Determine the correct base branch for the PR: @@ -58,7 +106,7 @@ Determine the correct base branch for the PR: Store the result as `BASE_BRANCH`. -### STEP 3: Ensure Topic Branch +### STEP 4: Ensure Topic Branch If currently on `latest` or a `bX.Y.Z` branch, a new topic branch is required: @@ -73,7 +121,7 @@ If currently on `latest` or a `bX.Y.Z` branch, a new topic branch is required: If already on a topic branch (not `latest` or `bX.Y.Z`), continue on the current branch. -### STEP 4: Commit Changes (If Any) +### STEP 5: Commit Changes (If Any) If there are uncommitted changes: @@ -84,11 +132,7 @@ If there are uncommitted changes: git status ``` 2. Stage relevant files (prefer specific files over `git add -A`). -3. Write a commit message following git-conventions: - - JIRA-linked: `AG-XXXX <description>` (uppercase JIRA number) - - No JIRA: `<description>` (concise, imperative mood) - - Under 72 characters. - - Never attribute agentic tooling. +3. Write a commit message following git-conventions (see Commits section). 4. Commit: ```bash git commit -m "$(cat <<'EOF' @@ -99,7 +143,7 @@ If there are uncommitted changes: If there are no uncommitted changes and no unpushed commits, inform the user there is nothing to submit and **STOP**. -### STEP 5: Push Branch +### STEP 6: Push Branch Push the branch to the remote, setting the upstream: @@ -107,7 +151,7 @@ Push the branch to the remote, setting the upstream: git push -u origin <branch-name> ``` -### STEP 6: Create Pull Request +### STEP 7: Create Pull Request Create the PR using `gh`: @@ -118,17 +162,11 @@ EOF )" ``` -Follow git-conventions for the PR: - -- **Title:** Under 70 characters. JIRA-linked: `AG-XXXX <description>`. No JIRA: `<description>`. -- **Body:** JIRA-linked: include link(s) to the JIRA ticket(s). No JIRA: concise description of the change. -- Keep descriptions concise - this is a public repo. -- Never attribute agentic tooling. -- If JIRA-linked include "Fix #AG-XXXX" +Follow git-conventions (see Pull Requests section). If JIRA-linked, include "Fix #AG-XXXX" at the end of the body. -### STEP 7: Report Result +### STEP 8: Report Result -Output the PR URL and a brief summary: +Output the PR URL and a brief summary. If any symlinked repo PRs were created in STEP 2, include them as well: ``` PR created: <URL> @@ -136,6 +174,14 @@ PR created: <URL> Title: <title> ``` +If `SYMLINKED_REPO_PRS` is non-empty, also report: + +``` +Companion PRs (symlinked repos): + <repo-name>: <URL> + <repo-name>: <URL> +``` + ## Arguments `${ARGUMENTS}` can optionally include: diff --git a/external/ag-shared/prompts/skills/pr-review/SKILL.md b/external/ag-shared/prompts/skills/pr-review/SKILL.md index fd15c1aba28..fb87302850a 100644 --- a/external/ag-shared/prompts/skills/pr-review/SKILL.md +++ b/external/ag-shared/prompts/skills/pr-review/SKILL.md @@ -16,9 +16,13 @@ You are acting as a reviewer for a proposed code change. Your goal is to identif Parse the `ARGUMENTS` environment variable (or skill arguments) for flags and the PR number: - `--json` — output structured JSON instead of Markdown (used for inline commenting in CI) +- `--devils-advocate` — run an additional Devil's Advocate review pass after the standard review (see below) +- `--full` — run all additional review passes: Devil's Advocate + JIRA Completeness verification (see below) - Remaining positional argument — the PR number -Examples: `123`, `--json 123`, `123 --json` +Examples: `123`, `--json 123`, `123 --json`, `--devils-advocate 123`, `--full 123`, `--json --full 123` + +**Note:** `--full` implies `--devils-advocate`. You do not need to pass both. ## Output Format @@ -196,3 +200,88 @@ When `--json` is specified, output **ONLY** valid JSON. No markdown code fences, } } ``` + +## Devil's Advocate Mode (`--devils-advocate`) + +When the `--devils-advocate` flag is present, run an additional adversarial review pass after the standard review completes. This mode challenges assumptions, stress-tests edge cases, and questions whether the PR's approach is the right one. + +### Workflow + +1. **Run the standard review first.** Complete the full review as described above and collect all findings. +2. **Spawn a sub-agent with the Devil's Advocate instructions.** Use the Agent tool to spawn a sub-agent with the following prompt structure: + - Include the full contents of `agents/devils-advocate.md` (co-located in this skill's directory) as the sub-agent's instructions. + - Pass the PR diff, PR metadata, and the `--json` flag state to the sub-agent so it has full context. + - The sub-agent should read and follow `_review-core.md` for shared methodology. +3. **Merge findings from both passes.** + - Combine standard review findings with Devil's Advocate findings (prefixed with `[DA]`). + - Deduplicate: if both passes flag the same file and line for the same issue, keep the higher-priority version and note it was flagged by both passes. + - In Markdown mode, add a `## Devil's Advocate Findings` section after the standard `## Findings` section. + - In JSON mode, merge the Devil's Advocate findings into the `findings` array (the `[DA]` prefix in the title distinguishes them). +4. **Update the verdict.** If the Devil's Advocate pass surfaces P0 or P1 issues not found in the standard review, adjust the verdict and confidence accordingly. + +## Full Review Mode (`--full`) + +When `--full` is present, run **all** additional review passes in parallel after the standard review completes: + +1. Devil's Advocate (as described above) +2. JIRA Completeness Verification (described below) +3. Code Simplification Review (described below) + +All three sub-agents can be spawned simultaneously since they are independent of each other. + +### JIRA Completeness Verification + +#### 1. Extract JIRA IDs + +Scan these sources (in order) for JIRA ticket references matching the pattern `AG-\d+` or `ST-\d+`: + +1. **Branch name** — e.g., `ag-12345/fix-tooltip` or `ST-6789-update-utils` +2. **PR title and description** — from `gh pr view` or `$PR_TITLE` +3. **Commit messages** — from `git log` of the PR's commits + +Collect all unique ticket IDs found. The pattern match is case-insensitive (both `ag-12345` and `AG-12345` should match). Normalise to uppercase for the sub-agent (e.g., `AG-12345`). + +#### 2. Spawn the JIRA Completeness Sub-agent + +Use the Agent tool to spawn a sub-agent with: + +- The full contents of `agents/jira-completeness.md` (co-located in this skill's directory) as instructions. +- The extracted JIRA IDs. +- A brief PR summary (from the standard review or PR metadata). +- The list of changed files and diff stats. +- The `--json` flag state. + +#### 3. Merge JIRA Findings + +- Combine JIRA findings (prefixed with `[JIRA]`) with the standard review and Devil's Advocate findings. +- In Markdown mode, add a `## JIRA Completeness` section after `## Devil's Advocate Findings` (or after `## Findings` if Devil's Advocate is not separately flagged). +- In JSON mode, merge the JIRA findings into the `findings` array. Additionally, include the `jira_summary` object as a top-level field in the JSON output. +- If no JIRA IDs were found at all, include a P1 finding noting the PR has no associated JIRA ticket. + +#### 4. Update the Verdict + +If the JIRA verification reveals significant scope mismatches (PR does substantially different work than the ticket describes) or a missing JIRA link, factor this into the confidence score. JIRA hygiene findings alone (missing components, wrong status) should not change the code correctness verdict but should appear in `required_actions`. + +### Code Simplification Review + +#### 1. Spawn the Simplification Sub-agent + +Use the Agent tool to spawn a sub-agent that performs the `/simplify` skill's analysis on the files changed by this PR. The sub-agent prompt should: + +- List the files changed in the PR (from `git diff --name-only`). +- Instruct the agent to read those files and review the **changed sections** for opportunities to simplify — reuse existing utilities, reduce duplication, improve clarity, or eliminate unnecessary complexity. +- Instruct the agent to **report findings only, not make edits** — this is a review, not an auto-fix. +- Pass the `--json` flag state so output format matches. + +The sub-agent should focus on the same concerns as the `/simplify` skill: reuse, quality, and efficiency. It should not flag style issues handled by linters or raise concerns about code it hasn't read. + +#### 2. Merge Simplification Findings + +- Combine simplification findings (prefixed with `[SIMPLIFY]`) with other findings. +- In Markdown mode, add a `## Simplification Opportunities` section after the JIRA section (or after whatever the last preceding section is). +- In JSON mode, merge findings into the `findings` array with the `[SIMPLIFY]` title prefix. +- Deduplicate against standard review findings — if the standard review already flagged the same issue (e.g., duplicated logic), keep the standard review version. + +#### 3. Verdict Impact + +Simplification findings are advisory and should not change the code correctness verdict or confidence score. They appear as P2 or P3 suggestions in `required_actions` only if they represent genuine quality concerns (e.g., copy-pasted logic that should be extracted). diff --git a/external/ag-shared/prompts/skills/pr-review/_review-core.md b/external/ag-shared/prompts/skills/pr-review/_review-core.md index e5eaec29760..c9ca03b0dd7 100644 --- a/external/ag-shared/prompts/skills/pr-review/_review-core.md +++ b/external/ag-shared/prompts/skills/pr-review/_review-core.md @@ -153,3 +153,19 @@ To determine which method to use: 2. **Fallback**: Check for pre-fetched refs with `git rev-parse origin/pr/$ARGUMENTS 2>/dev/null` - If successful: Use git diff commands - If fails: Use `gh` CLI commands + +## 10. Devil's Advocate Mode + +When the `--devils-advocate` flag is passed, the review skill runs a second pass using an adversarial sub-agent defined in `agents/devils-advocate.md`. This agent challenges assumptions, stress-tests edge cases, questions necessity, and probes for gaps in testing. + +The Devil's Advocate agent follows the same priority scheme (P0-P3), line number guidelines, and environment detection described in this file. Its findings are prefixed with `[DA]` and merged with the standard review output. See `SKILL.md` for the full workflow and merge logic. + +## 11. Full Review Mode + +When the `--full` flag is passed, the review skill runs all additional passes in parallel: + +1. **Devil's Advocate** (Section 10) — adversarial review, findings prefixed `[DA]`. +2. **JIRA Completeness** — verifies associated JIRA tickets (matching `AG-\d+` or `ST-\d+` in branch name, commits, or PR metadata) are well-maintained and aligned with the PR. Agent defined in `agents/jira-completeness.md`, findings prefixed `[JIRA]`. +3. **Code Simplification** — reviews changed files for reuse, duplication, and unnecessary complexity (mirrors the `/simplify` skill). Findings prefixed `[SIMPLIFY]`. + +See `SKILL.md` for extraction logic, sub-agent spawning, and merge details. diff --git a/external/ag-shared/prompts/skills/pr-review/agents/devils-advocate.md b/external/ag-shared/prompts/skills/pr-review/agents/devils-advocate.md new file mode 100644 index 00000000000..15f2d62f757 --- /dev/null +++ b/external/ag-shared/prompts/skills/pr-review/agents/devils-advocate.md @@ -0,0 +1,109 @@ +# Devil's Advocate Review Agent + +You are a Devil's Advocate reviewer. Your job is to **challenge, question, and stress-test** a pull request that has already passed a standard review. You are not here to rubber-stamp; you are here to find what the standard review missed by thinking adversarially. + +**Read and follow the shared methodology in the co-located `_review-core.md` file** for priority definitions (P0-P3), line number guidelines, environment detection, and workflow basics. This document defines your additional focus areas and adversarial mindset. + +## Your Mandate + +You operate as a sub-agent spawned after the standard review pass. The standard review covers correctness, performance, security, and maintainability. Your role is to go deeper by challenging the PR from angles that a conventional review tends to skip. + +## Focus Areas + +### 1. Challenge Assumptions + +- Is this the right approach, or is there a simpler alternative the author did not consider? +- Is the abstraction level appropriate? Is something over-abstracted or under-abstracted? +- Would a different architecture or data structure serve the same goal with fewer trade-offs? +- Are there implicit assumptions about input shape, execution order, or environment that are not enforced? + +### 2. Stress-Test Edge Cases + +- What happens with empty inputs, null values, or undefined fields? +- How does this code behave under concurrent access or re-entrant calls? +- What happens with very large datasets, deeply nested structures, or extreme numeric values? +- Are there error conditions on the unhappy path that the implementation silently ignores? +- What happens if an upstream dependency changes its contract (e.g., returns a different shape)? + +### 3. Question Necessity + +- Is every file change in this PR actually needed to achieve the stated goal? +- Is there unnecessary complexity, premature generalisation, or over-engineering? +- Does the PR introduce scope creep beyond what the ticket or description claims? +- Could any of the new code be replaced by an existing utility or pattern already in the codebase? + +### 4. Challenge the Testing + +- Do the tests actually verify the behaviour that changed, or do they test incidental details? +- Could the tests pass while the code is still broken? (e.g., tests that assert on mocks rather than real behaviour) +- What scenarios are **not** covered by the test suite? Identify the most dangerous gaps. +- Are there integration or interaction effects between changed modules that unit tests would miss? +- If there are no new tests, should there be? + +### 5. Consider the Consumer + +- How does this change affect downstream users, consumers, or dependent packages? +- Are there breaking changes that are not obvious from the diff alone (e.g., subtle behaviour changes, type narrowing)? +- Would a consumer need to migrate or update their code? Is that migration path clear? +- Does this change alter any public API surface (types, events, callbacks, CSS classes)? + +### 6. Play the Adversary + +- If you were trying to break this code in production, how would you do it? +- What inputs or sequences of operations would trigger the worst outcome? +- Are there race conditions, resource leaks, or state corruption vectors? +- Could a malicious or careless consumer misuse the new API in a way that causes harm? + +## Output Guidelines + +- Use the same priority scheme as the standard review (P0-P3). +- Every finding **must** reference a specific file and line number from the diff. +- Prefix each finding title with `[DA]` so findings from this agent are clearly identifiable when merged with the standard review. +- Focus on **genuinely challenging questions and concrete risks**, not hypothetical nitpicks. +- If the PR is solid and you cannot find substantive issues, say so explicitly rather than manufacturing findings. A clean Devil's Advocate pass is a strong signal. +- Aim for depth over breadth: a few well-argued findings are more valuable than a long list of shallow ones. + +## Output Format + +Output your findings in the same format as the standard review (Markdown or JSON, depending on the `--json` flag passed to the parent skill). The parent skill will merge your findings with the standard review and deduplicate. + +### Markdown + +```markdown +## Devil's Advocate Findings + +### P0 - Critical + +{List P0 issues, or "None" if empty} + +- **`{filepath}:{line}`** - [DA] {Issue title} + {Explanation of why this is a genuine risk} + +### P1 - High + +... + +### P2 - Medium + +... + +### Summary + +{1-2 sentences on overall adversarial assessment: Is this PR robust, or are there substantive concerns the standard review missed?} +``` + +### JSON + +When `--json` is active, output a JSON array of finding objects using the same schema as the standard review's `findings` array. Prefix each `title` with `[DA]`. + +```json +[ + { + "priority": "P1", + "file": "src/example.ts", + "line": 42, + "title": "[DA] Assumption about input ordering is not enforced", + "description": "The code assumes items arrive sorted by timestamp, but nothing validates or enforces this. If the upstream data source changes its ordering, the binary search at line 42 will silently return wrong results." + } +] +``` diff --git a/external/ag-shared/prompts/skills/pr-review/agents/jira-completeness.md b/external/ag-shared/prompts/skills/pr-review/agents/jira-completeness.md new file mode 100644 index 00000000000..0b6537bbe76 --- /dev/null +++ b/external/ag-shared/prompts/skills/pr-review/agents/jira-completeness.md @@ -0,0 +1,127 @@ +# JIRA Completeness Verification Agent + +You are a JIRA completeness reviewer. Your job is to verify that the JIRA ticket(s) associated with a pull request are well-maintained and aligned with the actual code changes. + +## Inputs + +You receive: + +- **JIRA IDs**: One or more ticket IDs (e.g., `AG-12345`, `ST-6789`) extracted from the PR's branch name, commit messages, or PR title/description. +- **PR summary**: A brief description of what the PR does (from the standard review or PR metadata). +- **Changed files**: The list of files modified in the PR. +- **PR diff stats**: Lines added/removed, files changed. + +## Workflow + +### 1. Fetch Each JIRA Ticket + +Use `mcp__atlassian__getJiraIssue` with `cloudId: "1565837d-d6d1-4228-bcb2-4cb74df700f2"` and the ticket key. + +### 2. Fetch Ticket Comments + +Use `mcp__atlassian__addCommentToJiraIssue` is for writing — to **read** comments, use `mcp__atlassian__getJiraIssue` which includes comments in its response, or use `mcp__atlassian__fetch` with the REST endpoint `rest/api/3/issue/{issueKey}/comment`. + +Comments are important because requirements evolve as tickets progress through development and QA. New test cases, edge cases, revised acceptance criteria, and scope changes are frequently captured in comments rather than being back-ported into the description. Read through all comments to build a complete picture of the ticket's current requirements. + +### 3. Verify Ticket Completeness + +For each ticket, check the following fields and flag any that are missing or inadequate: + +| Check | What to Look For | Severity | +|-------|-----------------|----------| +| **Summary** | Present and descriptive (not just "fix bug" or a copy of the branch name) | P1 | +| **Description** | Non-empty, provides context on what and why | P1 | +| **Issue Type** | Set and appropriate for the work (Bug for fixes, Task/Feature Request for new work) | P2 | +| **Components** | At least one component assigned | P2 | +| **Track** | Track field (`customfield_10501`) is set | P2 | +| **Status** | Ticket is in an active state (e.g., "In Progress", "In Review") rather than "Backlog" or "To Do" | P2 | +| **Acceptance Criteria** | For Feature Requests and Tasks: description should contain testable acceptance criteria or a clear definition of done | P2 | +| **Comment Requirements** | Requirements, test cases, or scope changes added in comments that refine or extend the original description | P2 | + +### 4. Verify Alignment with PR + +Compare the JIRA ticket's description, summary, **and comments** against the actual PR changes: + +- **Scope match**: Do the PR changes correspond to what the ticket describes? Flag if the PR appears to contain significant work not mentioned in the ticket, or if the ticket describes work not addressed by the PR. +- **Comment-sourced requirements**: Check whether requirements or test cases added in comments are addressed by the PR. Flag any that appear unaddressed — these are easy to miss since they live outside the description. +- **Title consistency**: Does the PR title or branch name reference the correct ticket? +- **Ticket count**: If multiple JIRA IDs were found, note whether they appear to be related (e.g., parent/child, linked) or unrelated. + +### 5. Check for Missing JIRA Links + +If **no** JIRA IDs were found in the branch name, commits, or PR metadata, report this as a P1 finding — PRs should be traceable to a ticket. + +## Output Format + +Output your findings using the format specified by the `--json` flag state passed from the parent skill. + +### Markdown + +```markdown +## JIRA Completeness + +### Tickets Found + +| Ticket | Summary | Status | Verdict | +|--------|---------|--------|---------| +| AG-12345 | Fix tooltip in polar charts | In Progress | Complete | +| ST-6789 | Update shared utils | Backlog | Incomplete | + +### Findings + +- **AG-12345** - [JIRA] Description lacks acceptance criteria + The ticket is a Feature Request but the description does not include testable acceptance criteria or a definition of done. + +- **ST-6789** - [JIRA] Ticket still in Backlog + The ticket status is "Backlog" but a PR is already open. Move it to "In Progress" to reflect the current state. + +### Alignment + +{1-2 sentences on whether the PR changes match what the JIRA ticket(s) describe} +``` + +### JSON + +When `--json` is active, output findings as a JSON array using the same schema as the standard review's `findings` array. Prefix each `title` with `[JIRA]`. Use `file: "JIRA"` and `line: 0` since these findings don't reference code locations. + +Additionally include a `jira_summary` object: + +```json +{ + "findings": [ + { + "priority": "P2", + "file": "JIRA", + "line": 0, + "title": "[JIRA] AG-12345: Description lacks acceptance criteria", + "description": "The ticket is a Feature Request but has no testable acceptance criteria." + } + ], + "jira_summary": { + "tickets": [ + { + "key": "AG-12345", + "summary": "Fix tooltip in polar charts", + "status": "In Progress", + "type": "Feature Request", + "has_description": true, + "has_components": true, + "has_track": true, + "has_comment_requirements": true, + "verdict": "incomplete", + "gaps": ["No acceptance criteria"], + "comment_requirements": ["Edge case: tooltip should handle null series data (from QA comment 2025-03-01)"] + } + ], + "alignment": "PR changes match the ticket description.", + "no_jira_found": false + } +} +``` + +## Guidelines + +- Be pragmatic — not every ticket needs a novel-length description. A clear one-liner for a small bug fix is fine. +- Focus on gaps that would cause problems: traceability, scope mismatches, tickets stuck in wrong status. +- Do not flag cosmetic issues in JIRA formatting. +- If the Atlassian MCP tools are unavailable or the ticket fetch fails, report that you could not verify the ticket and suggest the reviewer check manually. Do not block the review. diff --git a/external/ag-shared/prompts/skills/rulesync/SKILL.md b/external/ag-shared/prompts/skills/rulesync/SKILL.md new file mode 100644 index 00000000000..6218dc54e1a --- /dev/null +++ b/external/ag-shared/prompts/skills/rulesync/SKILL.md @@ -0,0 +1,260 @@ +--- +targets: ['*'] +name: rulesync +description: 'Configure AI/agentic tooling using rulesync. Use when asking about .rulesync/, adding or editing commands, rules, skills, or subagents, configuring AI tools like Claude Code or Cursor, understanding where AI config files live, editing prompt files, or asking how to add a new command/rule/skill/agent. Also use when someone references .claude/, .cursor/, .copilot/ or similar tool-specific config directories.' +--- + +# Rulesync Configuration Guide + +`.rulesync/` is the **single source of truth** for all AI/agentic tooling configuration in this repository. Tool-specific directories like `.claude/`, `.cursor/`, `.copilot/`, etc. are **generated output** -- never edit them directly. All changes go through `.rulesync/` and are propagated by the rulesync generator. + +This matters because we support multiple AI tools, and maintaining separate configs for each would lead to drift and inconsistency. Rulesync solves this by generating tool-specific formats from one canonical source. + +See [Rulesync Configuration](https://github.com/dyoshikawa/rulesync?tab=readme-ov-file#configuration) for upstream documentation. + +## Directory Structure + +``` +.rulesync/ +├── .aiignore # Files to exclude from AI context +├── mcp.json # MCP server configuration +├── commands/ # Slash commands +├── rules/ # Context rules and guides +├── subagents/ # Agent definitions +└── skills/ # Complex workflow skills +``` + +## Content Source Tiers + +Files in `.rulesync/` are either local files or symlinks to one of three source tiers: + +| Source | Purpose | When to Use | +|--------|---------|-------------| +| `.rulesync/` directly | Repo-specific content | Content unique to this repo that can be public | +| `external/prompts/` | Repo-private shared content | Product-specific content (e.g. AG Charts testing, JIRA templates) | +| `external/ag-shared/prompts/` | Cross-repo shared content | Content reusable across AG Charts and AG Grid (git conventions, code quality) | + +**Decision guide:** +- Will AG Grid also need this? -> `external/ag-shared/prompts/` +- Is it product-specific but shared across worktrees? -> `external/prompts/` +- Is it unique to this repo checkout? -> `.rulesync/` directly + +## Frontmatter Reference + +All rulesync-managed files require YAML frontmatter. The required fields depend on the file type. + +### Commands + +```yaml +--- +targets: ['*'] +description: 'Brief description of what this command does' +--- +``` + +Commands become slash commands (e.g. `/docs-review`). The `description` appears in the skill/command list. + +### Rules/Guides + +```yaml +--- +globs: ['pattern1', 'pattern2'] +alwaysApply: true|false +--- +``` + +Rules are loaded automatically based on file context. Choose globs that match where the guidance applies: + +| Rule Type | Glob Pattern Example | When Loaded | +|-----------|---------------------|-------------| +| Domain-specific | `['**/series/**/*.ts']` | Working with series code | +| Test guidance | `['**/*.test.ts', '**/*.spec.ts']` | Working with test files | +| Script/tool | `['**/setup-prompts/**/*']` | Working with setup scripts | +| Always needed | `alwaysApply: true` | Every conversation | + +Use `alwaysApply: true` sparingly -- it adds to every conversation's context budget. Prefer specific globs. + +### Skills + +```yaml +--- +targets: ['*'] +name: skill-name +description: 'Brief description of the skill' +--- +``` + +Optional fields: +- `context: fork` -- for skills that manage branch/worktree context +- `invocable: user-only` -- prevents the agent from auto-invoking (user must type `/skill-name`) + +Skills become invocable via `/skill-name`. The `description` determines when the agent auto-invokes the skill, so make it thorough and include trigger phrases. + +### Subagents + +```yaml +--- +targets: ['*'] +name: agent-name +description: 'Brief description (quote if contains colons)' +claudecode: + model: opus|sonnet + tools: + - Read + - Grep + - Bash +--- +``` + +Subagents are specialised agents spawned via the Agent tool. The `model` and `tools` fields control their capabilities. + +### YAML Gotchas + +Quote descriptions containing colons -- YAML interprets bare colons as mapping keys: + +```yaml +# BAD - YAML sees "Context:" as a mapping key +description: Examples: <example>Context: The user... + +# GOOD - Quoted string handles colons correctly +description: "Examples: <example>Context: The user..." +``` + +## Symlink Patterns + +Content from `external/` is included via symlinks. The key distinction: + +- **Commands, rules, subagents**: Use **file** symlinks (`.md` file -> `.md` file) +- **Skills**: Use **directory** symlinks (directory -> directory) + +This is because skills can contain multiple files (SKILL.md + helpers, templates, scripts). + +```bash +# From .rulesync/commands/ +ln -s ../../external/ag-shared/prompts/commands/git/bisect.md git-bisect.md +ln -s ../../external/prompts/commands/docs-create.md docs-create.md + +# From .rulesync/rules/ +ln -s ../../external/ag-shared/prompts/guides/code-quality.md code-quality.md + +# From .rulesync/subagents/ +ln -s ../../external/ag-shared/prompts/agents/code-reviewer.md code-reviewer.md +ln -s ../../external/prompts/subagents/visual-qa.md visual-qa.md + +# From .rulesync/skills/ (DIRECTORY symlinks) +ln -s ../../external/ag-shared/prompts/skills/dev-server/ dev-server +ln -s ../../external/ag-shared/prompts/skills/jira jira +``` + +## Adding New Content + +### Adding a Command + +1. **Choose tier**: Local (`.rulesync/commands/`), private (`external/prompts/commands/`), or shared (`external/ag-shared/prompts/commands/`) +2. **Create the file** with command frontmatter: + ```yaml + --- + targets: ['*'] + description: 'What this command does' + --- + + # Command Name + + Instructions for the command... + ``` +3. **If not local**: Create a file symlink from `.rulesync/commands/`: + ```bash + cd .rulesync/commands + ln -s ../../external/prompts/commands/my-command.md my-command.md + ``` +4. **Regenerate and verify** (see below) + +### Adding a Rule/Guide + +1. **Choose tier** and create the `.md` file with rule frontmatter +2. **Choose globs** carefully -- specific patterns keep context budgets lean +3. **If not local**: Create a file symlink from `.rulesync/rules/`: + ```bash + cd .rulesync/rules + ln -s ../../external/ag-shared/prompts/guides/my-guide.md my-guide.md + ``` +4. **Regenerate and verify** + +### Adding a Skill + +1. **Choose tier** and create a **directory** with `SKILL.md` inside: + ``` + external/prompts/skills/my-skill/ + ├── SKILL.md + ├── template.md # Optional helper files + └── helper-script.sh # Optional scripts + ``` +2. **Write SKILL.md** with skill frontmatter. Make the `description` thorough -- it controls auto-triggering +3. **Create a directory symlink** from `.rulesync/skills/`: + ```bash + cd .rulesync/skills + ln -s ../../external/prompts/skills/my-skill/ my-skill + ``` +4. **Regenerate and verify** + +### Adding a Subagent + +1. **Choose tier** and create the `.md` file with subagent frontmatter +2. **Pick model and tools** appropriate to the agent's purpose (use `sonnet` for fast tasks, `opus` for complex reasoning) +3. **If not local**: Create a file symlink from `.rulesync/subagents/`: + ```bash + cd .rulesync/subagents + ln -s ../../external/prompts/subagents/my-agent.md my-agent.md + ``` +4. **Regenerate and verify** + +## Generation and Verification + +After any changes to `.rulesync/` or its source files: + +```bash +# Regenerate tool-specific configs from .rulesync/ +./external/ag-shared/scripts/setup-prompts/setup-prompts.sh + +# Verify everything is consistent +./external/ag-shared/scripts/setup-prompts/verify-rulesync.sh +``` + +The verification script checks: +- All frontmatter is valid YAML with required fields +- All symlinks resolve to existing files +- Generated output matches the expected file inventory +- Content integrity between source and generated output + +To validate that file path references within prompt files are correct, use the `/validate-prompts` skill. + +## Anti-patterns + +**Never edit generated directories directly.** `.claude/`, `.cursor/`, `.copilot/`, and other tool-specific directories are generated output. Edits will be overwritten on the next `setup-prompts.sh` run. Always edit in `.rulesync/` or the source file it symlinks to. + +**Never commit the TOON block in AGENTS.md.** Rulesync's `agentsmd` target prepends a TOON-format rules index to `AGENTS.md`. This block starts with `Please also reference the following rules as needed. The list below is provided in TOON format` and lists all rules with paths, descriptions, and `applyTo` globs. **Never keep or commit this block.** It is generated noise that bloats `AGENTS.md` with content already available. The `setup-prompts.sh --postinstall` flag handles this automatically (stash user edits, reset after rulesync, restore edits), but if you run rulesync manually and see this block appear in `AGENTS.md`, discard it with `git checkout -- AGENTS.md`. + +**Never reference `external/` paths from prompt cross-references.** When one rule or skill references another (e.g. "see the testing guide"), use `.rulesync/` paths (e.g. `.rulesync/rules/testing.md`), not `external/` source paths. Symlink setup instructions and source-tier descriptions naturally use `external/` paths — that's fine. The `/validate-prompts` skill checks for incorrect cross-references. + +**Never add `alwaysApply: true` without good reason.** Every always-applied rule consumes context budget in every conversation. Reserve this for core project rules that genuinely apply everywhere. + +## Troubleshooting + +### YAML Parse Errors +- Check for unquoted colons in description fields +- Ensure proper indentation (2 spaces) +- Validate with `npx yaml-lint <file>` + +### Missing Files in Output +- Verify symlink targets exist: `ls -la .rulesync/skills/my-skill` +- Check frontmatter has required fields (`targets` for commands/skills/subagents, `globs` for rules) +- Ensure file extension is `.md` + +### Skills Not Appearing +- Skills need **directory** symlinks, not file symlinks +- The skill directory must contain a `SKILL.md` file +- Check the `name` field matches the directory name + +### Verification Failures +- Run `rulesync generate -t claudecode` manually for detailed error output +- Check the temp directory output for comparison with expected inventory diff --git a/external/ag-shared/prompts/skills/sync-ag-shared/SKILL.md b/external/ag-shared/prompts/skills/sync-ag-shared/SKILL.md index 466ead7d1e7..9f408748ca5 100644 --- a/external/ag-shared/prompts/skills/sync-ag-shared/SKILL.md +++ b/external/ag-shared/prompts/skills/sync-ag-shared/SKILL.md @@ -3,6 +3,7 @@ targets: ['*'] name: sync-ag-shared description: 'Sync ag-shared subrepo changes across ag-charts, ag-grid, and ag-studio repos' invocable: user-only +context: fork --- # Sync ag-shared Subrepo Across AG Repos @@ -135,6 +136,7 @@ Display to the user: 4. Apply companion changes in each destination 5. Verify all repos 6. Push branches and create cross-linked PRs (reuse existing source PR if one exists) +7. Post-sync housekeeping (README updates, migration verification, user summary) ``` Use `AskUserQuestion` to confirm before proceeding. The user may want to adjust the plan or skip certain destinations. @@ -337,6 +339,42 @@ Output a summary: All repos verified. Working trees clean. ``` +## STEP 9: Post-Sync Housekeeping + +After all repos are synced, PRs created, and verification passed, complete these final tasks. + +### 9a. Update `.rulesync/README.md` + +Each repo's `.rulesync/README.md` is a crib-sheet of available agentic tools. Update it in every repo (source + destinations) to reflect the sync: + +- Add new skills to the Skills Reference table (alphabetical, with provenance emoji). +- Add new skills to the relevant section tables (Everyday Development, Testing, Planning, etc.). +- Remove deleted agents/skills/commands from all tables. +- Ensure provenance emojis are correct (🔵 for shared, 🟢 for local). + +### 9b. Verify SYNC-LOG Migration Actions + +Cross-check every migration action in `external/ag-shared/docs/SYNC-LOG.md` against each destination repo: + +- Verify broken symlinks are removed. +- Verify new skill/rule/command symlinks are created. +- Verify slim pointer rules replaced monolithic versions (if applicable). +- Run `find .rulesync/ -type l -exec test ! -e {} \; -print` to detect broken symlinks. +- Note: some actions may be repo-specific (🟠 Private skills) — skip those for repos that don't use them. + +### 9c. Write User Summary + +Output a concise summary of what changed for users of the agentic tooling: + +- New skills/commands/capabilities added. +- Removed or replaced items. +- Performance improvements (e.g. context optimisation). +- Any breaking changes to existing workflows. + +### 9d. Commit and Push + +Commit the README and any other post-sync changes in each repo, then push to the existing PR branches. + ## Error Handling - **Merge conflicts during subrepo pull:** Stop and ask the user to resolve manually. Provide the conflicting files and repo path. diff --git a/external/ag-shared/prompts/skills/website-astro/SKILL.md b/external/ag-shared/prompts/skills/website-astro/SKILL.md new file mode 100644 index 00000000000..f2615d88d8b --- /dev/null +++ b/external/ag-shared/prompts/skills/website-astro/SKILL.md @@ -0,0 +1,113 @@ +--- +targets: ['*'] +name: website-astro +description: 'Astro page creation patterns, layout props, content collections, and code conventions for AG product websites' +--- + +# Website Astro Pages + +This skill provides page creation patterns, layout props, content collections, and code conventions for AG product websites. + +## Project Overview + +- **Framework**: Astro 5 with React 18 for interactive components +- **Styling**: SCSS with CSS Modules + shared design system +- **Package Manager**: Yarn +- **Monorepo**: Nx-managed +- **Shared Components**: `external/ag-website-shared/src/components/` + +## Sub-Documents + +Load based on what you need: + +| Document | Purpose | When to Load | +|----------|---------|-------------| +| `page-patterns.md` | All 6 page patterns with full code examples | Creating or modifying page structure | +| `content-collections.md` | Content collection recipes and data fetching | Using `getEntry`, fetching nav/version/footer data | +| `shared-components.md` | Component catalog, path aliases, hydration directives | Importing shared or local components | + +## Most Common Pattern: Full Custom Page with Layout + +Most pages use this pattern — import Layout, add page content: + +```astro +--- +import Layout from '@layouts/Layout.astro'; +import styles from '@pages-styles/my-page.module.scss'; +--- + +<Layout + title="Page Title | <Product Name>" + description="SEO description for the page" + showSearchBar={true} + showDocsNav={false} +> + <div class={styles.pageContainer}> + <h1>My Page</h1> + <p>Content goes here</p> + </div> +</Layout> +``` + +## Layout Component Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | string | required | Page title (shown in browser tab) | +| `description` | string | metadata default | SEO meta description | +| `showSearchBar` | boolean | undefined | Show search in header | +| `showDocsNav` | boolean | undefined | Show docs navigation toggle | +| `hideHeader` | boolean | false | Hide the site header | +| `hideFooter` | boolean | false | Hide the site footer | + +## Directory Structure + +``` +packages/<website-package>/src/ +├── pages/ # Astro page routes (*.astro files) +├── layouts/ +│ └── Layout.astro # Main page layout wrapper +├── components/ # Local React & Astro components +├── content/ # Content collections (data, docs) +├── pages-styles/ # Page-specific SCSS modules +├── stores/ # Nanostores (state management) +└── utils/ # Utility functions + +external/ag-website-shared/src/ +├── components/ # Shared components across AG products +└── design-system/ # Design tokens and base styles +``` + +## Code Style + +### Import Order + +1. Astro imports (`astro:content`) +2. External packages +3. Shared components (`@ag-website-shared/*`) +4. Local components/utils (`@components/*`, `@utils/*`) +5. Styles + +### Naming Conventions + +| Type | Convention | Example | +|------|------------|---------| +| Astro pages | kebab-case | `license-pricing.astro` | +| Components | PascalCase | `MyComponent.tsx` | +| Style modules | kebab-case | `my-page.module.scss` | +| CSS classes | camelCase | `.pageContainer` | + +## Common Tasks + +### Add a New Standard Page + +1. Create `src/pages/my-page.astro` +2. Import Layout and any needed components +3. Use standard design system classes where possible +4. Create `src/pages-styles/my-page.module.scss` only if custom styles needed + +### Use a Shared Component + +1. Check `external/ag-website-shared/src/components/` for available components +2. Import with `@ag-website-shared/components/...` +3. Add `client:load` if it's a React component needing interactivity diff --git a/external/ag-shared/prompts/skills/website-astro/content-collections.md b/external/ag-shared/prompts/skills/website-astro/content-collections.md new file mode 100644 index 00000000000..2e4a235a81a --- /dev/null +++ b/external/ag-shared/prompts/skills/website-astro/content-collections.md @@ -0,0 +1,40 @@ +# Content Collections + +Fetch data from content collections using Astro's `getEntry`: + +```astro +--- +import { getEntry, type CollectionEntry } from 'astro:content'; + +// Navigation data +const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; +const { data: apiNavData } = (await getEntry('apiNav', 'nav')) as CollectionEntry<'apiNav'>; + +// Version data (entry key is product-specific, e.g. 'ag-grid-versions', 'ag-charts-versions') +const { data: versionsData } = (await getEntry('versions', '<product>-versions')) as CollectionEntry<'versions'>; + +// Footer data +const { data: footerItems } = (await getEntry('footer', 'footer')) as CollectionEntry<'footer'>; + +// Metadata +const { data: metadata } = (await getEntry('metadata', 'metadata')) as CollectionEntry<'metadata'>; +--- +``` + +## Utility Functions + +```typescript +// URL utilities +import { urlWithBaseUrl } from '@utils/urlWithBaseUrl'; +import { urlWithPrefix } from '@utils/urlWithPrefix'; + +// Add base URL to paths (base URL is product-specific) +urlWithBaseUrl('/example') // → '/<product-base>/example' (with configured base) + +// Add framework prefix +urlWithPrefix({ framework: 'react', url: './quick-start' }) // → '/react/quick-start' + +// Framework detection +import { getFrameworkFromPath } from '@components/docs/utils/urlPaths'; +const framework = getFrameworkFromPath(Astro.url.pathname); +``` diff --git a/external/ag-shared/prompts/skills/website-astro/page-patterns.md b/external/ag-shared/prompts/skills/website-astro/page-patterns.md new file mode 100644 index 00000000000..d6494d71575 --- /dev/null +++ b/external/ag-shared/prompts/skills/website-astro/page-patterns.md @@ -0,0 +1,159 @@ +# Astro Page Patterns + +All pages go in `packages/<website-package>/src/pages/`. The file path determines the URL: + +| File Path | URL | +|-----------|-----| +| `pages/index.astro` | `/` | +| `pages/pricing.astro` | `/pricing` | +| `pages/community.astro` | `/community` | +| `pages/community/events.astro` | `/community/events` | + +--- + +## Pattern 1: Full Custom Page with Layout + +Use this for pages that need custom content and styling. + +```astro +--- +import Layout from '@layouts/Layout.astro'; +import styles from '@pages-styles/my-page.module.scss'; +import { urlWithBaseUrl } from '@utils/urlWithBaseUrl'; + +// Optional: Fetch data from content collections +import { getEntry, type CollectionEntry } from 'astro:content'; +const { data: navData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; +--- + +<Layout + title="Page Title | <Product Name>" + description="SEO description for the page" + showSearchBar={true} + showDocsNav={false} +> + <div class={styles.pageContainer}> + <h1>My Page</h1> + <p>Content goes here</p> + </div> +</Layout> +``` + +--- + +## Pattern 2: Wrapper Page (Delegates to Shared Component) + +Use this when a shared component handles all the page logic. + +```astro +--- +import { getEntry, type CollectionEntry } from 'astro:content'; +import WhatsNew from '@ag-website-shared/components/whats-new/pages/whats-new.astro'; + +// Note: version entry keys are product-specific (e.g. 'ag-grid-versions', 'ag-charts-versions') +const { data: versionsData } = (await getEntry('versions', '<product>-versions')) as CollectionEntry<'versions'>; +const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; +--- + +<!-- Note: site prop is product-specific (e.g. 'grid', 'charts', 'dash') --> +<WhatsNew site="<product-site>" versionsData={versionsData} menuData={docsNavData} /> +``` + +--- + +## Pattern 3: Minimal Wrapper (Simplest) + +For pages that just render a shared component with no data. + +```astro +--- +import Home from '@ag-website-shared/components/community/pages/home.astro'; +--- + +<Home /> +``` + +--- + +## Pattern 4: Page with React Components + +Use this for interactive pages with client-side functionality. + +```astro +--- +import { LicensePricing } from '@ag-website-shared/components/license-pricing/LicensePricing'; +import Layout from '@layouts/Layout.astro'; +--- + +<Layout + title="<Product Name>: License and Pricing" + description="View license and pricing details." + showSearchBar={true} + showDocsNav={false} +> + <LicensePricing client:load /> +</Layout> +``` + +--- + +## Pattern 5: Page with Docs Navigation + +Use this for pages that should show the documentation sidebar. + +```astro +--- +import { getEntry, type CollectionEntry } from 'astro:content'; +import Layout from '@layouts/Layout.astro'; +import { getFrameworkFromPath } from '@components/docs/utils/urlPaths'; +import { Pipeline } from '@ag-website-shared/components/changelog/Pipeline'; +import { DocsNavFromLocalStorage } from '@ag-website-shared/components/docs-navigation/DocsNavFromLocalStorage'; +import styles from '@ag-website-shared/components/changelog/changelog.module.scss'; +import classnames from 'classnames'; + +const path = Astro.url.pathname; +const framework = getFrameworkFromPath(path); +const { data: docsNavData } = (await getEntry('docsNav', 'nav')) as CollectionEntry<'docsNav'>; +--- + +<Layout + title="Pipeline | <Product Name>" + description="Lists feature requests and bugs in our backlog." + showSearchBar={true} + showDocsNav={true} +> + <div class="layout-grid"> + <DocsNavFromLocalStorage client:load menuData={docsNavData} framework={framework} /> + + <div className={classnames('page-margin', styles.container)}> + <h1>Pipeline</h1> + <!-- Note: library prop is product-specific (e.g. 'grid', 'charts', 'dash') --> + <Pipeline client:load library="<product-library>" /> + </div> + </div> +</Layout> +``` + +--- + +## Pattern 6: Standalone Page (No Layout) + +For special pages like demos that need full control. + +```astro +--- +import HeroDashboard from '@components/hero-dashboard/HeroDashboard.astro'; +import '@pages-styles/scratchpad.scss'; +import '@pages-styles/example-controls.css'; +--- + +<!doctype html> +<html lang="en" translate="no"> + <head> + <title><Product Name> + + + + + +``` diff --git a/external/ag-shared/prompts/skills/website-astro/shared-components.md b/external/ag-shared/prompts/skills/website-astro/shared-components.md new file mode 100644 index 00000000000..9bfd6a28482 --- /dev/null +++ b/external/ag-shared/prompts/skills/website-astro/shared-components.md @@ -0,0 +1,50 @@ +# Shared Components & Imports + +## Available Shared Components + +Key components from `@ag-website-shared/components/`: + +| Component | Import Path | Purpose | +|-----------|-------------|---------| +| `LicensePricing` | `license-pricing/LicensePricing` | Pricing page | +| `Pipeline` | `changelog/Pipeline` | Development pipeline | +| `WhatsNew` | `whats-new/pages/whats-new.astro` | Release notes | +| `DocsNavFromLocalStorage` | `docs-navigation/DocsNavFromLocalStorage` | Docs sidebar | +| `FrameworkTextAnimation` | `framework-text-animation/FrameworkTextAnimation` | Animated framework text | +| `LandingPageFWSelector` | `landing-pages/LandingPageFWSelector` | Framework selector | +| `Footer` | `footer/Footer` | Site footer | +| `SiteHeader` | `site-header/SiteHeader.astro` | Site header | + +## Path Aliases + +| Alias | Path | +|-------|------| +| `@components/*` | `src/components/*` | +| `@layouts/*` | `src/layouts/*` | +| `@pages-styles/*` | `src/pages-styles/*` | +| `@stores/*` | `src/stores/*` | +| `@utils/*` | `src/utils/*` | +| `@constants` | `src/constants.ts` | +| `@ag-website-shared/*` | `external/ag-website-shared/src/*` | + +## React Component Hydration + +When using React components in Astro pages, add hydration directives: + +| Directive | When to Use | +|-----------|-------------| +| `client:load` | Needs immediate interactivity (most common) | +| `client:idle` | Can wait until browser is idle | +| `client:visible` | Only when scrolled into view | +| (none) | Static content only, no JavaScript | + +```astro + + + + + + + + +``` diff --git a/external/ag-shared/prompts/skills/website-css/SKILL.md b/external/ag-shared/prompts/skills/website-css/SKILL.md new file mode 100644 index 00000000000..d09807a4b75 --- /dev/null +++ b/external/ag-shared/prompts/skills/website-css/SKILL.md @@ -0,0 +1,87 @@ +--- +targets: ['*'] +name: website-css +description: 'CSS architecture, design system, design tokens, utility classes, and styling patterns for AG product websites' +--- + +# Website CSS & Styling + +This skill provides the CSS architecture, design system, and styling patterns for AG product websites. + +## Critical Rules + +These rules apply to **every** SCSS/CSS file edit: + +1. **Always import the design system:** `@use 'design-system' as *;` at the top of SCSS files +2. **Use semantic variables** (`--color-bg-primary`) — never raw colours (`--color-gray-50`) or hardcoded hex values +3. **Dark mode uses `data-dark-mode`:** Use `#{$selector-darkmode} &` in SCSS or `[data-dark-mode='true']` in CSS. Never use `prefers-color-scheme` +4. **Prefer standard utility classes** over custom styles (layout, typography, colour classes) +5. **SCSS modules for components:** Use `.module.scss` files with `@use 'design-system' as *` +6. **Test both light and dark modes** for any visual changes + +## Sub-Documents + +Load based on what you need: + +| Document | Purpose | When to Load | +|----------|---------|-------------| +| `colour-palette.md` | Grey, brand, warning, and special colour variables | Choosing colours or checking values | +| `design-tokens.md` | Spacing, breakpoints, typography, layout, radius, shadows | Using spacing, responsive breakpoints, or typography tokens | +| `utility-classes.md` | Layout grid, typography, colour, and interaction utility classes | Using or choosing standard classes | +| `dark-mode.md` | Dark mode patterns, SCSS/CSS/JS approaches, theme-aware components | Implementing dark mode support | + +## Design System Location + +``` +external/ag-website-shared/src/design-system/ +``` + +| File | Purpose | +|------|---------| +| `_root.scss` | All CSS custom properties (colours, typography, layout, shadows) | +| `core/_variables.scss` | SCSS variables (spacing, selectors, transitions) | +| `core/_breakpoints.scss` | Responsive breakpoint values | +| `_layout.scss` | Layout utility classes | +| `_typography.scss` | Typography utility classes | +| `_color.scss` | Colour utility classes | +| `_interactions.scss` | Interactive state classes | +| `_base.scss` | Base element styles | + +## Creating Custom Page Styles + +Only create custom styles when standard classes don't meet your needs. Create SCSS modules in `packages//src/pages-styles/`: + +```scss +// my-page.module.scss +@use 'design-system' as *; + +.heroSection { + padding-top: $spacing-size-16; + background-color: var(--color-bg-site-header); + + // Dark mode support + #{$selector-darkmode} & { + background-color: var(--color-bg-secondary); + } + + // Responsive + @media screen and (min-width: $breakpoint-hero-large) { + padding-top: $spacing-size-24; + } +} +``` + +## Using Styles in Astro + +```astro +--- +import styles from '@pages-styles/my-page.module.scss'; +import classnames from 'classnames'; +--- + +
+
+

Title

+
+
+``` diff --git a/external/ag-shared/prompts/skills/website-css/colour-palette.md b/external/ag-shared/prompts/skills/website-css/colour-palette.md new file mode 100644 index 00000000000..3760fa291fe --- /dev/null +++ b/external/ag-shared/prompts/skills/website-css/colour-palette.md @@ -0,0 +1,108 @@ +# Colour Palette Reference + +## How Variables Are Organised + +The design system uses CSS custom properties organised into semantic categories: + +```scss +:root { + // Abstract colours (raw palette) + --color-gray-50: #f9fafb; + --color-gray-100: #f2f4f7; + // ... through gray-950 + + --color-brand-50: #f4f8ff; + --color-brand-100: #e5effd; + // ... through brand-950 + + // Semantic colours (use these in components) + --color-bg-primary: var(--color-white); + --color-fg-primary: var(--color-gray-900); + --color-border-primary: var(--color-gray-300); +} +``` + +## Grey Scale + +| Variable | Light Mode | Hex | +| ------------------ | ---------- | --------- | +| `--color-gray-25` | Lightest | `#fcfcfd` | +| `--color-gray-50` | | `#f9fafb` | +| `--color-gray-100` | | `#f2f4f7` | +| `--color-gray-200` | | `#eaecf0` | +| `--color-gray-300` | | `#d0d5dd` | +| `--color-gray-400` | | `#98a2b3` | +| `--color-gray-500` | | `#667085` | +| `--color-gray-600` | | `#475467` | +| `--color-gray-700` | | `#344054` | +| `--color-gray-800` | | `#182230` | +| `--color-gray-900` | | `#101828` | +| `--color-gray-950` | Darkest | `#0c111d` | + +## Brand Colours (Blue) + +| Variable | Hex | +| ------------------- | --------- | +| `--color-brand-50` | `#f4f8ff` | +| `--color-brand-100` | `#e5effd` | +| `--color-brand-200` | `#d4e3f8` | +| `--color-brand-300` | `#a9c5ec` | +| `--color-brand-400` | `#3d7acd` | +| `--color-brand-500` | `#0e4491` | +| `--color-brand-600` | `#0042a1` | +| `--color-brand-700` | `#00388f` | +| `--color-brand-800` | `#002e7e` | +| `--color-brand-900` | `#00246c` | +| `--color-brand-950` | `#001a5a` | + +## Warning Colours (Orange/Yellow) + +| Variable | Hex | +| --------------------- | --------- | +| `--color-warning-50` | `#fffaeb` | +| `--color-warning-100` | `#fef0c7` | +| `--color-warning-200` | `#fedf89` | +| `--color-warning-300` | `#fec84b` | +| `--color-warning-400` | `#fdb022` | +| `--color-warning-500` | `#f79009` | +| `--color-warning-600` | `#dc6803` | +| `--color-warning-700` | `#b54708` | +| `--color-warning-800` | `#93370d` | +| `--color-warning-900` | `#7a2e0e` | +| `--color-warning-950` | `#4e1d09` | + +## Special Colours + +| Variable | Hex | Usage | +| ------------------ | ------------------------------------ | --------------------- | +| `--color-success` | `#28a745` (light) / `#64ea82` (dark) | Success states | +| `--color-positive` | `#28a745` | Positive indicators | +| `--color-negative` | `#dc3545` | Error/negative states | + +## Semantic Colour Categories + +### Background Colours (`--color-bg-*`) + +- `--color-bg-primary`: Main content background +- `--color-bg-secondary`: Secondary/elevated surfaces +- `--color-bg-tertiary`: Subtle backgrounds +- `--color-bg-toolbar`: Toolbar backgrounds +- `--color-bg-code`: Code block backgrounds + +### Foreground/Text Colours (`--color-fg-*`) + +- `--color-fg-primary`: Primary text +- `--color-fg-secondary`: Secondary/muted text +- `--color-fg-tertiary`: Subtle text +- `--color-fg-disabled`: Disabled state text + +### Border Colours (`--color-border-*`) + +- `--color-border-primary`: Primary borders +- `--color-border-secondary`: Subtle borders +- `--color-border-tertiary`: Very subtle borders + +### Link Colours (`--color-link*`) + +- `--color-link`: Default link colour +- `--color-link-hover`: Link hover state diff --git a/external/ag-shared/prompts/skills/website-css/dark-mode.md b/external/ag-shared/prompts/skills/website-css/dark-mode.md new file mode 100644 index 00000000000..70bf8e9974b --- /dev/null +++ b/external/ag-shared/prompts/skills/website-css/dark-mode.md @@ -0,0 +1,120 @@ +# Dark Mode + +## How Dark Mode Works + +Dark mode is triggered by the `data-dark-mode="true"` attribute on the `` element: + +```scss +html[data-dark-mode='true'] { + --color-bg-primary: color-mix(in srgb, var(--color-gray-800), var(--color-gray-900) 50%); + --color-fg-primary: var(--color-white); + // ... other overrides +} +``` + +## Dark Mode in SCSS Modules + +Use the `$selector-darkmode` SCSS variable for dark mode overrides in component styles: + +```scss +.myElement { + background-color: var(--color-bg-primary); + + #{$selector-darkmode} & { + background-color: var(--color-bg-secondary); + } +} +``` + +## Key Dark Mode Colours + +| Semantic Variable | Light Mode | Dark Mode | +| -------------------------- | ---------- | ---------------------------------------- | +| `--color-bg-primary` | `#ffffff` | Mix of `#182230` + `#101828` | +| `--color-bg-secondary` | `#f9fafb` | `#344054` | +| `--color-bg-tertiary` | `#f2f4f7` | `#182230` | +| `--color-fg-primary` | `#101828` | `#ffffff` | +| `--color-fg-secondary` | `#344054` | `#d0d5dd` | +| `--color-border-primary` | `#d0d5dd` | `#344054` | +| `--color-border-secondary` | `#eaecf0` | Mix of `#344054` + bg-primary | +| `--color-link` | `#0e4491` | `#a9c5ec` | + +## Detecting Dark Mode in JavaScript + +```typescript +// Check data attribute (preferred) +const isDark = document.documentElement.getAttribute('data-dark-mode') === 'true'; + +// Or check for dark mode class (fallback) +const isDark = document.documentElement.classList.contains('dark'); +``` + +## Creating Theme-Aware Components + +Use CSS custom properties that react to `data-dark-mode`: + +```css +/* Define variables for both modes */ +:root { + --my-component-bg: #ffffff; + --my-component-text: #101828; +} + +[data-dark-mode='true'] { + --my-component-bg: #182230; + --my-component-text: #d0d5dd; +} + +/* Use variables in component */ +.my-component { + background: var(--my-component-bg); + color: var(--my-component-text); +} +``` + +This approach ensures instant theme switching without JavaScript re-rendering. + +## Adding New Theme-Aware Styles + +When creating new components or features that need to support both themes: + +1. **Define CSS variables** in a `