EEGPrep Extension Development
Use this skill to build extension packages that work as standalone Python
packages and feel natural inside EEGPrep, including for researchers migrating
from EEGLAB.
Start Here
- Read
AGENTS.md and follow .agents/skills/eegprep-feature-development/SKILL.md
for repo workflow, notes, tests, and PR expectations.
- Inspect
src/eegprep/extensions.py and docs/source/api/extensions.rst before
writing registration code. Use real SDK symbols, especially
ExtensionSpec, ExtensionAction, ExtensionMenu, ExtensionPopFunction,
ExtensionResource, ExtensionDependency, LazyImport,
validate_extension_spec, and the eegprep.extensions entry-point group.
- If porting an EEGLAB plugin or workflow, inspect the matching EEGLAB MATLAB
source during development. Do not make runtime extension code depend on a
local EEGLAB checkout.
- Check
examples/extensions/ for templates, scaffolding, and focused example
packages. Reference or use them where available; do not duplicate their full
implementation inside custom extension code.
Plan the Extension
Define:
- Distribution path: local/private editable, GitHub-only, PyPI, or private
package index.
- Trust posture: experimental, private lab use, public uncurated, or catalog
submission. Curated means catalog-reviewed and mechanically validated, not
scientific endorsement.
- Contributions: actions, menus,
pop_* functions, help resources, package
data, console aliases, optional dependencies, and large model files.
- EEGPrep version contract through
eegprep_requires and extension API version.
- Tests that prove discovery, validation, lazy imports, resources, history,
console behavior, and GUI behavior where applicable.
Prefer the smallest SDK surface that expresses the feature. If the extension
needs to mutate EEGPrep internals directly, stop and add or request an SDK
surface instead.
Register Through the SDK
Create a normal Python package with pyproject.toml:
[project]
name = "eegprep-ext-foo"
version = "0.1.0"
dependencies = ["eegprep"]
[project.entry-points."eegprep.extensions"]
foo = "eegprep_ext_foo.register:register"
Keep register() lightweight:
from eegprep import ExtensionAction, ExtensionMenu, ExtensionPopFunction, ExtensionSpec, LazyImport
def register():
return ExtensionSpec(
name="foo",
display_name="Foo",
version="0.1.0",
package_name="eegprep-ext-foo",
eegprep_requires=">=0.2",
actions=(
ExtensionAction("foo.run", LazyImport("eegprep_ext_foo.actions", "run")),
),
menus=(ExtensionMenu(("Tools", "Foo"), "foo.run", "Run Foo"),),
pop_functions=(
ExtensionPopFunction("pop_foo", LazyImport("eegprep_ext_foo.pop_foo", "pop_foo")),
),
)
Use LazyImport for processing modules, dialogs, models, and optional
dependencies. A registration function may import the SDK and small metadata; it
must not import heavy code, start Qt, read data files, or call EEGLAB.
Menus, Help, Console, and History
- Declare menus with
ExtensionMenu; do not mutate Qt menu objects or patch
EEGPrep menu builders.
- Keep user-facing actions compatible with
EEGPrepSession. GUI actions should
store datasets, add history, and notify changes through session helpers.
- Implement user-facing
pop_* functions with return_com=True; return
(EEG, com) when history should be recorded.
- Preserve EEGLAB-facing command strings and indexing semantics while using
Python indexing internally.
- Package GUI Help and
pophelp text as Markdown resources and declare them
with ExtensionResource.
- Update the relevant Sphinx docs when an extension changes user-facing menus,
workflows, console/history behavior, optional dependency expectations,
package data, curation metadata, or troubleshooting guidance. Keep the docs
centered on standalone EEGPrep behavior, with EEGLAB comparisons only where
they clarify migration or compatibility.
- Put data/model resources in the package and access them with
importlib.resources or declared ExtensionResource records. Do not read
from src/eegprep/eeglab at runtime.
Dependencies and Large Files
- Declare required Python packages normally in
pyproject.toml.
- Use
ExtensionDependency(package, version_spec, optional=True) only for
optional behavior. Required missing dependencies should produce
missing_dependency, not a late crash.
- For large models, choose an explicit path: package extra, private index,
Git LFS-backed package source, or an extension-owned first-run download.
Document the choice and never imply EEGPrep hosts model artifacts.
GUI Extensions
For dialogs, windows, or menu UX:
- Use
.agents/skills/eeglab-gui-visual-parity/SKILL.md to compare labels,
alignment, order, defaults, enabled states, buttons, and workflow against
EEGLAB where an EEGLAB reference exists.
- Exercise mixed GUI plus
eegprep-console flows: GUI action, inspect EEG,
ALLEEG, CURRENTSET, LASTCOM, and ALLCOM; then run the pop_*
function from the console and verify GUI/history refresh.
- Use
.agents/skills/gui-agent-flow-qa/SKILL.md only when explicitly asked
for full GUI agent QA or when the project workflow requires it.
Tests and Checks
Write focused tests for:
- Entry-point discovery through
ExtensionRegistry or discover_extensions.
validate_extension_spec success and failure cases.
- Lazy imports remaining unloaded during discovery.
- Missing help/data resources and missing required dependencies.
- Duplicate action or
pop_* names when relevant.
pop_* return values, return_com=True, history strings, and console
auto-store behavior.
- GUI visual parity and user-flow behavior for GUI extensions.
- Wheel or source distribution contents when packaged resources matter.
Run the narrowest relevant pytest files first, then ./pre-commit.py --fix,
focused tests, and broader ruff/format/ty checks when the change affects
imports, docs, packaging, or public APIs.
Distribution Decision
- Local/private lab only:
uv add --editable /path/to/eegprep-ext-foo.
- GitHub-only:
uv add git+https://github.com/lab/eegprep-ext-foo; pin tags
or commits for reproducibility.
- PyPI:
uv add eegprep-ext-foo after normal package publishing.
- Private index:
uv add --index https://packages.lab.example/simple eegprep-ext-foo or uv add --default-index ... when the private index
replaces PyPI.
EEGPrep does not host extension zips, install arbitrary archives, or treat
curation as scientific endorsement.
Catalog Submission
Before proposing curation, verify:
- The package installs from a standard path and the entry point returns a valid
ExtensionSpec.
- The extension passes validation in a clean environment.
- Menus, help, package data,
pop_* history, and console behavior are covered.
- Optional dependencies and large files are documented and do not block startup.
- Experimental status is explicit in docs and metadata.
- Governance/catalog checks pass when the extension is intended for catalog
submission.
1---2name: eegprep-extension-development3description: Build EEGPrep external extension packages with SDK registration, declarative menus, pop_* console/history behavior, packaged help/data, distribution choices, tests, GUI visual parity, and catalog-readiness. Use when Codex or a researcher needs to create, port, review, package, or submit an EEGPrep extension outside the core package.4---56# EEGPrep Extension Development78Use this skill to build extension packages that work as standalone Python9packages and feel natural inside EEGPrep, including for researchers migrating10from EEGLAB.1112## Start Here13141. Read `AGENTS.md` and follow `.agents/skills/eegprep-feature-development/SKILL.md`15 for repo workflow, notes, tests, and PR expectations.162. Inspect `src/eegprep/extensions.py` and `docs/source/api/extensions.rst` before17 writing registration code. Use real SDK symbols, especially18 `ExtensionSpec`, `ExtensionAction`, `ExtensionMenu`, `ExtensionPopFunction`,19 `ExtensionResource`, `ExtensionDependency`, `LazyImport`,20 `validate_extension_spec`, and the `eegprep.extensions` entry-point group.213. If porting an EEGLAB plugin or workflow, inspect the matching EEGLAB MATLAB22 source during development. Do not make runtime extension code depend on a23 local EEGLAB checkout.244. Check `examples/extensions/` for templates, scaffolding, and focused example25 packages. Reference or use them where available; do not duplicate their full26 implementation inside custom extension code.2728## Plan the Extension2930Define:3132- Distribution path: local/private editable, GitHub-only, PyPI, or private33 package index.34- Trust posture: experimental, private lab use, public uncurated, or catalog35 submission. Curated means catalog-reviewed and mechanically validated, not36 scientific endorsement.37- Contributions: actions, menus, `pop_*` functions, help resources, package38 data, console aliases, optional dependencies, and large model files.39- EEGPrep version contract through `eegprep_requires` and extension API version.40- Tests that prove discovery, validation, lazy imports, resources, history,41 console behavior, and GUI behavior where applicable.4243Prefer the smallest SDK surface that expresses the feature. If the extension44needs to mutate EEGPrep internals directly, stop and add or request an SDK45surface instead.4647## Register Through the SDK4849Create a normal Python package with `pyproject.toml`:5051```toml52[project]53name = "eegprep-ext-foo"54version = "0.1.0"55dependencies = ["eegprep"]5657[project.entry-points."eegprep.extensions"]58foo = "eegprep_ext_foo.register:register"59```6061Keep `register()` lightweight:6263```python64from eegprep import ExtensionAction, ExtensionMenu, ExtensionPopFunction, ExtensionSpec, LazyImport656667def register():68 return ExtensionSpec(69 name="foo",70 display_name="Foo",71 version="0.1.0",72 package_name="eegprep-ext-foo",73 eegprep_requires=">=0.2",74 actions=(75 ExtensionAction("foo.run", LazyImport("eegprep_ext_foo.actions", "run")),76 ),77 menus=(ExtensionMenu(("Tools", "Foo"), "foo.run", "Run Foo"),),78 pop_functions=(79 ExtensionPopFunction("pop_foo", LazyImport("eegprep_ext_foo.pop_foo", "pop_foo")),80 ),81 )82```8384Use `LazyImport` for processing modules, dialogs, models, and optional85dependencies. A registration function may import the SDK and small metadata; it86must not import heavy code, start Qt, read data files, or call EEGLAB.8788## Menus, Help, Console, and History8990- Declare menus with `ExtensionMenu`; do not mutate Qt menu objects or patch91 EEGPrep menu builders.92- Keep user-facing actions compatible with `EEGPrepSession`. GUI actions should93 store datasets, add history, and notify changes through session helpers.94- Implement user-facing `pop_*` functions with `return_com=True`; return95 `(EEG, com)` when history should be recorded.96- Preserve EEGLAB-facing command strings and indexing semantics while using97 Python indexing internally.98- Package GUI Help and `pophelp` text as Markdown resources and declare them99 with `ExtensionResource`.100- Update the relevant Sphinx docs when an extension changes user-facing menus,101 workflows, console/history behavior, optional dependency expectations,102 package data, curation metadata, or troubleshooting guidance. Keep the docs103 centered on standalone EEGPrep behavior, with EEGLAB comparisons only where104 they clarify migration or compatibility.105- Put data/model resources in the package and access them with106 `importlib.resources` or declared `ExtensionResource` records. Do not read107 from `src/eegprep/eeglab` at runtime.108109## Dependencies and Large Files110111- Declare required Python packages normally in `pyproject.toml`.112- Use `ExtensionDependency(package, version_spec, optional=True)` only for113 optional behavior. Required missing dependencies should produce114 `missing_dependency`, not a late crash.115- For large models, choose an explicit path: package extra, private index,116 Git LFS-backed package source, or an extension-owned first-run download.117 Document the choice and never imply EEGPrep hosts model artifacts.118119## GUI Extensions120121For dialogs, windows, or menu UX:1221231. Use `.agents/skills/eeglab-gui-visual-parity/SKILL.md` to compare labels,124 alignment, order, defaults, enabled states, buttons, and workflow against125 EEGLAB where an EEGLAB reference exists.1262. Exercise mixed GUI plus `eegprep-console` flows: GUI action, inspect `EEG`,127 `ALLEEG`, `CURRENTSET`, `LASTCOM`, and `ALLCOM`; then run the `pop_*`128 function from the console and verify GUI/history refresh.1293. Use `.agents/skills/gui-agent-flow-qa/SKILL.md` only when explicitly asked130 for full GUI agent QA or when the project workflow requires it.131132## Tests and Checks133134Write focused tests for:135136- Entry-point discovery through `ExtensionRegistry` or `discover_extensions`.137- `validate_extension_spec` success and failure cases.138- Lazy imports remaining unloaded during discovery.139- Missing help/data resources and missing required dependencies.140- Duplicate action or `pop_*` names when relevant.141- `pop_*` return values, `return_com=True`, history strings, and console142 auto-store behavior.143- GUI visual parity and user-flow behavior for GUI extensions.144- Wheel or source distribution contents when packaged resources matter.145146Run the narrowest relevant pytest files first, then `./pre-commit.py --fix`,147focused tests, and broader ruff/format/ty checks when the change affects148imports, docs, packaging, or public APIs.149150## Distribution Decision151152- Local/private lab only: `uv add --editable /path/to/eegprep-ext-foo`.153- GitHub-only: `uv add git+https://github.com/lab/eegprep-ext-foo`; pin tags154 or commits for reproducibility.155- PyPI: `uv add eegprep-ext-foo` after normal package publishing.156- Private index: `uv add --index https://packages.lab.example/simple157 eegprep-ext-foo` or `uv add --default-index ...` when the private index158 replaces PyPI.159160EEGPrep does not host extension zips, install arbitrary archives, or treat161curation as scientific endorsement.162163## Catalog Submission164165Before proposing curation, verify:166167- The package installs from a standard path and the entry point returns a valid168 `ExtensionSpec`.169- The extension passes validation in a clean environment.170- Menus, help, package data, `pop_*` history, and console behavior are covered.171- Optional dependencies and large files are documented and do not block startup.172- Experimental status is explicit in docs and metadata.173- Governance/catalog checks pass when the extension is intended for catalog174 submission.