Check project briefs, client messages, and proposal scopes for missing details, vague wording, and scope-creep risks.
Install now: ScopeSeal on the Chrome Web Store
Try the web app: scopeseal.codezela.com · source at github.com/codezelat/scopeseal
This extension's source: github.com/codezelat/scopeseal-chrome-ext
- Overview
- Key Features
- How It Works
- Privacy-First Design
- Tech Stack
- Project Structure
- Getting Started
- Scripts
- Configuration
- Entrypoints
- The Deterministic Engine
- Message Protocol
- Storage
- Context Menu Integration
- Web App API Integration
- UI Components
- Theming & Design System
- Testing
- Cross-Browser Support
- Permissions Explained
- Build & Distribution
- Conventions & Pitfalls
- Links
- License
ScopeSeal is a privacy-first Manifest V3 browser extension that helps freelancers, agencies, and consultants write tighter project scopes and proposals. It scans project briefs, client messages, and proposal text for:
- Missing details — deliverables, timeline, payment milestones, revision limits, acceptance criteria, and more
- Vague or risky wording — "simple", "unlimited", "asap", "everything included", "make it like [competitor]", and other phrases that invite scope creep
- Scope-creep risks — categories that score low because critical information is absent
ScopeSeal offers two analysis modes:
| Mode | Where it runs | Network | Use case |
|---|---|---|---|
| Quick Scan | Locally in the popup (deterministic TypeScript engine) | None | Instant feedback, fully offline, zero data leaves the browser |
| Full Analysis | ScopeSeal web app API | POST to https://scopeseal.codezela.com/api/analyze |
Full report persisted server-side with a shareable link |
The local engine is the same deterministic engine used by the ScopeSeal web app, copied into the extension so Quick Scan results match the web app exactly.
- Local Quick Scan — a pure-TypeScript deterministic engine analyzes text entirely in the browser. No network calls, no tracking, no data collection.
- Full Analysis online — send text to the ScopeSeal web app for a persisted, shareable report.
- 8 project types — Website, SEO, Social Media Marketing, Branding, Custom Software, Mobile App, Maintenance/Support, and General Service. Each applies different weighting to scoring categories.
- 9 scoring categories — Deliverables, Timeline, Revisions, Payment, Client Responsibility, Technical Responsibility, Acceptance, Maintenance/Support, and Exclusions.
- 17 risky-phrase detectors with negation awareness — "not simple" is correctly left unflagged.
- 15 missing-item detectors with severity escalation based on project-type weighting.
- Sensitive content guard — detects credit card numbers, SSNs, password/email combos, NDA mentions, and large account numbers before they get sent anywhere.
- Score ring — an animated 0–100 score with a Clear / Review / Risky band label.
- Context menu integration — right-click selected text or the page to analyze instantly.
- Page text capture — extract visible text from the active tab on demand via a
TreeWalker. - Settings page — configure the API endpoint and test the connection.
- Cross-browser — Chrome and Firefox builds via WXT.
- Dark theme with a violet→indigo brand gradient matching the web app.
┌──────────────────────────────────────────────────────────────────┐
│ User triggers │
│ (popup button · context menu "Analyze with ScopeSeal" · │
│ context menu "Analyze page text") │
└───────────────┬───────────────────────┬──────────────────────────┘
│ │
▼ ▼
┌───────────────┐ ┌────────────────┐
│ Quick Scan │ │ Full Analysis │
│ (local) │ │ (online) │
│ │ │ │
│ analyze() │ │ fetch( │
│ engine runs │ │ /api/analyze) │
│ in popup │ │ │
└───────┬───────┘ └────────┬───────┘
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ Results view │
│ Score ring · missing items · risks · │
│ suggestions · shareable report link │
└──────────────────────────────────────────┘
- The user opens the popup, selects a project type, and either pastes text or captures page text.
- Quick Scan runs the deterministic engine synchronously in the popup — no network involved.
- Full Analysis sends the text to the web app API, which returns an
AnalysisResultplus ashareSlugfor a persisted, shareable report. - Results render as a score ring, missing-item and risk lists with severity badges, and (for Full Analysis) a link to the full online report.
ScopeSeal is built to be minimally invasive:
- No auto-injected content scripts. The extension never reads page content unless you explicitly trigger analysis (popup button or context menu).
- Quick Scan is fully local. Text never leaves the browser.
- Full Analysis only sends what you submit. Capture happens on a single explicit click, and the text is sent only to the configured ScopeSeal API endpoint.
- Minimal permissions. Only
activeTab,contextMenus,storage, andscriptingare requested — notabshistory, no broad host permissions, no content script access to all sites. - Sensitive content guard. The engine flags credit card numbers, SSNs, password combinations, and NDA/confidential mentions so sensitive data is surfaced before submission.
- Session storage for captured text. Text captured via the context menu is held in ephemeral
storage.sessionand consumed/removed immediately when the popup opens.
| Layer | Technology |
|---|---|
| Framework | WXT 0.20 — Manifest V3, file-based entrypoints |
| UI | React 19 + react-dom 19 |
| Language | TypeScript 5.7+ |
| Styling | Tailwind CSS v4 (@tailwindcss/vite) |
| Package Manager | Bun |
| Test Framework | Vitest (engine suite) + Playwright 1.61 (E2E) |
| Engine | Pure TypeScript deterministic engine (src/lib/engine/) |
scopeseal-chrome-ext/
├── entrypoints/
│ ├── popup/ # Main popup UI (input → loading → results)
│ │ ├── index.html
│ │ ├── main.tsx
│ │ └── App.tsx
│ ├── options/ # Settings page (API endpoint config)
│ │ ├── index.html
│ │ ├── main.tsx
│ │ └── App.tsx
│ └── background.ts # MV3 service worker (context menu, capture, messaging)
├── src/
│ ├── styles.css # Tailwind v4 + brand theme tokens + animations
│ ├── components/
│ │ ├── SealLogo.tsx # SVG logo with violet→indigo gradient
│ │ ├── ScoreRing.tsx # Animated circular score visualization
│ │ └── LoadingSpinner.tsx
│ └── lib/
│ ├── settings.ts # Settings storage (browser.storage.local)
│ └── engine/ # Deterministic analysis engine (copied from web app)
│ ├── index.ts # analyze() orchestrator + re-exports
│ ├── types.ts # All TypeScript types
│ ├── project-types.ts # 8 project types with weighted categories
│ ├── categories.ts # 9 scoring categories + signal patterns
│ ├── scoring.ts # Category + overall scoring, band mapping
│ ├── missing-items.ts # 15 missing-item detectors
│ ├── risk-detector.ts # 17 risky-phrase detectors (negation-aware)
│ ├── suggestions.ts # Severity-ordered suggestion builder
│ ├── outputs.ts # 4 generated output strings
│ ├── sensitive.ts # Sensitive content detector
│ ├── text-utils.ts # Normalization, sentence/phrase/quantity utils
│ └── __tests__/
│ └── engine.test.ts # Vitest suite
├── public/
│ └── icon/ # 16, 32, 48, 96, 128 px extension icons
├── package.json
├── wxt.config.ts # WXT config — manifest, modules, Vite/Tailwind, alias
├── tsconfig.json # Extends WXT tsconfig, JSX, path alias
├── AGENTS.md # AI agent instructions
└── LICENSE # Proprietary — © 2026 Codezela Technologies
- Bun (recommended) or any Node.js-compatible runtime that supports WXT
- Chrome, Edge, or any Chromium browser (for the Chrome build); Firefox for the Firefox build
bun installThis installs dependencies and automatically runs wxt prepare (via the postinstall script) to generate WXT's type definitions in .wxt/.
bun run devWXT starts a dev server and launches Chrome with the extension loaded unpacked. Hot-reload is active — edits to entrypoints and src/ rebuild and reload the extension automatically.
For Firefox development:
bun run dev:firefoxAfter building, load the unpacked extension from .output/chrome-mv3/:
- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked
- Select the
.output/chrome-mv3/directory
| Script | Description |
|---|---|
bun run dev |
Start WXT dev mode (Chrome) with hot-reload |
bun run dev:firefox |
Start WXT dev mode (Firefox) |
bun run build |
Production build → .output/chrome-mv3/ |
bun run build:firefox |
Production build for Firefox → .output/firefox-mv3/ |
bun run zip |
Package the Chrome build as a .zip in .output/ |
bun run zip:firefox |
Package the Firefox build as a .zip |
bun run type-check |
Run tsc --noEmit for type checking |
bun run postinstall |
Run wxt prepare to generate types (runs automatically on install) |
- Module:
@wxt-dev/module-reactfor React support - Path alias:
@→/src - Vite plugin:
@tailwindcss/vitefor Tailwind v4 - Manifest (MV3):
- Name:
ScopeSeal by Codezela: Brief & Project Scope Checker - Permissions:
activeTab,contextMenus,storage,scripting - Host permissions:
https://scopeseal.codezela.com/* - Icons: 16, 32, 48, 96, 128 px
- Action: popup at
popup.htmlwith default icons - Version:
1.0.0
- Name:
- Extends WXT's generated
./.wxt/tsconfig.json jsx: "react-jsx"(automatic JSX runtime)allowImportingTsExtensions: true- Path alias:
"@/*": ["src/*"] - Excludes
src/lib/engine/__tests__from the main type-check scope
Tailwind v4 is imported via @import "tailwindcss" with a custom @theme block defining the brand color palette, body sizing, gradients, and keyframe animations. See Theming & Design System.
Files: entrypoints/popup/{index.html,main.tsx,App.tsx}
The main user-facing surface — a 384px-wide popup with three views:
Input view
- Brand header (SealLogo + "ScopeSeal")
- Project type selector dropdown (8 project types)
- Textarea for scope text with a live character counter (minimum 50 characters to enable analysis)
- Capture page text button — sends a
CAPTURE_PAGE_TEXTmessage to the background service worker to extract visible text from the active tab - Quick scan (local) button — runs the deterministic engine synchronously in the popup
- Full analysis (online) button —
POSTs to the web app API - Footer links: Open full app ↗ and Settings (opens the options page)
Loading view
- Custom SVG spinner with the message "Analyzing scope clarity…"
Results view
ScoreRingshowing the 0–100 score with a Clear / Review / Risky band label- Top 3 missing items, each with a severity badge:
high→ red (missing)medium→ amber (risky)low→ violet (seal-violet)
- Top 3 detected risks with severity badges
- If a
shareSlugis present (from Full Analysis): a View full report ↗ link to${apiEndpoint}/result/${shareSlug} - Analyze another button to reset
Behaviors
- On mount, loads the API endpoint from settings and checks
browser.storage.sessionforcapturedText(set by the context menu flow), auto-populating the textarea and removing the key - Quick Scan uses a short
setTimeoutdelay for a smooth loading transition before the synchronous engine runs - Full Analysis catches errors and surfaces them inline in the input view
File: entrypoints/background.ts
An MV3 service worker that handles:
- Context menu creation on
runtime.onInstalled:"analyze-selection"— "Analyze with ScopeSeal" (context:selection)"analyze-page"— "Analyze page text with ScopeSeal" (context:page)
- Context menu click handler:
- For selection: uses
info.selectionText - For page: calls
capturePageText(tab?.id) - Stores the captured text in
browser.storage.sessionascapturedText - Calls
browser.action.openPopup?.()to open the popup (gracefully no-ops if the API is unavailable)
- For selection: uses
- Message listener (
runtime.onMessage) — handlesCAPTURE_PAGE_TEXTand returns{ success: true, text } capturePageText()— usesbrowser.scripting.executeScriptwith an inline function that:- Creates a
TreeWalkeroverdocument.bodyfiltering for text nodes - Rejects text inside
script,style,noscript,meta,linkelements - Rejects text shorter than 3 characters
- Joins text nodes with
\n\nand returns the first frame's result
- Creates a
Files: entrypoints/options/{index.html,main.tsx,App.tsx}
A settings page that opens as a popup (the manifest sets options_ui.open_in_tab: false):
- Brand header (SealLogo + "ScopeSeal Settings")
- API Configuration card:
- URL input for the API endpoint (default:
https://scopeseal.codezela.com) - Save button — persists to
browser.storage.local - Test connection button — sends a test
POSTto${endpoint}/api/analyzeand reports success/failure - Test result display (green for success, red for failure)
- URL input for the API endpoint (default:
- Footer: "ScopeSeal v1.0.0 / Codezela Technologies"
The engine at src/lib/engine/ is a deterministic, pure-TypeScript scope analysis engine copied from the ScopeSeal web app. It has no Node.js dependencies and runs entirely in the extension context. It must not be modified — keeping it identical to the web app ensures Quick Scan results match the web app exactly.
- Trims text; returns a zero-score empty result if blank
- Counts words
- Looks up project type metadata (per-category weights)
- Detects risky phrases →
risks[] - Detects missing items →
missing[] - Scores each of 9 categories using weighted signals + risk penalties →
categories[] - Computes a weighted overall score (0–100)
- Determines a band:
clear(≥70),review(≥40),risky(<40) - Builds up to 8 actionable suggestions
- Builds 4 output strings (internal summary, client-friendly note, proposal additions, rewritten scope)
- Checks for sensitive content
| File | Purpose |
|---|---|
index.ts |
Main analyze() orchestrator; re-exports all types and functions |
types.ts |
ProjectType, CategoryId, Band, Severity, CategoryResult, MissingItem, RiskHit, Outputs, AnalysisResult, ProjectTypeMeta |
project-types.ts |
8 project types with metadata and per-category weights |
categories.ts |
9 scoring categories with primary + secondary signal patterns |
scoring.ts |
scoreCategory(), computeOverall(), bandFor() |
missing-items.ts |
15 missing-item detectors with severity escalation |
risk-detector.ts |
17 risky-phrase detectors with negation awareness |
suggestions.ts |
buildSuggestions() — severity-ordered, capped at 8 |
outputs.ts |
4 generated output strings (risk summary, client note, proposal info, rewritten scope) |
sensitive.ts |
hasSensitiveContent() — cards, SSNs, passwords, NDAs, account numbers |
text-utils.ts |
Normalization, countWords, splitSentences, findPhrase (negation-aware), findFirstContext, findQuantity |
| ID | Label | Blurb |
|---|---|---|
website |
Website | Marketing sites, landing pages, e-commerce builds |
seo |
SEO | Search engine optimization campaigns and audits |
social |
Social Media Marketing | Social content creation, scheduling, and campaigns |
branding |
Branding | Logo design, brand identity, style guides |
software |
Custom Software | Web apps, APIs, custom development projects |
mobile |
Mobile App | iOS and Android app development |
maintenance |
Maintenance / Support | Ongoing support, bug fixes, retainer agreements |
general |
General Service | Consulting, design, or other professional services |
Each project type applies custom per-category weights — for example, software weights Technical Responsibility and Acceptance at 1.3×, which also escalates the severity of related missing items from medium to high.
Nine categories are scored 0–100 based on signal detection and risk penalties:
- Deliverables
- Timeline
- Revisions
- Payment
- Client Responsibility
- Technical Responsibility
- Acceptance
- Maintenance / Support
- Exclusions
Scoring logic: a primary signal found gives a 78–96 base; secondary-only signals give a proportional score up to 60; risk penalties subtract up to 12 per category. The overall score is a weighted average across categories, mapped to a band: Clear (≥70), Review (≥40), Risky (<40).
17 risky-phrase detectors flag vague and scope-creep-inviting language such as:
"simple", "quick", "unlimited", "asap", "everything included", "make it like", "same as competitor"
Each detection includes a severity and templated guidance. The detector is negation-aware — it uses a 6-token lookback window so phrases like "not simple" are correctly left unflagged, avoiding false positives.
15 missing-item definitions span all categories. Each has match patterns, a default severity, and guidance text. Severity auto-escalates from medium to high when the item's category weight is ≥1.3 for the selected project type. Special handling exists for final-deliverables, which also checks for quantified nouns (e.g., "5 pages", "3 mockups").
buildSuggestions()prioritizes high-severity missing items, then high-severity risks, then medium items/risks — capped at 8 suggestions.outputs.tsbuilds four output strings:- An internal risk summary for the professional
- A client-friendly note (polite tone)
- Proposal additional info (markdown-formatted bullet points)
- A rewritten scope template with placeholder blanks, with sections adapted per project type
hasSensitiveContent() detects:
- Credit card numbers (13–16 digit sequences)
- SSN patterns (with "social security" / "ssn" context)
- Phone + password combinations
- Email + password combinations
- NDA / confidential mentions
- Large account numbers with context
This surfaces sensitive data before it ever gets submitted to the API.
Communication between the popup and the background service worker uses browser.runtime.sendMessage / browser.runtime.onMessage:
| Message type | Direction | Purpose | Response |
|---|---|---|---|
CAPTURE_PAGE_TEXT |
Popup → Background | Capture visible text from the active tab | { success: true, text: string } |
Quick Scan runs entirely in the popup (synchronous engine call), and Full Analysis is a direct fetch() from the popup — neither requires a background round-trip.
Manages extension settings via browser.storage.local:
interface Settings {
apiEndpoint: string; // default: "https://scopeseal.codezela.com"
defaultProjectType: string; // default: "website"
}getSettings()— reads thesettingskey, merged with defaultssaveSettings(partial)— merges partial settings with current settings and persists
browser.storage.session is used for the capturedText key. The background context menu handler sets it; the popup consumes and immediately removes it on mount. Session storage is ephemeral and cleared when the session ends.
Two context menu items are registered on browser.runtime.onInstalled:
| Menu item | Context | Captures |
|---|---|---|
| Analyze with ScopeSeal | selection |
The selected text (info.selectionText) |
| Analyze page text with ScopeSeal | page |
Full visible page text via capturePageText() |
After capturing text, the background stores it in browser.storage.session as capturedText and attempts to open the popup via browser.action.openPopup?.(). When the popup loads, it checks for capturedText, auto-populates the textarea, and removes the key. If openPopup is unavailable (it's a relatively new MV3 API), the user clicks the extension icon manually.
- Base URL:
https://scopeseal.codezela.com(configurable in Options) - API route:
POST /api/analyze
{
"text": "<scope text>",
"projectType": "website"
}{
"result": { "AnalysisResult object" },
"shareSlug": "<slug>"
}The shareSlug enables a View full report ↗ link to ${apiEndpoint}/result/${shareSlug} — a server-side persisted report on the web app.
The Options page sends a test POST to /api/analyze with { text: "Test connection", projectType: "general" } and checks res.ok.
https://scopeseal.codezela.com/* is required for the fetch() calls to succeed under MV3's Content Security Policy.
| Component | File | Description |
|---|---|---|
SealLogo |
src/components/SealLogo.tsx |
SVG logo — a circle with a checkmark, using a violet→indigo linear gradient. Accepts a size prop (default 24). |
ScoreRing |
src/components/ScoreRing.tsx |
Circular progress ring showing the 0–100 score. SVG with a gradient stroke (url(#seal-grad)), an animated strokeDashoffset transition (1.2s cubic-bezier), and a band label (Clear/Review/Risky) color-coded in the center. Accepts score and size (default 120). |
LoadingSpinner |
src/components/LoadingSpinner.tsx |
SVG spinner — a partial arc rotating via the .seal-spinner animation (0.8s linear infinite). Accepts a size prop (default 40). |
ScopeSeal uses a dark-only theme with a custom Tailwind v4 @theme block. All colors come from CSS variables defined in src/styles.css:
| Token | Hex | Usage |
|---|---|---|
ink-950 |
#070B22 |
Background (darkest) |
ink-900 |
#0A0F2C |
Card backgrounds |
ink-800 |
#111740 |
Input/button backgrounds |
ink-700 |
#1B234F |
Borders |
ink-600 |
#2A3470 |
Border accents |
seal-violet |
#8B5CF6 |
Primary brand color |
seal-indigo |
#6366F1 |
Gradient end color |
seal-cyan |
#22D3EE |
Hover accent |
clear |
#10B981 |
"Clear" band / success |
risky |
#F59E0B |
"Review" band / warnings |
missing |
#F43F5E |
"Risky" band / errors |
muted |
#9798B0 |
Secondary text |
Additional styles:
body— 384px wide, 480px min-height,ink-950background, system font stack,overflow-x: hidden.bg-seal-gradient— 135° gradient fromseal-violettoseal-indigo(primary buttons).score-ring-track/.score-ring-fill— SVG ring styling with animated transitions@keyframes spin-seal— 0.8s linear rotation for the spinner@keyframes fade-in— 0.3s fade + translateY transition for view changes
src/styles.css is imported in both popup/main.tsx and options/main.tsx.
The engine ships with a comprehensive Vitest suite at src/lib/engine/__tests__/engine.test.ts. It uses 8 detailed fixture texts (vague website brief, detailed website scope, SEO monthly retainer, social media brief, software project, maintenance agreement, short WhatsApp message, professional proposal) and covers:
- Determinism — repeated runs produce identical results
- Empty input handling
- Vague brief vs detailed scope scoring calibration
- All 8 project types
- Risk detector — counts, context extraction, negation handling
- Missing items detection and severity escalation
- Outputs — risk summary, client note, proposal info, rewritten scope
- Sensitive content detection
- Performance — long text (2000 words) analyzed in well under the threshold
- Spec QA and text utilities
- Project type metadata validation
- Calibration regression guards
Note:
tsconfig.jsonexcludes the engine test directory from the main type-check scope. Run the suite via the web app monorepo or a Vitest configuration that includes the engine tests.
Playwright 1.61 is listed as a dev dependency for end-to-end browser automation testing of the extension.
ScopeSeal builds for both Chromium and Firefox via WXT's -b flag:
bun run build # Chrome / Edge / Chromium → .output/chrome-mv3/
bun run build:firefox # Firefox → .output/firefox-mv3/
bun run zip # Chrome .zip for distribution
bun run zip:firefox # Firefox .zip for distributionThe codebase consistently uses the browser.* API (WXT's webextension-polyfill) rather than chrome.*, enabling cross-browser compatibility.
| Permission | Why it's needed |
|---|---|
activeTab |
Access the current tab content only when the user explicitly clicks |
contextMenus |
Right-click "Analyze with ScopeSeal" menu items |
storage |
Save settings in storage.local; temporarily hold captured text in storage.session |
scripting |
Inject an on-demand content script to extract page text via browser.scripting.executeScript |
host_permissions: https://scopeseal.codezela.com/* |
Allow API calls to the ScopeSeal web app |
No tabs history, no broad host permissions, and no always-on content scripts.
bun run buildOutput lands in .output/chrome-mv3/ containing manifest.json, background.js, popup.html, options.html, icons, and compiled assets/ and chunks/ directories.
bun run zipProduces .output/chrome-mv3.zip ready for upload to the Chrome Web Store. ScopeSeal is already published — install it here.
bun run zip:firefoxProduces .output/firefox-mv3.zip for Firefox Add-ons.
- Use
browser.*APIs (WXT polyfill), notchrome.*. - Path alias
@/maps tosrc/— requires both the"paths"entry intsconfig.jsonand the"alias"entry inwxt.config.ts. - The engine is shared/copied from the web app and must not be modified — it keeps Quick Scan results identical to the web app.
- MV3 service workers are ephemeral — don't rely on global state persistence. Captured text is passed through
storage.session. browser.scripting.executeScriptreturns an array of results (one per frame) — ScopeSeal uses the first frame's result.- The engine is pure TypeScript — no Node.js APIs needed; it works in the extension context.
- Dark theme only — all colors come from CSS variables in
src/styles.css. browser.action.openPopup?.()uses optional chaining because the API is not available in all browsers — if unavailable, the user clicks the extension icon manually after using the context menu.
| Resource | URL |
|---|---|
| Chrome Web Store | ScopeSeal by Codezela |
| Live web app | scopeseal.codezela.com |
| This extension's source | github.com/codezelat/scopeseal-chrome-ext |
| Web app source | github.com/codezelat/scopeseal |
| Codezela Technologies | codezela.com |
Proprietary — Copyright © 2026 Codezela Technologies. All rights reserved. Unauthorized use, copying, modification, or distribution is prohibited without prior written permission from Codezela Technologies. See the LICENSE file for details.
ScopeSeal · Seal the scope before it slips. · Built by Codezela Technologies