synthadoc
Ingests PDFs, Word and PowerPoint files, images, Markdown and plain text into a searchable knowledge base for research. Suits individuals and teams building a document-backed knowledge store.
What academic-research-skills runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
Claude Code
> /plugin marketplace add Imbad0202/academic-research-skills> /plugin install academic-research-skills@academic-research-skills
Cursor
Cursor also runs Claude Code hooks from .claude/settings.json, so copy these hooks there to use them in Cursor.
What fires, and when
SessionStart
Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.
bash "${CLAUDE_PLUGIN_ROOT}/scripts/announce-ars-loaded.sh"PreToolUse
Write|Edit|MultiEdit|Bashbash "${CLAUDE_PLUGIN_ROOT}/hooks/run_guard.sh"Where it lives
#!/bin/sh
# version: 1.1.0
#
# ARS write-scope guard LAUNCHER — PreToolUse hook (#454 Windows portability fix).
#
# WHY THIS EXISTS: the guard hook used to be wired as `python3 ".../ars_write_scope_guard.py"`
# directly. On Windows, `python3` is commonly a 0-byte Microsoft Store App Execution Alias
# stub (not real Python); invoking it non-interactively fails BEFORE the guard's Python runs,
# so none of the guard's own fail-safes apply — it just errors and spams the hook log (#454).
#
# This launcher finds a REAL Python (skipping stubs), then runs the guard as a SUPERVISED
# subprocess. Design: docs/design/2026-06-17-454-windows-python-hook-portability-design.md.
#
# POSTURE (Plan A — graceful degradation; the guard is OPTIONAL v3.10 hardening and ARS core
# needs no Python): if no real Python is found, OR the guard subprocess misbehaves, the
# launcher emits a valid PASS-THROUGH hook JSON and exits 0. It NEVER exits non-zero on these
# degraded paths (a non-2 exit blocks nothing anyway and only spams logs; exit 2 would
# hard-lock the user out of all writes/Bash for an environment gap or an ARS-side bug — wrong
# for an optional layer). It stays SILENT on stderr on degraded paths: PreToolUse is a hot
# path, so any per-call stderr IS the spam #454 is about.
#
# Bash 3.2 / POSIX sh compatible (same constraint as scripts/announce-ars-loaded.sh). On
# Windows this runs under Git Bash; with no Git Bash, CC falls back to PowerShell which can't
# run this .sh — the guard is then inactive (accepted degradation, see spec §3.3).
# Canonical pass-through output: no permissionDecision => falls back to the normal permission
# flow (NEVER emit "allow" — that would skip every other permission rule).
PASS_THROUGH='{"hookSpecificOutput":{"hookEventName":"PreToolUse"}}'
emit_passthrough_and_exit() {
printf '%s\n' "$PASS_THROUGH"
exit 0
}
# --- Resolve the guard script from THIS launcher's own location (codex P1) ---------------
# CC substitutes ${CLAUDE_PLUGIN_ROOT} into the hook COMMAND text before the shell, but does
# NOT guarantee it as an env var inside this script. So compute the guard path from $0.
# (No production env override: the guard path is ALWAYS derived from the launcher's own
# location. Tests that need a broken/alternate guard run the launcher from a temp plugin
# layout, so there is no production back door — P2-e.)
# shellcheck disable=SC1007 # `CDPATH= cd` is intentional: clear CDPATH for this one cd only
SELF_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) || emit_passthrough_and_exit
GUARD="$SELF_DIR/../scripts/ars_write_scope_guard.py"
# --- Read the payload from stdin ONCE (we must replay it to the guard subprocess) ---------
# Known trade-off (gemini round-6 P2, accepted): the payload is held in a shell variable and
# replayed with `printf '%s'`. `printf` is a builtin so it is NOT bound by ARG_MAX, and the
# stripped trailing newline is irrelevant to the guard's JSON parse, so for ordinary hook
# payloads this is safe. A multi-megabyte Write payload on a POSIX shell that caps variable
# length is the narrow case this does not cover; buffering to a private temp file would handle
# it but adds another temp-file lifecycle (and symlink surface) to a hot path, so it is left as
# documented degradation rather than fixed speculatively.
PAYLOAD=$(cat)
# --- Marker: did this candidate start real Python? ---------------------------------------
# Each candidate runs one Python process (#1015, below) whose first output is the marker line.
# A 0-byte Store stub fails to execute / prints nothing, so it is skipped. We bound each run
# so a broken-but-hanging interpreter can't wedge the hot path (spec §3.3): prefer `timeout`
# when present, else a portable process-group watchdog.
MARKER=ARS_PY_OK
END_MARKER=ARS_PY_END
NL='
'
# Per-candidate (and guard) wall-clock bound, seconds. A small ops knob; validated to be a
# bare integer so it can't smuggle anything into the `timeout`/`sleep` args. Default 3.
PROBE_BOUND=${ARS_PROBE_BOUND:-3}
case "$PROBE_BOUND" in
''|*[!0-9]*) PROBE_BOUND=3 ;;
esac
# ARS_GUARD_FORCE_WATCHDOG=1 forces the no-`timeout` watchdog path even on hosts that HAVE
# `timeout`. This is a test/debug switch: it only changes the BOUNDING MECHANISM (timeout
# binary vs process-group watchdog), never the security decision — both paths enforce the
# same wall-clock bound and the same pass-through-on-overrun posture. Honored if set; safe
# to leave unset (the default prefers a usable `timeout` binary when present).
#
# #970: a `timeout` on PATH is used only if it accepts the GNU form we call it with
# (`timeout <N>s cmd`). On Git for Windows, `timeout` can resolve to the Windows built-in
# timeout.exe (`/T <seconds>`), which rejects that form and exits non-zero; trusting mere
# existence failed every bounded call, so no Python was ever found and every write passed
# through. A present-but-foreign `timeout` now takes the watchdog fallback, like a missing one.
# Decided once per launch, not per bounded call. `-k 1s` bounds the probe itself at about 2s
# even if the `true` on PATH hangs and ignores SIGTERM; a `timeout` without `-k` fails the
# probe and also takes the fallback, so the probe can never disable the guard.
TIMEOUT_USABLE=
if [ -z "${ARS_GUARD_FORCE_WATCHDOG:-}" ] && command -v timeout >/dev/null 2>&1 \
&& timeout -k 1s 1s true </dev/null >/dev/null 2>&1; then
TIMEOUT_USABLE=1
fi
have_timeout() {
[ -n "$TIMEOUT_USABLE" ]
}
# Reserved exit status meaning "the bounded command was killed for overrunning its bound".
TIMEOUT_STATUS=124
# Run "$@" with a wall-clock bound; its stdout flows to OUR stdout (capture via $(...)).
# Returns the command's real exit status, or $TIMEOUT_STATUS if it overran the bound.
# Stdin for "$@" is whatever the caller arranges (we redirect it per call site) — the guard
# call site PIPES the payload in, so the bounded command MUST keep that stdin.
run_bounded() {
if have_timeout; then
# GNU timeout exits 12Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
A comprehensive suite of Claude Code skills for academic research, covering the full pipeline from research to publication.
Ingests PDFs, Word and PowerPoint files, images, Markdown and plain text into a searchable knowledge base for research. Suits individuals and teams building a document-backed knowledge store.
Guides your AI research agent through a disciplined analysis workflow, from framing a question and planning the analysis before touching data, to investigating anomalies, running parallel checks and executing a pre-registered plan with review checkpoints.
Co-Researcher is an academic research plugin for Claude Code, Gemini CLI, OpenAI Codex and OpenCode. It runs literature reviews, systematic reviews, peer review and citation checks inside the AI assistant you already use.
A Claude Code plugin that orchestrates deep research across two coding agents, Claude Code and Codex CLI (OpenAI), running in parallel with cross-pollination refinement and final synthesis.