Output-Waste Optimizer
804 Hacks — Track 2, Build a Sustainable Future.
Most AI footprint tools show you one number. This one shows what your AI use is wasting — energy, water, CO₂ and money — then gives you three fixes for it.
- A standing instruction block. Paste once into your AI's custom-instructions setting. Applies to every future chat.
- A shorter prompt. Rule-based and local, shown as a diff you approve.
- A model suggestion. A lighter model when the task likely doesn't need a bigger one. Suggestion only — it never switches your model.
Any change that gets called safe first passes a quality gate: it runs against saved examples with known answers, once per task. Checking every request would burn more energy than the change saves, which is the trap this design exists to avoid.
Full scope, sources and open decisions: docs/output-waste-optimizer-plan.md.
Run it
The project needs Node 22.13+. The analysis engine runs locally on every surface. Accounts, aggregate usage history and the public leaderboard use Firebase Authentication, Firestore and Hosting — all three on the free Spark plan, with no Cloud Functions and no billing account.
npm run dev
Then open http://127.0.0.1:5173/.
| Command | What it does |
|---|---|
npm run dev |
Serves the portal at :5173 |
npm run cli -- --help |
Runs the provider-neutral terminal optimizer |
npm test |
Runs the core, CLI, and privacy-contract tests |
npm run seed |
Regenerates the demo data in data/ (run it the morning of the event so dates look current) |
npm run grid |
Fetches real PJM grid data — needs EIA_API_KEY |
npm run typecheck |
Full type check — needs npm install |
npm run build:ext |
Builds the browser extension into extension/dist/ — needs npm install |
npm run build:web |
Builds the static Firebase Hosting site into dist/web/ |
npm run firebase:deploy |
Builds the site and deploys Hosting + Firestore rules |
npm run firebase:rules |
Deploys firestore.rules on its own |
npm run firebase:check |
Signs in to your project and round-trips a test event |
Connect Firebase (free Spark plan, no card required)
- Create a Firebase project and add a Web app.
- Authentication → Sign-in method → enable Email/Password.
- Firestore Database → Create database → production mode. Any region.
- Copy
config/firebase.example.jsontoconfig/firebase.jsonand paste the four Web app values (apiKey,authDomain,projectId,appId). npx firebase-tools login, thennpx firebase-tools use --addand pick the project.npm run firebase:deploy— deploys Hosting and the Firestore rules. Nothing else needs deploying.- Open the
web.appURL, create an account, then sign in with the same account in the extension popup and withpromptfootprint login --email [email protected]. npm run firebase:checkproves the whole path works: sign in, write an event, read it back, publish a leaderboard row, delete the test event.
Rebuild the extension after step 4 (npm run build:ext) so your project values are copied into extension/config/.
Why there is no Cloud Function. Functions need the paid Blaze plan, so every surface writes to Firestore directly as the signed-in user. That makes firestore.rules the entire validation layer rather than a safety net behind a server: it checks ownership and shape, so an account can only ever write documents that look exactly like a usage event or a leaderboard row. There is no field for prompt or response text for one to arrive in.
Firebase Web configuration is not an admin secret — it identifies the project, it does not authorize anything. Never put a service-account private key in this repository.
What each surface writes
| Collection | Who reads it | Who writes it | What is in it |
|---|---|---|---|
users/{uid} |
only you | only you | your leaderboard alias |
users/{uid}/events/{id} |
only you | you, from any surface | token counts, efficiency, which detectors fired, resource ranges |
leaderboard/{uid} |
anyone | only you, from the portal | alias, totals, efficiency, surfaces used |
Events are append-only: the rules allow create and delete but never update, because an edited past reading is not a reading. The leaderboard is public on purpose — that is the ranking feature — so it carries an alias and never an email.
Staying inside the free limits
Spark gives 50,000 Firestore reads, 20,000 writes and 1 GiB of storage a day. One usage event is one write. The dashboard polls every 20 seconds and stops while the tab is hidden, which is about 4,300 reads a day for a tab left open all day. A hackathon demo is nowhere near either limit.
Why the portal has no build step
The demo gets one chance to run, on conference wifi, possibly on a different machine. A build is a thing that can fail at 7:55am. Node 22 strips TypeScript types itself, so the tests run .ts directly and scripts/serve.ts strips types on the fly for the browser. The browser runs the same source the tests do, with no artefact in between.
What's built
Demo core (D1–D9 in the plan) — all working, all tested:
| Feature | |
|---|---|
| D1 | Output-waste detection: 5 detectors, 100% precision and recall on 20 labelled fixtures |
| D2 | Efficiency % and the standing instruction block (capped at 60 words) |
| D3 | Resource feedback: energy, water, CO₂, cost — every figure a range with a source badge |
| D4 | Per-prompt rewrite with a diff and token delta |
| D5 | Model suggestion with confidence and every signal shown |
| D6 | Quality gate, with its own cost measured and subtracted |
| D7 | Paste-in analyzer |
| D8 | Grid feed with cache and labelled fallback |
| D9 | Weekly efficiency card |
The browser extension has dedicated support for ChatGPT, Claude, Gemini, Perplexity, Microsoft Copilot, Grok, Poe, DeepSeek, Mistral Le Chat, Meta AI, and You.com. For another browser-based AI tool, open its chat page and choose Enable on this AI site in the extension popup. To add its aggregate measurements to the platform, paste a device endpoint and key from the platform's Connections page into the popup.
The CLI's run command fixes the prompt before it reaches a terminal AI, automatically adds a concise output instruction, recommends a model tier, runs the AI, and analyzes the answer. It works with any tool that accepts its prompt as the final argument:
# Gemini CLI: the optimized prompt is appended after -p
npm run cli -- run --prompt "Please explain DNS please" -- gemini -p
# Claude, Codex, and Ollama use the same wrapper
npm run cli -- run --prompt "Summarize this repository" -- claude -p
npm run cli -- run --prompt "Find the failing test" -- codex exec
npm run cli -- run --prompt "Explain DNS" -- ollama run llama3
# For a CLI that needs the prompt in a specific position, use the placeholder
npm run cli -- run --prompt "Explain DNS" -- my-ai --message "{prompt}" --quiet
# Preview the automatic change without calling the AI
npm run cli -- run --prompt "Please explain DNS please" --dry-run -- gemini -p
# The original post-response analyzer remains available for saved output
npm run cli -- analyze --prompt-file prompt.txt --response-file response.txt
# Pass exact provider usage and return clean machine-readable output
node apps/cli/index.ts analyze -p "Summarize this" -r "Great question..." \
--input-tokens 25 --output-tokens 80 --model-class medium --json
The AI provider still receives the optimized prompt, but PromptFootprint's rewrite, suggestion, and waste analysis all stay local. The original AI answer stays on stdout so it can still be piped or redirected; PromptFootprint's preflight and report go to stderr.
Sign in once; later run and analyze commands sync automatically:
npm run cli -- login --email [email protected]
It asks for the password without echoing it, then stores the refresh token (never the password) in ~/.promptfootprint/config.json with owner-only permissions. logout clears it. --password and PROMPTFOOTPRINT_PASSWORD exist for scripts, but a password on a command line lands in your shell history.
Not built, by choice: CLI log reader (Tier 2) and the carbon-aware scheduler (roadmap). Automatic model rerouting is permanently out of scope — the user always decides.
Before you show this to judges
The numbers on screen are placeholders. config/sources.json ships with unverified order-of-magnitude values so the pipeline runs end to end. The portal shows an amber banner listing every one, and badges each figure derived from them.
Fill in config/sources.json, set verified: true, and the banner clears itself. Until then, don't quote a figure from this app.
Still to verify (plan §20):
- [ ] Real EIA API call made; endpoint and field names recorded
- [ ] Emission factor source chosen; lifecycle or direct stated
- [ ] Energy-per-output-token range sourced per model class
- [ ] Water intensity range sourced
- [ ] One Virginia data center figure, sourced and dated
- [ ] Each competitor's current features checked
- [ ] Backup demo video recorded
How it's laid out
packages/core/src/ The engine. Pure functions, no I/O, no network.
detect.ts 5 waste detectors (plan §5.2)
estimate.ts tokens -> energy, water, CO2, cost (plan §8.1)
config.ts every number read through here, with provenance tracking
instructions.ts fix 1
rewrite.ts fix 2
suggestModel.ts fix 3
gate.ts the quality gate (plan §7)
grid.ts PJM carbon intensity
card.ts weekly card
packages/core/test/ Core, CLI, gate, and sync privacy tests
firestore.ts Firebase Auth + Firestore over REST, no SDK, no deps
sync.ts the privacy-safe usage event and leaderboard aggregation
apps/portal/ Account dashboard, trends, leaderboard, and universal analyzer
apps/cli/ Terminal optimizer and account login
extension/ MV3 browser extension: the pre-send nudge and reply receipt
firestore.rules The whole validation layer: ownership and document shape
config/ sources.json — every figure with its source
data/ seeded demo data, all labelled as such
scripts/ serve, seed, fetch_grid
Design rules this code follows
These are in the plan and enforced in the code, not just written down:
- Never invent a number. Every figure comes from
config/sources.json. A source with no value yet throwsUnsourcedValueErrorrather than silently becoming zero. - Estimates are ranges.
formatRange()is the only path to the screen and will not print a bare number. - Conversation text stays on the device. Analysis is local. When account sync is connected, only token counts, efficiency, finding types, tool name, and resource ranges are uploaded—never prompts, responses, excerpts, or highlighted text.
- Automatic fixes are opt-in.
analyzeand the browser UI only show changes. The CLI applies a rewrite only when the user explicitly invokesrun; model changes remain suggestions because provider flags are not universal. - Demo data says it's demo data. Seeded grid rows use
source: "seeded", never"eia". Gate runs from recorded outputs are taggedrunner_kind: "recorded"and the UI prints it. - Precision over recall. Only high-confidence flags affect the score; fuzzy signals are advisory and never counted.
Known limits
Say these before a judge asks:
- These are estimates, not measurements. You can't measure a hosted model's energy from outside it.
- Average grid intensity, not marginal. Marginal is the right signal for load shifting; average is what free hourly data supports.
- Input energy counted as zero — a deliberate output-centred assumption. So prompt trimming shows as a money saving, not an energy one.
- Hidden reasoning tokens are invisible in web chat, so output there is undercounted.
- Brevity can cost quality. That's what the gate is for, and it only covers tasks with a checkable answer.
- A paraphrased repeat isn't detected. "Summarise this" vs "I'd like a summary of this" needs sentence meaning — which means a model call, which means energy spent to save energy and your text leaving the device. Out of scope on purpose.
- We never detect hallucinations. Nothing here claims to.
Log in or sign up for Devpost to join the conversation.