Chrome extension hiding shopping prices for 24 hours
Manifest V3 Chrome extension that hides shopping prices on listed sites for 24 hours to curb impulse buying, built with plain JavaScript.
javascripthtmlcssmanifest-v3node
Built 1 time, downloaded 0 times.
Install from your terminal
Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.
npx claude-lab add chrome-extension-hiding-shopping-prices-for-24-hours-e8add2CLAUDE.md# the app: project memory
## What this is
A Chrome extension (Manifest V3) that hides shopping prices on a short list of sites for 24 hours after you click Start. The goal is to reduce impulse buying. The window is firm: there is no override in v1.
## Stack
No framework was chosen, so the project uses:
- Plain JavaScript (ES modules are not required), HTML, and CSS.
- No build step and no runtime npm dependencies.
- Node 20 or newer, used only to run tests.
## Layout
- `manifest.json`: the MV3 manifest.
- `src/background.js`: service worker. Stores `hideUntil` in `chrome.storage.local` and sets the badge.
- `src/content.js`: injects a style that hides price elements on supported sites.
- `src/price-hide.js`: pure logic. Exports `HIDE_MS`, `isWithinHideWindow(hideUntilMs, nowMs)`, and `computeHideUntil(nowMs)`.
- `src/sites.js`: host names and CSS selectors for prices. Fragile; needs manual updates.
- `src/popup.html` and `src/popup.js`: Start button and time remaining.
- `tests/`: `node:test` files.
- `.claude/skills/verify/`: the verify skill.
## Commands
- Unit tests: `node --test tests/`
- Syntax check: `node --check src/*.js`
- Load the extension: open chrome://extensions, turn on Developer mode, click Load unpacked, and select this folder.
## Rules
- `src/price-hide.js` must not call any `chrome.*` API, so tests run in Node.
- No network requests, no analytics, no remote code, no `eval`, no inline scripts (MV3 content security policy).
- Request only `storage` and `activeTab`, plus host permissions for the sites in `src/sites.js`.
- Never store personal data.
- Never add secrets. If a key is ever needed, use a `YOUR_...` placeholder.
## Gotchas
- The service worker sleeps. Never keep state in global variables; always read and write through `chrome.storage`.
- After editing `content.js` or `background.js`, click reload on the extension card at chrome://extensions, then refresh the shopping tab.
- Selectors break when sites change. Expect to update `src/sites.js`.
## Working style
- Make small changes and explain each step in plain language.
- Ask before adding a dependency or changing permissions.
- Work in phases from `PLAN.md`. Start each phase in a fresh session with `/clear`.
## Done means
- `node --test tests/` passes.
- The verify skill reports all PASS.
- The relevant items in `SPEC.md` pass.
## Review standard
@REVIEW.md
.claude/settings.json{
"model": "sonnet",
"permissions": {
"allow": [
"Bash(node --test:*)",
"Bash(node --check:*)",
"Bash(node -e:*)",
"Bash(ls:*)",
"Bash(cat:*)"
],
"ask": [
"Bash(git push:*)",
"Bash(npm install:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push --force:*)",
"Bash(git reset --hard:*)"
]
}
}
.claude/skills/verify/SKILL.md---
name: verify
description: Runs syntax checks, unit tests, manifest validation, and forbidden-pattern scans for the Chrome extension, then reports pass or fail; use after any code change and before saying a task is done.
---
# Verify
Run these checks in order from the project root. Show the real output of every command. Never mark a check PASS without showing its output.
## Step 1: Syntax checks
Run:
```
node --check src/*.js
node --check tests/*.js
```
Both must exit 0. If one fails, record FAIL, note the file and line from the error, and continue to the next step.
## Step 2: Unit tests
Run:
```
node --test tests/
```
It must exit 0 with zero failures. Show the pass count from the summary (the lines starting with `# pass` and `# fail`).
If there are no test files yet, record FAIL and say so. Do not skip this check.
## Step 3: Manifest validation
Run:
```
node -e "JSON.parse(require('fs').readFileSync('manifest.json','utf8'))"
```
It must exit 0. Then open `manifest.json` and confirm:
- `manifest_version` is `3`.
- `permissions` contains only `storage` and `activeTab`.
- `host_permissions` lists only the hosts in `src/sites.js`, with nothing extra.
Any mismatch is a FAIL. Quote the offending line in the report.
## Step 4: Forbidden-pattern scan
Run:
```
grep -rn -E "eval\(|new Function|<script|onclick=|fetch\(|https?://" src/
```
Any hit is a FAIL unless it is inside a comment. For each hit:
- Read the line in context.
- If it is code that runs, record FAIL and name the file and line.
- If it is a comment, note it as an allowed hit with the reason.
Do not change code during this step. Report only.
## Step 5: Manual reminder
If the change touched `src/content.js`, `src/popup.html`, `src/popup.js`, `src/background.js`, or `manifest.json`, tell the user:
1. Open chrome://extensions and click reload on the extension card.
2. Refresh any open shopping tab.
3. Walk the acceptance checklist in SPEC.md.
Mark this step as MANUAL, not PASS. Only the user can confirm it.
## Output format
Print a table with one row per check:
| Check | Result | Evidence |
|-------|--------|----------|
| Syntax (src) | PASS or FAIL | command output summary |
| Syntax (tests) | PASS or FAIL | command output summary |
| Unit tests | PASS or FAIL | pass and fail counts |
| Manifest JSON | PASS or FAIL | exit code and manifest_version |
| Manifest permissions | PASS or FAIL | permissions and host list |
| Forbidden patterns | PASS or FAIL | number of hits, allowed or not |
| Manual reminder | MANUAL | what the user must check |
Then write one line naming the first thing to fix. If everything passed, write "All automated checks passed. Manual checklist still needed if the UI changed."
INSTALL.md# Install and first run
This guide sets up the project, Claude Code, and the Chrome extension. Follow the steps in order.
## What you need
- Node.js 20 or newer, from nodejs.org. Node is used only to run tests.
- Claude Code, installed with the official install guide.
- Google Chrome.
## Step 1: Check Node
Open a terminal and run:
```
node --version
```
It should print `v20` or higher. If not, install Node 20 or newer and open a new terminal.
## Step 2: Check Claude Code
Run:
```
claude --version
```
It should print 2.1.296 or newer. If it is older, update Claude Code and check again.
## Step 3: Create the project folder
```
mkdir the-app
cd the-app
git init
```
## Step 4: Copy the starter files
Copy these into the `the-app` folder:
- CLAUDE.md
- SPEC.md
- PLAN.md
- REVIEW.md
- INSTALL.md
- The `.claude` folder
Check that `.claude/settings.json` and `.claude/skills/verify/SKILL.md` are present.
## Step 5: Start Claude Code
From inside the `the-app` folder, run:
```
claude
```
Accept the trust prompt when it appears. Claude Code reads CLAUDE.md at the start of each session.
## Step 6: Install the two plugins
Inside the Claude Code session, run these two commands one at a time:
```
/plugin install code-review@claude-plugins-official
/plugin install commit-commands@claude-plugins-official
```
- `code-review` checks each phase's changes against REVIEW.md.
- `commit-commands` makes each phase a clean, reversible commit.
## Step 7: Get Chrome ready
1. Open Chrome and go to `chrome://extensions`.
2. Turn on Developer mode (top right).
3. Keep this tab open. You will use it later.
You will not load the extension yet. It needs files that Phase 1 and later phases create.
## Step 8: Start Phase 1
In the Claude Code session, paste this prompt and press Enter:
```
/goal Build Phase 1 of PLAN.md. Measurable end state: the repo contains manifest.json (Manifest V3, permissions limited to storage and activeTab), src/price-hide.js exporting HIDE_MS (24 * 60 * 60 * 1000), isWithinHideWindow(hideUntilMs, nowMs), and computeHideUntil(nowMs), plus tests/price-hide.test.js with at least 4 passing cases covering before the deadline, exactly at it, after it, and a missing or non-number hideUntil. Check: run `node --test tests/` and `node --check src/price-hide.js`; both must exit 0, and the test output must show the pass count with zero failures. Constraints: plain JavaScript, no npm dependencies, no build step, no chrome.* calls inside src/price-hide.js, no network calls, no eval. Only create or edit manifest.json, src/price-hide.js, tests/price-hide.test.js, and CLAUDE.md. Do not start Phase 2. Stop after 25 turns and report what is blocking if the condition is not met.
```
A `/goal` is a finish line. Claude keeps working until a small checker model confirms the condition is met, or the turn limit is reached. You can watch progress, and you can type messages while it runs.
## Step 9: Move through the phases
When a phase finishes:
1. Read the summary and check the test output.
2. Run `/clear` to start a fresh context.
3. Open PLAN.md and paste the `/goal` prompt for the next phase.
Repeat for Phases 2 through 5. Do not skip a phase.
## Step 10: Load the extension
After Phase 3, the extension has a manifest, background script, and popup, so it can load.
1. Go to `chrome://extensions`.
2. Click Load unpacked.
3. Select the `the-app` folder.
After each code change:
1. Click the reload icon on the extension card.
2. Refresh any shopping tab you are testing.
## If a /goal stalls
1. Run `/goal clear` to stop it.
2. Read the last message Claude gave.
3. Ask Claude what is blocking the work.
4. Restart the phase with a narrower prompt, for example one file at a time.
## Token use
- Run `/usage` now and then to see where tokens went.
- Use `/clear` between unrelated tasks.
- Run the verify skill before each commit. It is a check Claude runs with `/verify`.
## Before you finish
1. Run the verify skill and confirm all checks pass.
2. Walk the manual checklist in SPEC.md.
3. Write your confirmation in NOTES.md.
4. Commit the final state with git.
## Troubleshooting
- **`node --test` not found:** Node is not installed or the terminal is old. Open a new terminal after installing Node.
- **Plugin install fails:** Run `/plugin` to see the marketplace list. The official marketplace should be added automatically.
- **Extension shows errors at chrome://extensions:** Read the error text on the card. Fix the file it names, then click reload.
- **Prices do not hide:** Check that the site is in src/sites.js and that the 24-hour window is active. Selectors can change when a site updates, so expect to edit src/sites.js.
PLAN.md# the app: build plan
Before each phase, start a fresh session with `/clear`. Run `/usage` now and then to see token spend.
Each phase ends with a `/goal` prompt you can paste. A `/goal` is a finish line: Claude keeps working until a small checker model confirms the condition is met, or the turn limit is reached.
## Phase 1: skeleton
Files touched: `manifest.json`, `src/price-hide.js`, `tests/price-hide.test.js`, `CLAUDE.md`.
Goal prompt:
```text
/goal Phase 1 of PLAN.md is complete. End state: manifest.json (Manifest V3, permissions storage and activeTab only); src/price-hide.js exporting HIDE_MS (24 * 60 * 60 * 1000), isWithinHideWindow(hideUntilMs, nowMs), and computeHideUntil(nowMs); tests/price-hide.test.js with at least 4 passing cases (before, exactly at, after, missing or non-number hideUntil). Check: `node --test tests/` and `node --check src/price-hide.js` both exit 0, with zero failures shown. Constraints: plain JavaScript, no npm dependencies, no build step, no chrome.* calls in src/price-hide.js, no network calls, no eval. Do not start Phase 2. Stop after 25 turns and report what is blocking.
```
Human checkpoint: read `src/price-hide.js` and the test names. Ask Claude to explain the 24-hour math in plain language. Then say "next".
## Phase 2: background and storage
Files touched: `src/background.js`, a pure logic file for message handling, `tests/`.
Goal prompt:
```text
/goal Phase 2 of PLAN.md is complete. End state: src/background.js handles a {type: "start"} message by writing hideUntil = computeHideUntil(Date.now()) to chrome.storage.local, sets the badge to a dot while the window is active, clears it when inactive, and restores the badge on service worker startup. The message-handling decision lives in a pure function with no chrome.* calls, covered by tests. Check: `node --test tests/` exits 0 with zero failures, and `node --check src/*.js` exits 0. Constraints: no state in service worker globals, read and write through chrome.storage only, a click must never shorten an active window, no network calls, no eval. Only touch src/background.js, the pure logic file, and tests/. Do not start Phase 3. Stop after 25 turns and report what is blocking.
```
Human checkpoint: load the extension at chrome://extensions, press the toolbar icon once, and confirm the badge dot appears. Then say "next".
## Phase 3: popup
Files touched: `src/popup.html`, `src/popup.js`, a pure formatting function, `tests/`.
Goal prompt:
```text
/goal Phase 3 of PLAN.md is complete. End state: src/popup.html has a Start button and a countdown area; src/popup.js is loaded from an external file and reads hideUntil from chrome.storage.local to show a live countdown in hours and minutes; the countdown formatting is a pure function with tests. Check: `node --test tests/` exits 0 with zero failures; `node --check src/*.js` exits 0; `grep -n "<script>" src/popup.html` returns no inline script. Constraints: no inline scripts or event attributes (MV3 CSP), no network calls, no eval, no framework. Only touch popup files, the pure formatting file, and tests/. Do not start Phase 4. Stop after 25 turns and report what is blocking.
```
Human checkpoint: reload the extension, open the popup, press Start, and confirm the countdown reads about 24 hours. Then say "next".
## Phase 4: content hiding
Files touched: `src/sites.js`, `src/content.js`, a pure site-selection function, `tests/`.
Goal prompt:
```text
/goal Phase 4 of PLAN.md is complete. End state: src/sites.js lists 3 to 4 hosts with CSS price selectors, each marked as needing manual confirmation; src/content.js adds a style that hides matched price elements only while isWithinHideWindow is true, and re-checks every 60 seconds with setInterval; the function that picks active sites for a host is pure and tested. Check: `node --test tests/` exits 0 with zero failures; `node --check src/*.js` exits 0. Constraints: no inline scripts, no network calls, no eval, no remote code, manifest host permissions match src/sites.js exactly, state read from chrome.storage only. Only touch the content and site files, manifest.json, and tests/. Do not start Phase 5. Stop after 25 turns and report what is blocking.
```
Human checkpoint: reload the extension, refresh one listed shopping site, and confirm prices are hidden. Then say "next".
## Phase 5: verify and polish
Files touched: any file needed for fixes, `NOTES.md`.
Goal prompt:
```text
/goal Phase 5 of PLAN.md is complete. End state: the verify skill reports every check as PASS with the command output shown; every failure found was fixed; the user has walked the SPEC.md manual acceptance checklist and typed their confirmation in this session; NOTES.md exists and records that confirmation with the date. Check: run the verify skill and show its table; read NOTES.md back to the user. Constraints: fix only what verify reports or what the checklist exposes, no new features, no new permissions, no network calls. Stop after 30 turns and report what is blocking.
```
Human checkpoint: walk the SPEC.md acceptance checklist yourself on two listed sites. Type your confirmation in the session, then run `/usage` to review token spend. Commit with the commit-commands plugin once verify is green.
REVIEW.md# Review checklist
Used by `/code-review` and by you before merging any change. This file is imported into CLAUDE.md with `@REVIEW.md`, so Claude sees it in every session.
## Security and privacy
- [ ] No network calls: no `fetch`, `XMLHttpRequest`, WebSocket, or beacon calls to any server.
- [ ] No analytics, tracking, or telemetry of any kind.
- [ ] No remote code: nothing loaded from a URL or CDN.
- [ ] No `eval`, no `new Function`, no string-based timers.
- [ ] No inline scripts or `onclick=` attributes (MV3 content security policy).
- [ ] Permissions are only `storage` and `activeTab`, plus the hosts listed in `src/sites.js`.
- [ ] No secret, API key, or personal data appears anywhere in the repo.
## Architecture
- [ ] `src/price-hide.js` makes no `chrome.*` calls, so tests run in Node.
- [ ] Saved state lives in `chrome.storage.local`, never in global variables of the service worker.
- [ ] The only stored key is `hideUntil`, as described in SPEC.md.
## Tests
- [ ] Every pure function has at least one test.
- [ ] `node --test tests/` passes with zero failures.
- [ ] No test is skipped without a written reason in the test file.
## Manifest
- [ ] `manifest.json` is valid JSON.
- [ ] `manifest_version` is `3`.
- [ ] Permissions are the minimum needed.
- [ ] `host_permissions` match the hosts in `src/sites.js` exactly.
## Clarity for a beginner
- [ ] File and function names say what they do.
- [ ] Each file starts with a one-line comment saying what it does.
- [ ] No clever one-liners. Prefer plain steps the user can read aloud.
## Fail the review if
- A secret or personal data appears anywhere in the repo.
- A permission is added without a reason written in SPEC.md.
- The 24-hour window can be shortened by a click or by any code path.
SPEC.md# the app: product spec
## Goal
Reduce impulse buying by hiding prices on supported shopping sites for exactly 24 hours after the user clicks Start in the popup.
## User stories
1. Click the toolbar icon, press Start, and prices disappear on supported sites.
2. The popup shows hours and minutes remaining.
3. The window survives closing the browser.
4. When 24 hours pass, prices return automatically with no click needed.
5. The badge shows a dot while hiding is active.
## Supported sites (v1)
Hosts live in `src/sites.js`:
- amazon.com
- walmart.com
- target.com
- ebay.com
Each selector list is marked as needing manual confirmation. Selectors break when sites change, so check them in a real browser before trusting them.
## Data model
One key in `chrome.storage.local`:
- `hideUntil`: epoch milliseconds (number), or absent.
No other stored data.
## Permissions and reasons
- `storage`: saves `hideUntil`.
- `activeTab`: lets the extension act on the current tab only when the user clicks it.
- Host permissions for the four sites above: lets the content script hide prices there.
Any new permission needs a reason written in this section first.
## Behavior rules
- The window is exactly 24 hours from the Start click.
- A click can never shorten an active window.
- Starting again while a window is active does nothing, or extends it. It never shortens it.
- There is no reveal-on-demand override in v1. The window is firm by design.
## Out of scope (v1)
- Price comparison
- Blocking purchases
- Mobile
- Accounts and sync
- Analytics
- Reveal-on-demand override
- Stop or Clear button (add only deliberately, and update this spec first)
## Acceptance checklist (manual)
- [ ] Extension loads with no errors in chrome://extensions.
- [ ] Start sets `hideUntil` about 24 hours ahead.
- [ ] Prices are hidden on each listed site.
- [ ] Popup countdown is correct.
- [ ] Badge shows a dot while hiding is active.
- [ ] Restarting Chrome keeps hiding.
- [ ] With `hideUntil` in the past, prices show again.
- [ ] No Stop or Clear control is offered.
## Test checklist (automated)
- [ ] `isWithinHideWindow` returns true before `hideUntil`.
- [ ] `isWithinHideWindow` returns false at `hideUntil` and after it.
- [ ] `isWithinHideWindow` returns false when `hideUntil` is missing or not a number.
- [ ] `computeHideUntil(now)` returns `now + HIDE_MS`.
- [ ] Countdown formatting returns hours and minutes correctly.
- [ ] Active-site selection returns only sites from `src/sites.js` when the window is active.