SLOPSHOPPER

agentex

AgenTeX: 14 skills + 2 hooks that keep LaTeX papers honest to the code behind them: verified citations with a cite guard, results-to-macros sync with…

bandguardprompttimer
v0.4.0MITupdated 2026-10-18Metahemeralism/Agentex
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · agentex
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM A ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
A
README

<img src="docs/logo/agentex-logo.svg" alt="AgenTeX" width="440">

Keep your LaTeX paper honest to the code behind it.

Verified citations · numbers that can't go stale · a build that fixes itself · publication-grade TikZ · arXiv-ready packages

License: MIT Skills Hooks Dependencies


AgenTeX is a Claude Code plugin (Agent Skills plus hooks) for researchers whose results live in code and whose papers live in LaTeX.

Prose polishing is already well served by many tools. AgenTeX targets the problems where an agent with a shell, logs and APIs beats both chat assistants and editors, and it solves them with deterministic tools that make the final call, with the LLM organising the work and explaining the results.

Why

  • Fabricated references survive peer review. In 2025, "roughly one in twenty NeurIPS and USENIX Security papers contains at least two likely hallucinated" references (Phantom References, arXiv:2607.00738). 100 fabricated citations reached 53 accepted NeurIPS 2025 papers despite "review by 3-5 expert researchers per paper" (arXiv:2602.05930).
  • Numbers drift. A rerun changes 87.3 to 86.9, but the abstract still says 87.3, and nobody remembers which script made fig3.pdf.
  • Compiling isn't correctness. The vTikZ benchmark finds that current LLMs "struggle to reliably modify code in alignment with visual intent" (arXiv:2505.04670). Agents need to check the log and look at the result.

(Each source was checked with AgenTeX's own cite_verify.py, and each quote against its abstract.)

Three guarantees

What you getHow
1 · Citations are realEvery cited entry is resolved against Crossref, arXiv, Semantic Scholar, OpenAlex and dblp, then classified as verified, mismatch ("Frankenstein" metadata), bad-doi, not-found (likely fabricated) or published (preprint has a published version). A cite-guard hook blocks the agent from adding any \cite whose entry isn't verified.cite_verify.py + hooks/cite_guard.py
2 · Numbers come from codeResults JSON → \res{exp.acc} macros. An audit finds stale numbers (equal to a previous value of a result), hard-coded numbers and undefined keys. Generated booktabs tables. A provenance lock records the script, input hashes and commit behind every figure, and rebuilds stale ones.results.py
3 · The build stays greenAfter a turn that edited LaTeX, a compile-loop hook builds the paper. If there are errors or new undefined references or citations, it hands the agent a short digest to fix before you ever see the broken PDF (it gives up after 3 attempts).hooks/compile_loop.py + texlog.py

Changes to references and numbers are never applied silently. Corrections are written as suggestions (.agentex/suggested.bib, audit replacements) for you to approve as a diff.

Gallery

Tested TikZ patterns from latex-tikz, about 20 lines each on one shared style sheet. The agent copies the closest one, edits only what differs, then renders and looks at the result.

<table> <tr> <td align="center"><img src="docs/gallery/transformer.png" height="230"><br><sub>Transformer block</sub></td> <td align="center"><img src="docs/gallery/pgm.png" height="150"><br><sub>Graphical model with plates</sub></td> <td align="center"><img src="docs/gallery/plot.png" height="150"><br><sub>pgfplots with ±1 sd band</sub></td> </tr> <tr> <td align="center"><img src="docs/gallery/cnn.png" width="300"><br><sub>Network with tensor shapes</sub></td> <td align="center"><img src="docs/gallery/mlp.png" height="150"><br><sub>MLP from layer sizes</sub></td> <td align="center"><img src="docs/gallery/geometry.png" height="140"><br><sub>Exact geometric construction</sub></td> </tr> <tr> <td align="center"><img src="docs/gallery/pipeline.png" width="300"><br><sub>Pipeline with feedback loop</sub></td> <td align="center"><img src="docs/gallery/fsm.png" height="100"><br><sub>State machine</sub></td> <td align="center"><img src="docs/gallery/matrix.png" height="130"><br><sub>Matrix with dimension braces</sub></td> </tr> </table>

Skills

SkillWhat it does
Verifylatex-referencesCitation verification against five databases, BibTeX from DOIs, natbib/biblatex setup, label/citation linter
latex-results-syncResults registry → macros, stale/hard-coded number audit, generated tables, figure provenance and rebuilds
latex-consistencyPaper ↔ code audit: equations vs implementation, hyperparameters vs configs, overclaims, leakage red flags
latex-debugLog digest (a 1,150-line log → 1 line), error lookup table, bisection workflow
Writelatex-projectPaper/thesis scaffold next to your code; texmap.py project map so agents read only what they need
latex-mathCode → equations via a symbol map, with shape/index checks against the implementation and notation audits
latex-tikzReads your code and suggests figures to pick from; 12 tested patterns, shared styles, offline search of 1,229 official TikZ manual examples, render-and-look loop
latex-algorithmsFunctions → algorithmicx pseudocode, or minted/listings pulled from source files
latex-tables-figuresbooktabs/siunitx tables, matplotlib/pgfplots figures sized to the column
latex-beamerPaper → timed talk with claim-style titles and reused figures and notation
Reviewlatex-research-checkClosest prior work across OpenAlex/arXiv/Semantic Scholar, ranked by similarity to your abstract
latex-plagiarism-checkSentence-level overlap with sources, with cited/quoted status (honest about what it can't do)
latex-reviewVenue-style mock review, 3-reviewer panel + meta-review, anonymity checklist, rebuttal prep
Shiplatex-arxivOnly the files the document uses, comments stripped, arXiv's 2025 bibliography rules (.bbl format 3.3), test-compiled

Install

Claude Code plugin (skills + hooks), recommended. One command in your terminal:

claude plugin install agentex --marketplace Metahemeralism/AgenTeX

Or with the animated installer (it runs the same command):

curl -fsSL https://raw.githubusercontent.com/Metahemeralism/AgenTeX/main/install.sh | bash

Or from inside Claude Code:

/plugin marketplace add Metahemeralism/AgenTeX
/plugin install agentex

Restart Claude Code afterwards. To update: /plugin marketplace update agentex.

Skills only (no hooks): cp -r AgenTeX/skills/* ~/.claude/skills/. To get the guarantees without the plugin, copy the hooks block from hooks/hooks.json into your settings.json, replacing ${CLAUDE_PLUGIN_ROOT} with your clone's path.

Claude apps / other agents: each skill is a folder with a SKILL.md in the open Agent Skills format. Zip one and upload it under Settings → Capabilities → Skills, or point any agent at it.

Requirements: Python 3.8+ (all scripts are standard library only), and TeX Live / MacTeX or tectonic for compiling. Optional: poppler (pdftoppm, pdftotext) for previews and PDF sources, and S2_API_KEY (free) for reliable preprint→published detection.

SettingEffect
AGENTEX_CITE_GUARD=0turn off the cite guard
AGENTEX_COMPILE_HOOK=0turn off the compile loop
S2_API_KEY=…Semantic Scholar key (anonymous use is rate-limited)
AGENTEX_MAILTO=you@uni.edujoins Crossref's polite pool

Caches and state live in .agentex/ in your project, which ignores itself in git.

The AgenTeX band. A small band above the prompt shows what AgenTeX is doing while its checks run (verifying citations, typesetting, rendering a figure). TeX's lowered E bobs as it works:

AgenT X  ⠹ verifying citations  4s
     E

It also plays a one-time welcome after install. Turn it off under /config → Animations. The band uses Claude Code's hooks-module API, which is marked early access (it may change between releases). It is purely cosmetic: the skills, cite guard and compile loop do not depend on it.

Use it

> Check every citation in paper/ and show me what's wrong.
> I reran the experiments. What in the paper is now stale?
> Make results/main.csv into a table with the best numbers in bold.
> Suggest diagrams for this repo.          (reads the code, offers high-level and component figures to pick from)
> Draw the model in src/model.py as a TikZ figure with tensor shapes.
> Has anyone done this before? Find the closest work to my abstract.
> Review my paper as a NeurIPS reviewer.
> Package paper/ for arXiv.

Scripts you can run yourself (or in CI)

ScriptDoes
latex-references/scripts/cite_verify.py paper/ [--suggest]Verify cited entries; exit 1 on fabricated, mismatched or duplicate entries
`latex-results-sync/scripts/results.py build\audit\table\stale\make`Registry, stale-number audit, tables, provenance
latex-references/scripts/check_refs.py paper/Undefined/duplicate labels, missing/uncited bib entries
latex-debug/scripts/texlog.py main.logLog digest
latex-project/scripts/texmap.py paper/Compact project map
latex-tikz/scripts/diagram_ideas.py .Diagram ideas from your code (pipelines, architectures, loops, models), each with its pattern and file:line
latex-tikz/scripts/tikzref.py brace decorationOffline TikZ manual search
latex-tikz/scripts/tikzcheck.sh fig.texCompile a figure; errors only + small PNG preview
latex-research-check/scripts/related.py --from-tex main.texClosest prior work
latex-plagiarism-check/scripts/overlap.py paper/ sources/Overlap screening
latex-arxiv/scripts/arxiv_pack.py main.tex --compilearXiv package, test-compiled

Built to be token-efficient

Lessons from auditing the ponytail and agent-skills plugins, applied here:

TechniqueIn AgenTeXEffect (measured on this repo)
Short always-loaded descriptions~250 characters per skillbodies load only when used
Deterministic scripts instead of readingtexlog.py, cite_verify.py, results.py audita 1,150-line compile log → 1 line
Compact project maptexmap.pytemplate project mapped in 663 characters
Local reference cachetikzref.py over extracted manual examples248 characters returned vs a 624 KB manual page
Result caches.agentex/cite-cache.jsonre-verifying a bibliography goes from ~20 s to near-instant
Write styles once; copy and editagentex-tikz.sty, patterns, templatesoutput tokens spent only on what differs
Cheap visual checks100 dpi PNG previewsa few hundred image tokens per look

Honest limits

  • Citation checks depend on public APIs. dblp currently puts scripted clients behind a bot check (AgenTeX skips it automatically), and Semantic Scholar rate-limits anonymous use. not-found means "no database knows it", so check by hand before calling it fabricated. Books, theses and software are reported as unverifiable, not not-found.
  • The results audit reads LaTeX structure, not a regex over raw text, but it is still heuristic. It refuses to guess when a number matches more than 3 results.
  • Overlap screening is not iThenticate: it only sees sources it can read, and AgenTeX offers no "AI-text detection", because such detectors are unreliable.
  • The compile loop needs latexmk or tectonic and adds a build at the end of turns that edited LaTeX. Turn it off with AGENTEX_COMPILE_HOOK=0.

Roadmap

From a ranked study of LaTeX bottlenecks for people who code:

  • ☐ /venue-convert and /camera-ready: class/bibliography-style swaps, page-limit report, anonymous ↔ final mode
  • ☐ Figure styling from \columnwidth: one matplotlib style file, detection of font-distorting \includegraphics rescales
  • ☐ Notation-guardian subagent: a symbol table across all files, catching symbols with two meanings and drifting macros and acronyms
  • ☐ paper-ci: pinned TeX build + GitHub Actions running cite/results audits and attaching a latexdiff
  • ☐ /rebuttal: response letter mapped to the actual latexdiff/git changes
  • ☐ Accessibility: tagged PDF (\DocumentMetadata), alt text drafted from the code and data behind each figure, veraPDF checks

Repository layout

.claude-plugin/          plugin.json + marketplace.json
hooks/                   hooks.json, cite_guard.py, compile_loop.py, ui.tsx + frames.ts (the band) and tests
types/                   state contract for the band (TypeScript)
install.sh               animated installer
skills/
  latex-references/      cite_verify.py, check_refs.py
  latex-results-sync/    results.py
  latex-consistency/     latex-debug/ (texlog.py)   latex-project/ (templates, texmap.py)
  latex-math/            latex-tikz/ (assets, tikzref.py, tikzcheck.sh, manual index)
  latex-algorithms/      latex-tables-figures/       latex-beamer/ (talk template)
  latex-research-check/  latex-plagiarism-check/     latex-review/ (venue forms)
  latex-arxiv/           (arxiv_pack.py)
docs/gallery/            rendered pattern previews
docs/logo/               wordmark source (.tex) + PDF/SVG/PNG, card and transparent variants

Contributing

Issues and PRs are welcome, especially roadmap items, venue review forms, and error→fix pairs for latex-debug.

  • One skill = one folder with a SKILL.md. Its description decides when it triggers, so say when to use it, in under ~300 characters.
  • Prefer a deterministic script to instructions wherever a check can be computed. Scripts are standard library only, each with tests: ``bash for d in hooks skills/*/scripts; do python3 -m unittest discover -s "$d" -q || exit 1; done claude plugin validate . && claude plugin test . # the band's module, in Claude Code's own test kit ``
  • New TikZ patterns must compile standalone with agentex-tikz.sty.
  • Try your changes without installing: claude --plugin-dir path/to/AgenTeX loads the plugin straight from disk for that session.

License

MIT, except skills/latex-tikz/reference/tikz-manual.jsonl. That file contains examples extracted from the PGF/TikZ manual (© Till Tantau and the PGF/TikZ team) and is distributed under LPPL 1.3c. See its notice.

Source 3 files
hooks/ui.tsx 101 lines
1// AgenTeX's band above the prompt: a one-time welcome after install, and a small animation
2// while AgenTeX work runs (its scripts, and the compile check its Stop hook does).
3import { atom, read, update } from 'claude-code'
4import type { EngineInterface, Register } from 'claude-code'
5
6import { activityRows, labelFor, welcomeRows, WELCOME_FRAMES } from './frames'
7
8const activity = atom({ plugin: 'agentex', key: 'activity' } as const, null)
9const frame = atom({ plugin: 'agentex', key: 'frame' } as const, 0)
10const welcome = atom({ plugin: 'agentex', key: 'welcome' } as const, null)
11
12// Module variables reset on reload; what the band draws lives in $.state.
13let running = 0 // AgenTeX tasks in flight; the band shows while any is
14let ticker: { cancel: () => void } | null = null
15let welcomeChecked = false
16
17async function begin($: EngineInterface, label: string) {
18  running += 1
19  await update($, frame, () => 0)
20  await update($, activity, () => ({ label }))
21  ticker ??= $.clock.every(100, () => void update($, frame, n => n + 1))
22}
23
24async function end($: EngineInterface) {
25  running = Math.max(0, running - 1)
26  if (running > 0) return
27  ticker?.cancel()
28  ticker = null
29  await update($, activity, () => null)
30}
31
32async function maybeWelcome($: EngineInterface) {
33  if (welcomeChecked) return
34  welcomeChecked = true
35  if (await $.store.get('welcomed')) return
36  await $.store.set('welcomed', true) // first: an interrupted run must not replay forever
37  let n = 0
38  await update($, welcome, () => 0)
39  const t = $.clock.every(90, () => {
40    n += 1
41    void update($, welcome, () => (n >= WELCOME_FRAMES ? null : n))
42    if (n >= WELCOME_FRAMES) t.cancel()
43  })
44}
45
46export const register: Register = (on, options) => {
47  if (options.animations === false) return // switched off in /config: register nothing
48  // Installed mid-session, the module may join after session.start: the first prompt covers that.
49  // A UI hook must never block a prompt: on any failure, just continue.
50  on('session.start', async ($, e, next) => (await maybeWelcome($), next(e))).catch(($, e, next) => next(e))
51  on('prompt.submit', async ($, e, next) => (await maybeWelcome($), next(e))).catch(($, e, next) => next(e))
52
53  on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
54    const label = labelFor(e.command ?? '')
55    if (!label) return next(e)
56    await begin($, label)
57    try {
58      return await next(e)
59    } finally {
60      await end($)
61    }
62  }).catch(($, e, next) => next(e))
63
64  // The compile loop is a command hook on Stop; animate only if it takes a noticeable time.
65  on('classic.Stop', async ($, e, next) => {
66    let shown = false
67    const t = $.clock.after(600, () => {
68      shown = true
69      void begin($, 'typesetting · checking the build')
70    })
71    try {
72      return await next(e)
73    } finally {
74      t.cancel()
75      if (shown) await end($)
76    }
77  }).catch(($, e, next) => next(e))
78
79  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
80    if (e.props.hasSurvey) return next(e)
81    const w = await read($, welcome)
82    const a = await read($, activity)
83    if (w === null && a === null) return next(e)
84    const rows = w !== null ? welcomeRows(w) : activityRows(await read($, frame), a!.label)
85    const { Box, Text } = $.ui.resolve(e)
86    return (
87      <Box flexDirection="column" key="agentex">
88        {rows.map((row, i) => (
89          <Text key={`r${i}`}>
90            {row.map((s, j) => (
91              <Text key={`s${j}`} color={s.color} bold={s.bold} dimColor={s.dim}>
92                {s.text}
93              </Text>
94            ))}
95          </Text>
96        ))}
97      </Box>
98    )
99  })
100}
101
hooks/frames.ts 61 lines
1// Pure frame logic for the AgenTeX band: no engine calls, so it is unit-testable.
2// The motif is TeX's lowered E: it drops into place on first run and bobs while work runs.
3
4export const BLUE = '#56B4E9' // "Agen", the logo's Okabe-Ito sky blue
5export const WELCOME_FRAMES = 48 // x 90 ms = about 4.3 s
6const SPIN = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
7
8export type Seg = { text: string; color?: string; bold?: boolean; dim?: boolean }
9export type Row = Seg[]
10
11// Which shell commands are AgenTeX work, and what to call them while they run.
12const LABELS: [RegExp, string][] = [
13  [/cite_verify\.py/, 'verifying citations'],
14  [/results\.py\s+(audit|stale)\b/, 'checking numbers against the code'],
15  [/results\.py\s+make\b/, 'rebuilding stale figures'],
16  [/results\.py/, 'syncing results'],
17  [/arxiv_pack\.py/, 'packaging for arXiv'],
18  [/tikzcheck\.sh/, 'rendering figure'],
19  [/diagram_ideas\.py/, 'reading your code for diagram ideas'],
20  [/related\.py/, 'searching prior work'],
21  [/overlap\.py/, 'screening for overlap'],
22  [/check_refs\.py/, 'checking references'],
23  [/texmap\.py/, 'mapping the project'],
24  [/\b(latexmk|tectonic|pdflatex|lualatex|xelatex)\b/, 'typesetting'],
25]
26
27export function labelFor(command: string): string | null {
28  for (const [re, label] of LABELS) if (re.test(command)) return label
29  return null
30}
31
32/** "AgenT X" over "     E": `shown` letters of AgenTEX typed so far; E up or dropped. */
33export function logo(shown: number, eDown: boolean): [Row, Row] {
34  const has = (i: number) => shown > i
35  const e = has(5) ? 'E' : ' '
36  return [
37    [
38      { text: 'Agen'.slice(0, Math.min(shown, 4)).padEnd(4), color: BLUE, bold: true },
39      { text: (has(4) ? 'T' : ' ') + (eDown ? ' ' : e) + (has(6) ? 'X' : ' '), bold: true },
40    ],
41    [{ text: '     ' }, { text: eDown ? e : ' ', bold: true }, { text: ' ' }],
42  ]
43}
44
45export function welcomeRows(n: number): Row[] {
46  const [top, bottom] = logo(Math.min(n + 1, 7), n >= 9) // type it out, then drop the E
47  if (n < 11) return [top, bottom]
48  return [
49    [...top, { text: '  installed ✓', bold: true }],
50    [...bottom, { text: '  cite guard and compile loop on · try "suggest diagrams for this repo"', dim: true }],
51  ]
52}
53
54export function activityRows(frame: number, label: string): Row[] {
55  const [top, bottom] = logo(7, Math.floor(frame / 5) % 2 === 0) // E bobs every 0.5 s
56  return [
57    [...top, { text: `  ${SPIN[frame % SPIN.length]} ${label}`, color: BLUE }, { text: `  ${Math.floor(frame / 10)}s`, dim: true }],
58    bottom,
59  ]
60}
61
types/index.d.ts 16 lines
1// State the AgenTeX UI module keeps for the session (drawn by the band above the prompt).
2export type Activity = { label: string }
3
4declare module 'claude-code' {
5  interface PluginState {
6    agentex: {
7      /** AgenTeX work in flight (a script, the compile check), or null. */
8      activity: Activity | null
9      /** Animation tick while `activity` is set (100 ms each). */
10      frame: number
11      /** Welcome animation tick on first run, or null when it is not playing. */
12      welcome: number | null
13    }
14  }
15}
16