Apegmsh Helper
ASecurityUse whenever the user is working with apeGmsh — the structural-FEM wrapper around Gmsh with OpenSees integration. Triggers on building FEM models from CAD/STEP imports, Part-based assembly workflows, composite-based geometry/mesh/constraint APIs (g.model, g.mesh, g.physical, g.constraints, etc.), void/hole authoring (as_void, add_polyline, add_void_sweep/loft, boolean.apply_voids — ADR 0097), labelled g.decouple_node as RBE2/RBE3 master, the apeSees(fem) OpenSees bridge with typed primitives ...
- 8 stars
- 0 votes
- 0 copies
- 0 views
- Added November 7, 2026
Works with
Security analysis
92/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 17 files and shows the line behind each finding
npx -y skills add nmorabowen/apeGmsh --skill apegmsh-helper --agent claude-codeAre you the author of Apegmsh Helper?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/nmorabowen-apegmsh-helper)---
name: apegmsh-helper
description: Use whenever the user is working with apeGmsh — the structural-FEM wrapper around Gmsh with OpenSees integration. Triggers on building FEM models from CAD/STEP imports, Part-based assembly workflows, composite-based geometry/mesh/constraint APIs (g.model, g.mesh, g.physical, g.constraints, etc.), void/hole authoring (as_void, add_polyline, add_void_sweep/loft, boolean.apply_voids — ADR 0097), labelled g.decouple_node as RBE2/RBE3 master, the apeSees(fem) OpenSees bridge with typed primitives and automatic MP-constraint emission, staged analysis (ops.stage), loads/masses/constraints resolution into the FEMData broker, native model.h5 persistence (FEMData.to_h5/from_h5, save_to=/g.save(), apeGmsh.from_h5), assembling saved models with Assembly v2 (from apeGmsh.assembly import Assembly: instance/tie/node/couplings/bridge/partition_rank, ADR 0117), post-processing OpenSees output via Results (from_native/from_mpco/from_recorders) with the interactive and web viewers (results.viewer / results.show_web), and exporting models to OpenSees Tcl or openseespy scripts. Covers apeGmsh's own abstractions on top of Gmsh and OpenSees. For raw gmsh API questions see the gmsh-structural skill; for raw OpenSees analysis commands see opensees-expert; for FEM theory first principles see fem-mechanics-expert.
---
# apeGmsh — structural FEM wrapper around Gmsh
<!-- skill-freshness: verified against apeGmsh main@5c92ca92 (2026-08-15) · signatures live in the references; src/ is not the authoring lookup (ADR 0096) -->
apeGmsh is the user's in-house Gmsh wrapper, and the *core idea* is:
> Describe a model **once** — geometry, labels, loads, masses, constraints,
> per-node ndf — then hand a solver-agnostic **snapshot** (`FEMData`) to any
> FEM solver. OpenSees gets a first-class bridge; anyone else reads the
> snapshot. The same snapshot persists to `model.h5`, composes into larger
> assemblies, and drives post-processing of OpenSees output.
Whenever the user is working in this project or writing code against
apeGmsh, prefer the apeGmsh API over raw `gmsh.*` calls. Raw `gmsh` calls
still work (you're holding the same session), but the whole point of the
wrapper is that you don't have to write them.
## Lookup (ADR 0096)
Pick **one** row, read that file (or that heading), then write code.
Do not slurp this whole skill. Do not grep `src/apeGmsh/` to author a
model script. Prefer labels / physical-group names over tags.
`src/` read/grep is an **index miss**: allowed only when the reference
has no hit **and** you are changing apeGmsh itself.
| Task | Read only |
|---|---|
| First script / session skeleton | `references/workflows.md` |
| Writing a model script (shape, naming, checks) | `references/model-scripts.md` (rules + the one nearest reference script) |
| Public composite signature | MCP `lookup(symbol)` or `python -m apeGmsh.studio.lookup SYMBOL` |
| Public API map (headings) | `references/api-cheatsheet.md` (matching heading) |
| FEMData / `model.h5` | `references/fem-broker.md` |
| `apeSees` / stages / emit | `references/opensees-bridge.md` |
| After mesh or solve check | `references/assess.md` |
| Results / combinations / `.user.h5` / stills | `references/results.md` |
| Assembly / compose readers / `from_h5` | `references/compose.md` |
| Rebar cages | `references/rebar.md` |
| ETABS / analytical import | `references/interop.md` |
| Section properties / fiber section | `references/section-properties.md` |
| Ladruno fork-only | `references/ladruno.md` |
| Build failed / anti-pattern | `references/gotchas.md` |
| Studio / MCP / picks | this file, **Studio** paragraph |
| Promote a miss / skill lie | `python -m apeGmsh.studio.profile --promote` (MCP `kind=miss` only; writes nothing) |
What each file contains (do not read them all):
- `api-cheatsheet.md` — public `g.*` / `apeSees` map (matching heading; signatures via MCP `lookup` / lookup CLI)
- `fem-broker.md` — `FEMData`, `model.h5`
- `opensees-bridge.md` — `apeSees`, stages, emit, ndf, remote
- `assess.md` — verdict + stills after mesh/solve (not Qt); apeGmshViewer stills (`render_stills`)
- `results.md` — `from_ladruno`, `convert_mpco`, `.user.h5`, plot, render
- `compose.md` — Assembly, compose readers, `from_h5`
- `rebar.md` — `g.rebar` cages
- `interop.md` — ETABS / analytical import
- `section-properties.md` — `SectionProperties`, fiber handoff
- `workflows.md` — end-to-end patterns (not a forced pipeline)
- `model-scripts.md` — Script English rules + reference scripts (truss, 3-D frame, staged footing)
- `gotchas.md` — anti-patterns
- `ladruno.md` — fork-only emit/read
- `viewer.md` — what apeGmshViewer shows
Published docs: <https://nmorabowen.github.io/apeGmsh/> (`docs/`, ADR 0079).
`internal_docs/` is unpublished. Changelog Unreleased is the moving truth.
## Mental model
Four concepts, in this order:
**1. A session (`g`) owns a single Gmsh kernel.** Open it with
`g.begin()` / `g.end()` or a `with apeGmsh(...) as g:` block. Every
composite — `g.model`, `g.mesh`, `g.loads`, `g.masses`, `g.constraints`,
`g.embed`, `g.rebar`, `g.decouple_node`, etc. — is a thin namespace that talks
to that shared kernel.
At the top level the session *is* the assembly — `apeGmsh.Assembly` does
**not** exist (a deliberate v1.0 guard). OpenSees is **not** a session
composite (`g.opensees` was removed) — it is the separate post-session
bridge `apeSees(fem)`.
> To build one model out of several saved `model.h5` files, use
> **Assembly v2** (ADR 0117), a sub-path import: `from apeGmsh.assembly
> import Assembly` → `.instance(label, "part.h5", translate=, rotate=,
> partition_rank=)` → `.tie(...)` / couplings → `.bridge(ndm, ndf)`, which
> returns one ordinary `apeSees`. Every instance is namespaced
> `{label}.{name}` (no host), its model content (mesh, groups, materials,
> sections, element specs) travels, and supports, loads and the analysis
> are declared on the bridge. See `references/compose.md` §"Assembly v2".
> The v1 path (`g.compose`, `FEMData.compose`, `Assembly.add / couple /
> materialize`) was removed without deprecation (ADR 0117 D7, AS5-c).
> **Mixed element ORDER across parts (hex20 + hex8) needs one
> session per part** — `set_order` is session-global, and
> `Part` is geometry-only by contract (ADR 0085); order-mismatched
> interfaces want `tie(method="mortar", enforce="equation")` (ADR 0086) —
> see `references/compose.md` §"Independent meshes per part".
**2. Composites split by concern.** `g.model` splits into
`geometry / boolean / transforms / io / queries`. `g.mesh` splits into
`generation / sizing / field / structured / editing / queries /
partitioning`. The OpenSees bridge `apeSees(fem)` exposes typed namespaces
(`uniaxialMaterial / nDMaterial / section / geomTransf / beamIntegration /
element / timeSeries / pattern / recorder`) plus flat verbs (`model / fix /
mass / build / stage / tcl / py / h5 / run`). Every method has a home — you
rarely reach into `gmsh.*`.
**3. `FEMData` is the solver contract.** When the mesh is ready, call
`fem = g.mesh.queries.get_fem_data(dim=3)` (or `dim=2`, or `None` for all
dims). The returned `FEMData` is an immutable snapshot with `fem.nodes`,
`fem.elements`, `fem.info`, `fem.inspect`. It works without a live Gmsh
session, round-trips through `model.h5` (`to_h5` / `from_h5`), composes into
larger assemblies, and every solver bridge consumes it. **Turn coordinates
into labels with the `.select()` chain** — `g.model.select(...)` pre-mesh,
`fem.nodes.select(...)` / `fem.elements.select(...)` / `g.mesh_selection.select(...)`
post-mesh; never raw tags (see `references/api-cheatsheet.md`).
**4. MP constraints + per-node ndf emit automatically.** When the snapshot
carries multi-point constraints (`fem.nodes.constraints`,
`fem.elements.constraints`) the `apeSees(fem)` bridge auto-emits the matching
`equalDOF` / `rigidLink` / `rigidDiaphragm` / `ASDEmbeddedNodeElement` deck
lines (and an `ops.constraints.Transformation()` handler when present); per-node
ndf is inferred from element classes and wired in too. **Do not hand-emit
these** — it double-constrains the model. (Shipped v2.0.0, ADR 0022.) The
same is true of the side-lists that emit *elements* rather than MP equations:
`fem.elements.contacts` and `fem.elements.interfaces`
(`g.constraints.interface()`, the unilateral + slip-capped coincident-pair
`zeroLength` bed — ADR 0093).
## Core workflow
Every apeGmsh script follows the same skeleton. Learn this, and everything
else is filling in blanks:
<!-- reference-script: skeleton.py -->
```python
# verified: tests/test_femdata_from_h5.py::test_session_save_then_from_h5
# tests/opensees/integration/test_runnable_deck.py::test_tcl_deck_contains_constraint_lines
from apeGmsh import apeGmsh
from apeGmsh.opensees import apeSees
# DATA (SI: m, N, kg, s)
LENGTH = 10.0 # m, cantilever span along x
WIDTH = 5.0 # m
DEPTH = 2.0 # m
DENSITY = 2400.0 # kg/m^3
GRAVITY = (0.0, 0.0, -9.81) # m/s^2
YOUNG = 30e9 # Pa
POISSON = 0.2 # -
MESH_SIZE = 0.5 # m
X_AXIS = (1.0, 0.0, 0.0) # normal of both end faces
FACE_TOL = 1e-6 # m
TIP_NODE_FORCE = (0.0, 0.0, -5e4) # N, at every Tip node
with apeGmsh(model_name="my_model", units="N_m_s", save_to="my_model.h5") as g:
# GEOMETRY (occ) → PHYSICAL GROUPS (dim-unique names) → pre-mesh
# LOADS/MASSES/CONSTRAINTS (on labels/PGs) → MESH → SNAPSHOT (workflows.md §1).
g.model.geometry.add_box(0.0, 0.0, 0.0, LENGTH, WIDTH, DEPTH, label="body")
g.physical.add_volume("body", name="Body")
g.model.select(None, dim=2).on_plane((0.0, 0.0, 0.0), X_AXIS, tol=FACE_TOL).to_physical("Base")
g.model.select(None, dim=2).on_plane((LENGTH, 0.0, 0.0), X_AXIS, tol=FACE_TOL).to_physical("Tip")
with g.loads.case("dead"):
g.loads.gravity("Body", g=GRAVITY, density=DENSITY)
g.masses.volume("Body", density=DENSITY)
g.mesh.sizing.set_global_size(MESH_SIZE)
g.mesh.generation.generate(dim=3)
fem = g.mesh.queries.get_fem_data(dim=3) # the solver contract; autosaved on exit
print(fem.info)
assert fem.nodes.select(pg="Base").ids and fem.nodes.select(pg="Tip").ids
# OPENSEES (optional): post-session bridge. Loads are OPT-IN via p.from_model
# (ADR 0051); masses and fixities are restated; MP constraints + ndf auto-emit.
ops = apeSees(fem)
ops.model(ndm=3, ndf=3)
conc = ops.nDMaterial.ElasticIsotropic(E=YOUNG, nu=POISSON) # no rho: g.masses carries it
ops.element.FourNodeTetrahedron(pg="Body", material=conc)
ops.fix(pg="Base", dofs=(1, 1, 1))
ops.mass_from_model()
with ops.pattern.Plain(series=ops.timeSeries.Linear()) as p:
p.from_model("dead")
p.load(pg="Tip", forces=TIP_NODE_FORCE)
ops.py("model.py") # or ops.tcl / ops.h5 / ops.run
```
This is the **happy path**. For anything more involved (multi-part assembly,
coupled shell + frame, pushover, staged SSI, reading results back), read the
matching reference — don't improvise.
**Studio (ADR 0095).** `python -m apeGmsh.studio script.py` replays
up to `--phase` (default `model`: **stops before** `generate()`;
`--phase mesh` stops before `apeSees` / `Results`). The session stays
open. MeshViewer if the replay produced elements, otherwise
ModelViewer. Picks write `.apegmsh/selection.json` under the resolved project root
(`root=` / `--root` / `APEGMSH_ROOT` / `APEGMSH_STUDIO_ROOT` /
nearest `.apegmsh/` / cwd — ADR 0095 INV-15). A successful
stop writes `.apegmsh/names.json` — labels / PGs / counts / bboxes of
what exists, not what was clicked — and appends `.apegmsh/runs.jsonl`
(timestamp, hash, phase, counts). `python -m apeGmsh.studio --status`
reads those files without replaying. The host window refreshes itself
(ADR 0095 Amendment 4): a Refresh toolbar action / F5 replays the
entry script, and a file watch replays it automatically when the
saved file's mtime settles (`--no-watch` or the Watch toggle turn it
off) — both honor the `busy.json` lock and append `runs.jsonl` with
`trigger: "refresh"` / `"watch"`, so after editing the script the
human's window follows the save; the agent does not need to reopen
the host, and must not treat a `BUSY` `run_until` as an error while
the host is replaying. Before a spatial edit, read the
envelope, `names.json`, and `--status` — identity is
`labels` / `physical_groups` / `phase`, not dimtags.
An unnamed pick's first script edit is `.to_label()` / `.to_physical()`.
After solve, still `assess()` + stills, not `viewer()`.
When the agent must look, write visors under `.apegmsh/visors/` and
read those PNGs (`render_stills` on Windows: `references/results.md`
§5). Never capture the OS desktop or the human's window.
`python -m apeGmsh.studio init --name <habitat> --model <model_id>`
stamps a fresh APE Studio habitat from the packaged template (ADR 0095
Amendment 6) — playbook, lifecycle scripts, checker, model lineage —
and leaves it under local git: a repo on `main` with one initial
commit; `--no-git` opts out, and a machine without git gets a WARN
and a valid habitat (Amendment 8). Habitat git follows the
**checkpoint contract** (`APE/instructions/checkpoints.md`): `main`
holds checkpoints only, work happens on `work/<model>/<slug>`
branches, a `--no-ff` merge + `checkpoint/<model>/<slug>` tag is
acceptance, and each case's `run.json` carries `model_sha` /
`git_dirty` via the habitat's own
`scripts/_habitat.py::git_provenance()`. Results never enter git (the
shipped `.gitignore`); lifecycle scripts print git state but never
write it; forges are optional — a PR is only the review surface.
Before building a covered shape from scratch, check
`python -m apeGmsh.studio example list [--tag <domain>]` — the
oracle-bearing example library (ADR 0095 Amendment 7): `show <name>`
prints the manifest + README, `copy <name> [--dest DIR]` lands a
runnable directory (script + `manifest.json` + `README.md` +
`verify.py`) anywhere apeGmsh is installed, refusing to overwrite.
Every example's metrics were produced by an actual run (INV-22) —
discovery displaces src-grep, same as ADR 0096's lookup discipline
but for recipes, not symbols. CLI only, not an MCP tool.
Read-only MCP (ADR 0095 S4a–S4e / S5a / 0096 S3): `python -m apeGmsh.studio.mcp` (needs
`pip install mcp`). Tools: `status`, `get_selection`, `lookup`, `run_until`,
`assess`, `render`, `animate(kind=history|yield)`, `results_pin`,
`emit_report(format=markdown|html|canvas)`, `highlight`,
`promote_selection` — names first, no Qt, no `setup=`. Every tool
accepts optional `root=` (INV-15). `status(mode=brief|full)` defaults
to **brief** (root / last-run / labels / PGs / counts / pick + `text`);
use `mode=full` only for debugging. **Root check → brief; entity
hunting → `lookup` / `names.json`, not a full status dump.**
`lookup(symbol)`
is See-family inspect of the generated index (~20 lines); not CAD;
does not grep `src/`. `kind=yield`
is a von Mises contour of `von_mises_stress` on auto-scaled deform
(0.12 of diagonal; not an iso-clip; π-plane later). `highlight`
writes `.apegmsh/highlight.json` only (host file-poll + `select_batch`;
does not write `selection.json`). `promote_selection` suggests
`g.model.select(None, dim=).in_box(lo, hi).to_label()` /
`.to_physical()` and does not write the `.py`. `emit_report`:
markdown is the archive (`docs/`); html is a print skin there;
canvas is a live Cursor projection (IDE `canvases/`, not
git-portable) and still writes the markdown chapter.
Run `assess` any time before `emit_report` — it writes
`.apegmsh/assess.json` and the chapter's Assess section picks up that
snapshot's verdict, findings, and skipped list automatically (`--pin`
copies it, ADR 0095 Amendment 5); do not paste `assess` output into
the chapter by hand.
`kind=formation` is later. Do not invent MCP tools for
`g.model.*` / `apeSees` primitives. Token-budget profiler is
`python -m apeGmsh.studio.profile` (sidecar; not an MCP tool).
Studio generation order (CAD then
mesh, quotations on, parallel processes) is a **recommendation** for
a given script, not a required path (ADR 0096 INV-14). Authored
chapters go in `docs/`;
visors stay under `.apegmsh/visors/`.
**MCP wiring (ADR 0095 Amendment 12).** Each project stamps its own
`apegmsh-studio-habitat` server in `.cursor/mcp.json` (and `.mcp.json`
for Claude Code), binding **both** `APEGMSH_ROOT` and
`APEGMSH_STUDIO_ROOT` to that project's Studio root. The user-level
`~/.cursor/mcp.json` server is deliberately **unbound** — it carries no
root env and no `cwd`, because a global root is stale the moment you
switch projects. Set `LADRUNO_OPENSEES_QUIET=1` before Python starts
(the office venv `.pth` banner goes to stdout and would break
JSON-RPC)::
{
"mcpServers": {
"apegmsh-studio-habitat": {
"command": "<venv>/Scripts/python.exe",
"args": ["-m", "apeGmsh.studio.mcp"],
"env": {
"LADRUNO_OPENSEES_QUIET": "1",
"APEGMSH_ROOT": "<studio root>",
"APEGMSH_STUDIO_ROOT": "<studio root>",
"APEGMSH_PYTHON": "<venv>/Scripts/python.exe",
"PYTHONPATH": "<apeGmsh>/src"
}
}
}
}
For a Workbench project with a Studio overlay, `<studio root>` is the
**overlay folder** (e.g. `Staged Model/`), not the Workbench root. Do
not pin `cwd` to the library repo — it has its own `.apegmsh/` and the
ancestor walk would bind there.
**Agents: check `status.root` first.** If it is not the habitat you
mean, pass `root=` on *every* Studio tool call for the rest of the
session — do not assume the server is bound correctly.
## When things go wrong: the usual suspects
1. **`RuntimeError: session is already open`** — `begin()` called twice, or a
crashed notebook cell left Gmsh initialized. Fix: `gmsh.finalize()` or
restart the kernel. Prefer the `with` form.
2. **Physical group resolves to wrong dimension** — `g.physical.add(1, ...)`
and `g.physical.add(2, ...)` are different groups. Keep PG names
dimension-unique so `apeSees(fem)` `pg=` selectors resolve unambiguously.
3. **"No label, physical group, or part named X"** — resolution order is
`label → physical group → part label`; the name exists in none. Inspect
with `g.labels.get_all()`, `g.physical.summary()`, `g.parts.labels()`.
4. **`fem.elements.connectivity` raises `TypeError`** — the mesh has multiple
element types. Iterate `for group in fem.elements:` or filter with
`fem.elements.select(element_type="tet4").connectivity` (or
`.result().resolve(element_type=...)` for a mixed selection).
5. **Labels without physical groups** — labels on the main session don't
auto-promote to PGs (only `Part` sessions do). Call
`g.labels.promote_to_physical("name")` or add `g.physical.add(...)`.
6. **Reaching for `from_native` / `from_mpco`** — they are retiring;
read `.ladruno` (`references/results.md` §1).
7. **Don't open Qt to check a solve — and don't capture the human's
screen.** After `get_fem_data` / `Results.from_*`,
`report = fem.assess()` / `results.assess(figures=True)`; print
`report.text`; `read_file` each PNG in `report.figures`; act on
`severity="error"`. Inspect is inventory, not the verdict. Extra
stills go under `.apegmsh/visors/` (`references/results.md` §5) —
never OS-screenshot the desktop or the live viewer. Viewers are for humans who asked to look.
`results.viewer()` blocking rules (an explicit `blocking=True` kills
a Jupyter kernel): `references/results.md` §5. See `references/assess.md`.
Which reference covers a given failure: `BridgeError` / staged / ndf →
`opensees-bridge.md`; `MalformedH5Error` / `SchemaVersionError` →
`fem-broker.md`; finding codes / after-solve check → `assess.md`;
`LineageError` / viewer crash → `results.md`;
`MeshRecipeError` / selection → `api-cheatsheet.md`; `GeometryValidationError`
→ `gotchas.md`; `SectionMeshError` / `CompositeSectionError` /
`SectionAnalysisError` → `section-properties.md`.
## Version & layout facts you can rely on
These are the anti-hallucination guardrails — the removed/renamed surfaces an
agent most often gets wrong. Everything quantitative (schema integers, full
signatures) lives in the references.
- **Version is v2.0.0** (`pyproject.toml`). A stale editable install may report
`v1.6.0` as `apeGmsh.__version__` — trust the source, not the install.
Anything claiming "v1.0" is stale. v2.0.0 shipped the three-broker chain
`FEMData ⊂ OpenSeesModel ⊂ Results`, auto MP-constraint emission, and deleted
`BindError`.
- **Work from the latest apeGmsh.** At session open, pull (or upgrade) the
checkout behind the install, then verify the import actually points at it —
`apeGmsh.__file__` + `apeGmsh.__version__` — before the first run. After a
pull the environment can still silently resolve a stale install (multi-entry
`PYTHONPATH`, a forgotten editable install elsewhere); a session debugged
against a stale library is wasted.
- **Removed/renamed** (never recommend these): `g.mass` → `g.masses`;
`g.opensees` → the post-session `apeSees(fem)` bridge; `g.node_ndf` → ndf is
inferred from element classes + `ops.ndf` for element-less nodes (ADR
0048/0049, see `opensees-bridge.md`); flat v0.x forms (`g.add_point`,
`g.model.fuse`, `g.initialize`) → sub-composite forms; the attribute
`apeGmsh.apeGmshViewer` → `results.viewer()` / `results.session()`
(desktop app: `viewer.md`). The legacy `ResultsViewer`
is **deleted** — no import path reaches it; the post-solve
document is a `ResultsSession` (`results.viewer()` or
`python -m apeGmsh.viewers <file>`, until the Qt sunset).
- **One results format, `.ladruno`** (ADR 0131), and a set's files
(`.h5`, `.ladruno`, `.user.h5`, `.workspace.h5`): `references/results.md` §1.
- **Removed (ADR 0116):** the plotly preview (`apeGmsh.preview`,
`g.model.preview`, `g.mesh.preview`), `GeomTransfViewer`,
`apeGmsh.sensitivity` and the import banner. Inline pictures are
`g.plot` (matplotlib) or `render(...)` stills.
- **Sidecar modules** (separate imports, NOT session composites):
`from apeGmsh.hpc import Cluster, Job` (remote SLURM, pairs with
`ops.run_remote`), `from apeGmsh.studio import SelectionEnvelope`
(`python -m apeGmsh.studio script.py --phase model` — stops before
`generate()`; `.apegmsh/selection.json` + `names.json` + `runs.jsonl`;
`--status` inspects them, ADR 0095). Details in `api-cheatsheet.md`.
- **Schema constants** live in `fem-broker.md` (neutral + bridge zones, ADR
0023) and the results session-snapshot version in `results.md` — they
drift, so read the number from there, not from memory.
Before claiming a method or signature exists, call MCP `lookup(symbol)`
or run `python -m apeGmsh.studio.lookup SYMBOL` (ADR 0096). If that
misses, read `references/api-cheatsheet.md` (the matching heading).
Do not `Read` `src/apeGmsh/studio/_api_index.json`.
`src/apeGmsh/` is for maintaining the library, not for writing a
model script.
Promotion (ADR 0096 S5): a MCP `lookup` **miss** (`kind=miss`, not
ambiguous) that repeats ≥3 times in the **model-cwd**
`.apegmsh/mcp_calls.jsonl` is eligible for a reviewed index/router
PR. `--promote` prints that list and writes nothing. A skill error
(skill vs index) owes a PR after one confirmation. Working ImageMage
steps stay comments in *that* `.py` and/or a Recommendation in
`workflows.md` — never a required pipeline. Propose a PR; do not
write `skills/apegmsh/` or run `sync_skill.py` in-session; do not
edit `.claude/skills/apegmsh-helper/`.
Files in this skill
- SKILL.md
- references/api-cheatsheet.md
- references/assess.md
- references/compose.md
- references/fem-broker.md
- references/gotchas.md
- references/interop.md
- references/ladruno.md
- references/model-scripts.md
- references/opensees-bridge.md
- references/rebar.md
- references/results.md
- references/section-properties.md
- references/viewer.md
- references/workflows.md
- scripts/lint_model_script.py
- scripts/removed_symbols.json
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.