Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
76c6c87
Add design spec for AI contributor skills and validation pipeline
timosachsenberg Mar 24, 2026
39ef3cd
Address spec review feedback: clarify imports, migration, conftest te…
timosachsenberg Mar 24, 2026
a81dbaf
Add implementation plan for AI contributor skills and validation pipe…
timosachsenberg Mar 24, 2026
5a38336
Add ruff.toml with E/F/W/I rule set, line-length 120
timosachsenberg Mar 24, 2026
1b94ea9
Migrate peptide_mass_calculator to per-script directory structure
timosachsenberg Mar 24, 2026
bd44bf7
Migrate protein_digest to per-script directory structure
timosachsenberg Mar 24, 2026
a5ec12f
Migrate spectrum_file_info to per-script directory with synthetic tes…
timosachsenberg Mar 24, 2026
2436785
Migrate feature_detection_proteomics to per-script directory with syn…
timosachsenberg Mar 24, 2026
70019e4
Migrate mass_accuracy_calculator to per-script directory structure
timosachsenberg Mar 24, 2026
b575f69
Migrate isotope_pattern_matcher to per-script directory structure
timosachsenberg Mar 24, 2026
85d6fd3
Migrate metabolite_feature_detection to per-script directory with syn…
timosachsenberg Mar 24, 2026
7313299
Add validate-script and contribute-script Claude Code skills
timosachsenberg Mar 24, 2026
9c280ad
Add AGENTS.md platform-agnostic AI contributor guide
timosachsenberg Mar 24, 2026
ec6b25d
Add GitHub Actions CI workflow for per-script isolated validation
timosachsenberg Mar 24, 2026
9f3e0e1
Update CLAUDE.md and README.md for per-script directory structure
timosachsenberg Mar 24, 2026
6ca1237
Fix review findings: add bulk-test command, fix algorithm name in README
timosachsenberg Mar 24, 2026
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
109 changes: 109 additions & 0 deletions .claude/skills/contribute-script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: contribute-script
description: Guide creation of a new pyopenms script contribution β€” scaffolding through validation
---

# Contribute Script

Guide an AI agent through creating a new pyopenms CLI tool for the agentomics repo. Follow every step β€” this is a rigid skill.

## Prerequisites

Read `AGENTS.md` in the repo root for the full contributor guide and code patterns.

## Steps

### 1. Understand the tool

Ask the user:
- What does this tool do? What pyopenms functionality does it use?
- What gap in OpenMS/pyopenms does it fill?

### 2. Determine the domain

Ask: Is this a **proteomics** or **metabolomics** tool? If neither fits, discuss whether a new domain directory is needed.

### 3. Pick a name

Choose a descriptive snake_case name for the tool (e.g. `peptide_mass_calculator`, `isotope_pattern_matcher`). Confirm with the user.

### 4. Create a feature branch

```bash
git checkout -b add/<tool_name>
```

### 5. Scaffold the directory

```bash
mkdir -p scripts/<domain>/<tool_name>/tests
```

Create these files:

**`requirements.txt`:**
```
pyopenms
```
Add any additional dependencies the script needs (one per line, no version pins).

**`tests/conftest.py`:**
```python
import sys
import os

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

try:
import pyopenms # noqa: F401

HAS_PYOPENMS = True
except ImportError:
HAS_PYOPENMS = False

requires_pyopenms = pytest.mark.skipif(not HAS_PYOPENMS, reason="pyopenms not installed")
```

### 6. Write the script

Create `scripts/<domain>/<tool_name>/<tool_name>.py` following these patterns:

- Module-level docstring with description, supported features, and CLI usage examples
- pyopenms import guard:
```python
try:
import pyopenms as oms
except ImportError:
sys.exit("pyopenms is required. Install it with: pip install pyopenms")
```
Comment on lines +75 to +80
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether script modules using sys.exit also import sys.
# Expected: no output (or review any printed files).

fd -e py . scripts | while read -r f; do
  if rg -n '\bsys\.exit\s*\(' "$f" >/dev/null; then
    if ! rg -n '^\s*import\s+sys\b|^\s*from\s+sys\s+import\b' "$f" >/dev/null; then
      echo "Missing import sys: $f"
    fi
  fi
done

Repository: OpenMS/agentomics

Length of output: 43


🏁 Script executed:

cat -n .claude/skills/contribute-script.md | sed -n '70,85p'

Repository: OpenMS/agentomics

Length of output: 773


🏁 Script executed:

cat -n .claude/skills/contribute-script.md | sed -n '60,90p'

Repository: OpenMS/agentomics

Length of output: 1231


Add import sys to the pyopenms import-guard pattern.

The documented pattern at lines 75–80 uses sys.exit() without showing import sys. Contributors copying this exact snippet will encounter a NameError on the import failure path.

Suggested doc fix
 - pyopenms import guard:
   ```python
+  import sys
   try:
       import pyopenms as oms
   except ImportError:
       sys.exit("pyopenms is required. Install it with:  pip install pyopenms")
</details>

<!-- suggestion_start -->

<details>
<summary>πŸ“ Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/contribute-script.md around lines 75 - 80, The snippet uses
sys.exit in the ImportError handler but never imports sys, which will raise
NameError on failure; add an import sys at the top of the block so the
try/except around importing pyopenms (import pyopenms as oms) can call
sys.exit(...) safely when ImportError is caught.

- `PROTON = 1.007276` constant where mass-to-charge calculations are needed
- Importable functions as the primary interface (with type hints and numpy-style docstrings)
- `main()` function with argparse CLI
- `if __name__ == "__main__": main()` guard

### 7. Write tests

Create `scripts/<domain>/<tool_name>/tests/test_<tool_name>.py`:

- Import `requires_pyopenms` from conftest
- Decorate test classes with `@requires_pyopenms`
- Use `from <tool_name> import <function>` inside test methods
- For file-I/O scripts: generate synthetic data using pyopenms objects in test fixtures, write to `tempfile.TemporaryDirectory()`
- Cover: basic functionality, edge cases, key parameters

### 8. Write README

Create `scripts/<domain>/<tool_name>/README.md` with a brief description and CLI usage examples.

### 9. Validate

Invoke the `validate-script` skill on the new script directory. Both ruff and pytest must pass.

### 10. Commit

```bash
git add scripts/<domain>/<tool_name>/
git commit -m "Add <tool_name>: <brief description>"
```
34 changes: 34 additions & 0 deletions .claude/skills/validate-script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: validate-script
description: Validate a pyopenms script in an isolated venv β€” runs ruff lint and pytest
---

# Validate Script

Validate any script in the agentomics repo by running ruff and pytest in a fresh isolated venv.

## Steps (follow exactly β€” rigid skill)

1. **Identify the script directory.** If the user provided a path, use it. Otherwise, ask which script to validate. The path should be `scripts/<domain>/<tool_name>/`.

2. **Verify the directory structure.** Confirm it contains:
- `<tool_name>.py`
- `requirements.txt`
- `tests/` directory with at least one `test_*.py` file

3. **Create a temporary venv and run validation.** Execute these commands:

```bash
SCRIPT_DIR=<path-to-script-directory>
VENV_DIR=$(mktemp -d)
python -m venv "$VENV_DIR"
"$VENV_DIR/bin/python" -m pip install -r "$SCRIPT_DIR/requirements.txt"
"$VENV_DIR/bin/python" -m pip install pytest ruff
"$VENV_DIR/bin/python" -m ruff check "$SCRIPT_DIR/"
PYTHONPATH="$SCRIPT_DIR" "$VENV_DIR/bin/python" -m pytest "$SCRIPT_DIR/tests/" -v
rm -rf "$VENV_DIR"
```
Comment on lines +21 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Demonstrate cleanup behavior with and without trap.
# Expected: without trap, temp dir remains after failure; with trap, it is removed.

set -euo pipefail

echo "Without trap:"
if bash -c 'set -e; V=$(mktemp -d); false; rm -rf "$V"' 2>/dev/null; then :; fi
# Can't read V from subshell directly; show equivalent reproducible pattern:
bash -c 'set -e; V=$(mktemp -d); echo "$V" > /tmp/venv_path_no_trap; false; rm -rf "$V"' || true
V1=$(cat /tmp/venv_path_no_trap)
if [ -d "$V1" ]; then echo "Leaked: $V1"; rm -rf "$V1"; fi

echo "With trap:"
bash -c 'set -e; V=$(mktemp -d); echo "$V" > /tmp/venv_path_trap; trap "rm -rf \"$V\"" EXIT; false' || true
V2=$(cat /tmp/venv_path_trap)
if [ -d "$V2" ]; then echo "Unexpected leak: $V2"; else echo "Cleaned as expected"; fi

Repository: OpenMS/agentomics

Length of output: 133


🏁 Script executed:

# Check if the file exists and read the relevant lines
if [ -f ".claude/skills/validate-script.md" ]; then
  echo "=== File exists, reading lines 15-40 for context ==="
  sed -n '15,40p' ".claude/skills/validate-script.md" | cat -n
else
  echo "File not found at .claude/skills/validate-script.md"
  # Search for similar files
  find . -name "*validate*script*" -o -name "*.md" -path "*claude*" 2>/dev/null | head -20
fi

Repository: OpenMS/agentomics

Length of output: 1064


🏁 Script executed:

# Get actual line numbers and check for line 34 reference
wc -l ".claude/skills/validate-script.md"
echo "=== Lines 20-35 (to see full context and line 34) ==="
sed -n '20,35p' ".claude/skills/validate-script.md" | cat -n

Repository: OpenMS/agentomics

Length of output: 886


Add trap-based cleanup to ensure venv removal on failure.

The current code does not guarantee cleanup if any command fails before the final rm -rf. Step 5 (line 34) explicitly requires cleanup even when validation fails. Add trap 'rm -rf "$VENV_DIR"' EXIT after VENV_DIR=$(mktemp -d) and remove the final rm -rf "$VENV_DIR" line.

Suggested command block update
   ```bash
   SCRIPT_DIR=<path-to-script-directory>
   VENV_DIR=$(mktemp -d)
+  trap 'rm -rf "$VENV_DIR"' EXIT
   python -m venv "$VENV_DIR"
   "$VENV_DIR/bin/python" -m pip install -r "$SCRIPT_DIR/requirements.txt"
   "$VENV_DIR/bin/python" -m pip install pytest ruff
   "$VENV_DIR/bin/python" -m ruff check "$SCRIPT_DIR/"
   PYTHONPATH="$SCRIPT_DIR" "$VENV_DIR/bin/python" -m pytest "$SCRIPT_DIR/tests/" -v
-  rm -rf "$VENV_DIR"
</details>

Also applies to: 34-34

<details>
<summary>πŸ€– Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.claude/skills/validate-script.md around lines 21 - 30, Add a trap-based
cleanup to guarantee removal of the temporary virtualenv: immediately after
VENV_DIR=$(mktemp -d) register trap 'rm -rf "$VENV_DIR"' EXIT (so it runs on any
exit/failure) and delete the final explicit rm -rf "$VENV_DIR" line at the end;
ensure the trap references the same VENV_DIR variable and uses the EXIT signal
so the venv is removed on both success and failure.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->


4. **Report results.** Summarize pass/fail for both ruff and pytest. If either fails, show the relevant error output so the user can fix it.

5. **Clean up.** Ensure the temporary venv is removed even if validation fails.
65 changes: 65 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Validate Scripts

on:
pull_request:
paths:
- 'scripts/**'

jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.detect.outputs.matrix }}
has_changes: ${{ steps.detect.outputs.has_changes }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- id: detect
name: Detect changed script directories
run: |
# Note: github.base_ref is only available on pull_request events
# Find all script directories that changed in this PR
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- 'scripts/' \
| grep -oP 'scripts/[^/]+/[^/]+/' \
| sort -u \
| jq -R -s -c 'split("\n") | map(select(length > 0))')
Comment on lines +24 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Reproduce grep no-match behavior under bash -e in a command-substitution pipeline.
# Expected: first block fails; second block with `|| true` succeeds.

set -euo pipefail

echo "a/b/c.txt" > /tmp/changed_paths.txt

echo "Case 1 (without || true) - should fail:"
if bash -e -c 'X=$(cat /tmp/changed_paths.txt | grep -oP "scripts/[^/]+/[^/]+/" | sort -u); echo "$X"'; then
  echo "Unexpected success"
else
  echo "Failed as expected"
fi

echo "Case 2 (with || true) - should succeed:"
bash -e -c 'X=$(cat /tmp/changed_paths.txt | grep -oP "scripts/[^/]+/[^/]+/" || true); echo "X=${X:-<empty>}"'

Repository: OpenMS/agentomics

Length of output: 170


🏁 Script executed:

cat -n .github/workflows/validate.yml | head -50

Repository: OpenMS/agentomics

Length of output: 1897


🏁 Script executed:

git ls-files '.github/workflows/'

Repository: OpenMS/agentomics

Length of output: 91


🌐 Web query:

GitHub Actions bash run shell set -e default behavior

πŸ’‘ Result:

In GitHub Actions, for run steps on Linux/macOS runners (non-Windows), the default shell behavior when no shell is explicitly specified is to use bash -e {0} if bash is available, or sh -e {0} otherwise. The -e flag enables set -e semantics, causing the script to exit immediately if any command returns a non-zero exit code (fail-fast behavior). When shell: bash is explicitly specified, it uses bash --noprofile --norc -eo pipefail {0}, which includes set -e, set -o pipefail (fail on pipeline failure), and suppresses profile/rc loading. This is the default to enforce fail-fast behavior in CI scripts, ensuring steps fail early on errors. To disable, use shell: bash {0} or set +e in the script. On Windows, the default is PowerShell, not bash.

Citations:


🏁 Script executed:

fd -t f '.github/workflows/' -name '*.yml' -o -name '*.yaml' | xargs grep -l 'git diff' | head -5

Repository: OpenMS/agentomics

Length of output: 291


🏁 Script executed:

#!/bin/bash
# Test grep behavior with pipefail (which GitHub Actions uses by default)

echo "a/b/c.txt" > /tmp/changed_paths.txt

echo "Case 1 (bash -eo pipefail, without || true) - should fail:"
if bash -eo pipefail -c 'X=$(cat /tmp/changed_paths.txt | grep -oP "scripts/[^/]+/[^/]+/" | sort -u); echo "$X"'; then
  echo "Unexpected success"
else
  echo "Failed as expected (exit code: $?)"
fi

echo ""
echo "Case 2 (bash -eo pipefail, with || true) - should succeed:"
if bash -eo pipefail -c 'X=$(cat /tmp/changed_paths.txt | grep -oP "scripts/[^/]+/[^/]+/" || true | sort -u); echo "X=${X:-<empty>}"'; then
  echo "Succeeded as expected"
else
  echo "Unexpected failure"
fi

Repository: OpenMS/agentomics

Length of output: 245


🏁 Script executed:

fd '.github/workflows' -type f

Repository: OpenMS/agentomics

Length of output: 231


🏁 Script executed:

find .github/workflows -type f -name '*.yml' -o -name '*.yaml'

Repository: OpenMS/agentomics

Length of output: 91


🏁 Script executed:

grep -r "grep -oP" .github/workflows/

Repository: OpenMS/agentomics

Length of output: 139


Add fallback to grep to prevent pipeline failure on zero matches.

In GitHub Actions, the default shell (bash -eo pipefail) will fail the entire step if grep finds no matches and returns exit code 1. When no script directories change, this pipeline fails before reaching the error-handling check at line 29. Add || true after grep to ensure the pipeline continues.

Suggested fix
          CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- 'scripts/' \
            | grep -oP 'scripts/[^/]+/[^/]+/' \
+            || true \
            | sort -u \
             | jq -R -s -c 'split("\n") | map(select(length > 0))')
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/validate.yml around lines 24 - 27, The CHANGED assignment
pipeline can fail when grep finds no matches; modify the command that pipes to
grep (the part using grep -oP 'scripts/[^/]+/[^/]+/') to add a fallback (append
|| true) so grep's non-zero exit doesn't break the step, ensuring CHANGED
becomes an empty list instead of causing the job to fail; update the pipeline
around the CHANGED variable assignment and keep the rest of the processing (sort
-u | jq -R -s -c ...) unchanged.


if [ "$CHANGED" = "[]" ] || [ -z "$CHANGED" ]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "matrix=[]" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
echo "matrix=$CHANGED" >> "$GITHUB_OUTPUT"
fi

validate:
needs: detect-changes
if: needs.detect-changes.outputs.has_changes == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
script_dir: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
name: Validate ${{ matrix.script_dir }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Create venv and install dependencies
run: |
python -m venv /tmp/validate_venv
/tmp/validate_venv/bin/python -m pip install -r ${{ matrix.script_dir }}requirements.txt
/tmp/validate_venv/bin/python -m pip install pytest ruff

- name: Lint with ruff
run: |
/tmp/validate_venv/bin/python -m ruff check ${{ matrix.script_dir }}

- name: Run tests
run: |
PYTHONPATH=${{ matrix.script_dir }} /tmp/validate_venv/bin/python -m pytest ${{ matrix.script_dir }}tests/ -v
106 changes: 106 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# AGENTS.md β€” AI Contributor Guide

This file instructs AI agents (Claude Code, GitHub Copilot, Cursor, Gemini, etc.) how to contribute scripts to the agentomics repository.

## Project Purpose

Agentomics is a collection of standalone CLI tools built with [pyopenms](https://pyopenms.readthedocs.io/) for proteomics and metabolomics workflows. These tools fill gaps not yet covered by OpenMS/pyopenms. All code in this repo is written by AI agents.

## Contribution Requirements

Every script must be a **self-contained directory** under `scripts/<domain>/<tool_name>/`:

```
scripts/<domain>/<tool_name>/
β”œβ”€β”€ <tool_name>.py # The tool itself
β”œβ”€β”€ requirements.txt # pyopenms + any script-specific deps (no version pins)
β”œβ”€β”€ README.md # Brief description + CLI usage examples
└── tests/
β”œβ”€β”€ conftest.py # Shared test config (see below)
└── test_<tool_name>.py
```

### Rules

- `<domain>` is `proteomics` or `metabolomics`
- `requirements.txt` always includes `pyopenms` with no version pin β€” builds against latest
- No cross-script imports β€” each script is fully independent
- No `__init__.py` files β€” these are NOT Python packages
- No scripts that duplicate functionality already in OpenMS/pyopenms

## Code Patterns

### Script structure

Every script must have:

1. **Module docstring** with description, features, and usage examples
2. **pyopenms import guard:**
```python
import sys
try:
import pyopenms as oms
except ImportError:
sys.exit("pyopenms is required. Install it with: pip install pyopenms")
```
3. **Importable functions** as the primary interface (with type hints and numpy-style docstrings)
4. **`main()` function** with argparse CLI
5. **`if __name__ == "__main__": main()`** guard
6. **`PROTON = 1.007276`** constant where mass-to-charge calculations are needed

### Test structure

Every `tests/conftest.py` must contain:

```python
import sys
import os

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

try:
import pyopenms # noqa: F401
HAS_PYOPENMS = True
except ImportError:
HAS_PYOPENMS = False

requires_pyopenms = pytest.mark.skipif(not HAS_PYOPENMS, reason="pyopenms not installed")
```

Test files:
- Decorate test classes with `@requires_pyopenms` from conftest
- Import script functions inside test methods: `from <tool_name> import <function>`
- For file-I/O scripts: generate synthetic data using pyopenms objects, write to `tempfile.TemporaryDirectory()`

## Validation

Every script must pass validation in an **isolated venv** before it can be merged. Run these commands from the repo root:

```bash
SCRIPT_DIR=scripts/<domain>/<tool_name>
VENV_DIR=$(mktemp -d)
python -m venv "$VENV_DIR"
"$VENV_DIR/bin/python" -m pip install -r "$SCRIPT_DIR/requirements.txt"
"$VENV_DIR/bin/python" -m pip install pytest ruff
"$VENV_DIR/bin/python" -m ruff check "$SCRIPT_DIR/"
PYTHONPATH="$SCRIPT_DIR" "$VENV_DIR/bin/python" -m pytest "$SCRIPT_DIR/tests/" -v
rm -rf "$VENV_DIR"
```

Both ruff and pytest must pass with zero errors.

## Linting

Ruff is configured in `ruff.toml` at the repo root:
- Line length: 120
- Rules: E (pycodestyle errors), F (pyflakes), W (pycodestyle warnings), I (isort)

## What NOT to Do

- Do not add cross-script imports
- Do not add dependencies to a shared/root requirements file
- Do not create scripts that duplicate existing pyopenms CLI tools or OpenMS TOPP tools
- Do not pin pyopenms to a specific version
- Do not add `__init__.py` files
63 changes: 63 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Purpose

Agentomics is a collection of standalone CLI tools built with [pyopenms](https://pyopenms.readthedocs.io/) for proteomics and metabolomics workflows. These tools fill gaps not yet covered by OpenMS/pyopenms. All code in this repo is agentic-only development β€” written entirely by AI agents.

## Commands

```bash
# Install dependencies for a specific script
pip install -r scripts/proteomics/peptide_mass_calculator/requirements.txt

# Lint a specific script
ruff check scripts/proteomics/peptide_mass_calculator/

# Run tests for a specific script
PYTHONPATH=scripts/proteomics/peptide_mass_calculator python -m pytest scripts/proteomics/peptide_mass_calculator/tests/ -v

# Lint all scripts
ruff check scripts/

# Run all tests across all scripts
for d in scripts/*/*/; do PYTHONPATH="$d" python -m pytest "$d/tests/" -v; done

# Run a script directly
python scripts/proteomics/peptide_mass_calculator/peptide_mass_calculator.py --sequence PEPTIDEK --charge 2
python scripts/metabolomics/isotope_pattern_matcher/isotope_pattern_matcher.py --formula C6H12O6
```

## Architecture

### Per-Script Directory Structure

Each script is a self-contained directory under `scripts/<domain>/<tool_name>/`:

```
scripts/<domain>/<tool_name>/
β”œβ”€β”€ <tool_name>.py # The tool (importable functions + argparse CLI)
β”œβ”€β”€ requirements.txt # pyopenms + script-specific deps
β”œβ”€β”€ README.md # Usage examples
└── tests/
β”œβ”€β”€ conftest.py # requires_pyopenms marker + sys.path setup
└── test_<tool_name>.py
```

Domains: `proteomics/`, `metabolomics/`

### Key Patterns

- pyopenms import wrapped in try/except with user-friendly error message
- Mass-to-charge: `(mass + charge * PROTON) / charge` with `PROTON = 1.007276`
- Every script has dual interface: importable functions + argparse CLI + `__main__` guard
- Tests use `@requires_pyopenms` skip marker from conftest.py
- File-I/O scripts use synthetic test data generated with pyopenms objects

## Contributing

See `AGENTS.md` for the full AI contributor guide. Two Claude Code skills are available:

- **`contribute-script`** β€” guided workflow for adding a new script
- **`validate-script`** β€” validate any script in an isolated venv (ruff + pytest)
Loading
Loading