Metadata-Version: 2.4
Name: instaprop
Version: 0.1.0
Summary: Agentic NSF proposal first-draft generator with a panel review loop
Author: Rajesh Kavasseri
License: MIT
Project-URL: Homepage, https://github.com/kavasserirocks/instaprop
Project-URL: Issues, https://github.com/kavasserirocks/instaprop/issues
Project-URL: Repository, https://github.com/kavasserirocks/instaprop
Project-URL: Changelog, https://github.com/kavasserirocks/instaprop/releases
Keywords: nsf,proposal,academic,llm,agent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Text Processing :: Markup
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: anthropic>=0.30.0
Requires-Dist: click>=8.1
Requires-Dist: rich>=13.0
Requires-Dist: pypdf>=4.0
Requires-Dist: pdfplumber>=0.10
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: api
Requires-Dist: fastapi>=0.110.3; extra == "api"
Requires-Dist: uvicorn[standard]>=0.29.0; extra == "api"
Requires-Dist: python-multipart>=0.0.9; extra == "api"
Dynamic: license-file

# instaprop

**CLI-first open-source toolkit for drafting NSF proposals with an agentic review panel.**

Give it a solicitation and a set of intellectual merit bullets. It returns a
5-page concept note vetted by a simulated panel of five NSF reviewers — in
minutes, not weeks.

```bash
python3.12 -m pip install instaprop
export ANTHROPIC_API_KEY=sk-ant-...
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../nsf26-501/solicitation \
  --contributions my_bullets.txt
```

---

## How it works

instaprop supports three practical operating modes.

```
Starter Mode:
  Solicitation + contribution brief
    -> Writer
    -> Review panel
    -> Rubric + revision brief
    -> inspect outputs manually

Best-Of-N Mode:
  Solicitation + fixed contribution brief
    -> Writer
    -> Review panel
    -> Rubric + revision brief
    -> revise draft
    -> repeat N rounds
    -> pick best draft

Idea-On-Steroids Mode:
  Solicitation + initial contribution brief
    -> inner loop: draft -> review -> rubric -> revision brief
    -> outer loop: refine the contribution brief itself
    -> draft again from the improved brief
    -> stop when target score / fatal-flag criteria are met
    -> otherwise continue until max rounds, then return the best draft
```

In the strongest mode, there is a genuine double loop:

- inner loop: proposal development
- outer loop: idea / contribution-brief refinement

That lets the system improve not just the wording of the draft, but the raw
research framing, novelty, literature positioning, and evaluation plan feeding
the next draft round.

Inspired by Andrej Karpathy's writing on agentic AI systems, applied to the
unglamorous but high-stakes task of academic proposal writing.

---

## Installation

```bash
python3.12 -m pip install instaprop

# From source
git clone https://github.com/kavasserirocks/instaprop
cd instaprop
python3.12 -m pip install -e ".[dev]"
```

Set your Anthropic API key and verify the CLI:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
instaprop check
```

If you want OpenAI-backed models instead, install the optional extra:

```bash
python3.12 -m pip install "instaprop[openai]"
```

If you want the local FastAPI server for proposal review/generation workflows:

```bash
python3.12 -m pip install "instaprop[api]"
uvicorn api:app --reload --port 8000
```

That server exposes:

- `GET /health`
- `POST /review/text`
- `POST /review/file`
- `POST /generate`

If you are preparing a release, see [RELEASE.md](/Users/rajeshkavasseri/instaprop/RELEASE.md).

---

## CLI

The CLI has four user-facing commands:

```bash
instaprop generate
instaprop scores
instaprop reviewers
instaprop check
```

For built-in help:

```bash
instaprop --help
instaprop generate --help
instaprop check --help
```

For a full CLI walkthrough, see [docs/CLI.md](/Users/rajeshkavasseri/instaprop/docs/CLI.md).

---

## CLI Modes

### 1. Starter Mode

Use this when you have a raw idea and want one quick pass to see whether the
framing is viable.

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions idea.txt \
  --rounds 1 \
  --verbose
```

What it does:

- writes one draft
- runs one panel review
- emits a scorecard and revision log
- lets you inspect the first set of objections before spending more tokens

### 2. Best-Of-N Mode

Use this when you want the system to revise the proposal draft multiple times
and then select the strongest round.

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions idea.txt \
  --rounds 3 \
  --verbose
```

You can increase `--rounds` up to `5` in the standard fixed-round mode.

What it does:

- runs a visible write-review-revise loop for `N` rounds
- keeps the contribution brief fixed
- chooses the best-scoring draft at the end

### 3. Idea-On-Steroids Mode

Use this when you want the system to revise both the draft and the underlying
contribution brief between rounds, and keep iterating toward a pass condition.

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions idea.txt \
  --target-score 3.2 \
  --stop-on-no-fatal-flags \
  --max-rounds 5 \
  --evolve-brief \
  --verbose
```

What it does:

- drafts the proposal
- reviews it
- rewrites the contribution brief using reviewer feedback
- drafts again from the improved brief
- uses the target-score / no-fatal-flags criteria as the stop condition
- repeats until the proposal clears the threshold or hits the round cap

This is the strongest autonomous CLI mode currently implemented.

---

## Generate

### Supplying the solicitation

instaprop accepts the solicitation in two common forms:

**1. NSF.gov URL** (recommended — always current)

Use the solicitation page URL directly. Both the opportunity page and the
full solicitation page are accepted:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/collaboratory-advance-mathematics-education-learning-k-12/nsf26-501/solicitation \
  --contributions my_bullets.txt
```

**2. Plain text file** (escape hatch)

```bash
instaprop generate \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.txt
```

### Supplying your contributions

Use a plain `.txt` file or a Markdown `.md` file for `--contributions`.
Markdown is optional; simple plain text is fully supported and is the
lowest-friction default.

Recommended:

```bash
instaprop generate \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.txt
```

Also supported:

```bash
instaprop generate \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.md
```

If your notes currently live in Word or PDF, convert them to `.txt` or `.md`
before passing them to the CLI.

### All options

| Flag | Default | Description |
|------|---------|-------------|
| `--solicitation / -s` | — | NSF.gov solicitation URL |
| `--solicitation-file` | — | Local plain-text solicitation |
| `--contributions / -c` | required | Plain text or Markdown file with intellectual merit bullets |
| `--output / -o` | `draft.md` | Output path for the proposal draft |
| `--rounds / -r` | `3` | Writer-panel revision rounds (best-of-N) |
| `--target-score` | off | Optional early-stop threshold for rubric overall score |
| `--max-rounds` | `--rounds` | Cap when using stop criteria |
| `--stop-on-no-fatal-flags` | off | Stop early once a round has no fatal rubric criteria |
| `--evolve-brief` | off | Rewrite the underlying contribution brief between rounds |
| `--provider` | `anthropic` | LLM backend for writer + reviewers |
| `--model` | `claude-opus-4-5` | Model for writer + reviewers |
| `--extraction-provider` | `anthropic` | LLM backend for non-URL solicitation extraction |
| `--extraction-model` | `claude-haiku-4-5-20251001` | Model for non-URL solicitation extraction |
| `--pages` | `5` | Target page count |
| `--verbose / -v` | off | Stream reviewer critiques and diffs to stdout |
| `--scorecard / --no-scorecard` | on | Emit `.scorecard.json` |
| `--log / --no-log` | on | Emit `.revlog.md` revision log |

### Common command patterns

Default Anthropic flow:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../nsf26-501/solicitation \
  --contributions my_bullets.md
```

Save to a custom output path:

```bash
instaprop generate \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.txt \
  --output outputs/camel_draft.md
```

Run fewer rounds for a cheaper/faster pass:

```bash
instaprop generate \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.txt \
  --rounds 1
```

Keep iterating until the draft clears a target score or reaches the cap:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions my_bullets.txt \
  --target-score 3.2 \
  --stop-on-no-fatal-flags \
  --max-rounds 5 \
  --verbose
```

Turn on the stronger self-correction loop that also rewrites the contribution brief:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions my_bullets.txt \
  --target-score 3.2 \
  --stop-on-no-fatal-flags \
  --max-rounds 5 \
  --evolve-brief \
  --verbose
```

Show reviewer progress and revision output live:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions my_bullets.txt \
  --verbose
```

Disable auxiliary files:

```bash
instaprop generate \
  --solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
  --contributions my_bullets.txt \
  --no-scorecard \
  --no-log
```

Use OpenAI-backed models if you installed the optional extra:

```bash
pip install "instaprop[openai]"

export OPENAI_API_KEY=...
instaprop generate \
  --provider openai \
  --model gpt-5-mini \
  --solicitation-file solicitation.txt \
  --contributions my_bullets.txt
```

### Other commands

```bash
instaprop scores draft.scorecard.json   # pretty-print a scorecard
instaprop reviewers                     # list the five reviewer personas
instaprop check                         # verify Anthropic connectivity
```

Check Anthropic explicitly:

```bash
instaprop check --provider anthropic
```

Check OpenAI explicitly:

```bash
instaprop check --provider openai
```

---

## Outputs

| File | Contents |
|------|----------|
| `draft.md` | Best-round proposal draft in Markdown |
| `draft.scorecard.json` | Per-reviewer and per-criterion scores for each round |
| `draft.revlog.md` | Revision log: rubric scorecard + panel discussion + revision brief per round |

### Scorecard structure

The scorecard is rubric-aligned. Each round contains:

- **Per-reviewer overall** (1–4, mapping to NSF tier labels)
- **Per-criterion scores** across 17 rubric criteria in 4 sections
- **Fatal flags** — any criterion any reviewer scored 1
- **Section weighted scores** (first two pages 25%, merit 35%, impacts 20%, writing 20%)
- **Simulated panel discussion** — what each reviewer would say aloud
- **Revision brief** — prioritised action list for the writer

---

## The review panel

Each reviewer scores only the criteria within their focus area — the rubric
aggregator merges sparse partial scorecards into a complete weighted score.

| Reviewer | Focus | Primary criteria |
|----------|-------|-----------------|
| **Domain expert** | Field-specific depth | novelty, detail, feasibility, literature, scope |
| **Program officer** | NSF fit & fundability | hook, gap, approach summary, scope |
| **Methodological skeptic** | Rigor & validity | detail, feasibility, problem statement, scope |
| **Interdisciplinary generalist** | Clarity & framing | hook, gap, prose, coherence, structure |
| **Societal impact** | Real-world stakes | specific impact, application, exciting |

Panel dynamics are grounded in the NSF reviewing process described by
[C. Grimm (Oregon State)](https://web.engr.oregonstate.edu/~grimmc/NSF/TheNSFReviewingProcess.html).

---

## Writing your contributions file

The `--contributions` file is the most important input. A strong one:

```markdown
## Project title
Learning Causal Structure from Observational Time-Series

## Core intellectual contributions

- **Non-trivial novelty**: We introduce a provably identifiable estimator for
  causal DAGs in non-stationary, non-Gaussian settings — a regime where
  existing methods (PCMCI, Dynotears) fail.

- **Theoretical foundation**: We prove identification under a new "local
  stationarity" condition weaker than global stationarity, with sample
  complexity bounds.

- **Scalable algorithm**: Our algorithm runs in O(T log T d²) vs O(T² d³)
  for existing approaches, enabling application to climate and neuroscience
  datasets (T > 10⁶, d > 500).

- **Empirical validation**: Three domains — synthetic benchmarks, ERA5 climate
  data with expert-annotated causal structure, Allen Brain Atlas neural recordings.

## Why this is non-trivial
Existing methods assume stationarity or Gaussianity — neither holds in
the target domains. Relaxing both simultaneously requires a new theoretical
framework, not just an engineering improvement.
```

The panel's domain expert will scrutinise these claims hardest.
Be specific, be falsifiable, cite the gap.

---

## Contributing

Contributions welcome. The highest-value areas:

- **Reviewer prompts** (`instaprop/prompts/reviewer_prompts.py`) — if you
  have served on NSF panels, your input on the personas is invaluable
- **Evaluation harness** — scoring generated drafts against real funded proposals
- **CLI ergonomics** — better progress output, resumability, and export options
- **`--resume` flag** — continue from a saved round state

## Publishing

The package metadata is set up for PyPI/TestPyPI publication. The minimal flow is:

```bash
python3.12 -m pip install -e ".[dev]"
python3.12 -m build
twine check dist/*
twine upload dist/*
```

For a fuller release checklist, use [RELEASE.md](/Users/rajeshkavasseri/instaprop/RELEASE.md).

---

## Roadmap

- [x] v0.1 — Core agentic loop
- [x] v0.1 — NSF.gov URL fetcher (HTML + structured extraction)
- [x] v0.1 — PDF solicitation reader (pypdf → Claude extraction)
- [x] v0.1 — Rubric-aligned scorecard (17 criteria, 4 sections, fatal flags)
- [x] v0.1 — Panel discussion simulation + revision brief
- [ ] v0.2 — Streaming output to terminal during generation
- [ ] v0.2 — `--resume` flag to continue from a saved round
- [ ] v0.3 — Configurable reviewer personas via YAML
- [ ] v0.4 — Better CLI workflows and integration hooks for external frontends
- [ ] v1.0 — Full 15-page proposal mode

---

## License

MIT. See [LICENSE](LICENSE).

---

## Citation

If instaprop contributes to a funded proposal (congratulations!), consider
adding an acknowledgment:

> This proposal draft was developed with assistance from instaprop
> (https://github.com/kavasserirocks/instaprop).
