decktective
Build a weekly activity deck (PPTX) from a git repository by filling a .pptx template.
Package details
Install decktective from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:decktective- Package
decktective- Version
0.2.3- Published
- Sep 16, 2026
- Downloads
- 2,015/mo · 2,015/wk
- Author
- rizqiyi
- License
- MIT
- Types
- extension, skill
- Size
- 758.9 KB
- Dependencies
- 6 dependencies · 0 peers
Pi manifest JSON
{
"skills": [
"./skills"
],
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
decktective
Turn a git repo's activity into a weekly PPTX, by filling your own template.
Run it:
node src/cli.ts --repo https://github.com/owner/name --preset last --out out -y
open out/*.pptx
Takes about 10 seconds offline, or 1–2 minutes with a model. Requires Node ≥ 26.
Install
Three options, easiest first:
As a pi/omp package (once published)
pi install npm:decktectiveThen ask in plain language — no commands, no paths.
As a local extension — one line in
~/.omp/agent/config.yml:extensions: - /absolute/path/to/decktectiveNaming the package directory loads its extension entry and its
agents/andskills/folders.As a plain checkout — clone it and run
node src/cli.tsdirectly. No install step.
For option 3, install deps first:
npm install
Run it
Plain language (recommended)
Once installed as a package or extension, just say what you want:
make me a pptx for this repo from Jan 5 to Jan 12
weekly deck for https://github.com/owner/name, last week
The agent works out the parameters, asks for anything missing — in one message, not one question at a time — then builds it. It resolves relative dates and states them back before running.
Command line
# This week
node src/cli.ts --repo . --preset this --out out -y
# An explicit window (ISO instants with an offset; end is EXCLUSIVE)
node src/cli.ts --repo . \
--start 2026-01-05T00:00:00+07:00 \
--end 2026-01-12T00:00:00+07:00 \
--tz Asia/Jakarta --out out -y
# Fill your own template
node src/cli.ts --repo . --preset last \
--pptx-template templates/template1.pptx --out out -y
# No model: fully deterministic, nothing leaves the machine
node src/cli.ts --repo . --preset last --offline --out out -y
--repo accepts a URL, an owner/repo, or a folder path. Remote repos clone once into ~/.cache/decktective/repos/, then refresh on later runs.
Workflow
git (local or URL)
↓ fetch activity
DayEntry[] numbers frozen here, from git
↓ group into work items ← model, or deterministic
↓ articulate ← model, or deterministic
↓ review & validate ← loops back on failure
DeckIR
↓ fill the .pptx template
PPTX
Five things worth knowing:
- The model groups and names; the code counts. Churn, files, commits and impact are computed from git. A number absent from the facts fails the build.
- Commit count and change magnitude are separate axes. A day with 3 commits and +4,120 lines is not a day with 34 small commits. Day profiles (
burst,heavy,drop,rename,scattered) encode that. - Each stage falls back to a deterministic path with no model configured, so a deck is always produced.
- The output contract is judged, not assumed. A review gate checks the draft against
i-have-adhdand retries with guidance. - Some fields cannot come from git — pull-request numbers, team names. These are reported as gaps, never invented.
Output
One PPTX, plus a JSON IR beside it:
out/2026-01-05_filled.pptx the deck
out/2026-01-05_2026-W02.json every number the deck used
The deck skeleton: cover → overview → one slide per active day → highlights → work-item summary → blockers. Day slides are generated from real dates (1–7 of them), not a fixed Mon–Fri.
Models
Optional. With a key set, the model groups commits into work items and writes the narrative. With none, every stage falls back to a deterministic path — and the deck still builds, so a blank or sparse deck on a new machine is almost always a missing key or an empty window, not a broken install.
Set a key
Add one to your shell profile, then open a new terminal:
# CommandCode gateway — glm, grok, deepseek, qwen, kimi, gpt
echo 'export COMMANDCODE_API_KEY=sk-...' >> ~/.zshrc
# or OpenAI
echo 'export OPENAI_API_KEY=sk-...' >> ~/.zshrc
# or Anthropic
echo 'export ANTHROPIC_API_KEY=sk-ant-...' >> ~/.zshrc
source ~/.zshrc
Verify it is visible:
printenv COMMANDCODE_API_KEY | head -c 8; echo "…"
Pick a model
node src/cli.ts --repo . --preset this --llm commandcode/deepseek/deepseek-v4-flash -y
node src/cli.ts --repo . --preset this --llm openai/gpt-5 -y
node src/cli.ts --repo . --preset this --llm anthropic/claude-sonnet-5 -y
| Provider | Key env var | Example --llm |
|---|---|---|
| CommandCode | COMMANDCODE_API_KEY |
commandcode/deepseek/deepseek-v4-flash |
| OpenAI | OPENAI_API_KEY |
openai/gpt-5 |
| Anthropic | ANTHROPIC_API_KEY |
anthropic/claude-sonnet-5 |
With no key set, the tool runs offline automatically — nothing leaves the machine.
CommandCode model IDs
The gateway wants the bare model id, with no provider prefix:
deepseek/deepseek-v4-flash ✅ (not commandcode/deepseek/…)
z-ai/glm-5.3-flash ✅
xai/grok-4.5 ✅
The --llm value's first path segment is the provider name; everything after it
is passed to that provider untouched. To list what a gateway actually serves:
curl -sS https://api.commandcode.ai/provider/v1/models \
-H "Authorization: Bearer $COMMANDCODE_API_KEY" | head -40
Adding a gateway
One entry in KNOWN_PROVIDERS in src/llm/provider.ts — a base URL, a wire
protocol (openai covers any OpenAI-compatible gateway), and the env var holding
its key.
If the deck comes out empty
- No commits in the window. The tool says
no activity in the window. Ask for a week that has commits — a repo with 2025 history asked for "this week" in 2026 is legitimately empty. - A private repo failed to clone. Look for a clone error; the tool names the URL it could not reach.
- No key set, so the narrative is deterministic and terse — install a key above for the full version.
- Wrong timezone.
--tzgoverns day bucketing; the wrong zone can move commits out of the window.
Templates
The deck is filled into a .pptx — your file, your branding.
# Use the one that ships
node src/cli.ts --repo . --preset this --pptx-template templates/template1.pptx -y
# Ask a model to learn YOUR template (once), then reuse that plan free, offline
node src/cli.ts --template-outline --pptx-template my.pptx
node src/cli.ts --learn-template --pptx-template my.pptx --llm commandcode/deepseek/deepseek-v4-flash
node src/cli.ts --repo . --preset this --pptx-template my.pptx \
--manifest ~/.cache/decktective/manifests/my.manifest.json -y
A model cannot read a .pptx, so it is given a shape outline instead — shape ids, positions, sizes, placeholder text. It returns a manifest (which shape gets which data), which is validated and then executed by the same deterministic filler. The plan is cached, so learning happens once.
Commands
| Flag | Effect |
|---|---|
--repo <url|path> |
Repository. URL, owner/repo, or local path |
--preset this|last|4w |
Relative window |
--start / --end |
Explicit ISO instants; --end is exclusive |
--tz <zone> |
IANA timezone. Default Asia/Jakarta |
--branch <ref> |
Branch, tag or ref to walk. Default: the repo HEAD |
--all |
Walk every ref instead of one line of history |
--include-merges |
Count merge commits too |
-v, --verbose |
Show the pipeline: stage timings, every model call, the grouped work items, each review verdict |
--out <dir> |
Output directory. Default out |
--title <text> |
Deck title |
--exclude <prefixes> |
Path prefixes removed from churn (not globs) |
--pptx-template <file> |
Fill this template instead of generating slides |
--manifest <file> |
Use a learned plan for that template |
--template-outline |
Print a template's shape outline and exit |
--learn-template |
Derive + cache a plan for a template, then exit |
--llm <provider>/<model> |
Model for the pipeline stages |
--offline |
No model at all |
--pptx-only / --pdf-only |
Emit one format |
-y, --yes |
Never prompt; use the default window |
Template mode emits PPTX only — producing PDF from a .pptx would need LibreOffice, which this project does not depend on.
Seeing the work
decktective --repo . --preset this --verbose -y
· repo: . (local)
· window: 2026-09-14T00:00:00+07:00 .. 2026-09-21T00:00:00+07:00 tz=Asia/Jakarta
· model: commandcode/deepseek/deepseek-v4-flash
· pipeline (group → articulate → review) …
· llm commandcode/deepseek/deepseek-v4-flash ← 1885 chars (json)
· llm deepseek/deepseek-v4-flash → 565 chars in 8757ms
· llm commandcode/deepseek/deepseek-v4-flash ← 8314 chars (json)
· llm deepseek/deepseek-v4-flash → 545 chars in 11944ms
· review attempt 1: aligned
· work items (8):
· High feat 3 refs Export pipeline moved off the main thread
· resolve layout (10 slides) — 34ms
· plan: 10 slides, 52 draw ops
· emit pptx — 21ms
· emit pdf — 42ms
Model calls are the slow, costly, occasionally-failing part of a run — without this, the only sign of one is a pause. The review call dominates: on a real repo it took ~12s of a ~50s run at 8.3k prompt chars.
Repos whose default branch is merge-only
If master only receives merges and the work lives on feature branches, the default walk reports 0 commits — it uses --first-parent --no-merges, which cannot see that shape:
| Flags | Sees |
|---|---|
| (default) | 1 of 4 — first-parent, non-merge only |
--include-merges |
2 of 4 — the merge commits too |
--all |
3 of 4 — every non-merge commit across all refs |
--all --include-merges |
4 of 4 — everything (git rev-list --count --all) |
--all drops --first-parent, which is meaningless across refs and would hide the very work it exists to surface.
Development
npm test # 68 tests, ~1.5s, offline
npm run typecheck # must be clean
node scripts/calibrate-profiles.mjs ~/code/your-repo # check the day classifier
node scripts/preview-pptx.mjs out/*.pptx 3 /tmp/p.png # LOOK at a slide
Verify by looking, not by extracting
Text extraction passes while layout is broken. A deleted panel and overflowing text both extract as perfectly good text. Before reporting a deck as done:
pdftotext out/*.pdf - # is the text real?
node scripts/preview-pptx.mjs out/*.pptx 3 /tmp/p.png
Then open the PNG. If you cannot look, say so.
Troubleshooting
"No commits recorded" / an empty deck. Either the window has no activity, or the clone is shallow. Re-clone without --depth.
Wrong day boundaries. Days bucket on author date; --since/--until filter on committer date. Squashes and rebases move work across days, and the tool warns when they diverge.
Churn numbers look wrong. Vendored and generated files inflate them. --exclude dist/,package-lock.json — prefixes, not globs.
"summary shows N of M work items". The summary table is full. The rest are still in the day slides.
A day profile you never see. burst needs ≥8 commits. Run the calibration script above to check the thresholds against your repos.