Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .claude/rules/status_conduct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
paths:
- ".status/**"
---

# Working in .status/

- `.status/` is gitignored: the only copy is on this machine. Never delete,
move, or rewrite a file here without asking first. Appending is fine.
- Every markdown file written here opens with a `For humans:` summary - three
or four sentences at the very top: what the file is and what a person needs
to take from it.
- Nothing new is created at the `.status/` root. New material goes in
`plans/`, `prompts/`, `notes/`, or `scratch/`.
- Only `plans/` and `prompts/` are edited in place. `notes/` is write-once.
`decisions.md` is append-only - never rewrite an entry.
- Temporary scripts, experiments, and one-off test files go in
`.status/scratch/` - never the repository root. `scratch/` may hold any
file type and is deleted unread; everywhere else is markdown only.
- Do not read `archive/` unless a file is named for you.
- Filenames: lowercase ASCII, `_` as separator. Notes are
`YYYY-MM-DD_slug.md` (date first); plans and prompts are `slug.md` - no
date, no status word in the name.
45 changes: 45 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"blockReadsOutsideWorkingDirectories": true,
"allow": [
"Bash(python --version)",
"Bash(unzip -l *)",
"Bash(python -m sphinx *)",
"Bash(python docs/patch_matlabdomain.py)",
"Bash(git status)",
"Bash(git status *)",
"Bash(git log)",
"Bash(git log *)",
"Bash(git diff)",
"Bash(git diff *)",
"Bash(git branch)",
"Bash(git ls-files *)",
"Bash(ls)",
"Bash(ls *)",
"Bash(wc *)"
],
"ask": [
"Bash(matlab -batch *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git checkout -- *)",
"Bash(git restore *)",
"Bash(git stash *)"
],
"deny": [
"Read(.env)",
"Read(.envrc)",
"Read(.status/archive/**)",
"Bash(rm -rf *)",
"Bash(rm -r *)",
"Bash(git push)",
"Bash(git push *)",
"Bash(git reset --hard *)",
"Bash(pip install *)",
"Bash(python -m pip install *)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}
46 changes: 46 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Set default behavior to automatically normalize line endings to LF
* text=auto eol=lf

# Explicitly declare text files you want to always be normalized and converted to LF on checkout
*.xml text eol=lf
*.mediawiki text eol=lf
*.tsv text eol=lf
*.md text eol=lf
*.rst text eol=lf
*.txt text eol=lf
*.m text eol=lf
*.tex text eol=lf
*.py text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.toml text eol=lf
*.svg text eol=lf
*.css text eol=lf
*.js text eol=lf
*.html text eol=lf

# Denote all files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.pdf binary
*.zip binary
*.set binary
*.fdt binary
*.gz binary
*.tar binary
*.mp3 binary
*.mp4 binary
*.mov binary
*.avi binary
*.exe binary
*.dll binary
*.so binary
*.dylib binary
*.class binary
*.jar binary
*.war binary
18 changes: 18 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# EEG-Clean-Tools

**`AGENTS.md` at the repository root is the instruction set for this project.
Read it before answering, and follow it.**

This file is a pointer and duplicates nothing. One source, several pointers: a
rule stated in two files is a rule that will disagree with itself.

Path-specific instructions live in `.github/instructions/*.instructions.md`
and load automatically when the files being worked on match their `applyTo`
glob - for example, `status_conduct.instructions.md` applies under `.status/`.
Nothing needs to reference them; this note exists so a reader knows they are
there.

Machine-specific facts - interpreter, local paths, cache locations - are in
`.status/local-environment.md`, which is gitignored: read it when it is there
and ignore its absence when it is not. No committed file in this repository
may contain a local path or a drive letter.
22 changes: 22 additions & 0 deletions .github/instructions/status_conduct.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
applyTo: ".status/**"
---

# Working in .status/

- `.status/` is gitignored: the only copy is on this machine. Never delete,
move, or rewrite a file here without asking first. Appending is fine.
- Every markdown file written here opens with a `For humans:` summary - three
or four sentences at the very top: what the file is and what a person needs
to take from it.
- Nothing new is created at the `.status/` root. New material goes in
`plans/`, `prompts/`, `notes/`, or `scratch/`.
- Only `plans/` and `prompts/` are edited in place. `notes/` is write-once.
`decisions.md` is append-only - never rewrite an entry.
- Temporary scripts, experiments, and one-off test files go in
`.status/scratch/` - never the repository root. `scratch/` may hold any
file type and is deleted unread; everywhere else is markdown only.
- Do not read `archive/` unless a file is named for you.
- Filenames: lowercase ASCII, `_` as separator. Notes are
`YYYY-MM-DD_slug.md` (date first); plans and prompts are `slug.md` - no
date, no status word in the name.
88 changes: 88 additions & 0 deletions .github/workflows/deploy-docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Deploy Documentation

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

# Read-only by default. Only the deploy job, which runs only on pushes to
# master, gets the Pages and OIDC write permissions; the build job runs code
# from the branch (docs/conf.py, the patch script) and stays read-only.
permissions:
contents: read

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
pages: read # actions/configure-pages reads the Pages site settings
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
python-version: '3.10'
enable-cache: true
cache-dependency-glob: "**/pyproject.toml"

- name: Create virtual environment
run: |
uv venv --clear .venv
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH

- name: Install dependencies
run: uv pip install -e ".[docs]"

- name: Patch sphinxcontrib-matlabdomain
run: python docs/patch_matlabdomain.py

- name: Configure Git for GitHub Pages
run: |
git config user.name github-actions
git config user.email github-actions@github.com

- name: Build documentation
run: |
sphinx-build -b html docs docs/_build/html

- name: Setup Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6

- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
path: ./docs/_build/html

#------------------------------------------------
# Deploy Job: Deploys the built site
#------------------------------------------------
deploy:
# This job depends on the 'build' job completing successfully
needs: build
# Only deploy when pushing to master, not on pull requests
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
permissions:
pages: write
id-token: write
runs-on: ubuntu-latest

# Specify the deployment environment
environment:
name: github-pages
# The URL will be automatically set by the deployment step's output
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
# This is the official action for deploying the artifact to GitHub Pages
id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
26 changes: 25 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
*.asv
*.asv

# Personal per-repo Claude Code notes (imports .status/local-environment.md)
CLAUDE.local.md

# Personal per-repo settings (absolute paths, claudeMdExcludes)
.claude/settings.local.json

# Real tokens and machine values; .env.example is the committed documentation
.env

# Working notes, plans, prompts, and decision records. Deliberately NOT
# committed: these repos are public and .status/ holds half-formed thinking.
# The consequence is that it lives on one machine only and is absent from
# fresh clones and from `claude --worktree` worktrees.
.status/

# Documentation build and its Python toolchain
docs/_build/
.venv/
__pycache__/
*.egg-info/

# Output of the example scripts
PrepPipeline/examples/output/
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,

"python.testing.pytestEnabled": false,
"python.testing.unittestEnabled": false
}
69 changes: 69 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# EEG-Clean-Tools

Purpose: the PREP pipeline - standardized early-stage EEG preprocessing (boundary handling, detrending, line-noise removal, robust average referencing, bad-channel detection and interpolation, reporting) as a MATLAB toolbox and an EEGLAB plugin.

Not in scope: EEGLAB itself, which PREP runs inside and depends on, and the downstream analysis PREP deliberately leaves open (final high-pass filtering, ICA).

## Commands

Test framework: none. There is no test suite. Do not add a suite as a side effect of other work.

- Smoke check (no EEGLAB needed): `matlab -batch "addpath(genpath('PrepPipeline')); disp(getPrepVersion())"` - prints the version string, for example `PrepPipeline0.57.0`
- Run standalone: add `PrepPipeline` and its subfolders to the MATLAB path, then `[EEG, params, computationTimes] = prepPipeline(EEG, params)` on an EEGLAB `EEG` structure with channel locations. Needs EEGLAB and the Signal Processing Toolbox on the path.
- Run as a plugin: unzip `EEGLABPlugin/PrepPipeline<version>.zip` into EEGLAB's `plugins/` folder; the menu entry is Tools -> Run PREP pipeline.
- Check the plugin zip: `unzip -l EEGLABPlugin/PrepPipeline<version>.zip`
- Install the docs toolchain: `python -m venv --clear .venv`, activate it, then `python -m pip install -e ".[docs]"` and `python docs/patch_matlabdomain.py` (required after every install of `sphinxcontrib-matlabdomain`; it fixes a Sphinx 7+ incompatibility in that package). Locally, use pip and the activated `.venv`, never `uv` or `uvx`: uv misbehaves on Windows. The GitHub Actions workflows use uv, and that stays.
- Build docs: `python -m sphinx -b html docs docs/_build/html` - `.github/workflows/deploy-docs.yaml` runs the same build and publishes it to GitHub Pages on pushes to `master`

## Layout

- `PrepPipeline/` - entry points: `prepPipeline.m`, `pop_prepPipeline.m` (EEGLAB GUI wrapper), `prepPostProcess.m`, `prepReport.m`, `publishPrepReport.m`, `eegplugin_prepPipeline.m` (EEGLAB menu registration)
- `PrepPipeline/utilities/` - the algorithms (`removeTrend`, `cleanLineNoise`, `performReference`, `findNoisyChannels`, defaults, version); `chronux_2_modified/` is vendored third-party code (GPL v2; see the licensing table in `README.md`)
- `PrepPipeline/reporting/` - report and collection-statistics functions
- `PrepPipeline/interface/` - the EEGLAB parameter GUIs
- `PrepPipeline/derived/`, `PrepPipeline/examples/`, `PrepPipeline/extracted/` - scripts built on the pipeline
- `EEGLABPlugin/` - the released plugin as a zip
- `docs/` - Sphinx source for the documentation site (MyST markdown and `.rst`); images in `docs/_static/images/`. `pyproject.toml` exists only to declare the docs toolchain.
- `CHANGELOG.md` - release history
- `.status/` - working notes. Gitignored; local to each machine.

## Conventions that differ from defaults

- **ASCII only** in prose, code, comments, and filenames: `-` not em or en dashes, `->` not arrows, `...` not an ellipsis character, straight quotes. Exception: genuine data (author names, dataset titles, recorded API responses) keeps whatever characters it actually contains.
- Markdown headers are sentence case: capitalize only the first word, proper nouns, and acronyms (PREP, EEG, EEGLAB, MATLAB).
- MATLAB: camelCase for functions. The file name must match the function name, so `.m` files keep their mixed case.
- The default branch is `master`, not `main`.

## Rules that are easy to get wrong

- The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together.
- Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/`.
- `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line does not appear there, though MATLAB `help` still finds it. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives.
- Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history.

## Related repositories

Referred to by name; none is vendored here.

- `eeglab` - the MATLAB toolbox PREP runs in, required at runtime.
- `hed-matlab` - the model for this repository's layout and documentation setup.

## Where the thinking lives

`.status/` is gitignored, so it exists only on the machine that wrote it and never in a fresh clone or worktree.

- `.status/README.md` - the index. Read this first; it lists what is active.
- `.status/decisions.md` - why things are the way they are. Read before proposing structural changes. Append entries; never rewrite one.
- `.status/plans/*.md` - active plans. Check the `Status:` header and the `[ ]` / `[x]` markers before starting work.
- `.status/local-environment.md` - this machine's paths, interpreter, and quirks. Tool-agnostic. Never copy its contents into a committed file.
- IMPORTANT: do not read `.status/archive/` unless a file is named for you. Nothing new is created at the `.status/` root.

## Working agreements

- IMPORTANT: every file written to `.status/` opens with a `For humans:` summary - three or four sentences, at the very top: what the file is and what a person needs to take from it. The same applies to a long answer in a session: lead with the conclusion.
- IMPORTANT: temporary scripts, experiments, and one-off test files go in `.status/scratch/` - **never the repository root**. Delete them when the experiment ends; anything in `scratch/` may be deleted unread.
- IMPORTANT: never delete or rewrite a file under `.status/` without asking first. Appending is fine.
- For a change spanning more than three files, write a plan to `.status/plans/` and stop for review before editing.
- When you are guessing about an external API or data format, say so explicitly rather than assuming.
- Show evidence, not assertions: the command you ran and its actual output.
- Do not commit, push, or create branches unless asked.
Loading
Loading