Onekgpd
K-DenseCommunityCreated by DnaerysDistributed by K-Dense
- Skill
- Tool integration
- Science and research
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
OneKGPd provides a specialized interface for querying the 1000 Genomes Project dataset, which includes 3,202 whole-genome-sequenced individuals on the GRCh38 assembly. The skill allows researchers to perform targeted queries for variants and individuals based on genomic regions, zygosity, and various annotation filters including allele frequencies, AlphaMissense scores, and clinical significance. It also includes offline tools for exploring sample metadata, population statistics, and kinship relationships within the cohort.
What it does
- Selects variants within specific genomic regions across the cohort or for selected individuals.
- Identifies individuals carrying variants that match specific criteria.
- Calculates kinship coefficients and determines relatedness between two individuals.
- Filters results by allele frequency, AlphaMissense scores, and VEP annotations.
- Provides offline access to sample metadata, population breakdowns, and pedigree information.
- Supports counting operations to estimate result set sizes before performing detailed selections.
When to use it
- When you need to identify which 1000 Genomes participants carry specific genetic variants.
- When analyzing variant distribution or allele frequencies within the 1000 Genomes cohort.
- When performing kinship analysis between two specific individuals in the dataset.
- When you need to filter variants by clinical significance or predicted impact in a specific region.
- When exploring population-level statistics or metadata for the 3,202-sample cohort.
Advantages
- Enables granular, individual-level queries rather than just aggregate population data.
- Integrates multiple annotation sources like gnomAD v4.1 and AlphaMissense directly into queries.
- Includes built-in counting commands to help manage large result sets and prevent memory issues.
- Provides offline metadata tools for rapid exploration of population and pedigree data.
- Standardized interface for consistent querying of the 1000 Genomes Project dataset.
Usage examples
- “How many individuals carry likely pathogenic missense variants in the BRCA1 gene region?”
- The agent uses the count-samples command with the specified GRCh38 coordinates and filters to return the number of individuals matching the criteria.
- “List the individuals who are homozygous reference at position chr17:43044295.”
- The agent executes the select-samples-hom-ref command to retrieve a list of sample IDs that are homozygous reference at the specified position.
- “What is the kinship relationship between individuals HG00096 and NA21130?”
- The agent runs the kinship command to return the degree of relatedness and the KING kinship coefficient for the two specified individuals.
- “Find all YRI population samples in the 1000 Genomes dataset.”
- The agent uses the select-samples-by-population command to retrieve the list of sample IDs belonging to the YRI population.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/k-dense-ai/onekgpd/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip onekgpd.zip -d .agents/skills
Documentation by Dnaerys
OneKGPd: Individual-Level Queries over the 1000 Genomes Project
Scope
This skill queries the 1000 Genomes Project dataset — the extended high-coverage cohort
of 3,202 whole-genome-sequenced individuals, on the GRCh38 assembly. All results
are drawn from this cohort, and sample names returned by the skill (for example
HG00096 or NA21130) identify its participants.
Queries resolve against the cohort's per-individual genotype data. This supports two complementary classes of question: selecting variants carried within a region (across the whole cohort or within a specified set of individuals), and selecting the individuals who carry variants matching given criteria. Variant selection can be filtered by allele frequency, predicted consequence, clinical significance, AlphaMissense classification, and the other annotation axes listed below. Relatedness between two named individuals is also available.
The genotype state in which a variant is carried — heterozygous or homozygous — is a criterion that queries may specify; results are returned as variants or as sample names, not as raw genotypes.
The public service is TLS gRPC at db.dnaerys.org:443, accessed with
dnaerys 0.2.1 (Python 3.11+); it is not a REST base URL. The maintained
service snapshot advertises VEP 115 / GENCODE 49, ClinVar 202502, and gnomAD
4.1. These are the service's annotation releases, not the latest release of
each upstream resource. Record them when interpreting results.
When to Use
Use this skill when you need to:
Find variants carried in a region or set of regions matching some criteria across the whole cohort (
select-variants).Find variants carried in a region or set of regions matching some criteria in specific set of individuals (
select-variants-in-samples).Find which 1000 Genomes individuals carry variants matching some criteria in a region or set of regions (
select-samples).Count how many individuals carry specific variants (
count-samples).Restrict any variant query to heterozygous-only or homozygous-only carriage, or query both together (default).
Identify which individuals are homozygous reference at a single position (
select-samples-hom-ref).Determine the relatedness between two named 1000 Genomes individuals — both the degree (twin / 1st / 2nd / 3rd / unrelated) and the KING kinship coefficient (
kinship).Get dataset totals — sample count, sex split, variant count, assembly (
dataset-info).Variant selection can be specified by KGP allele frequency, gnomAD 4.1 exome and gnomAD 4.1 genome allele frequency, AlphaMissense Score and AlphaMissense Class, ClinVar significance (202502), and VEP annotations (impact, biotype, feature type, variant class, consequences).
Do NOT use this skill for:
Resolving a gene symbol, rsID, or transcript to coordinates, or fetching reference sequence. Resolve coordinates first (see Coordinate Provenance below), then query this skill with the resolved GRCh38 region.
Any cohort other than the 1000 Genomes Project — this skill serves only that dataset.
Prerequisites
uv: This skill's script is run withuv run, which reads the script's inline dependency metadata and provisions an ephemeral environment. Ensureuvis installed and on PATH (https://docs.astral.sh/uv/).Data use terms: The 1000 Genomes Project data is open; users should be aware of the 1000 Genomes Project / IGSR data-use terms (https://www.internationalgenome.org/data).
Access constraints: There is no API key, no
.envfile, and no rate-limit token to configure.Timeouts: network commands use
--timeout 30seconds per RPC by default. A positive finite override is allowed. Pagination makes several RPCs and retryable failures retry the whole fetch up to three times, so this is not a deadline for the whole command.
Core Rules
Use the Wrappers: ALWAYS execute the provided helper scripts rather than constructing your own client calls or network requests. Use
scripts/onekgpd_api.pyfor variant/sample/kinship queries (it handles the connection, streaming, pagination, and JSON serialization), andscripts/onekgpd_meta.pyfor sample/population metadata (offline, see Sample & population metadata).Coordinates MUST be resolved against an authoritative source first — see Coordinate Provenance. This is mandatory, not advisory.
Count before you select: every variant and sample selection has a paired counting command. Call the count command FIRST to size the result set, then select only if the count is manageable.
Zygosity defaults to both: selection and counting commands include both heterozygous and homozygous carriage by default. Narrow with
--het-onlyor--hom-onlywhen the question is specifically about one state. (You do not need to pass anything to get both.)Output: scripts write full JSON to a file (
--output, default under/tmp/) and print a concise summary to stdout. Do not read large JSON files into context — usejqor a small disposableuv run pythonsnippet to extract fields.--page-sizeretrieves every page but accumulates all variants in RAM; it is not a bounded-memory export. Size the query first.Check completeness:
result_incomplete=truemeans results cannot support a definitive zero/absence claim. Re-run after service recovery. For capped variant selections,truncated=truemeans the limit was reached and more records may exist, even if the cluster result itself was complete.
Coordinate Provenance (MANDATORY FIRST STEP)
Before any region-based query, resolve the gene or feature to GRCh38
coordinates against an authoritative source (for example Ensembl or NCBI), and query
with those resolved coordinates. Inputs are 1-based, inclusive: a BED interval
[start0, end0) becomes start=start0+1, end=end0. Record the source accession,
annotation release, and retrieval date; gene boundaries can differ by annotation
release even on the same assembly. The assembly must be explicit, and a gene-range
must be resolved to precise positions before use. This is structural, not
advisory: there is no source-side guardrail that would catch a misplaced region,
so an unverified coordinate produces results for an unintended location with no
error.
# Resolve gene symbol -> GRCh38 region with an authoritative source FIRST,
# then pass the verified coordinates to the OneKGPd query below.[!CAUTION] The dataset is GRCh38. A GRCh37 coordinate, or any region that does not correctly correspond to the intended feature on GRCh38, will return results for an unintended location without raising an error. Verify the assembly and the resolved coordinates before querying.
Command Selection Guide
Match the question to the command. Counting commands are cheap and should precede their selection counterpart.
Which individuals carry matching variants in a region →
count-samplesthenselect-samplesWhich variants are carried in a region, cohort-wide →
count-variantsthenselect-variantsWhich variants are carried in a region, within a named set of individuals →
count-variants-in-samplesthenselect-variants-in-samplesWho is homozygous-reference at a single position →
count-samples-hom-refthenselect-samples-hom-refRelatedness (degree + coefficient) between two named individuals →
kinshipDataset totals (sample count, sex split, variant total, assembly) →
dataset-info
Cohort interpretation
The 3,202-sample cohort includes relatives: the additional 698 high-coverage
samples extend the original 2,504-sample panel. Carrier counts therefore are
not counts of independent observations, and cohort AF is not a population
prevalence estimate. For association or frequency comparisons, document the
selected populations and relatedness policy; use the bundled pedigree metadata
and kinship when choosing or auditing the analysis set. See the
IGSR cohort announcement.
Annotation filters (shared across variant and sample selection/counting)
All variant- and sample-selection commands (count-variants,
select-variants, their -in-samples forms, count-samples, select-samples)
accept the same annotation filters. Different filter fields are combined with
AND; multiple values within one field are combined with OR. Enum values
are case-insensitive (e.g. missense_variant or MISSENSE_VARIANT).
These are selection criteria applied on the server. The fields returned on a selected variant are listed under Variant-returning commands; a criterion used for filtering is not necessarily echoed back on the returned variant.
--af-lt/--af-gt: 1000 Genomes dataset allele frequency bounds--gnomad-exomes-af-lt/--gnomad-exomes-af-gt: gnomAD v4.1 exome AF bounds--gnomad-genomes-af-lt/--gnomad-genomes-af-gt: gnomAD v4.1 genome AF bounds--clin-significance: ClinVar significance terms, CSV (e.g.PATHOGENIC,LIKELY_PATHOGENIC)--consequence: Sequence Ontology consequence terms, CSV (e.g.MISSENSE_VARIANT,STOP_GAINED)--impact: VEP impact, CSV (HIGH,MODERATE,LOW,MODIFIER)--variant-type,--feature-type,--bio-type: SO variant class / VEP feature / VEP biotype, CSV--alpha-missense-class:AM_LIKELY_BENIGN,AM_LIKELY_PATHOGENIC,AM_AMBIGUOUS(CSV)--alpha-missense-score-lt/--alpha-missense-score-gt: AlphaMissense score bounds--biallelic-only/--multiallelic-only--exclude-males/--exclude-females--min-len-bp/--max-len-bp: alternate-allele length bounds (bp)
[!NOTE]
--alpha-missense-classand--alpha-missense-score-*are mutually exclusive (the engine ignores the class when a score bound is set).--biallelic-onlyand--multiallelic-onlyare mutually exclusive.--exclude-malesand--exclude-femalesare mutually exclusive. Setting a*-gtbound greater than or equal to its matching*-ltbound defines an empty range and will return nothing.
[!NOTE]
gnomad_exomes_af,gnomad_genomes_af, andam_scoreuse0.0for not annotated in this service snapshot. This does not establish absence from the current gnomAD release, biological rarity, or a benign prediction. The dataset's ownaffield is a different statistic, not this sentinel.
[!CAUTION] A zero numeric filter is unset on the server, so
--gnomad-exomes-af-gt 0does not exclude missing annotations. The wrapper rejects zero, nonfinite, out-of-range, and float32-underflowing bounds. Choose an explicit positive threshold (for example--gnomad-exomes-af-gt 0.000001means AF > 1e-6, not merely annotation presence). For exact> 0, retrieve a complete variant set and post-filter the returned AF locally. A< Xfilter alone includes unannotated zero values. Apply the same missing-score caution to AlphaMissense.
Categorical annotations are retained across transcripts. Combining consequence
and impact filters does not establish that they describe the same transcript.
amino_acids may contain multiple HGVSp entries; the generic gRPC service places
canonical annotations first, whereas the separate MCP layer trims its output.
Preserve transcript identifiers, and do not treat a model's likely-pathogenic
class as a clinical diagnosis or a participant phenotype.
Quick Start
# Step 1. NCBI Gene 672, GRCh38.p14 / NC_000017.11, RS_2025_08:
# BRCA1 spans chr17:43044295-43170327 (1-based inclusive).
# Source: https://www.ncbi.nlm.nih.gov/gene/672 ; re-resolve for your analysis.
# Step 2. Size the result set: how many individuals carry predicted likely-pathogenic
# missense variants in this region?
uv run scripts/onekgpd_api.py count-samples \
--chrom chr17 --start 43044295 --end 43170327 \
--consequence MISSENSE_VARIANT \
--alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/count.json
# Step 3. If the count is manageable, list those individuals.
uv run scripts/onekgpd_api.py select-samples \
--chrom chr17 --start 43044295 --end 43170327 \
--consequence MISSENSE_VARIANT \
--alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/samples.json
# Step 4. Count then select variants for actual returned sample IDs.
# HG03169,NA20506 below are illustrative IDs; substitute the Step 3 results.
uv run scripts/onekgpd_api.py count-variants-in-samples \
--chrom chr17 --start 43044295 --end 43170327 \
--samples HG03169,NA20506 \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/variant_count.json
uv run scripts/onekgpd_api.py select-variants-in-samples \
--chrom chr17 --start 43044295 --end 43170327 \
--samples HG03169,NA20506 \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/variants.jsonCommands
Each command writes full JSON to a file (--output PATH, default a temp file)
and prints a concise stdout summary. All region/sample commands share: the
region input (--chrom/--start/--end with optional --ref/--alt, or one
or more repeated --region CHR:START-END), the zygosity flags
(--het-only/--hom-only, default both), and the annotation filters above.
The full per-flag tables live in
references/onekgpd_commands.md.
Variant-returning commands
select-* return matching variants; count-* return an integer count.
count-variants— count variants in a region, cohort-wide.select-variants— select variants in a region, cohort-wide. Use--limit N(hard cap, default 200) or--page-size N(retrieve the full set in pages); the two are mutually exclusive. The summary flagstruncatedwhen the cap is reached.count-variants-in-samples— ascount-variants, restricted to--samples NAME1,NAME2,...(required).select-variants-in-samples— asselect-variants, restricted to--samples NAME1,NAME2,...(required).
Each returned variant carries these 22 keys: chr, start, end, ref,
alt, af, ac, an, hom_samples, het_samples, mis_samples,
hom_samples_fx, het_samples_fx, mis_samples_fx, hom_samples_mxy,
het_samples_mxy, mis_samples_mxy, gnomad_exomes_af, gnomad_genomes_af,
am_score, amino_acids, biallelic.
ClinVar significance and VEP consequence are filter criteria only and are not
returned. Full schema:
references/onekgpd_commands.md.
Sample-returning commands
count-samples— count individuals carrying a matching variant in a region.select-samples— list the names of individuals carrying a matching variant. Supports--skip Nand--limit N. Returns names only; to see which variants qualified an individual, feed the names intoselect-variants-in-samples.
Homozygous-reference commands
Single position via --chrom + --position (not a region).
count-samples-hom-ref— count individuals with a 0/0 call at the position. The count uses a sentinel:-1= no variant exists at that position at all;0= a variant exists but no individual is homozygous reference;>0= the number of homozygous-reference individuals. These interpretations requireresult_incomplete=false; otherwisevariant_presentisnull. No variant record is not evidence that all 3,202 individuals have callable 0/0 genotypes.select-samples-hom-ref— list the individuals with a 0/0 call at the position.
Relatedness command
kinship --sample1 NAME --sample2 NAME— relatedness between two named individuals: the degree (TWINS_MONOZYGOTIC/FIRST_DEGREE/SECOND_DEGREE/THIRD_DEGREE/UNRELATED) and the KING kinship coefficient (phi_bwf).
Dataset metadata command
dataset-info— dataset totals:samples_total(3,202), female/male split,variants_total,assembly(GRCh38), and the cohort breakdown. No region required; doubles as a connectivity check.
Sample & population metadata (offline)
Population, sex, pedigree, and superpopulation questions are answered by a second
script, scripts/onekgpd_meta.py, from a data file bundled in the skill — no
network, no credentials, no coordinates. The sample IDs are the same names the
variant commands use, so the two layers compose (e.g. pick a cohort by population,
then query its variants). Run uv run scripts/onekgpd_meta.py <command>.
The cohort has 5 superpopulations (AFR, AMR, EAS, EUR, SAS) and 26
populations. Population/superpopulation values match case-insensitively by
short code or full name; sample IDs are case-sensitive.
sample-metadata --samples NA19240,HG00096— family, gender, parents, children, population, superpopulation, and phase3 status for the given samples.list-populations— all 26 populations with superpopulation and sample count (use to discover valid values).list-superpopulations— the 5 superpopulations with sample count and constituent populations.population-stats --populations YRI [--populations CHS …]— per-population sex split, phase3 count, and trio membership. Repeat--populationsfor multiple values (full names contain commas, so they are not comma-separated).superpopulation-summary --superpopulations EAS [--superpopulations EUR …]— per-superpopulation totals with a per-population breakdown.select-samples-by-population --population YRIand/or--superpopulation AFR, with optional--skip/--limit(default 0 / 50, max 3202) — the sample IDs in a population and/or superpopulation; both given intersects. Feed the names intoselect-variants-in-samplesto see their variants.
See references/onekgpd_commands.md for full argument tables and JSON output schemas.
Typical Workflows
Which individuals, then which variants they carry
The following is an illustrative template; replace all angle-bracket placeholders.
# Step 1: resolve gene -> verified GRCh38 region (authoritative source).
# Step 2: count individuals carrying a qualifying variant in the region.
uv run scripts/onekgpd_api.py count-samples \
--chrom <chr> --start <start> --end <end> \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/n.json
# Step 3: list those individuals.
uv run scripts/onekgpd_api.py select-samples \
--chrom <chr> --start <start> --end <end> \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/who.json
# Step 4: count variants for those individuals before selecting.
uv run scripts/onekgpd_api.py count-variants-in-samples \
--chrom <chr> --start <start> --end <end> \
--samples <name1,name2,...> \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/variant_count.json
uv run scripts/onekgpd_api.py select-variants-in-samples \
--chrom <chr> --start <start> --end <end> \
--samples <name1,name2,...> \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC \
--output /tmp/variants.jsonHomozygous-reference carriers at a position of interest
Illustrative template; replace the placeholders with verified coordinates.
# After identifying a position of interest (verified coordinate):
uv run scripts/onekgpd_api.py count-samples-hom-ref \
--chrom <chr> --position <pos> --output /tmp/homref_n.json
uv run scripts/onekgpd_api.py select-samples-hom-ref \
--chrom <chr> --position <pos> --output /tmp/homref.jsonCommon Mistakes
Mistake: Querying with an unverified coordinate. Fix: Always resolve gene/feature → GRCh38 against an authoritative source first. A misplaced region returns results for an unintended location without error.
Mistake: Calling a selection command before its counting command. Fix: Count first; selection result sets can be large.
Mistake: Assuming a GRCh37 coordinate will work. Fix: The dataset is GRCh38 only.
References
references/onekgpd_commands.md — full per-command argument tables and the returned-variant output schema.
references/annotation_vocabularies.md — the controlled-vocabulary terms accepted by the CSV filter flags (consequence, impact, biotype, feature type, ClinVar significance, AlphaMissense class, variant class).
Current API contract and live-check scope: the command reference.
1000 Genomes Project / IGSR: https://www.internationalgenome.org/
1000 Genomes Project dataset online: https://dnaerys.org/online/
Files
- ATTRIBUTION.mdAdded by AutoGineer902 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md22.0 KiB
- assets/kgpe.json1012.9 KiB
- references/annotation_vocabularies.md4.7 KiB
- references/onekgpd_commands.md17.7 KiB
- scripts/onekgpd_api.pyScript31.8 KiB
- scripts/onekgpd_meta.pyScript18.9 KiB
scripts/onekgpd_api.py
# /// script
# requires-python = ">=3.11"
# dependencies = ["dnaerys>=0.2.1,<0.3.0"]
# ///
"""OneKGPd — individual-level queries over the 1000 Genomes Project.
A single command-line wrapper exposing ten subcommands over the 1000 Genomes
Project cohort (3,202 whole-genome-sequenced individuals, GRCh38): selecting and
counting variants in a region (cohort-wide or within named individuals),
selecting and counting the individuals who carry matching variants, homozygous-
reference queries at a single position, pairwise relatedness, and dataset totals.
Every command writes full JSON to a file (``--output``, or a temp file by
default) and prints a concise human-readable summary to stdout. Coordinates are
GRCh38, 1-based inclusive; resolve a gene/feature to coordinates against an
authoritative source BEFORE querying.
Examples
--------
uv run scripts/onekgpd_api.py dataset-info
uv run scripts/onekgpd_api.py count-samples --chrom chr17 --start 43044295 --end 43170327 \
--consequence MISSENSE_VARIANT --alpha-missense-class AM_LIKELY_PATHOGENIC
uv run scripts/onekgpd_api.py kinship --sample1 NA19238 --sample2 NA19240
MIT License. Author: Dnaerys.
"""
from __future__ import annotations
import argparse
import json
import math
import struct
import os
import sys
import tempfile
import time
import warnings
from typing import Any, NoReturn
from dnaerys import (
AnnotationFilter,
DnaerysError,
DnaerysIncompleteResultWarning,
DnaerysClient,
Region,
)
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
DEFAULT_ENDPOINT = "db.dnaerys.org:443" # public 1000 Genomes instance (fixed)
DEFAULT_TIMEOUT = 30.0 # seconds per RPC, including internal paging calls
DEFAULT_VARIANT_LIMIT = 200 # hard cap when neither --limit nor --page-size given
MAX_RETRIES = 3 # bounded retry attempts on retryable errors
RETRY_BASE_DELAY = 1.0 # seconds; exponential backoff: 1, 2, ...
PREVIEW_ROWS = 10 # rows shown in a stdout summary preview
INCOMPLETE_NOTE = "[!] Result may be incomplete: some data was unreachable."
# ---------------------------------------------------------------------------
# Small generic helpers
# ---------------------------------------------------------------------------
def _fail(msg: str) -> NoReturn:
"""Print a clean one-line message to stderr and exit non-zero."""
print(msg, file=sys.stderr)
sys.exit(1)
def _split_csv(s: str) -> list[str]:
"""Split a comma-separated value into a list of non-empty trimmed tokens."""
return [part.strip() for part in s.split(",") if part.strip()]
def _positive_finite(value: str | float) -> float:
number = float(value)
if not math.isfinite(number) or number <= 0:
raise argparse.ArgumentTypeError("must be a positive finite number")
return number
def _client(args) -> DnaerysClient:
"""Keep the public TLS endpoint and GRCh38 explicit; bound each RPC."""
return DnaerysClient(
DEFAULT_ENDPOINT, tls=True, assembly="GRCh38",
default_timeout=getattr(args, "timeout", DEFAULT_TIMEOUT),
)
def _sample_names(csv: str) -> list[str]:
names = _split_csv(csv)
if not names:
raise ValueError("--samples must name at least one individual; an empty list would query the whole cohort")
return names
def _save_json(data: Any, prefix: str, output_path: str | None = None) -> str:
"""Write *data* as indented JSON to *output_path* or a temp file; return path."""
if output_path:
path = output_path
with open(path, "w") as fh:
json.dump(data, fh, indent=2)
else:
fd, path = tempfile.mkstemp(prefix=f"onekgpd_{prefix}_", suffix=".json", text=True)
with os.fdopen(fd, "w") as fh:
json.dump(data, fh, indent=2)
return path
def _emit(data: Any, prefix: str, summary_lines: list[str], output: str | None) -> None:
"""Save full JSON to a file and print the summary + saved-path line to stdout."""
path = _save_json(data, prefix, output)
for line in summary_lines:
print(line)
print(f"[*] Full JSON saved to {path}")
def _call_with_retry(fn):
"""Run *fn*; on a retryable ``DnaerysError`` retry with bounded backoff.
The whole fetch-and-materialize closure is retried (a fresh client per
attempt), so streaming errors that surface during iteration are covered.
"""
for attempt in range(1, MAX_RETRIES + 1):
try:
return fn()
except DnaerysError as e:
if e.is_retryable and attempt < MAX_RETRIES:
time.sleep(RETRY_BASE_DELAY * (2 ** (attempt - 1)))
continue
raise
# ---------------------------------------------------------------------------
# Input builders
# ---------------------------------------------------------------------------
def _parse_region_str(s: str) -> Region:
"""Parse ``CHR:START-END`` into a ``Region`` (coordinate validation deferred)."""
try:
chrom, span = s.split(":", 1)
start_s, end_s = span.split("-", 1)
start, end = int(start_s), int(end_s)
except ValueError:
raise ValueError(
f"invalid --region {s!r}; expected CHR:START-END, "
"e.g. chr17:43044295-43170327"
)
return Region(chrom, start, end)
def _build_regions(args) -> tuple[Region | None, list[Region] | None]:
"""Return exactly one of (single Region, None) or (None, list[Region])."""
has_single = args.chrom is not None
has_multi = bool(args.region)
if has_single and has_multi:
raise ValueError("use either --chrom/--start/--end or --region, not both")
if not has_single and not has_multi:
raise ValueError(
"a region is required: pass --chrom/--start/--end or "
"one or more --region CHR:START-END"
)
if has_multi:
if args.ref or args.alt:
raise ValueError(
"--ref/--alt apply only to a single --chrom/--start/--end region"
)
return None, [_parse_region_str(r) for r in args.region]
if args.start is None or args.end is None:
raise ValueError("--chrom requires --start and --end")
return Region(args.chrom, args.start, args.end, ref=args.ref, alt=args.alt), None
def _zygosity(args) -> tuple[bool, bool]:
"""Return (hom, het); default both True, narrowed by --het-only/--hom-only."""
if getattr(args, "het_only", False):
return (False, True)
if getattr(args, "hom_only", False):
return (True, False)
return (True, True)
_CSV_FIELDS = [
("clin_significance", "clin_significance"),
("consequence", "consequence"),
("impact", "impact"),
("variant_type", "variant_type"),
("feature_type", "feature_type"),
("bio_type", "bio_type"),
("alpha_missense_class", "am_class"),
]
_FLOAT_FIELDS = [
("af_lt", "af_lt"),
("af_gt", "af_gt"),
("gnomad_exomes_af_lt", "gnomad_exomes_af_lt"),
("gnomad_exomes_af_gt", "gnomad_exomes_af_gt"),
("gnomad_genomes_af_lt", "gnomad_genomes_af_lt"),
("gnomad_genomes_af_gt", "gnomad_genomes_af_gt"),
("alpha_missense_score_lt", "am_score_lt"),
("alpha_missense_score_gt", "am_score_gt"),
]
_BOOL_FIELDS = [
("biallelic_only", "biallelic_only"),
("multiallelic_only", "multiallelic_only"),
("exclude_males", "exclude_males"),
("exclude_females", "exclude_females"),
]
def _build_annotation_filter(args) -> AnnotationFilter | None:
"""Map the shared annotation flags to an ``AnnotationFilter`` (or None).
Note: ``--min-len-bp``/``--max-len-bp`` are NOT filter fields; they are passed
as the client method's ``variant_min_length``/``variant_max_length`` kwargs.
"""
kwargs: dict[str, Any] = {}
for arg_name, field in _CSV_FIELDS:
raw = getattr(args, arg_name, None)
if raw is not None:
values = _split_csv(raw)
if not values:
raise ValueError(f"--{arg_name.replace('_', '-')} must contain at least one term")
kwargs[field] = values
for arg_name, field in _FLOAT_FIELDS:
val = getattr(args, arg_name, None)
if val is not None:
if not math.isfinite(val) or not 0 < val <= 1:
raise ValueError(
f"--{arg_name.replace('_', '-')} must be finite and in (0, 1]; "
"zero means an unset server filter. Use an explicit positive bound "
"or post-filter complete variant results for exact > 0."
)
if struct.unpack("!f", struct.pack("!f", val))[0] == 0:
raise ValueError(f"--{arg_name.replace('_', '-')} underflows the API float32 bound to zero")
kwargs[field] = val
for arg_name, field in _BOOL_FIELDS:
if getattr(args, arg_name, False):
kwargs[field] = True
# am-class vs am-score cannot be one argparse group (am-score is a range pair).
if kwargs.get("am_class") and ("am_score_lt" in kwargs or "am_score_gt" in kwargs):
_fail(
"Error: --alpha-missense-class cannot be combined with "
"--alpha-missense-score-lt/--alpha-missense-score-gt"
)
if not kwargs:
return None
return AnnotationFilter(**kwargs)
# ---------------------------------------------------------------------------
# Display / serialization helpers
# ---------------------------------------------------------------------------
def _chr_to_str(chrom) -> str:
"""Render a ``Chromosome`` enum as ``chr17`` / ``chrX`` / ``chrMT``."""
return "chr" + chrom.name[len("CHR"):]
def _region_one_label(r: Region) -> str:
return f"{_chr_to_str(r.chr)}:{r.start}-{r.end}"
def _region_label(region: Region | None, regions: list[Region] | None) -> str:
if region is not None:
return _region_one_label(region)
return ", ".join(_region_one_label(r) for r in regions or [])
def _zyg_label(hom: bool, het: bool) -> str:
if hom and het:
return "hom+het"
if het:
return "het only"
return "hom only"
def _variant_to_dict(v) -> dict:
"""Serialize a ``Variant`` to its in-scope output keys (enum rendered as text)."""
return {
"chr": _chr_to_str(v.chr),
"start": v.start,
"end": v.end,
"ref": v.ref,
"alt": v.alt,
"af": v.af,
"ac": v.ac,
"an": v.an,
"hom_samples": v.hom_samples,
"het_samples": v.het_samples,
"mis_samples": v.mis_samples,
"hom_samples_fx": v.hom_samples_fx,
"het_samples_fx": v.het_samples_fx,
"mis_samples_fx": v.mis_samples_fx,
"hom_samples_mxy": v.hom_samples_mxy,
"het_samples_mxy": v.het_samples_mxy,
"mis_samples_mxy": v.mis_samples_mxy,
"gnomad_exomes_af": v.gnomad_exomes_af,
"gnomad_genomes_af": v.gnomad_genomes_af,
"am_score": v.am_score,
"amino_acids": v.amino_acids,
"biallelic": v.biallelic,
}
def _filters_echo(args) -> dict:
"""Echo the annotation/length flags that were actually set (for provenance)."""
out: dict[str, Any] = {}
for arg_name, _ in _FLOAT_FIELDS:
val = getattr(args, arg_name, None)
if val is not None:
out[arg_name] = val
for arg_name, _ in _CSV_FIELDS:
raw = getattr(args, arg_name, None)
if raw:
out[arg_name] = _split_csv(raw)
for arg_name, _ in _BOOL_FIELDS:
if getattr(args, arg_name, False):
out[arg_name] = True
if getattr(args, "min_len_bp", None) is not None:
out["variant_min_length"] = args.min_len_bp
if getattr(args, "max_len_bp", None) is not None:
out["variant_max_length"] = args.max_len_bp
return out
def _request_echo(args, *, samples, region, regions, hom, het) -> dict:
"""Build the request-provenance block included in every region-command JSON."""
req: dict[str, Any] = {}
if region is not None:
req["region"] = _region_one_label(region)
if region.ref:
req["ref"] = region.ref
if region.alt:
req["alt"] = region.alt
if regions is not None:
req["regions"] = [_region_one_label(r) for r in regions]
if samples is not None:
req["samples"] = samples
req["zygosity"] = _zyg_label(hom, het)
filt = _filters_echo(args)
if filt:
req["filters"] = filt
return req
# ---------------------------------------------------------------------------
# Command workers / handlers
# ---------------------------------------------------------------------------
def _run_count_variants(args, samples: list[str] | None) -> None:
region, regions = _build_regions(args)
hom, het = _zygosity(args)
ann = _build_annotation_filter(args)
def fetch():
with _client(args) as client:
return client.count_variants(
region=region,
regions=regions,
samples=samples,
hom=hom,
het=het,
annotations=ann,
variant_min_length=args.min_len_bp,
variant_max_length=args.max_len_bp,
)
result = _call_with_retry(fetch)
incomplete = result.metadata.affected
data = {
"command": args.command,
"count": result.count,
"request": _request_echo(
args, samples=samples, region=region, regions=regions, hom=hom, het=het
),
"result_incomplete": incomplete,
}
summary = [
f"{result.count:,} variants match in {_region_label(region, regions)} "
f"({_zyg_label(hom, het)})"
]
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, args.command.replace("-", "_"), summary, args.output)
def _run_select_variants(args, samples: list[str] | None) -> None:
region, regions = _build_regions(args)
hom, het = _zygosity(args)
ann = _build_annotation_filter(args)
page_size = args.page_size
limit = args.limit
def fetch():
with _client(args) as client:
if page_size is not None:
pq = client.paginate_variants(
page_size=page_size,
region=region,
regions=regions,
samples=samples,
hom=hom,
het=het,
annotations=ann,
variant_min_length=args.min_len_bp,
variant_max_length=args.max_len_bp,
)
collected = []
for page in pq:
collected.extend(page.variants)
return collected, pq.metadata, False
stream = client.select_variants(
region=region,
regions=regions,
samples=samples,
hom=hom,
het=het,
annotations=ann,
variant_min_length=args.min_len_bp,
variant_max_length=args.max_len_bp,
limit=limit,
)
collected = stream.to_list()
truncated = limit is not None and len(collected) >= limit
return collected, stream.metadata, truncated
variants, meta, truncated = _call_with_retry(fetch)
incomplete = meta.affected
request = _request_echo(
args, samples=samples, region=region, regions=regions, hom=hom, het=het
)
if page_size is not None:
request["page_size"] = page_size
else:
request["limit"] = limit
data = {
"command": args.command,
"count_returned": len(variants),
"truncated": truncated,
"request": request,
"result_incomplete": incomplete,
"variants": [_variant_to_dict(v) for v in variants],
}
summary = [
f"Returned {len(variants)} variants in {_region_label(region, regions)} "
f"({_zyg_label(hom, het)})"
]
for v in variants[:PREVIEW_ROWS]:
summary.append(
f" {_chr_to_str(v.chr)}:{v.start} {v.ref}>{v.alt} "
f"af={v.af:.6g} am_score={v.am_score:.6g} aa={v.amino_acids or '-'}"
)
if not variants:
summary.append(" (no variants matched)")
if truncated:
summary.append(
f"[!] Truncated at --limit {limit}; raise --limit or use "
"--page-size to retrieve the full set."
)
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, args.command.replace("-", "_"), summary, args.output)
def cmd_count_variants(args) -> None:
_run_count_variants(args, samples=None)
def cmd_count_variants_in_samples(args) -> None:
_run_count_variants(args, samples=_sample_names(args.samples))
def cmd_select_variants(args) -> None:
_run_select_variants(args, samples=None)
def cmd_select_variants_in_samples(args) -> None:
_run_select_variants(args, samples=_sample_names(args.samples))
def cmd_count_samples(args) -> None:
region, regions = _build_regions(args)
hom, het = _zygosity(args)
ann = _build_annotation_filter(args)
def fetch():
with _client(args) as client:
return client.count_samples(
region=region,
regions=regions,
hom=hom,
het=het,
annotations=ann,
variant_min_length=args.min_len_bp,
variant_max_length=args.max_len_bp,
)
result = _call_with_retry(fetch)
incomplete = result.metadata.affected
data = {
"command": args.command,
"count": result.count,
"request": _request_echo(
args, samples=None, region=region, regions=regions, hom=hom, het=het
),
"result_incomplete": incomplete,
}
summary = [
f"{result.count:,} individuals carry a matching variant in "
f"{_region_label(region, regions)} ({_zyg_label(hom, het)})"
]
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, "count_samples", summary, args.output)
def cmd_select_samples(args) -> None:
region, regions = _build_regions(args)
hom, het = _zygosity(args)
ann = _build_annotation_filter(args)
def fetch():
with _client(args) as client:
return client.select_samples(
region=region,
regions=regions,
hom=hom,
het=het,
annotations=ann,
variant_min_length=args.min_len_bp,
variant_max_length=args.max_len_bp,
skip=args.skip,
limit=args.limit,
)
result = _call_with_retry(fetch)
names = list(result.samples)
incomplete = result.metadata.affected
request = _request_echo(
args, samples=None, region=region, regions=regions, hom=hom, het=het
)
if args.skip is not None:
request["skip"] = args.skip
if args.limit is not None:
request["limit"] = args.limit
data = {
"command": args.command,
"count": len(names),
"samples": names,
"request": request,
"result_incomplete": incomplete,
}
summary = [
f"{len(names)} individuals carry a matching variant in "
f"{_region_label(region, regions)} ({_zyg_label(hom, het)})"
]
for name in names[:PREVIEW_ROWS]:
summary.append(f" {name}")
if not names:
summary.append(" (none)")
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, "select_samples", summary, args.output)
def cmd_count_samples_hom_ref(args) -> None:
Region(args.chrom, args.position, args.position) # validate 1-based position
def fetch():
with _client(args) as client:
return client.count_samples_hom_ref(chr=args.chrom, position=args.position)
result = _call_with_retry(fetch)
count = result.count
incomplete = result.metadata.affected
present = None if incomplete else count != -1
pos = f"{args.chrom}:{args.position}"
data = {
"command": args.command,
"count": count,
"variant_present": present,
"result_incomplete": incomplete,
"request": {"chrom": args.chrom, "position": args.position},
}
if incomplete:
summary = [f"Homozygous-reference response at {pos}: {count}; presence and count are not definitive.", INCOMPLETE_NOTE]
elif count == -1:
summary = [
f"No variant exists at {pos} in the dataset; "
"homozygous-reference count is undefined here."
]
elif count == 0:
summary = [
f"A variant exists at {pos}, but no individual is homozygous reference."
]
else:
summary = [f"{count:,} individuals are homozygous reference at {pos}."]
_emit(data, "count_samples_hom_ref", summary, args.output)
def cmd_select_samples_hom_ref(args) -> None:
Region(args.chrom, args.position, args.position) # validate 1-based position
def fetch():
with _client(args) as client:
return client.select_samples_hom_ref(chr=args.chrom, position=args.position)
result = _call_with_retry(fetch)
names = list(result.samples)
incomplete = result.metadata.affected
pos = f"{args.chrom}:{args.position}"
data = {
"command": args.command,
"result_incomplete": incomplete,
"count": len(names),
"samples": names,
"request": {"chrom": args.chrom, "position": args.position},
}
summary = [f"{len(names)} individuals are homozygous reference at {pos}"]
for name in names[:PREVIEW_ROWS]:
summary.append(f" {name}")
if not names:
summary.append(" (none)")
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, "select_samples_hom_ref", summary, args.output)
def cmd_kinship(args) -> None:
def fetch():
with _client(args) as client:
return client.kinship_duo(sample1=args.sample1, sample2=args.sample2)
result = _call_with_retry(fetch)
if not result.pairs:
_fail(f"Error: no relatedness result returned for {args.sample1}, {args.sample2}")
pair = result.pairs[0]
degree = pair.degree.name
phi = pair.phi_bwf
incomplete = result.metadata.affected
data = {
"command": "kinship",
"sample1": pair.sample1,
"sample2": pair.sample2,
"degree": degree,
"phi_bwf": phi,
"result_incomplete": incomplete,
}
summary = [
f"{pair.sample1} <-> {pair.sample2}: {degree} "
f"(KING kinship coefficient phi = {phi:.4f})"
]
if incomplete:
summary.append(INCOMPLETE_NOTE)
_emit(data, "kinship", summary, args.output)
def cmd_dataset_info(args) -> None:
def fetch():
with _client(args) as client:
return client.dataset_info()
info = _call_with_retry(fetch)
data = {
"command": "dataset-info",
"samples_total": info.samples_total,
"females_total": info.females_total,
"males_total": info.males_total,
"variants_total": info.variants_total,
"assembly": info.assembly.name,
"cohorts": [
{
"cohort_name": c.cohort_name,
"samples_count": c.samples_count,
"female_count": c.female_count,
"male_count": c.male_count,
"synthetic": c.synthetic,
}
for c in info.cohorts
],
}
summary = [
f"1000 Genomes Project - {info.samples_total:,} individuals "
f"(F: {info.females_total:,}, M: {info.males_total:,}); "
f"{info.variants_total:,} variants; {info.assembly.name}"
]
for c in info.cohorts:
summary.append(
f" {c.cohort_name}: {c.samples_count:,} "
f"(F {c.female_count:,} / M {c.male_count:,})"
)
_emit(data, "dataset_info", summary, args.output)
# ---------------------------------------------------------------------------
# Argument parsing
# ---------------------------------------------------------------------------
def _add_annotation_flags(p: argparse.ArgumentParser) -> None:
g = p.add_argument_group("annotation filters (different fields AND; CSV within a field OR)")
g.add_argument("--af-lt", type=float, help="Keep variants with 1000 Genomes AF < this value.")
g.add_argument("--af-gt", type=float, help="Keep variants with 1000 Genomes AF > this value.")
g.add_argument("--gnomad-exomes-af-lt", type=float, help="gnomAD v4.1 exomes AF < this value.")
g.add_argument("--gnomad-exomes-af-gt", type=float, help="gnomAD v4.1 exomes AF > this value.")
g.add_argument("--gnomad-genomes-af-lt", type=float, help="gnomAD v4.1 genomes AF < this value.")
g.add_argument("--gnomad-genomes-af-gt", type=float, help="gnomAD v4.1 genomes AF > this value.")
g.add_argument("--clin-significance", metavar="CSV", help="ClinVar significance terms (CSV). See references/annotation_vocabularies.md.")
g.add_argument("--consequence", metavar="CSV", help="Sequence Ontology consequence terms (CSV).")
g.add_argument("--impact", metavar="CSV", help="VEP impact (CSV: HIGH,MODERATE,LOW,MODIFIER).")
g.add_argument("--variant-type", metavar="CSV", help="SO variant class terms (CSV).")
g.add_argument("--feature-type", metavar="CSV", help="VEP feature types (CSV).")
g.add_argument("--bio-type", metavar="CSV", help="VEP biotypes (CSV).")
g.add_argument("--alpha-missense-class", metavar="CSV", help="AM_LIKELY_BENIGN,AM_LIKELY_PATHOGENIC,AM_AMBIGUOUS (CSV).")
g.add_argument("--alpha-missense-score-lt", type=float, help="AlphaMissense score < this value.")
g.add_argument("--alpha-missense-score-gt", type=float, help="AlphaMissense score > this value.")
bm = p.add_mutually_exclusive_group()
bm.add_argument("--biallelic-only", action="store_true", help="Keep only biallelic sites.")
bm.add_argument("--multiallelic-only", action="store_true", help="Keep only multiallelic sites.")
ef = p.add_mutually_exclusive_group()
ef.add_argument("--exclude-males", action="store_true", help="Exclude male samples.")
ef.add_argument("--exclude-females", action="store_true", help="Exclude female samples.")
z = p.add_mutually_exclusive_group()
z.add_argument("--het-only", action="store_true", help="Heterozygous carriage only (default: both).")
z.add_argument("--hom-only", action="store_true", help="Homozygous carriage only (default: both).")
def build_parser() -> argparse.ArgumentParser:
conn_parser = argparse.ArgumentParser(add_help=False)
conn_parser.add_argument(
"--timeout", type=_positive_finite, default=DEFAULT_TIMEOUT,
help="Positive finite seconds per RPC (default 30); paging and retries may take longer overall.",
)
conn_parser.add_argument(
"--output",
help="Write full JSON to this path (default: a temp file in the system temp dir).",
)
region_parser = argparse.ArgumentParser(add_help=False)
region_parser.add_argument("--chrom", help="Chromosome, e.g. chr17, 17, X, MT (single-region mode).")
region_parser.add_argument("--start", type=int, help="1-based inclusive start (with --chrom).")
region_parser.add_argument("--end", type=int, help="1-based inclusive end (with --chrom).")
region_parser.add_argument("--ref", help="Reference allele to narrow to one allele (single-region only).")
region_parser.add_argument("--alt", help="Alternate allele to narrow to one allele (single-region only).")
region_parser.add_argument(
"--region", action="append", metavar="CHR:START-END",
help="A region as CHR:START-END; repeat for multiple regions (multi-region mode).",
)
region_parser.add_argument("--min-len-bp", type=int, help="Minimum alternate-allele length (bp).")
region_parser.add_argument("--max-len-bp", type=int, help="Maximum alternate-allele length (bp).")
annot_parser = argparse.ArgumentParser(add_help=False)
_add_annotation_flags(annot_parser)
parser = argparse.ArgumentParser(
prog="onekgpd_api.py",
description="Individual-level queries over the 1000 Genomes Project "
"(3,202 WGS individuals, GRCh38).",
)
sub = parser.add_subparsers(dest="command", required=True)
region_parents = [conn_parser, region_parser, annot_parser]
# dataset-info
p = sub.add_parser("dataset-info", parents=[conn_parser], help="Dataset totals (samples, sex split, variants, assembly).")
p.set_defaults(func=cmd_dataset_info)
# count-variants
p = sub.add_parser("count-variants", parents=region_parents, help="Count variants in a region, cohort-wide.")
p.set_defaults(func=cmd_count_variants)
# select-variants
p = sub.add_parser("select-variants", parents=region_parents, help="Select variants in a region, cohort-wide.")
lp = p.add_mutually_exclusive_group()
lp.add_argument("--limit", type=int, default=DEFAULT_VARIANT_LIMIT, help=f"Hard cap on returned variants (default {DEFAULT_VARIANT_LIMIT}).")
lp.add_argument("--page-size", type=int, help="Retrieve ALL matching variants in pages of this size (full walk).")
p.set_defaults(func=cmd_select_variants)
# count-variants-in-samples
p = sub.add_parser("count-variants-in-samples", parents=region_parents, help="Count variants in a region within named individuals.")
p.add_argument("--samples", required=True, metavar="CSV", help="Comma-separated sample names.")
p.set_defaults(func=cmd_count_variants_in_samples)
# select-variants-in-samples
p = sub.add_parser("select-variants-in-samples", parents=region_parents, help="Select variants in a region within named individuals.")
p.add_argument("--samples", required=True, metavar="CSV", help="Comma-separated sample names.")
lp = p.add_mutually_exclusive_group()
lp.add_argument("--limit", type=int, default=DEFAULT_VARIANT_LIMIT, help=f"Hard cap on returned variants (default {DEFAULT_VARIANT_LIMIT}).")
lp.add_argument("--page-size", type=int, help="Retrieve ALL matching variants in pages of this size (full walk).")
p.set_defaults(func=cmd_select_variants_in_samples)
# count-samples
p = sub.add_parser("count-samples", parents=region_parents, help="Count individuals carrying a matching variant in a region.")
p.set_defaults(func=cmd_count_samples)
# select-samples
p = sub.add_parser("select-samples", parents=region_parents, help="List individuals carrying a matching variant in a region.")
p.add_argument("--skip", type=int, help="Skip the first N individuals.")
p.add_argument("--limit", type=int, help="Return at most N individuals.")
p.set_defaults(func=cmd_select_samples)
# count-samples-hom-ref
p = sub.add_parser("count-samples-hom-ref", parents=[conn_parser], help="Count individuals homozygous reference at a single position.")
p.add_argument("--chrom", required=True, help="Chromosome, e.g. chr17, 17, X, MT.")
p.add_argument("--position", type=int, required=True, help="1-based position.")
p.set_defaults(func=cmd_count_samples_hom_ref)
# select-samples-hom-ref
p = sub.add_parser("select-samples-hom-ref", parents=[conn_parser], help="List individuals homozygous reference at a single position.")
p.add_argument("--chrom", required=True, help="Chromosome, e.g. chr17, 17, X, MT.")
p.add_argument("--position", type=int, required=True, help="1-based position.")
p.set_defaults(func=cmd_select_samples_hom_ref)
# kinship
p = sub.add_parser("kinship", parents=[conn_parser], help="Relatedness (degree + KING coefficient) between two individuals.")
p.add_argument("--sample1", required=True, help="First sample name.")
p.add_argument("--sample2", required=True, help="Second sample name.")
p.set_defaults(func=cmd_kinship)
return parser
def main(argv: list[str] | None = None) -> None:
# Incompleteness is surfaced via result metadata and the summary, not warnings.
warnings.simplefilter("ignore", DnaerysIncompleteResultWarning)
args = build_parser().parse_args(argv)
try:
args.func(args)
except (DnaerysError, ValueError, OSError) as e:
_fail(f"Error: {e}")
if __name__ == "__main__":
main()
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Needs an API key, Needs Python
The author says: Requires Python >=3.11. Variant and sample queries require outbound network access to the public 1000 Genomes query endpoint over TLS; the sample/population metadata commands run fully offline over a data file bundled in the skill. No credentials, API keys, or environment variables are used.
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Dnaerys
- Publisher
- K-Dense (Community)
- Distributed by
- K-Dense
- Source
- K-Dense-AI/scientific-agent-skills · skills/onekgpd · commit a25cf9e
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Dnaerys or K-Dense.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download onekgpd.zip