Customise Easy8 with a coding agent¶
A coding agent such as Claude Code, Codex or OpenCode can write Easy8 customisations for you. Set it up so that every change lands in your own modification plugin; that is what lets the change survive an Easy8 update.
Why a modification plugin¶
An Easy8 update replaces the core and every Easy8 plugin. Code you change there is lost with the next version. Keep all your code in one plugin named modification_<name> in plugins/easyproject/easy_plugins/:
- Easy8 always loads it, after all its own plugins, so your views and patches take precedence.
- You carry the folder over to the next version unchanged and run its tests.
See Where to put your code for the plugin structure and how to deploy it.
Before you start¶
- The source package (zip) of the Easy8 version you run, downloaded from Client Zone, with a working development environment. See Set up a development environment.
- A coding agent that you start in the root folder of the Easy8 source.
Give the agent the Easy8 rules¶
Save the skill below in the Easy8 folder:
- Claude Code:
.claude/skills/modification-plugin/SKILL.md - OpenCode:
.opencode/skills/modification-plugin/SKILL.md - Other agents: add the text to
AGENTS.mdin the Easy8 folder.
---
name: modification-plugin
description: "Build or change customer-specific behaviour of Easy8 in a modification_<name> plugin that survives Easy8 updates. Use for any customisation requested for one customer or installation."
---
# Easy8 modification plugin
Customer code lives only in `plugins/easyproject/easy_plugins/modification_<name>/`. An Easy8 update replaces the core and every other plugin, so a change anywhere else is lost. Easy8 always activates `modification*` plugins and loads them after its own plugins (`easy_lib/easy_plugin_loader.rb`).
## Boundaries
- Change files only inside the plugin. If the request cannot be done from the plugin, stop and explain which hook or patch point is missing; do not edit the core, other plugins or gems.
- The one exception is branding on a source installation: logos, fonts and the login background go to `app/frontend/assets/custom_brand/` and `app/frontend/public/custom_brand/`, colours to `app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss`. On Docker, branding stays in the plugin, see Deployment limits.
- Use one plugin per customer. Ask for its name if the request does not give one.
- Guard optional Easy8 plugins with `Redmine::Plugin.installed?(:plugin_name)` before using their classes, routes or menus.
## Structure
Easy8 treats the folder as a plugin when it has `lib/` or a `Gemfile`. It requires `lib/<plugin_name>.rb` and every `config/initializers/**/*.rb`, loads `config/locales/*.yml` and `config/routes.rb` (plain route DSL, no `Rails.application.routes.draw`), prepends `app/views`, autoloads `app/*` and loads `patches/**/*.rb`.
| Need | Use |
| --- | --- |
| New screen or endpoint | own controller, views and routes in the plugin |
| Add markup to an existing screen | view hook: a `Redmine::Hook::ViewListener` subclass registered with `EasyInitHelper.register_redmine_view_hooks("ModificationAcme::Hooks")` |
| Change behaviour of an existing class | module in `patches/` registered with `EasyPatchManager.register_model_patch`, `register_controller_patch`, `register_helper_patch` or `register_other_patch` |
| Permissions, menus | `EasyInitHelper.register_access_control_block`, `EasyInitHelper.register_menu_block` in `config/initializers/` |
| Replace an existing view | same path under the plugin's `app/views`; last resort, the copy goes stale with every update |
- Namespace classes under the plugin module (`ModificationAcme::`). Prefix new tables and locale keys with the plugin name.
- Migrations go to the plugin's `db/migrate/` as `ActiveRecord::Migration[8.1]` with a UTC timestamp file name (`Time.now.utc.strftime("%Y%m%d%H%M%S")`). Data migrations go to `db/data/`. Never change a migration that already ran; add a new one.
- Frontend: server-rendered views or Hotwire for new screens. Tailwind classes in plugin files are not compiled; use the design system helpers or the plugin's own stylesheet.
- Specs go to the plugin's `test/spec/` and start with `require "easy_extensions/spec_helper"`.
## Deployment limits
- Docker installations mount the plugin into the `app` and `sidekiq` containers. The image contains precompiled assets, so a mounted plugin cannot add stylesheets or scripts that Easy8 compiles. Tell the user when a change needs an asset build.
- Branding on Docker: a `view_layouts_base_html_head_top` hook renders a partial that overrides the CSS variables, and the brand files are mounted to `public/<plugin_name>/`. Never mount `favicon.ico` into `custom_brand`; every page then fails with HTTP 500. Guide: https://developers.easy8.com/Frontend_tutorials/theme_configuration/#branding-on-docker
- Source installations rebuild assets with `RAILS_ENV=production bundle exec rails assets:precompile`.
## Verify
1. `git status` (or a diff) shows changes only inside the plugin folder.
2. `RAILS_ENV=development bundle exec rails easy8:install` when migrations changed.
3. `bundle exec rspec plugins/easyproject/easy_plugins/modification_<name>/test/spec`.
4. `bundle exec rubocop` on changed `.rb` files.
Guides: https://developers.easy8.com/Getting_started/Repository-structure/
Ask for the change¶
Name the plugin in the first request and keep using it:
Create the modification plugin modification_acme. Add a "Cost centre" field to projects,
show it on the project overview and let administrators edit it. Write specs and run them.
Describe what users should see and who may change it, not how to code it. Ask for specs in every request.
Check the result¶
- Run
git statusor compare the folder with the original package. Every changed path must start withplugins/easyproject/easy_plugins/modification_acme/. If the agent changed anything else, ask it to move the change into the plugin. -
Apply the plugin's migrations:
-
Run the plugin's specs:
Then deploy the plugin as described in Deploy the plugin.
After an Easy8 update¶
Download the source package of the new version, copy the plugin folder into it and run easy8:install and the plugin's specs. When a spec fails because Easy8 changed a class your plugin patches, ask the agent to adapt the patch to the new version.