From c237f3bab27ee52dff57504bb2d10590f2524c62 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 13 Feb 2026 12:36:29 +0100 Subject: [PATCH 1/4] chore(agents): remove agent-rules script --- automation/utils/bin/rui-agent-rules.ts | 50 ------------------------- automation/utils/package.json | 2 - package.json | 1 - turbo.json | 5 --- 4 files changed, 58 deletions(-) delete mode 100755 automation/utils/bin/rui-agent-rules.ts diff --git a/automation/utils/bin/rui-agent-rules.ts b/automation/utils/bin/rui-agent-rules.ts deleted file mode 100755 index db560e9f35..0000000000 --- a/automation/utils/bin/rui-agent-rules.ts +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env ts-node-script - -import { mkdir, readdir, lstat, symlink, unlink } from "fs/promises"; -import path from "node:path"; - -async function ensureSymlink(targetRel: string, linkPath: string): Promise { - try { - const stat = await lstat(linkPath).catch(() => null); - if (stat) { - await unlink(linkPath); - } - await symlink(targetRel, linkPath); - } catch (err) { - console.error(`Failed to create symlink for ${linkPath}:`, err); - } -} - -async function main(): Promise { - const repoRoot = path.resolve(process.cwd(), "../.."); - const SRC_DIR = path.join(repoRoot, "docs", "requirements"); - const CURSOR_DIR = path.join(repoRoot, ".cursor", "rules"); - const WINDSURF_DIR = path.join(repoRoot, ".windsurf", "rules"); - - // Ensure target directories exist - await Promise.all([mkdir(CURSOR_DIR, { recursive: true }), mkdir(WINDSURF_DIR, { recursive: true })]); - - const files = (await readdir(SRC_DIR)).filter(f => f.endsWith(".md")); - - await Promise.all( - files.map(async file => { - const base = path.basename(file, ".md"); - const srcAbsolute = path.join(SRC_DIR, file); - - const cursorLink = path.join(CURSOR_DIR, `${base}.mdc`); - const windsurfLink = path.join(WINDSURF_DIR, `${base}.md`); - - const relFromCursor = path.relative(path.dirname(cursorLink), srcAbsolute); - const relFromWindsurf = path.relative(path.dirname(windsurfLink), srcAbsolute); - - await Promise.all([ensureSymlink(relFromCursor, cursorLink), ensureSymlink(relFromWindsurf, windsurfLink)]); - }) - ); - - console.log("Agent rules links updated."); -} - -main().catch(err => { - console.error(err); - process.exit(1); -}); diff --git a/automation/utils/package.json b/automation/utils/package.json index 651a47e48f..76b15c56b0 100644 --- a/automation/utils/package.json +++ b/automation/utils/package.json @@ -5,7 +5,6 @@ "copyright": "© Mendix Technology BV 2025. All rights reserved.", "private": true, "bin": { - "rui-agent-rules": "bin/rui-agent-rules.ts", "rui-create-gh-release": "bin/rui-create-gh-release.ts", "rui-create-translation": "bin/rui-create-translation.ts", "rui-generate-package-xml": "bin/rui-generate-package-xml.ts", @@ -25,7 +24,6 @@ "tsconfig.json" ], "scripts": { - "agent-rules": "ts-node bin/rui-agent-rules.ts", "changelog": "ts-node bin/rui-changelog-helper.ts", "compile:parser:module": "peggy -o ./src/changelog-parser/parser/widget/widget.js ./src/changelog-parser/parser/widget/widget.pegjs", "compile:parser:widget": "peggy -o ./src/changelog-parser/parser/module/module.js ./src/changelog-parser/parser/module/module.pegjs", diff --git a/package.json b/package.json index 5d72d8091f..5ed811b161 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "create-gh-release": "turbo run create-gh-release --concurrency 1", "create-translation": "turbo run create-translation", "include-oss-in-artifact": "pnpm --filter @mendix/automation-utils run include-oss-in-artifact", - "postinstall": "turbo run agent-rules", "lint": "turbo run lint --continue --concurrency 1", "oss-clearance": "pnpm --filter @mendix/automation-utils run oss-clearance", "prepare": "husky install", diff --git a/turbo.json b/turbo.json index 648f49bc9d..98afa28d8e 100644 --- a/turbo.json +++ b/turbo.json @@ -75,11 +75,6 @@ "!*/**/__tests__/**" ], "outputs": [] - }, - "agent-rules": { - "cache": false, - "inputs": ["docs/requirements/**"], - "outputs": [] } } } From 1e8dc695826d72766babab382ec308fbb61b9525 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 13 Feb 2026 12:38:39 +0100 Subject: [PATCH 2/4] feat(agents): introduce PostToolUse hook for Claude Code and Copilot, auto-lint script --- .claude/hooks/post-edit.sh | 52 ++++++++++++++++++++++++++++++++++++++ .claude/settings.json | 15 +++++++++++ .github/hooks/hooks.json | 11 ++++++++ .gitignore | 1 + 4 files changed, 79 insertions(+) create mode 100755 .claude/hooks/post-edit.sh create mode 100644 .claude/settings.json create mode 100644 .github/hooks/hooks.json diff --git a/.claude/hooks/post-edit.sh b/.claude/hooks/post-edit.sh new file mode 100755 index 0000000000..f9991b30db --- /dev/null +++ b/.claude/hooks/post-edit.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Auto-format and lint after file edits. +# Works with both Claude Code (PostToolUse) and GitHub Copilot (postToolUse) hooks. +# +# Input formats: +# Claude Code: {"tool_input": {"file_path": "/path/to/file"}} +# Copilot: {"toolName": "edit", "toolArgs": "{\"path\": \"/path/to/file\"}"} +# +# Exit codes: +# 0 = success (formatting applied silently) +# 2 = lint errors found (fed back to Claude as feedback; ignored by Copilot) + +# Parse file path - handles both Claude Code and Copilot input formats +FILE_PATH=$(cat | node -e " + let d=''; + process.stdin.on('data',c=>d+=c); + process.stdin.on('end',()=>{ + try { + const j=JSON.parse(d); + if (j.tool_input?.file_path) { console.log(j.tool_input.file_path); return; } + if (j.toolArgs) { const a=JSON.parse(j.toolArgs); if (a.path) console.log(a.path); } + } catch {} + }) +") + +# Skip if no file path or file doesn't exist +[[ -z "$FILE_PATH" || ! -f "$FILE_PATH" ]] && exit 0 + +# Auto-format with prettier using the project's installed version +npx prettier --write "$FILE_PATH" 2>/dev/null + +# Find the nearest package directory with eslint config +SEARCH_DIR=$(dirname "$FILE_PATH") +PACKAGE_DIR="" +while [[ "$SEARCH_DIR" != "/" ]]; do + if [[ -f "$SEARCH_DIR/eslint.config.mjs" ]]; then + PACKAGE_DIR="$SEARCH_DIR" + break + fi + SEARCH_DIR=$(dirname "$SEARCH_DIR") +done + +# Run lint from the package directory using the project's lint setup +if [[ -n "$PACKAGE_DIR" ]]; then + LINT_OUTPUT=$(cd "$PACKAGE_DIR" && npm run lint 2>&1) + if [[ $? -ne 0 ]]; then + echo "$LINT_OUTPUT" >&2 + exit 2 + fi +fi + +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000000..645c92480f --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": ".claude/hooks/post-edit.sh" + } + ] + } + ] + } +} diff --git a/.github/hooks/hooks.json b/.github/hooks/hooks.json new file mode 100644 index 0000000000..d8c4374f37 --- /dev/null +++ b/.github/hooks/hooks.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "hooks": { + "postToolUse": [ + { + "type": "command", + "bash": "bash .claude/hooks/post-edit.sh" + } + ] + } +} diff --git a/.gitignore b/.gitignore index 70104cb4d9..05f92d3d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ tests automation/run-e2e/ctrf/*.json .cursor .windsurf +.claude/settings.local.json From 52f669e5fd2973567213ea6e09b166162f018416 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 13 Feb 2026 12:40:33 +0100 Subject: [PATCH 3/4] chore(agents): add CLAUDE.md, refine AGENTS.md --- AGENTS.md | 111 +++++++++++++----------------------------------------- CLAUDE.md | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 85 deletions(-) create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md index 2b77c77fc7..53fb367c83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,94 +1,35 @@ -# Mendix Web Widgets Repository - AI Agent Overview +# Mendix Web Widgets Repository -This document provides a comprehensive overview of the Mendix Web Widgets repository structure and conventions for AI development assistants. This repository contains the official collection of pluggable web widgets for the Mendix low-code platform. +Monorepo of official Mendix pluggable web widgets. pnpm workspaces + Turbo. -## Repository Overview +## Structure -The **Mendix Web Widgets** repository is the official home of all Mendix platform-supported pluggable web widgets and related modules. These are reusable UI components built with modern web technologies (React, TypeScript, SCSS) that integrate seamlessly into Mendix Studio Pro applications. +- `packages/pluggableWidgets/*-web` — Widget packages (React, TypeScript, SCSS) +- `packages/modules/*` — Mendix module packages +- `packages/shared/*` — Shared configs and utilities +- `automation/` — Build and release automation +- `docs/requirements/` — Detailed technical requirements -### Key Characteristics -- **Monorepo structure** using pnpm workspaces and Turbo for build orchestration -- **Modern web stack**: TypeScript, React, SCSS, Jest, ESLint, Prettier -- **Mendix integration**: Built using Mendix Pluggable Widgets API -- **Atlas UI alignment**: Consistent with Mendix's design system -- **Comprehensive testing**: Unit tests (Jest/RTL), E2E tests (Playwright) +## Commands -## Repository Structure +| Command | Description | +|---------|-------------| +| `pnpm install` | Install dependencies | +| `pnpm build` | Build all packages | +| `pnpm lint` | Lint all packages | +| `pnpm test` | Test all packages | +| `pnpm --filter @mendix/ run