
Liteparse
- 3.1k installs
- 74 repo stars
- Updated July 3, 2026
- run-llama/llamaparse-agent-skills
An agent skill for local document extraction with the lit CLI using parse-once, search-file patterns to minimize re-parsing and context waste.
About
Effective LiteParse teaches agents to extract document text locally with the lit CLI from LlamaIndex without model calls. The golden rule is parse once to a temp file with lit parse, then search that file with grep, sed, or the bundled BM25 search.py helper instead of re-parsing per query. Born-digital PDFs should use --no-ocr for speed; scanned PDFs drop --no-ocr and may need page screenshots at modest 150-200 DPI as a last resort. Search discipline batches independent lookups in one command, uses grep -C for inline context, bounds output with head, and switches to search.py after two failed greps. The skill documents real trace waste where agents re-parsed the same PDF up to nine times and single screenshots cost 140k+ characters. Setup requires Node 18+, npm i -g @llamaindex/liteparse, LibreOffice for Office files, and ImageMagick for images. Core flags include --format text or json, --no-ocr, --target-pages, and --dpi. Use when a task involves document files and you need text, tables, or specific values to answer questions or extract data cheaply.
- Parse once with lit parse to a file, then grep or search.py on that file
- Use --no-ocr for born-digital PDFs; screenshots only as last resort at 150-200 DPI
- Batch independent lookups in one command; use grep -C to avoid extra sed turns
- Switch to bundled BM25 search.py after two failed targeted greps
- Requires Node 18+, @llamaindex/liteparse, LibreOffice, and ImageMagick
Liteparse by the numbers
- 3,088 all-time installs (skills.sh)
- +124 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #256 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
liteparse capabilities & compatibility
- Capabilities
- parse pdf, docx, pptx, xlsx, and images with lit · search extracted files with grep c, sed, and bo · run bm25 ranking via bundled search.py for uncer · render single page screenshots at modest dpi whe · apply no ocr for born digital pdfs and targe
- Platforms
- macOS · Linux · Windows
- Runs
- Runs locally
- Pricing
- Free
npx skills add https://github.com/run-llama/llamaparse-agent-skills --skill liteparseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.1k |
|---|---|
| repo stars | ★ 74 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 3, 2026 |
| Repository | run-llama/llamaparse-agent-skills ↗ |
How do agents read PDFs and Office files without re-parsing every turn and flooding context with huge extracted text?
Extract text from PDF, DOCX, PPTX, XLSX, or images locally with the lit CLI using parse-once-then-search patterns to minimize agent context cost.
Who is it for?
Agents handling PDF, DOCX, PPTX, XLSX, or image files who can install the lit CLI and run shell search on extracted text.
Skip if: Skip when you need cloud OCR with layout bounding boxes as the primary workflow or cannot install Node 18+ and lit globally.
When should I use this skill?
Use when a task involves a document file and you need to read it, pull tables, or extract specific values to answer questions.
What you get
One lit parse per document, then bounded grep or BM25 search.py queries that return small targeted line windows.
- extracted text
- converted document content
By the numbers
- Version 0.1.0
- Requires Node 18+
- Install via `npm i -g @llamaindex/liteparse`
Files
Effective LiteParse
Extract text from documents locally with the lit CLI — a fast, model-free parser. This skill is about using it cheaply: each lit parse re-runs full extraction, and every line you dump into the conversation is paid for on every subsequent turn. The patterns below come from analyzing real agent traces where the same PDF was parsed up to 9 times and single image reads cost 140k+ characters of context. Don't repeat those mistakes.
The golden rule: parse ONCE to a file, then search the file
lit parse re-extracts the whole document every time you call it. Re-parsing per search is the #1 waste seen in traces. Parse a document exactly once, to a temp file, then run all your searches against that file:
# ONE TIME, per document. --no-ocr for born-digital PDFs (almost all reports) — much faster.
lit parse "/abs/path/doc.pdf" --format text --no-ocr -o /tmp/doc.txt && wc -l /tmp/doc.txtThen search the file with cheap shell tools — never re-run lit parse to search again.
Search discipline — minimize ROUND-TRIPS, then keep results small
Every Bash call is a full model round-trip (latency + re-read of context). The biggest waste after parsing is a serial loop: grep → look → grep again → sed to read the window → grep again. In traces this doubled the turn count versus just reading the doc. Two rules fix it:
1. Get context in the SAME command — don't grep then `sed`. Use grep -C so the surrounding lines come back with the hit. This removes the follow-up sed turn for the common case:
grep -n -i -C4 "total assets" /tmp/doc.txt | head -40 # location AND its window, one turnOnly fall back to sed -n 'A,Bp' when you already know the exact line and need a wider window than -C gave you.
2. Batch independent lookups into ONE command. When a question needs several distinct facts (e.g. emissions and revenue), don't spend one turn per term. Probe them together with labels:
for q in "carbon intensity" "scope 1" "total revenue"; do \
echo "=== $q ==="; grep -n -i -C3 "$q" /tmp/doc.txt | head -25; doneThen keep results small:
- Always bound output with
headand use-nfor line numbers. - Don't fan out blindly. Aim to resolve a question in ≤3 search commands. If two targeted greps
don't pin it down, switch to search.py (below) — don't keep firing keyword variations one per turn.
- Prefer Bash `grep`/`sed` on the saved file over the Read and Grep tools — fewer round-trips and
you control output size precisely.
Ranked search when keywords are uncertain (bundled helper)
When two targeted greps haven't pinned the answer, stop greping — don't iterate keyword variants one turn at a time. Run the bundled BM25 ranker ONCE to surface the most relevant line-windows in a single command:
./.claude/skills/effective-liteparse/scripts/search.py /tmp/doc.txt -q "materiality assessment priority topics" -k 8 -e 5-k = number of matches, -e = lines of context around each (so the window comes back inline — no follow-up sed turn). It returns ranked windows with line numbers. Use a rich natural-language query (several synonyms in one string), not a single keyword. This replaces a long chain of speculative greps.
Born-digital vs scanned
- Born-digital PDF (real text layer — nearly all corporate/finance/ESG reports): always pass
--no-ocr. It's much faster and the text is identical. Leaving OCR on wastes time.
- Scanned PDF / image: drop
--no-ocr. If the value is missing or digits look wrong, read the
page visually (see below) rather than trusting OCR.
Reading a page visually — last resort, ONE screenshot, modest DPI
Screenshots are the most expensive thing you can put in context: a single high-DPI page PNG ran ~140k characters in one trace, and agents often rendered the same page twice (default + hi-res).
Only screenshot when text/tables genuinely can't answer the question (dense multi-column tables, figures, charts). Then:
- Render one page at a time with
--target-pages "N"(note: it's--target-pages, NOT--pages). - Use modest DPI (~150–200). Do not start at 300+; do not re-render the same page at higher DPI
unless the text is actually illegible.
lit screenshot "/abs/path/doc.pdf" --target-pages "13" --dpi 150 -o /tmp/shots/ # then Read the PNGMany questions about the same document
Parsing once to a file already covers this: keep the /tmp/doc.txt and reuse it across every question instead of re-parsing.
Don't waste turns on preamble
Skip lit --version, ls -la, and lit … --help unless something actually failed. Go straight to the parse. Core flags you need:
--format text|json · --no-ocr · --target-pages "1-5,10" · --dpi <n> (default 150) · --ocr-language <iso>. Use --format json only when you need bounding boxes/layout — it's much larger; still search it, never load it whole.
Setup
PDFs work out of the box. If lit is missing: npm i -g @llamaindex/liteparse. Office docs need LibreOffice; images need ImageMagick (both auto-converted to PDF).
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "bm25s>=0.3.9,<1",
# "aiofiles>=25.1.0,<26",
# ]
# ///
import argparse
import asyncio
from typing import TypedDict, cast
import aiofiles
import bm25s
class LineRecord(TypedDict):
index: int
content: str
def _chunk(content: str) -> list[LineRecord]:
return [{"index": i, "content": c} for (i, c) in enumerate(content.splitlines())]
def _expand(corpus: list[LineRecord], match: LineRecord, n: int) -> str:
idx = match["index"]
start = max(0, idx - n)
end = min(len(corpus) - 1, idx + n)
return f"Lines {start} - {end}\n\n\n" + "\n".join(
[c["content"] for c in corpus[start : end + 1]]
)
def _retrieve(
corpus: list[LineRecord], query: str, top_k: int | None, expand: int = 0
) -> list[tuple[str, float]]:
corpus_tokens = bm25s.tokenize([c["content"] for c in corpus])
retriever = bm25s.BM25(corpus=corpus)
retriever.index(corpus_tokens)
query_tokens = bm25s.tokenize(query)
docs, scores = retriever.retrieve(query_tokens, k=top_k or 10)
results: list[tuple[str, float]] = []
for doc, score in zip(docs[0].tolist(), scores[0].tolist()):
window = _expand(corpus, doc, expand) if expand > 0 else [doc]
results.append((cast(str, window), score))
return results
def _chunk_and_retrieve(
content: str, query: str, top_k: int | None, expand_n: int
) -> list[tuple[str, float]]:
corpus = _chunk(content)
return _retrieve(corpus, query, top_k, expand_n)
async def process_chunk(
content: bytes, query: str, top_k: int | None, expand_n: int
) -> list[tuple[str, float]]:
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
_chunk_and_retrieve,
content.decode("utf-8"),
query,
top_k,
expand_n,
)
async def read_and_process(
file_path: str, query: str, top_k: int | None, expand_n: int | None
) -> list[str]:
tasks: list[asyncio.Task[list[tuple[str, float]]]] = []
async with asyncio.TaskGroup() as tg:
async with aiofiles.open(file_path, "rb") as f:
# read 64KB chunks
while chunk := await f.read(65536):
tasks.append(
tg.create_task(process_chunk(chunk, query, top_k, expand_n or 5))
)
results = [task.result() for task in tasks]
flattened = [r for result in results for r in result if r[1] >= 0.5]
flattened.sort(key=lambda x: x[1], reverse=True)
n = top_k or 10
return [f[0] for f in flattened][:n]
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("file_path", help="Path to the text file to search")
parser.add_argument(
"-q", "--query", help="Keyword-based query to search for", required=True
)
parser.add_argument(
"-k",
"--top-k",
help="Top K matches to retrieve. Defaults to 10.",
required=False,
type=int,
default=None,
)
parser.add_argument(
"-e",
"--expand",
help="Expand the matched content by N lines (before and after). Defaults to 5",
required=False,
type=int,
default=None,
)
args = parser.parse_args()
results = asyncio.run(
read_and_process(args.file_path, args.query, args.top_k, args.expand)
)
if results:
separator = "\n" + "─" * 60 + "\n"
for i, r in enumerate(results):
print(f"Match #{i}")
print(r.rstrip("\n").lstrip("\n"))
if i < len(results) - 1:
print(separator)
else:
print("No relevant matches found")
if __name__ == "__main__":
main()
Related skills
How it compares
Choose liteparse over cloud LlamaParse when documents must stay on-machine and only local text extraction is needed without LLM inference.
FAQ
Why parse to a file instead of re-running lit parse?
lit parse re-extracts the whole document every call. Parse once to /tmp/doc.txt, then run all searches against that file.
When should I use --no-ocr?
Pass --no-ocr for born-digital PDFs with a real text layer. Drop it for scanned PDFs or images where OCR is required.
When should I switch from grep to search.py?
After two targeted greps fail to pin the answer, run the bundled BM25 search.py once with a rich natural-language query instead of more keyword variants.
Is Liteparse safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.