CtrlK
BlogDocsLog inGet started
Tessl Logo

sayed/research-pipeline

Produce a rigorous, verified, fully-cited research report on ANY topic by running a disciplined five-stage pipeline: CLARIFY the question, PLAN (an editable research plan), FAN-OUT parallel search subagents, VERIFY every key claim (cross-source + adversarial + cross-model), then SYNTHESIZE a cited report. Use this WHENEVER the user wants real research rather than a quick answer — "research X", "do a deep dive on Y", "write me a report / brief / literature review on Z", "compare A vs B and back it with sources", "what's the state of the art in …", "investigate …", "find evidence for/against …", "give me a market/landscape/competitor analysis", or any question where being wrong is costly and the answer needs citations. Trigger even when the user does not say the word "research" but clearly needs multi-source, fact-checked, cited output. This is the general-topic web-research orchestrator — not the codebase-to-wiki `deep-research` skill. For a fast single-fact lookup, answer directly instead.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

checkpoint.pyscripts/

#!/usr/bin/env python3
"""checkpoint.py — self-describing research-pipeline state for resume-on-failure.

WHY THIS EXISTS
    Research runs are long and get interrupted (a subagent dies, the user pauses, the
    session ends). Without state, a resumed run redoes finished work — wasting tokens and
    time. This stores one JSON file per run so any future agent, with NO prior context, can
    read what's done and continue.

STATE SCHEMA (workspace/.research-pipeline/<slug>/state.json)
    {
      "slug": "ev-battery-suppliers",
      "question": "Who supplies EV batteries to European OEMs?",
      "tier": "standard",                  # quick | standard | deep
      "status": "in_progress",             # in_progress | complete
      "threads": {                         # one entry per PLAN sub-question
        "pricing":  {"status": "done",    "file": "findings/pricing.md"},
        "suppliers":{"status": "pending", "file": null}
      },
      "report": null                        # path to final report when complete
    }
    A thread status is one of: pending | in_progress | done.

USAGE
    checkpoint.py init   <slug> --question "..." [--tier standard] [--root <dir>]
    checkpoint.py plan   <slug> --thread pricing --thread suppliers      # register threads
    checkpoint.py save   <slug> --thread pricing --status done [--file findings/pricing.md]
    checkpoint.py status <slug>                                          # human-readable summary (read before fan-out)
    checkpoint.py get    <slug>                                          # raw JSON
    checkpoint.py done   <slug> --report report.md                      # mark run complete

    --root defaults to ./.research-pipeline ; state lives at <root>/<slug>/state.json
    Exit code 0 on success, 1 on error. `status` exits 0 even mid-run.

If Python is unavailable, maintain the same JSON by hand — the schema above is the contract.
"""
import argparse
import json
import os
import sys


def state_path(root: str, slug: str) -> str:
    return os.path.join(root, slug, "state.json")


def load(root: str, slug: str) -> dict:
    p = state_path(root, slug)
    if not os.path.exists(p):
        sys.stderr.write(f"no state for slug '{slug}' at {p}\n")
        sys.exit(1)
    with open(p, encoding="utf-8") as f:
        return json.load(f)


def store(root: str, slug: str, data: dict) -> None:
    d = os.path.join(root, slug)
    os.makedirs(d, exist_ok=True)
    with open(state_path(root, slug), "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)


def cmd_init(a):
    data = {
        "slug": a.slug,
        "question": a.question,
        "tier": a.tier,
        "status": "in_progress",
        "threads": {},
        "report": None,
    }
    store(a.root, a.slug, data)
    print(f"initialized run '{a.slug}' (tier={a.tier}) at {state_path(a.root, a.slug)}")


def cmd_plan(a):
    data = load(a.root, a.slug)
    for t in a.thread:
        data["threads"].setdefault(t, {"status": "pending", "file": None})
    store(a.root, a.slug, data)
    print(f"threads: {', '.join(data['threads'])}")


def cmd_save(a):
    data = load(a.root, a.slug)
    entry = data["threads"].setdefault(a.thread, {"status": "pending", "file": None})
    entry["status"] = a.status
    if a.file:
        entry["file"] = a.file
    store(a.root, a.slug, data)
    print(f"thread '{a.thread}' -> {a.status}" + (f" ({a.file})" if a.file else ""))


def cmd_status(a):
    data = load(a.root, a.slug)
    threads = data.get("threads", {})
    done = [t for t, v in threads.items() if v["status"] == "done"]
    pending = [t for t, v in threads.items() if v["status"] != "done"]
    print(f"run '{data['slug']}' | tier={data['tier']} | status={data['status']}")
    print(f"question: {data['question']}")
    print(f"done ({len(done)}): {', '.join(done) or '-'}")
    print(f"pending ({len(pending)}): {', '.join(pending) or '-'}")
    if data.get("report"):
        print(f"report: {data['report']}")


def cmd_get(a):
    print(json.dumps(load(a.root, a.slug), indent=2, ensure_ascii=False))


def cmd_done(a):
    data = load(a.root, a.slug)
    data["status"] = "complete"
    if a.report:
        data["report"] = a.report
    store(a.root, a.slug, data)
    print(f"run '{a.slug}' complete" + (f" -> {a.report}" if a.report else ""))


def main():
    p = argparse.ArgumentParser(description="research-pipeline checkpoint state")
    p.add_argument("--root", default=".research-pipeline", help="state root dir")
    sub = p.add_subparsers(dest="cmd", required=True)

    pi = sub.add_parser("init"); pi.add_argument("slug"); pi.add_argument("--question", required=True); pi.add_argument("--tier", default="standard", choices=["quick", "standard", "deep"]); pi.set_defaults(func=cmd_init)
    pp = sub.add_parser("plan"); pp.add_argument("slug"); pp.add_argument("--thread", action="append", required=True); pp.set_defaults(func=cmd_plan)
    ps = sub.add_parser("save"); ps.add_argument("slug"); ps.add_argument("--thread", required=True); ps.add_argument("--status", default="done", choices=["pending", "in_progress", "done"]); ps.add_argument("--file"); ps.set_defaults(func=cmd_save)
    pst = sub.add_parser("status"); pst.add_argument("slug"); pst.set_defaults(func=cmd_status)
    pg = sub.add_parser("get"); pg.add_argument("slug"); pg.set_defaults(func=cmd_get)
    pd = sub.add_parser("done"); pd.add_argument("slug"); pd.add_argument("--report"); pd.set_defaults(func=cmd_done)

    a = p.parse_args()
    a.func(a)


if __name__ == "__main__":
    main()

README.md

SKILL.md

tile.json