Skip to content

codezelat/scopeseal-chrome-ext

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ScopeSeal

Brief & Project Scope Checker — Chrome Extension

Check project briefs, client messages, and proposal scopes for missing details, vague wording, and scope-creep risks.

Manifest V3 WXT React TypeScript Tailwind CSS Bun

by Codezela Technologies

Chrome Web Store Live Site Extension Repo Web App Repo

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


Table of Contents


Overview

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.


Key Features

  • 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.

How It Works

┌──────────────────────────────────────────────────────────────────┐
│                         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     │
        └──────────────────────────────────────────┘
  1. The user opens the popup, selects a project type, and either pastes text or captures page text.
  2. Quick Scan runs the deterministic engine synchronously in the popup — no network involved.
  3. Full Analysis sends the text to the web app API, which returns an AnalysisResult plus a shareSlug for a persisted, shareable report.
  4. 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.

Privacy-First Design

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, and scripting are requested — no tabs history, 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.session and consumed/removed immediately when the popup opens.

Tech Stack

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/)

Project Structure

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

Getting Started

Prerequisites

  • 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

Install

bun install

This installs dependencies and automatically runs wxt prepare (via the postinstall script) to generate WXT's type definitions in .wxt/.

Develop

bun run dev

WXT 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:firefox

Load the extension manually (from a build)

After building, load the unpacked extension from .output/chrome-mv3/:

  1. Open chrome://extensions
  2. Enable Developer mode
  3. Click Load unpacked
  4. Select the .output/chrome-mv3/ directory

Scripts

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)

Configuration

wxt.config.ts

  • Module: @wxt-dev/module-react for React support
  • Path alias: @/src
  • Vite plugin: @tailwindcss/vite for 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.html with default icons
    • Version: 1.0.0

tsconfig.json

  • 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

src/styles.css

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.


Entrypoints

Popup UI

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_TEXT message 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

  • ScoreRing showing 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 shareSlug is 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.session for capturedText (set by the context menu flow), auto-populating the textarea and removing the key
  • Quick Scan uses a short setTimeout delay for a smooth loading transition before the synchronous engine runs
  • Full Analysis catches errors and surfaces them inline in the input view

Background Service Worker

File: entrypoints/background.ts

An MV3 service worker that handles:

  1. Context menu creation on runtime.onInstalled:
    • "analyze-selection" — "Analyze with ScopeSeal" (context: selection)
    • "analyze-page" — "Analyze page text with ScopeSeal" (context: page)
  2. Context menu click handler:
    • For selection: uses info.selectionText
    • For page: calls capturePageText(tab?.id)
    • Stores the captured text in browser.storage.session as capturedText
    • Calls browser.action.openPopup?.() to open the popup (gracefully no-ops if the API is unavailable)
  3. Message listener (runtime.onMessage) — handles CAPTURE_PAGE_TEXT and returns { success: true, text }
  4. capturePageText() — uses browser.scripting.executeScript with an inline function that:
    • Creates a TreeWalker over document.body filtering for text nodes
    • Rejects text inside script, style, noscript, meta, link elements
    • Rejects text shorter than 3 characters
    • Joins text nodes with \n\n and returns the first frame's result

Options Page

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 POST to ${endpoint}/api/analyze and reports success/failure
    • Test result display (green for success, red for failure)
  • Footer: "ScopeSeal v1.0.0 / Codezela Technologies"

The Deterministic Engine

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.

analyze(text, projectType) flow

  1. Trims text; returns a zero-score empty result if blank
  2. Counts words
  3. Looks up project type metadata (per-category weights)
  4. Detects risky phrases → risks[]
  5. Detects missing items → missing[]
  6. Scores each of 9 categories using weighted signals + risk penalties → categories[]
  7. Computes a weighted overall score (0–100)
  8. Determines a band: clear (≥70), review (≥40), risky (<40)
  9. Builds up to 8 actionable suggestions
  10. Builds 4 output strings (internal summary, client-friendly note, proposal additions, rewritten scope)
  11. Checks for sensitive content

Engine files

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

Project Types

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.

Scoring Categories

Nine categories are scored 0–100 based on signal detection and risk penalties:

  1. Deliverables
  2. Timeline
  3. Revisions
  4. Payment
  5. Client Responsibility
  6. Technical Responsibility
  7. Acceptance
  8. Maintenance / Support
  9. 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).

Risk Detection

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.

Missing Items

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").

Suggestions & Outputs

  • buildSuggestions() prioritizes high-severity missing items, then high-severity risks, then medium items/risks — capped at 8 suggestions.
  • outputs.ts builds four output strings:
    1. An internal risk summary for the professional
    2. A client-friendly note (polite tone)
    3. Proposal additional info (markdown-formatted bullet points)
    4. A rewritten scope template with placeholder blanks, with sections adapted per project type

Sensitive Content Guard

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.


Message Protocol

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.


Storage

src/lib/settings.ts

Manages extension settings via browser.storage.local:

interface Settings {
  apiEndpoint: string;          // default: "https://scopeseal.codezela.com"
  defaultProjectType: string;   // default: "website"
}
  • getSettings() — reads the settings key, merged with defaults
  • saveSettings(partial) — merges partial settings with current settings and persists

Session storage

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.


Context Menu Integration

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.


Web App API Integration

Endpoint

  • Base URL: https://scopeseal.codezela.com (configurable in Options)
  • API route: POST /api/analyze

Request

{
  "text": "<scope text>",
  "projectType": "website"
}

Response

{
  "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.

Connection test

The Options page sends a test POST to /api/analyze with { text: "Test connection", projectType: "general" } and checks res.ok.

Host permission

https://scopeseal.codezela.com/* is required for the fetch() calls to succeed under MV3's Content Security Policy.


UI Components

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).

Theming & Design System

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-950 background, system font stack, overflow-x: hidden
  • .bg-seal-gradient — 135° gradient from seal-violet to seal-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.


Testing

Engine tests — Vitest

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.json excludes 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.

E2E tests — Playwright

Playwright 1.61 is listed as a dev dependency for end-to-end browser automation testing of the extension.


Cross-Browser Support

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 distribution

The codebase consistently uses the browser.* API (WXT's webextension-polyfill) rather than chrome.*, enabling cross-browser compatibility.


Permissions Explained

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.


Build & Distribution

Production build

bun run build

Output lands in .output/chrome-mv3/ containing manifest.json, background.js, popup.html, options.html, icons, and compiled assets/ and chunks/ directories.

Package for the Chrome Web Store

bun run zip

Produces .output/chrome-mv3.zip ready for upload to the Chrome Web Store. ScopeSeal is already published — install it here.

Firefox package

bun run zip:firefox

Produces .output/firefox-mv3.zip for Firefox Add-ons.


Conventions & Pitfalls

  • Use browser.* APIs (WXT polyfill), not chrome.*.
  • Path alias @/ maps to src/ — requires both the "paths" entry in tsconfig.json and the "alias" entry in wxt.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.executeScript returns 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.

Links

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

License

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

About

ScopeSeal is a privacy-first Manifest V3 browser extension that helps freelancers, agencies, and consultants write tighter project scopes and proposals.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors