diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e5f596d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..146c73b --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# This toolset reads NO environment variables, by design. This file exists so +# nobody goes hunting for configuration that does not exist. +# +# Every tool here is a Chrome extension that runs against a HighLevel +# sub-account you are already signed into. Authentication is borrowed from the +# page's own session (window.SHELL_STORE.$http) inside the browser tab; no +# token, API key, or password is ever read, stored, or configured outside it. +# +# If a future tool genuinely needs configuration, document its variables here +# and in the README's Configuration table, and keep the values obviously fake +# (replace-me / https://example.invalid) so a copy-paste mistake fails loudly. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..48d5df3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,21 @@ +# CODEOWNERS +# Reference: raw/get-started--codeowners--about-code-owners-official-docs.md +# +# Rules: +# - Owners MUST have write access to this repository (or be a visible team +# with write access). +# - Last matching pattern wins, same as .gitignore, EXCEPT: \# escaping, +# ! negation, and [ ] character ranges do NOT work here even though they +# look like gitignore syntax. +# - Multiple owners for one pattern must be listed on the same line, or only +# the last-listed owner is applied. +# - Enable "Require review from Code Owners" on branch protection / a +# ruleset for this file to actually gate merges (see guide 06). + +# Default owner for everything not matched below. +* @legioncodeinc + +# Lock down changes to repository governance and CI/CD itself. +/.github/ @legioncodeinc +/.github/CODEOWNERS @legioncodeinc +/.github/workflows/ @legioncodeinc diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d4177c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,37 @@ +--- +name: Bug report +about: Report something that is broken +title: "bug: " +labels: bug +assignees: "" +--- + + + +## Describe the bug + +{A clear, concise description of what is broken.} + +## Steps to reproduce + +1. {Step one} +2. {Step two} +3. {See error} + +## Expected behavior + +{What you expected to happen instead.} + +## Actual behavior + +{What actually happened. Include exact error text or a stack trace if you have one.} + +## Environment + +- Tool and version: {e.g. ghl-workflow-exporter 1.0.0 — see its manifest.json} +- Browser: {browser_and_version} +- HighLevel app domain: {app.gohighlevel.com or your white-labelled domain — never paste tokens} + +## Additional context + +{Logs, screenshots, or anything else that helps diagnose this. Do not paste secrets, tokens, session JWTs, or signed URLs here: this tracker is public.} diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..a5da005 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea or enhancement +title: "feat: " +labels: enhancement +assignees: "" +--- + + + +## Problem + +{What problem does this solve? What can't you do today?} + +## Proposed solution + +{What you'd like to see happen. Be as concrete as you can.} + +## Alternatives considered + +{Other approaches you thought about and why you didn't propose them instead. Delete this section if there weren't any.} + +## Additional context + +{Mockups, links, prior art, or anything else that helps evaluate this request.} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a63200a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ + + +## What + +{One or two sentences describing the change. Not the how, the what.} + +## Why + +{The problem this solves or the request it satisfies. Link the issue: Closes #{issue_number}} + +## How + +{Notable implementation decisions a reviewer needs to know before reading the diff. Skip this section if the diff speaks for itself.} + +## Type of change + +- [ ] `feat`: new feature +- [ ] `fix`: bug fix +- [ ] `docs`: documentation only +- [ ] `refactor`: no behavior change +- [ ] `test`: test-only change +- [ ] `chore` / `ci`: tooling, build, or CI change +- [ ] Breaking change (see Conventional Commits `!` / `BREAKING CHANGE:` footer) + +## Testing + +{How this was verified: `node scripts/validate-manifests.mjs` output, the sub-account scenario exercised, screenshots for popup UI changes.} + +## Checklist + +- [ ] I ran `node scripts/validate-manifests.mjs` locally and it passes +- [ ] I loaded the affected tool via `chrome://extensions` and smoke-tested it against a real sub-account +- [ ] I updated `CHANGELOG.md` under `Unreleased` if this is a notable change +- [ ] I updated documentation (README, tool READMEs, guides) if behavior or setup changed +- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +- [ ] No secrets, credentials, signed URLs, or `.env` values are included in this diff diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..88d7d5e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,25 @@ +# Dependabot version updates. Zero-config baseline: no external app install, +# GitHub-native, wired to GitHub Advisories for security PRs automatically. +# For monorepos, multi-Git-platform needs, or >30 ecosystems, see guide +# 05-commit-and-release-hygiene.md for the Renovate swap-in. +# Grounded in: raw/get-started--dependency-updates--dependabot-vs-renovate-jsonic.md + +version: 2 +updates: + # NOTE: no package-ecosystem entry yet. This repo is dependency-free vanilla + # JavaScript with no package.json/lockfile. When a tool introduces one, add: + # - package-ecosystem: "npm" + # directory: "/" + # schedule: { interval: "weekly" } + + # Keep GitHub Actions themselves current, including SHA-pinned actions. + # Dependabot understands SHA pins and updates the SHA plus the version + # comment in the same PR. See raw/get-started--ci-security--secure-pipelines-cheat-sheet.md + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "ci" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..52f6142 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: CI + +# Validates every tool's Chrome extension manifest on push and pull request +# against main. This repo is dependency-free vanilla JavaScript, so there is +# no install step, no lockfile, and nothing to cache; lint/typecheck/test jobs +# arrive when a tool introduces a build system. +# Grounded in least-privilege GITHUB_TOKEN guidance: +# raw/get-started--ci-security--actions-secure-use-official-docs.md +# raw/get-started--ci-security--secure-pipelines-cheat-sheet.md + +on: + push: + branches: [main] + pull_request: + branches: [main] + +# Default every job to read-only. Jobs that need more must grant it themselves. +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + name: Validate extension manifests + runs-on: ubuntu-latest + permissions: + contents: read + steps: + # Pin third-party actions to a full commit SHA, not a tag, and keep the + # version comment for readability. Resolve the SHA for your installed + # major version with: gh api repos/actions/checkout/git/ref/tags/vX.Y.Z --jq '.object.sha' + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: ".nvmrc" + + - name: Validate manifests + run: node scripts/validate-manifests.mjs diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..617fd75 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,65 @@ +name: CodeQL + +# Advanced-setup CodeQL scan, portable as a committed workflow file. +# GitHub's own recommendation is native "default setup" (Settings > Advanced +# Security > CodeQL analysis > Set up > Default), which needs no workflow file +# at all. Use this workflow when the project wants scanning defined as code, +# or when default setup isn't available for the plan/visibility in use. +# Grounded in: raw/get-started--codeql--configuring-default-setup-official-docs.md + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Weekly scan of the default branch, catches newly disclosed query rules + # against unchanged code. Adjust the cron to your own quiet hours. + - cron: "0 6 * * 1" + +permissions: + contents: read + +jobs: + analyze: + # Code scanning uploads require Code Security (GHAS) enabled on private + # repositories — this repo has it off, and turning it on is a paid org + # decision. This job therefore skips (neutral on PRs) until a repository + # variable CODEQL_ENABLED=true is set: + # Settings > Secrets and variables > Actions > Variables > CODEQL_ENABLED=true + if: vars.CODEQL_ENABLED == 'true' + # Static name: a skipped job renders a matrix-templated name literally. + name: Analyze (CodeQL) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read + + strategy: + fail-fast: false + matrix: + # List every CodeQL-supported language actually present in the repo. + language: [javascript-typescript] + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Initialize CodeQL + uses: github/codeql-action/init@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 + with: + languages: ${{ matrix.language }} + # default is precision-tuned; security-extended adds queries at + # some precision cost. See the distilled research for the tradeoff. + queries: security-extended + + # No build step: this repo ships plain ES modules loaded directly by + # Chrome, so autobuild has nothing to do and JS/TS needs no config. + - name: Autobuild + uses: github/codeql-action/autobuild@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 + with: + category: "/language:${{ matrix.language }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..283ff52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +# TypeScript / Node baseline. Grounded in: +# raw/get-started--gitignore--github-gitignore-node-template.md +# Extend the "Project-specific" section at the bottom; don't edit the rest +# unless your stack genuinely differs from Node/TypeScript. + +# ---- Environment / secrets ---- +# Never commit real .env files. .env.example is the one exception, since it +# documents required variables without holding real values. +.env +.env.* +!.env.example + +# ---- Dependencies ---- +node_modules/ +jspm_packages/ + +# ---- Build output ---- +dist/ +build/ +out/ +*.tsbuildinfo + +# ---- Coverage / test artifacts ---- +coverage/ +*.lcov +.nyc_output/ + +# ---- Logs ---- +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# ---- Caches ---- +.npm +.eslintcache +.stylelintcache +.cache/ +.parcel-cache/ + +# ---- Yarn v2+ ---- +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# ---- Framework build dirs ---- +.next/ +.nuxt/ +.svelte-kit/ +.vercel/ +.turbo/ +.serverless/ + +# ---- Editor / IDE ---- +.vscode/* +!.vscode/extensions.json +.idea/ +*.swp +*.swo + +# ---- OS cruft ---- +.DS_Store +Thumbs.db +Desktop.ini + +# ---- Project-specific ---- +# Local tool state (security-scanner hooks); never belongs in the repo. +.mimosa/ + +# Exported backups produced by the tools themselves. +*.zip + +# Chrome packaging artifacts. A .pem here would be the Web Store upload key: +# a real secret that must never be committed. +*.crx +*.pem diff --git a/.mimosa/hook-state/sess_d7f1bf06-bc47-42a3-8df8-355feba8b2b7.continue.json b/.mimosa/hook-state/sess_d7f1bf06-bc47-42a3-8df8-355feba8b2b7.continue.json deleted file mode 100644 index fb44e2b..0000000 --- a/.mimosa/hook-state/sess_d7f1bf06-bc47-42a3-8df8-355feba8b2b7.continue.json +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":"mimosa-stop-continuation/v1","generation":"mt2kt1js-61904-21c9faf2bc","used":false,"reportPersisted":false,"claim":null,"updatedAt":"2026-08-21T06:36:17.569Z"} \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..31c4f99 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + +## [0.1.0] - 2026-08-21 + +### Added + +- Repository baseline: per-tool layout, CI manifest validation, CODEOWNERS, issue/PR templates, security policy, AGPL-3.0 license. +- `ghl-workflow-exporter` — first tool in the set: exports every workflow in the current HighLevel sub-account as re-importable JSON, packaged as a deterministic ZIP for version control. + +[Unreleased]: https://github.com/legioncodeinc/ghl-toolset/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/legioncodeinc/ghl-toolset/releases/tag/v0.1.0 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2a48141 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,83 @@ +# CLAUDE.md + +Guidance for AI coding agents working in this repository. Read this before touching code. + +## What this repo is + +A **multi-tool set of Chrome extensions for GoHighLevel (HighLevel) sub-accounts** — one focused, standalone extension per facet of an account (workflows today; pipelines, calendars, custom values, etc. as the set grows). It is deliberately **not** a single bulk tool that migrates or dumps everything from a sub-account. Never propose merging tools or adding "modes" to an existing tool; a new facet means a new tool folder. + +The first tool is [`ghl-workflow-exporter/`](ghl-workflow-exporter/). The direction: a tool for every facet of the account that can be read through the app's own session. + +**Positioning:** the project is published for **R&D purposes only** — it rides on undocumented, internal HighLevel endpoints and is not affiliated with HighLevel. Keep that framing in user-facing docs; never imply production support or endorsement. + +## Commands + +```bash +node scripts/validate-manifests.mjs # the entire local gate + CI check +``` + +That is the only command. There is **no package.json, no dependency install, no build step, no lockfile** — every tool is plain ES modules loaded directly by Chrome from its folder. Do not introduce npm dependencies or a bundler without an explicit decision; the whole extension set is meant to stay auditable in one sitting. + +Manual verification loop: `chrome://extensions` → Developer mode → Load unpacked → the tool's folder → run it against a real (test) sub-account. Reload the tool's card after every edit. + +## Tool anatomy + +Every tool follows the same shape (the contract is also documented in the root README, which is the source of truth for the shared rules): + +``` +ghl--/ + manifest.json # MV3; permissions: activeTab, scripting, downloads — nothing else + popup.html/.css # UI shell + popup.js # orchestration: probes tab, drives export loop, builds files, downloads + agent.js # functions injected into the page's MAIN world + zip.js # dependency-free ZIP writer (STORE method, fixed 1980-01-01 timestamp) + icons/ + README.md # the tool's own docs +``` + +## Hard rules (violations are release blockers) + +1. **Injected functions must be entirely self-contained.** `agent.js` exports are passed to `chrome.scripting.executeScript({ world: 'MAIN', func })`, which serializes and re-parses them in the page. No imports, no closure variables, no references to module scope. Duplicating a helper (e.g. `findJwt`) inside an injected function is correct here; "DRYing it up" breaks the tool at runtime. + +2. **Never read, store, or transmit credentials.** Authentication is borrowed: prefer `window.SHELL_STORE.$http` (the app's axios instance whose interceptor attaches the session token). Fallback: locate the session JWT inside the page's Vuex auth state and use it *only within the page*, never exfiltrate it. This is the design promise of the whole toolset. + +3. **Read-only.** Every request a tool makes is a GET. Never add POST/PUT/DELETE calls to a sub-account. + +4. **Minimal permissions.** `activeTab`, `scripting`, `downloads` only. No `host_permissions`, no background service worker, no remote code. + +5. **Never hardcode a GHL host.** Tools run on white-labelled domains; the app origin is whatever tab is active. API endpoints point at `backend.leadconnectorhq.com`, which is host-agnostic. + +6. **Deterministic output.** Serialize with `stableJson` (recursively sorted keys) from `zip.js`, write ZIPs with the fixed DOS timestamp, and strip volatile fields before serialization. Known examples in workflow exports: `workflowData.fileUrl` (a *signed* Firebase URL carrying an access token — must never land in a repo; keep `filePath`), and `permissionMeta` (per-user access rights, not content). When exporting a new facet, identify its equivalent volatile/secret fields and strip them. + +7. **No secrets in any diff.** No tokens, session JWTs, signed URLs, or `.env` values ever get committed. `.gitignore` excludes `*.pem` (that would be a Web Store upload key) and `*.zip`. + +## GHL API facts (learned, currently load-bearing) + +- List workflows: `GET backend.leadconnectorhq.com/workflow/{locationId}/list` with `limit`/`skip` pagination (`limit: 200` per page; stop when `rows.length >= count`). +- Full workflow definition: `GET backend.leadconnectorhq.com/workflow/{locationId}/{workflowId}?includeTriggers=true` — `includeTriggers=true` is what swaps bare metadata for the `{ workflowData, triggers, dependentAssets }` shape the workflow builder's JSON import accepts. +- Raw-fetch fallback headers (when not using `$http`): `Authorization: Bearer `, `channel: APP`, `source: WEB_USER`, `Version: 2021-07-28`. +- Detecting the sub-account: `window.SHELL_STORE.state.locations.currentLocation` (`.id`/`._id`, `.name`), with the URL path `/location/` as a secondary source. +- HighLevel rate-limits bursts per location. Keep the courtesy pause between per-item requests (~120ms) and the retry-with-backoff pattern (3 attempts, 400ms·n) when adding new export loops. +- These are undocumented internal endpoints; they can change without notice. If an export starts failing, suspect the endpoint shape first. + +## Adding a new tool + +1. Create `ghl--/` at the repo root with the anatomy above (copy `ghl-workflow-exporter` as the starting skeleton). +2. Give it its own `manifest.json`, icons, and README following the existing tool's README structure (Install / Use / What comes out / How it works / Design notes / Limits). +3. Add a row to the tool table in the root `README.md` (move the facet out of the roadmap list). +4. `scripts/validate-manifests.mjs` picks up any `*/manifest.json` automatically — no registration needed. +5. Follow every hard rule above; new endpoints discovered go into the GHL API facts section here. + +## Documentation system + +`library/` is the structured docs tree (Library Schema v2 — see `library/README.md` and `library/knowledge/private/standards/documentation-framework.md`). Plans for new tools go in `library/requirements/` as PRDs; ADRs go in `library/knowledge/private/architecture/`. `library/notes/` is human-only — never read or write it. + +## Git conventions + +- Conventional Commits (`feat:`, `fix:`, `docs:`, …), enforced socially not by tooling. +- Branches off `main`, named `feat/` / `fix/`. +- Releases: bump the tool's `manifest.json` version, update `CHANGELOG.md` (Keep a Changelog format), tag `v`. + +## License + +AGPL-3.0 (see `LICENSE`). Keep tool READMEs and the repo consistent with it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a3372ef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing to GHL Toolset + +Thanks for putting in the work to improve this project. This document covers what a contributor needs before opening a pull request. + +## Before you start + +- Search open issues and pull requests before starting substantial work, so two people don't build the same tool. +- New facets belong in a new tool folder (`ghl--/`), not as modes bolted onto an existing tool — that split is the point of this repo. + +## Development setup + +```bash +git clone https://github.com/legioncodeinc/ghl-toolset.git +cd ghl-toolset +``` + +No dependencies to install and no build step: every tool is plain ES modules loaded directly by Chrome. See the [README](./README.md#development) for the per-tool dev loop (edit → reload in `chrome://extensions` → smoke-test). + +## Branching and commits + +- Branch off `main` for every change. Name branches `/` using the Conventional Commits type as the prefix (e.g. `feat/pipeline-exporter`, `fix/zip-checksum`). +- Write commit messages in [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format: `[optional scope]: `. Common types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`. +- Mark breaking changes with `!` before the colon (`feat!: ...`) or a `BREAKING CHANGE:` footer. +- Keep commits focused. If a commit conforms to more than one type, split it. + +## Before opening a pull request + +Run the full local gate: + +```bash +node scripts/validate-manifests.mjs +``` + +It must pass, and you must have loaded the affected tool via `chrome://extensions` and smoke-tested it against a real (ideally test) sub-account. CI runs the same validation. The pull request template asks you to confirm both. + +## Pull requests + +- Fill out every section of the [pull request template](./.github/PULL_REQUEST_TEMPLATE.md). +- One tool per PR. A PR that adds a new tool and reworks an existing one is two PRs. +- Link the issue it closes, if any. +- Expect review comments to land in the blocker / suggestion / nit taxonomy; only blockers must be resolved before merge. + +## Code review + +- CODEOWNERS are requested automatically for files they own; wait for their approval on those paths. +- Address review feedback with new commits rather than force-pushing over history mid-review, so reviewers can see what changed. + +## Reporting bugs and requesting features + +Use the [issue templates](./.github/ISSUE_TEMPLATE/). Do not report security vulnerabilities as public issues: see [SECURITY.md](./SECURITY.md). + +## Release process + +Releases are cut manually by a maintainer: bump the affected tool's `version` in its `manifest.json`, update [CHANGELOG.md](./CHANGELOG.md) (rename `Unreleased` to a dated version), commit, and tag `v`. There is no publishing pipeline — consumers pin to tags of this repo. + +## Questions + +Open a GitHub issue with your question; there is no chat channel yet. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be3f7b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..180dd0c --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +# GHL Toolset + +[![CI](https://img.shields.io/github/actions/workflow/status/legioncodeinc/ghl-toolset/ci.yml?branch=main&label=CI)](https://github.com/legioncodeinc/ghl-toolset/actions/workflows/ci.yml) +[![License](https://img.shields.io/github/license/legioncodeinc/ghl-toolset)](https://github.com/legioncodeinc/ghl-toolset/blob/main/LICENSE) + +A growing set of small Chrome extensions that export, back up, and version-control every facet of a GoHighLevel (HighLevel) sub-account — one focused tool per facet, for agencies and operators who keep their sub-account configuration in git. + +> **R&D purposes only.** This toolset is built and published for research and development purposes only. The tools talk to undocumented, internal HighLevel endpoints that can change or break without notice, and this project is not affiliated with or endorsed by HighLevel. Verify everything a tool produces before relying on it anywhere real. + +## What it is + +A toolbox, not a monolith. Each tool in this repo is a standalone Chrome extension living in its own folder, with its own manifest and its own README. You install only the tool you need, and each tool does exactly one job against the sub-account you already have open in a tab. + +This is a deliberate design choice: rather than one bulk extension that migrates or dumps everything from a sub-account in a single pass, the toolset covers the account facet by facet — workflows today, the other surfaces as focused tools as the set grows. Small tools are auditable in one sitting, fail independently, and can be pointed at exactly the data you want without touching the rest. + +The first tool is live: + +| Tool | Status | What it does | +| --- | --- | --- | +| [`ghl-workflow-exporter`](./ghl-workflow-exporter/) | Available | Exports every workflow in the current sub-account as re-importable JSON in a ZIP | +| `ghl-*` — pipelines, calendars, custom values, templates, funnels, forms, and the rest of the account | Planned | One focused tool per facet; see [Roadmap](#roadmap) | + +## Why it exists + +HighLevel has no built-in way to get a sub-account's configuration out as data you can diff, review, and restore. Agencies white-labelling GHL manage dozens of sub-accounts where a workflow edit is effectively irreversible — no history, no rollback, no code review. + +Bulk export tools that exist are all-or-nothing: they assume you want everything, in one format, through one flow. Real operations need the opposite — pull the pipelines for a migration audit, snapshot the workflows before a release, diff custom values between two sub-accounts. That is a per-facet job, so this repo builds a per-facet toolset. + +Every tool in the set follows the same contract, so behavior learned on one transfers to all of them: + +- **No credential handling.** Tools borrow the page's own authenticated HTTP client (`window.SHELL_STORE.$http`) inside the tab you are signed into. No token is read, stored, or transmitted by the extension. +- **Read-only.** Every request a tool makes is a GET. Nothing in the sub-account is modified. +- **Minimal permissions.** `activeTab`, `scripting`, `downloads`. No host permissions, no background service worker, no remote code. +- **Works on any GHL host.** White-labelled domains included — tools never hardcode `app.gohighlevel.com`. +- **Deterministic output.** Sorted keys, fixed timestamps, and volatile fields (signed URLs, per-user permission metadata) stripped, so an unchanged sub-account re-exports byte-identically and `git status` stays quiet. + +## Quick start + +About a minute, no build step: + +1. Open `chrome://extensions`. +2. Turn on **Developer mode** (top right). +3. **Load unpacked** → select the `ghl-workflow-exporter` folder from your clone of this repo. +4. Open a HighLevel sub-account tab, click the extension icon, hit **Export workflows**. + +## Install + +- Chrome or a Chromium browser (Edge, Brave, Arc) with extensions developer mode available +- A signed-in session on the HighLevel sub-account you want to work with + +```bash +git clone https://github.com/legioncodeinc/ghl-toolset.git +``` + +Then load the folder of the tool you want via `chrome://extensions` → **Load unpacked**, as in the quick start. Each tool's folder is self-contained; there is nothing to install or build. + +## Usage + +Each tool has its own README with its exact flow — start with [`ghl-workflow-exporter/README.md`](./ghl-workflow-exporter/README.md). The shared pattern across all of them: + +1. Open the sub-account tab you care about (the tool shows which sub-account it detected). +2. Click the tool's icon and run its action. +3. A ZIP lands in your downloads, shaped for unzipping straight into a git repo. + +The typical workflow-export session produces: + +```text +legendary-academy-/ +├── index.json # one entry per workflow: name, status, version, counts +├── snapshot.json # everything in one file, for diffing a release as a unit +├── README.md # provenance note, written into every export +└── workflows/ # one re-importable JSON file per workflow +``` + +## Roadmap + +The goal is a focused tool for every facet of a sub-account that can be read through the app's own session. Planned, in no committed order: + +- Pipelines and stages +- Calendars and appointment configuration +- Custom values and fields +- Email / SMS templates and media +- Funnels, websites, and blogs +- Forms and surveys +- Users and roles +- Whatever else the account exposes read-only through its own client + +Each lands as its own folder here when it ships — not as modes piled onto an existing tool. + +## Configuration + +None, by design. No environment variables, no API keys, no options page — see [.env.example](./.env.example) for the rationale. Authentication travels with your signed-in tab and never leaves it. + +## Architecture + +Every tool is the same three-part shape (MV3, no build step): + +```mermaid +flowchart LR + A[Popup UI
popup.html/js] -->|chrome.scripting
MAIN world| B[Injected agent fns
agent.js] + B -->|borrows| C[App's own HTTP client
window.SHELL_STORE.$http] + C -->|same-session GETs| D[GHL backend
backend.leadconnectorhq.com] + B -->|results| A + A -->|deterministic ZIP
zip.js| E[Download] +``` + +- `popup.js` — orchestration: probes the tab, drives the export loop, builds files. +- `agent.js` — functions injected into the page's MAIN world. They must be fully self-contained (no imports, no closures) because they are serialized and re-parsed in the page. +- `zip.js` — dependency-free ZIP writer with a fixed timestamp, so identical content produces an identical archive. + +## Development + +```bash +git clone https://github.com/legioncodeinc/ghl-toolset.git +cd ghl-toolset +node scripts/validate-manifests.mjs +``` + +To work on a tool: edit its folder, hit reload on its card in `chrome://extensions`, and re-run it against a test sub-account. To add a new tool: create a `ghl--/` folder with a `manifest.json`, popup, agent, and README following the contract above — the validator and the tool table in this README pick it up. See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full workflow and [CLAUDE.md](./CLAUDE.md) for the codebase conventions in depth. + +## Testing + +```bash +node scripts/validate-manifests.mjs +``` + +Passing looks like one `ok` line per tool manifest and exit code 0. This is the same check CI runs; beyond it, each tool is smoke-tested manually against a real (test) sub-account before release. + +## Deployment + +There is no pipeline to ship: tools are loaded unpacked straight from a checkout of this repo. Distributing via the Chrome Web Store is a future decision; until then, pin consumers to a tag of this repo. Exported data never transits any server — it goes from the browser tab to the ZIP on disk. + +## Contributing + +PRs welcome, especially for the roadmap facets above — one tool per PR, following the shared contract. See [CONTRIBUTING.md](./CONTRIBUTING.md) for branching, commit conventions, and the local gate before opening a PR. + +## License + +GHL Toolset is licensed under the [GNU Affero General Public License v3.0](./LICENSE). Each tool in the set carries the same license. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2ea3bf3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,49 @@ + + +# Security Policy + +## Supported Versions + +| Version | Supported | +| --- | --- | +| 0.x | :white_check_mark: | +| < 0.1 | :x: | + +## Reporting a Vulnerability + +Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests. + +Instead, report vulnerabilities through [GitHub private vulnerability reporting](https://github.com/legioncodeinc/ghl-toolset/security/advisories/new) for this repository. + +If private vulnerability reporting is unavailable or unusable for your report, email the maintainers at marioaldayuz315@gmail.com. + +When reporting a vulnerability, please include: + +- The affected version, tag, or commit SHA +- A description of the issue and why you believe it is security-sensitive +- Steps to reproduce, or a proof of concept +- Any relevant logs, payloads, or screenshots +- The potential impact +- Any suggested mitigations or fixes, if known + +## What to Expect + +You can expect an acknowledgment within 3 business days. + +After acknowledgment, we will assess the report and follow up with next steps. If the issue is confirmed, we will work on a fix and coordinate disclosure timing with the reporter when appropriate. + +If a report is validated, we may publish a GitHub Security Advisory once remediation details are ready to share publicly. + +## Scope + +In scope: the browser-extension code in this repository. + +Out of scope: HighLevel's own platform (report platform issues to HighLevel), and the contents of any sub-account you do not own or operate. Note that these tools intentionally never handle credentials — a report that a session token was mishandled would be treated as high severity precisely because the design promise is that it never happens. diff --git a/library/README.md b/library/README.md new file mode 100644 index 0000000..716781e --- /dev/null +++ b/library/README.md @@ -0,0 +1,38 @@ +--- +ai_description: | + This is the root of the repository's documentation library (schema v2). + You own everything under library/ except notes/, which is human-only. + Sub-trees: knowledge/ (public and private docs), requirements/ (product + work: PRDs), issues/ (reactive bug/incident work: IRDs), notes/ (junk + drawer, read-only to agents). + Schema reference: this README plus knowledge/private/standards/documentation-framework.md. +human_description: | + Root of this repository's documentation library. + - knowledge/: reference documentation split by audience (public vs private) + - requirements/: planned product work (PRDs) with backlog/in-work/completed lifecycle + - issues/: reactive bug and incident work (IRDs) with same lifecycle + - notes/: unstructured scratch space; only humans write here + If your organization keeps a shared library schema in another repository, + link it here instead of duplicating it. +--- + +# Library + +Documentation root for this repository. Schema version: **v2**. + +See [`knowledge/private/standards/documentation-framework.md`](knowledge/private/standards/documentation-framework.md) for the full specification. If your organization maintains a shared, cross-repository schema doc, link it here instead. + +## Top-level layout + +| Folder | What goes here | +|---|---| +| `knowledge/public/` | End-user / customer-facing docs: overviews, guides, FAQs | +| `knowledge/private/` | Internal engineering and business docs: ADRs, standards, domain knowledge | +| `requirements/` | Product and feature work: PRDs in backlog/in-work/completed | +| `issues/` | Reactive bug and incident work: IRDs in backlog/in-work/completed | +| `notes/` | Human-only scratch space | + +## What does NOT belong here + +- Brand assets → keep in a dedicated `brand/` or `assets/` location outside `library/` +- Any generated/derived documentation mirror your tooling produces (a wiki export, a rendered docs site) → treat as read-only output, never edit it directly; edit the source in `library/` instead diff --git a/library/issues/README.md b/library/issues/README.md new file mode 100644 index 0000000..6422926 --- /dev/null +++ b/library/issues/README.md @@ -0,0 +1,46 @@ +--- +ai_description: | + This folder contains all reactive bug and incident work (IRDs). + It is a PEER of requirements/, not nested under it. + Sub-folders: backlog/, in-work/, completed/ (same lifecycle as requirements/). + IRD folder naming: ird-<###>-/ + IRD numbers match the GitHub issue number for this repo. + Never invent IRD numbers; a GitHub issue must exist first. + IRDs are single-scope: one issue per IRD, no sub-IRDs. + Do NOT put PRDs here: those go in requirements/. +human_description: | + Reactive bug and incident work (IRDs), organized by lifecycle stage. + - backlog/: tracked issues with a fix plan, not yet started + - in-work/: issues currently being fixed + - completed/: resolved issues (move entire folder) + IRD numbers match GitHub issue numbers. Create an IRD only after the + GitHub issue exists. +--- + +# Issues + +Reactive bug and incident work (IRDs), organized by lifecycle state. + +## Sub-folders + +| Folder | State | Description | +|---|---|---| +| `backlog/` | Tracked | IRDs with a fix plan, not yet in progress | +| `in-work/` | Active | Issues currently being resolved | +| `completed/` | Resolved | Entire IRD folder moves here when the issue closes | + +## IRD folder structure + +``` +ird-042-stale-cache/ + ird-042-stale-cache-index.md single-scope fix plan + qa/ + ird-042-stale-cache-qa.md QA audit (written by your QA reviewer) +``` + +## Naming rules + +- Folder: `ird-<###>-/` +- Index: `ird-<###>--index.md` +- IRD number = GitHub issue number (never invented) +- No sub-IRDs (scope one issue per IRD) diff --git a/library/issues/backlog/README.md b/library/issues/backlog/README.md new file mode 100644 index 0000000..fffd590 --- /dev/null +++ b/library/issues/backlog/README.md @@ -0,0 +1,26 @@ +--- +ai_description: | + Contains IRD folders for tracked issues not yet in active fix work. + Create a new IRD here only AFTER the GitHub issue exists for this repo. + IRD folder: ird-<###>-/ where ### = GitHub issue number. + Must contain: ird-<###>--index.md (the fix plan) and qa/ folder. + IRDs are single-scope: do not add sub-IRDs. +human_description: | + IRDs planned but not yet in active fix work. Create IRDs here. + - Naming: ird-042-stale-cache/ with ird-042-stale-cache-index.md inside + - IRD number must match the GitHub issue number + - Create only after the GitHub issue exists + Move to in-work/ when fix work begins. +--- + +# Issues: Backlog + +Tracked issues with a fix plan, not yet in active resolution. + +## Creating a new IRD + +1. Confirm the GitHub issue number (e.g., #42). +2. Create `ird-042-/`. +3. Create `ird-042--index.md`: the single-scope fix plan. +4. Create `qa/` subfolder (empty; `your QA reviewer` writes here). +5. No sub-IRDs: keep scope to one issue. diff --git a/library/issues/completed/README.md b/library/issues/completed/README.md new file mode 100644 index 0000000..0227184 --- /dev/null +++ b/library/issues/completed/README.md @@ -0,0 +1,13 @@ +--- +ai_description: | + Resolved IRD folders. Entire ird-<###>-/ folders move here from + in-work/ when the corresponding GitHub issue is closed and verified. + Read-only after landing: do NOT edit or re-open IRDs here. +human_description: | + Resolved issue folders. Move entire ird-NNN-slug/ here from in-work/ + when the GitHub issue is closed and the fix is confirmed. Read-only. +--- + +# Issues: Completed + +Resolved IRD folders. Entire `ird-<###>-/` folders land here when the GitHub issue closes and the fix is confirmed. Do not edit files here after landing. diff --git a/library/issues/in-work/README.md b/library/issues/in-work/README.md new file mode 100644 index 0000000..f024719 --- /dev/null +++ b/library/issues/in-work/README.md @@ -0,0 +1,13 @@ +--- +ai_description: | + IRD folders actively being resolved. Mirror of requirements/in-work/ + but for issues. Move entire ird-<###>-/ folder from backlog/ + here when fix work begins, then to completed/ when the issue closes. +human_description: | + IRDs currently being fixed. Move folder from backlog/ here when work + starts, and to completed/ when the GitHub issue is closed. +--- + +# Issues: In Work + +IRDs currently being resolved. Move from `backlog/` → here when fix work starts, then `completed/` when the GitHub issue closes. diff --git a/library/knowledge/README.md b/library/knowledge/README.md new file mode 100644 index 0000000..37bb88b --- /dev/null +++ b/library/knowledge/README.md @@ -0,0 +1,34 @@ +--- +ai_description: | + This folder contains all reference documentation for this repository, + split by intended audience: public/ for end-users, private/ for internal + team and AI agents. When filing a new doc, default to private/. Promote + to public/ only when the content is intentionally customer-facing. + Allowed writes: knowledge/public//.md and + knowledge/private//.md. ADRs always go in + knowledge/private/architecture/ADR--.md. + Never write to knowledge/ itself (write to the sub-folders). +human_description: | + Reference documentation split by audience. + - public/: docs that will eventually be surfaced to customers or published + - private/: internal engineering, architecture, business, and strategy docs + When adding a new doc, pick the right subdomain folder inside public/ or + private/. If the domain doesn't exist yet, create it. +--- + +# Knowledge + +Reference documentation for this repository, organized by audience. + +## Sub-folders + +| Folder | Audience | Typical content | +|---|---|---| +| `public/` | End-users, customers, external | Overviews, user guides, FAQs | +| `private/` | Internal team + AI agents | ADRs, standards, architecture, domain engineering docs | + +## Decision rule: public vs private + +> "Would I publish this on a help center or product docs site?" + +Yes → `public/`. No → `private/`. When in doubt, `private/`. diff --git a/library/knowledge/private/README.md b/library/knowledge/private/README.md new file mode 100644 index 0000000..3f0ff91 --- /dev/null +++ b/library/knowledge/private/README.md @@ -0,0 +1,40 @@ +--- +ai_description: | + This folder contains internal engineering and business documentation. + ADRs MUST live in architecture/ADR--.md. + Engineering standards MUST live in standards/documentation-framework.md. + Other domain folders (/) are repo-specific and may be created as + needed (ai/, auth/, data/, frontend/, infrastructure/, integrations/, + marketing/, operations/, personas/, reporting/, roadmap/, scanners/, + security/, strategy/, etc.). + Do NOT file customer-facing content here (that goes in knowledge/public/). + Write path: library/knowledge/private//.md. +human_description: | + Internal engineering and business documentation. + - architecture/: Architecture Decision Records (ADRs) + - standards/: Documentation framework and coding standards + - /: Any repo-specific knowledge domain (ai/, auth/, data/, etc.) + Default landing zone for any doc that does not need to be customer-facing. + When creating a new domain folder, add a README.md explaining what belongs. +--- + +# Knowledge: Private + +Internal documentation for engineers, product, and AI agents. + +## Required sub-folders (always present) + +| Folder | Contents | +|---|---| +| `architecture/` | ADRs: `ADR--.md`. Locked decisions with context, alternatives, consequences. | +| `standards/` | `documentation-framework.md` and any repo-specific writing rules. | + +## Optional domain folders + +Create any of these as needed: `ai/`, `auth/`, `data/`, `frontend/`, `infrastructure/`, `integrations/`, `marketing/`, `operations/`, `personas/`, `reporting/`, `roadmap/`, `scanners/`, `security/`, `strategy/`, `reference/`, `-ux-ui/`. + +## What does NOT belong here + +- Customer-facing content (put in `knowledge/public/`) +- PRDs or IRDs (put in `requirements/` or `issues/`) +- Brand assets (keep those in a dedicated `brand/` or `assets/` location outside `library/`) diff --git a/library/knowledge/private/standards/documentation-framework.md b/library/knowledge/private/standards/documentation-framework.md new file mode 100644 index 0000000..3f27181 --- /dev/null +++ b/library/knowledge/private/standards/documentation-framework.md @@ -0,0 +1,160 @@ +--- +ai_description: | + Canonical Library Schema v2 for a repository. Use this file to decide where + PRDs, IRDs, knowledge, ADRs, reports, and human notes belong. Lifecycle is + represented by folder location. Never read or write library/notes/ as an AI. +human_description: | + The rules for organizing repository planning and knowledge. Read this when + you are unsure where a document belongs or when a PRD or IRD changes state. +--- + +# Documentation Framework + +Library Schema v2 gives every important document one obvious home. Think of it like labeled shelves: product plans, bug plans, reference knowledge, and scratch notes stay separate so people and AI tools can find the right source without guessing. + +Version: **2.0** +Updated: **August 2026** + +## Top-level map + +```text +library/ + knowledge/ + public/ + private/ + requirements/ + backlog/ + in-work/ + completed/ + reports/ + issues/ + backlog/ + in-work/ + completed/ + notes/ +``` + +| Folder | What belongs there | What does not belong there | +| --- | --- | --- | +| `knowledge/public/` | End-user guides, public overviews, and FAQs | Private architecture, security details, or product plans | +| `knowledge/private/` | Architecture, ADRs, engineering standards, and internal explanations | Active product requirements or bug-fix plans | +| `requirements/` | Product and feature work written as PRDs | Reactive bugs and incidents | +| `issues/` | Reactive work written as IRDs and tied to GitHub issue numbers | Planned features | +| `notes/` | Human-only scratch material | Anything authoritative or anything an AI agent should read | + +## Knowledge documents + +Knowledge files explain what is true now. They are reference material, not promises about future work. + +Use `knowledge/public/` for information you would be comfortable publishing to customers. Use `knowledge/private/` for internal engineering, business, security, or architecture material. When unsure, start in `private/`. + +Architecture Decision Records live at: + +```text +library/knowledge/private/architecture/ADR--.md +``` + +An ADR records one important decision, its context, alternatives, and consequences. It does not replace a PRD. + +## Product Requirements Documents + +A PRD is a build blueprint and an inspection checklist for planned product work. New PRDs always begin in `requirements/backlog/`. + +```text +library/requirements/backlog/prd-007-user-export/ + prd-007-user-export-index.md + prd-007a-user-export-backend.md + prd-007b-user-export-interface.md + qa/ +``` + +Rules: + +1. Use the next unused three-digit repository-local number. +2. Keep the index and every sub-PRD inside one `prd--/` folder. +3. Write testable acceptance criteria. A reviewer must be able to answer pass or fail from evidence. +4. Create the PRD in `backlog/`, move the entire folder to `in-work/` when implementation begins, then move it to `completed/` only after the work ships and verification passes. +5. Treat `completed/` as read-only history. Correct a shipped requirement with a new PRD or an explicitly documented amendment process. + +## Issue Requirements Documents + +An IRD is a focused fix plan for a bug, incident, or other reactive issue. Its number matches the GitHub issue number. + +```text +library/issues/backlog/ird-042-stale-cache/ + ird-042-stale-cache-index.md + qa/ +``` + +Rules: + +1. Create the GitHub issue first. +2. Use that issue number in the IRD folder and index filename. +3. Keep an IRD single-scope. Do not create sub-IRDs. +4. Move the entire folder from `backlog/` to `in-work/` when the fix begins, then to `completed/` after the issue is closed and the fix is verified. + +## Reports and QA evidence + +Evidence tied to a PRD or IRD stays inside that document's `qa/` folder. This keeps the plan and proof together. + +Routine repository-wide reports that are not tied to one PRD or IRD live in: + +```text +library/requirements/reports/--report.md +``` + +Examples include a periodic security scan, repository-health audit, or general QA sweep. + +## Human notes + +`library/notes/` is a human-only scratch area. AI agents must not read it, write it, summarize it, or cite it. Notes are not authoritative. When a note becomes durable knowledge, a human moves or rewrites it into the appropriate `knowledge/` path. + +## Document frontmatter + +Every seeded folder README uses two descriptions: + +- `ai_description` tells an AI what it may do in the folder. +- `human_description` gives a quick plain-language explanation. + +Content documents may add fields such as status, version, owner, and updated date when the team's workflow requires them. Do not invent metadata that nobody maintains. + +## Naming rules + +- Use lowercase kebab-case for folders and ordinary knowledge files. +- Use `prd-<###>-` for PRD folders. +- Use `ird--` for IRD folders. +- Use `ADR--.md` for ADRs. +- Use ISO dates (`YYYY-MM-DD`) in report filenames. +- Keep filenames stable after other documents link to them. + +## Choosing the right document + +| If you need to... | Create or update... | +| --- | --- | +| Plan a new feature | PRD under `requirements/backlog/` | +| Fix a tracked bug or incident | IRD under `issues/backlog/` | +| Record why an architecture choice was made | ADR under `knowledge/private/architecture/` | +| Explain how the system works now | Knowledge document | +| Capture temporary personal thoughts | Human note under `notes/` | +| Record independent proof for one plan | That PRD or IRD's `qa/` folder | +| Record a repository-wide audit | `requirements/reports/` | + +## Lifecycle gate + +Folder location is the lifecycle status: + +```text +backlog -> in-work -> completed +``` + +Do not copy a folder to the next state and leave the original behind. Move the entire folder. Do not mark work complete because code exists; move it only after its acceptance criteria are verified and required security and quality checks pass. + +## Bootstrap checklist + +After copying this example into a real repository: + +1. Replace project-specific placeholders with facts from the target repository. +2. Confirm the public/private knowledge boundary with the team. +3. Confirm who reviews security and quality evidence. +4. Link the target repository's contribution and security policies. +5. Create the first PRD or IRD only when real work exists. Do not fill the library with fake sample plans. diff --git a/library/knowledge/public/README.md b/library/knowledge/public/README.md new file mode 100644 index 0000000..57b8e4c --- /dev/null +++ b/library/knowledge/public/README.md @@ -0,0 +1,39 @@ +--- +ai_description: | + This folder contains customer-facing / end-user documentation. + Approved sub-folders: overview/, guides/, faqs/, and any domain + folder explicitly designated public by the team. + Do NOT file internal engineering docs, ADRs, pricing strategy, or + security-sensitive material here. + Write path: library/knowledge/public//.md. + All files here may eventually be surfaced in the public help center + (Phase 2). Mark each doc with the standard knowledge-base header: + Category / Version / Date / Status. +human_description: | + Customer-facing documentation. Content here may be published externally. + - overview/: what this product is, glossary, elevator pitch + - guides/: how-to guides written for users, not developers + - faqs/: frequently asked questions + Only add content here that you are comfortable sharing publicly. + Internal notes, pricing strategy, and architecture docs belong in + knowledge/private/ instead. +--- + +# Knowledge: Public + +Customer-facing documentation. Anything in this folder may eventually be published. + +## Approved sub-folders + +| Folder | Contents | +|---|---| +| `overview/` | What this product is, glossary, elevator pitch, high-level FAQs | +| `guides/` | Step-by-step user guides (written for customers, not developers) | +| `faqs/` | Frequently asked questions from customers | + +## What does NOT belong here + +- Internal architecture docs or ADRs +- Pricing strategy or competitive analysis +- Engineering standards +- Anything you would not want a customer to read diff --git a/library/notes/README.md b/library/notes/README.md new file mode 100644 index 0000000..51efb4a --- /dev/null +++ b/library/notes/README.md @@ -0,0 +1,21 @@ +--- +ai_description: | + HUMAN-ONLY junk drawer. Agents MUST NOT read from, write to, create + files in, or reference files in this folder for any purpose. + If you want to capture something persistent, write to + library/knowledge/private//.md instead. + This invariant is absolute and has no exceptions. +human_description: | + Unstructured scratch space for humans. Agents do not touch this folder. + Put anything here: rough notes, links, half-formed ideas, meeting notes. + Nothing in notes/ is authoritative or maintained. + For persistent reference, move content to knowledge/private/ when it matures. +--- + +# Notes + +Human-only scratch space. Agents never read or write here. + +Put rough notes, links, and half-formed ideas here. Nothing here is authoritative. + +When a note matures into reference material, move it to `knowledge/private//`. diff --git a/library/requirements/README.md b/library/requirements/README.md new file mode 100644 index 0000000..14384de --- /dev/null +++ b/library/requirements/README.md @@ -0,0 +1,51 @@ +--- +ai_description: | + This folder contains all planned product and feature work (PRDs). + Sub-folders: backlog/ (queued, not started), in-work/ (actively + being implemented), completed/ (shipped), reports/ (routine code scans). + Lifecycle = location: move entire PRD folders between states. + PRD folder naming: prd-<###>-/ + PRD numbers are repo-local sequential. Take max+1 from all prd-* folders + across backlog/, in-work/, and completed/. + Never write PRD content outside of a prd-<###>-/ folder. + Do NOT put IRDs here: those go in issues/ (peer of requirements/). +human_description: | + Product and feature work (PRDs) organized by lifecycle stage. + - backlog/: planned work not yet started + - in-work/: currently being implemented + - completed/: shipped work (move entire folder here when done) + - reports/: routine code-scan and QA reports not tied to a specific PRD + To start a new PRD: create prd-<###>-/ in backlog/ with an index.md. + To move lifecycle: move the entire prd-<###>-/ folder. +--- + +# Requirements + +Product and feature work, organized by lifecycle state. + +## Sub-folders + +| Folder | State | Description | +|---|---|---| +| `backlog/` | Queued | PRDs planned but not yet started | +| `in-work/` | Active | PRDs currently being implemented | +| `completed/` | Shipped | Entire PRD folder moves here when work ships | +| `reports/` | Evergreen | Routine code-scan and QA reports not tied to a PRD | + +## PRD folder structure + +``` +prd-007-user-export/ + prd-007-user-export-index.md module overview + feature list + prd-007a-user-export-backend.md sub-feature a + prd-007b-user-export-ui.md sub-feature b + qa/ + prd-007-user-export-qa.md QA audit (written by your QA reviewer) +``` + +## Naming + +- Folder: `prd-<###>-/` (3-digit zero-padded) +- Index: `prd-<###>--index.md` +- Sub-PRDs: `prd-<###>--.md` +- PRD numbers are **repo-local sequential**, not GitHub issue numbers. diff --git a/library/requirements/backlog/README.md b/library/requirements/backlog/README.md new file mode 100644 index 0000000..fd6094c --- /dev/null +++ b/library/requirements/backlog/README.md @@ -0,0 +1,30 @@ +--- +ai_description: | + Contains PRD folders planned but not yet started. This is where a new + PRD folder gets created on "write a PRD for X". + PRD folder naming: prd-<###>-/ (3-digit zero-padded). + PRD number: take max+1 from all prd-* folders across backlog/, + in-work/, and completed/ in this repo. + Each PRD folder must contain: prd-<###>--index.md (always), + prd-<###>--.md (one per sub-feature, optional), + qa/ subfolder (empty on creation; your QA reviewer writes QA reports here). + Move entire folder to in-work/ when implementation begins. +human_description: | + PRDs planned but not yet started. Create new PRDs here. + - Naming: prd-007-feature-name/ with prd-007-feature-name-index.md inside + - Sub-features: prd-007a-feature-name-backend.md, prd-007b-feature-name-ui.md + - QA folder: qa/prd-007-feature-name-qa.md (created by your QA reviewer) + Move to in-work/ when implementation begins. +--- + +# Requirements: Backlog + +Planned PRDs not yet in implementation. All new PRD folders are created here. + +## Creating a new PRD + +1. Find `max_n` across `backlog/prd-*/`, `in-work/prd-*/`, `completed/prd-*/`. +2. Create `prd--/`. +3. Create `prd-<###>--index.md` (module overview + feature list). +4. Create `qa/` subfolder (empty; `your QA reviewer` writes reports here). +5. Add sub-PRDs `prd-<###>a--.md` etc. as needed. diff --git a/library/requirements/completed/README.md b/library/requirements/completed/README.md new file mode 100644 index 0000000..f8039ce --- /dev/null +++ b/library/requirements/completed/README.md @@ -0,0 +1,14 @@ +--- +ai_description: | + Contains shipped PRD folders. Entire prd-<###>-/ folders move + here from in-work/ when the work ships. Read-only after landing here: + do NOT edit or move files out of completed/. + The PRD index, sub-PRDs, and qa/ sub-folder all travel together. +human_description: | + Shipped PRD folders. Move entire prd-NNN-slug/ here from in-work/ when + the feature ships. Read-only: do not edit completed PRDs. +--- + +# Requirements: Completed + +Shipped PRD folders. Entire `prd-<###>-/` folders land here after the work ships and is confirmed in production. Do not edit files here after landing. diff --git a/library/requirements/in-work/README.md b/library/requirements/in-work/README.md new file mode 100644 index 0000000..a476953 --- /dev/null +++ b/library/requirements/in-work/README.md @@ -0,0 +1,19 @@ +--- +ai_description: | + Contains PRD folders actively being implemented. A folder lives here + from the moment implementation begins until the work ships. + Structure inside is identical to backlog/: prd-<###>-/index + sub-PRDs + qa/. + To promote: move entire prd-<###>-/ folder to completed/. + Do NOT create new PRD folders here; create them in backlog/ first, + then move to in-work/ when implementation starts. +human_description: | + PRDs currently being implemented. Do not start new PRDs here: + create them in backlog/ and move the folder here when work begins. + When work ships, move the entire folder to completed/. +--- + +# Requirements: In Work + +PRDs currently being implemented. Folder location = lifecycle state. + +Move an entire `prd-<###>-/` folder **from** `backlog/` → here when implementation starts, and **from** here → `completed/` when the work ships. diff --git a/library/requirements/reports/2026-08-21-security-audit.md b/library/requirements/reports/2026-08-21-security-audit.md new file mode 100644 index 0000000..43faa46 --- /dev/null +++ b/library/requirements/reports/2026-08-21-security-audit.md @@ -0,0 +1,62 @@ +# Security audit - 2026-08-21 - repo-init baseline (pre-first-PR) + +## Executive summary + +- Scope: full working tree at commit `ae90a0e` + uncommitted init files — `ghl-workflow-exporter/` (all JS/HTML/JSON), `scripts/validate-manifests.mjs`, `.github/` (both workflows, dependabot, CODEOWNERS, templates), all root docs, and full git history secret scan. +- Coverage: **reduced** — this skill's researched stack (SvelteKit/Neon/Drizzle/WorkOS/Stripe/Vercel/Doppler/GHL-webhook surfaces) is absent from this repo. The audited surface is a Chrome MV3 extension toolset. Secrets, HTML-sink, eval, storage, supply-chain, git-history, and GitHub-workflow checks were run per the skill's sweeps and are grounded; **Chrome-extension platform rules** (MV3 remote-code prohibitions, permission scoping) were checked from first principles, not from this skill's research archive. +- Findings: 0 Critical, 0 High, 0 Medium, 1 Low +- Ship Gate status: **cleared to proceed to quality-stinger** + +## Surface coverage checklist + +### Chrome extension attack surface (maps to skill's "SvelteKit attack surface") +None detected. `manifest.json` requests exactly `activeTab, scripting, downloads` — no `host_permissions`, `externally_connectable`, `web_accessible_resources`, or CSP overrides. All `chrome.scripting.executeScript` calls inject locally defined functions (`agent.js` exports), never remote strings. No `eval`/`new Function`, no `innerHTML`/`document.write` sinks (popup uses `textContent` exclusively). No `localStorage`/`sessionStorage`/`indexedDB` — nothing persists. Download filename is built through `slug()` (`popup.js:20-28`) which reduces to `[a-z0-9-]` — no path traversal via sub-account name. + +### Credential handling (maps to authorization/tenancy — the AI-dominant failure class) +None detected. `window.SHELL_STORE.$http` borrowing path never touches a token (`agent.js:53-56`). The fallback JWT locator (`agent.js:57-69,116-128`) finds the session token in page state and uses it only inside a page-context `fetch` to `backend.leadconnectorhq.com`; it never crosses the extension boundary, is never logged, never stored. `sanitize()` (`popup.js:160-165`) strips `workflowData.fileUrl` (signed Firebase URL with access token) and `permissionMeta` before serialization — verified in code, matching the documented claim. + +### Secrets and environment +None detected. No placeholder-shaped or real secret literals (LLM-common values swept). No `.env` file tracked now or anywhere in git history. `.env.example` contains only explanatory comments. Zero secret-shaped strings (`sk-`, `ghp_`, `AKIA`, `AIza`, JWT patterns) across all history (`git log --all -p` scan: 0 hits). + +### Webhooks and third-party intake +N/A — no webhook handlers exist in this repo. + +### Dependencies and supply chain +None detected. No `package.json`, no lockfile, no dependencies at all — supply chain is closed by construction. CI runs no package installation (only `node scripts/validate-manifests.mjs`). + +### CI / GitHub workflows +None detected. All `uses:` actions pinned to full commit SHAs (checkout v7.0.1, setup-node v7.0.0, codeql-action v3.30.1). No `pull_request_target` trigger (eliminates the secrets-exposure-to-fork-code class). No `run:` step interpolates untrusted `github.*` context into a shell. Both workflows declare least-privilege `permissions: contents: read` at workflow and job level (CodeQL analyze job adds only `security-events: write`, `actions: read`). Dependabot covers `github-actions` so pinned SHAs stay current. + +### PII and logging hygiene +None detected. Zero `console.*` calls in tool code — nothing is logged, sensitive or otherwise. Exported data stays on the user's disk. + +### AI-generated code patterns +None detected. No hardcoded credentials. No dependency names to slopsquat (zero deps). Documented security claims (README, CLAUDE.md, tool README) were verified against actual code rather than assumed — the `fileUrl`/`permissionMeta` stripping and read-only-GET claims all match implementation (`agent.js` exports are GET-only; no POST/PUT/DELETE exists). + +## Findings detail + +### [LOW] Personal email address published in SECURITY.md + +- **Location:** `SECURITY.md:27` +- **Surface:** Secrets and environment / public-doc hygiene +- **Description:** `marioaldayuz315@gmail.com` is the vulnerability-report fallback contact. Not a secret (already public in commit metadata), but publishing it in a SECURITY.md invites spam harvesting. +- **Evidence:** `If private vulnerability reporting is unavailable or unusable for your report, email the maintainers at marioaldayuz315@gmail.com.` +- **Remediation:** Optional: replace with a filtered alias (e.g. security@ on a owned domain) when one exists. GitHub private vulnerability reporting is already the primary channel. +- **Status:** documented for follow-up (human decision — no alias known to exist) + +## Remediation summary + +| Severity | Count | Fixed this session | Documented only | +|---|---|---|---| +| Critical | 0 | 0 | 0 | +| High | 0 | 0 | 0 | +| Medium | 0 | 0 | 0 | +| Low | 1 | 0 | 1 | + +## Re-evaluation + +N/A — no Medium-or-above findings required fixes. + +## Next step + +Cleared to invoke quality-stinger. diff --git a/library/requirements/reports/README.md b/library/requirements/reports/README.md new file mode 100644 index 0000000..42537b2 --- /dev/null +++ b/library/requirements/reports/README.md @@ -0,0 +1,31 @@ +--- +ai_description: | + Contains routine code-scan, QA, and security reports NOT tied to any + specific PRD or IRD. Naming: --report.md. + Authored by your QA reviewer or your security reviewer. + Do NOT put per-PRD QA reports here: those go in prd-<###>-/qa/. + Do NOT put IRD QA reports here: those go in ird-<###>-/qa/. +human_description: | + Routine scan and audit reports not tied to a specific PRD or IRD. + Examples: weekly security scans, periodic QA sweeps, dependency audits. + Naming: 2026-05-23-security-scan.md, 2026-06-01-qa-sweep.md. + Per-PRD QA reports live inside the PRD folder's qa/ subfolder instead. +--- + +# Requirements: Reports + +Routine code-scan and audit reports not tied to any specific PRD. + +## Naming + +`--report.md` + +Examples: +- `2026-05-23-security-scan.md` +- `2026-06-01-qa-sweep.md` +- `2026-06-15-dependency-audit.md` + +## What does NOT belong here + +- QA reports for a specific PRD → `requirements/backlog/prd-<###>-/qa/` +- QA reports for a specific IRD → `issues/backlog/ird-<###>-/qa/` diff --git a/library/requirements/reports/repo-init/2026-08-21-qa-report.md b/library/requirements/reports/repo-init/2026-08-21-qa-report.md new file mode 100644 index 0000000..92c85a2 --- /dev/null +++ b/library/requirements/reports/repo-init/2026-08-21-qa-report.md @@ -0,0 +1,81 @@ +# QA Report: repo-init baseline (get-started-stinger + /init) + +**Plan document:** get-started-stinger skill contract (`C:\Users\mario\.agents\skills\get-started-stinger`) + in-session user requirements (multi-tool positioning, R&D statement, AGPL-3.0, CLAUDE.md) +**Audit date:** 2026-08-21 +**Base branch:** `main` @ `ae90a0e` +**Head:** working tree (pre-branch) +**Auditor:** quality-worker-bee + +## Summary + +Pass. The initialization satisfies every item in the skill's copy-out contract and all four user requirements; the tool move committed in `ae90a0e` is verified content-identical (git rename detection: 0 changed lines on all 10 moved files). Correctness was verified by execution, not inspection: the manifest validator rejects a bad manifest with exit 1, all three `.github/` YAML files parse, and `.gitignore` negations behave as documented. Three non-blocking Suggestions follow. + +## Scorecard + +| Category | Status | Notes | +|---------------|--------|-------| +| Completeness | ✅ | All 14 traceable plan items implemented; no clobbered files | +| Correctness | ✅ | Validator failure-path, YAML validity, and gitignore behavior all verified by execution | +| Alignment | ✅ | Section order per README template; tool folder conventions match CLAUDE.md contract | +| Gaps | ✅ | No invented CI commands; no unresolved `{placeholder}` tokens (one found and fixed in-session) | +| Detrimental | ✅ | Tool code moved byte-identical; no regressions; no console.log/dead code introduced | + +## Critical Issues (must fix) + +None. + +## Warnings (should fix) + +None. + +## Suggestions (consider improving) + +- [ ] **Cut the `v0.1.0` tag with or right after this PR**, `CHANGELOG.md:37-38` — the `[Unreleased]`/`[0.1.0]` compare links point at `v0.1.0`, which does not exist yet, so they 404 until the tag is cut. + +- [ ] **Replace org-level CODEOWNERS with named owners when decided**, `.github/CODEOWNERS:16-21` — both the default and governance roles currently resolve to `@legioncodeinc`; flagged as a human decision in the init verification report. + +- [ ] **Validator suppresses `ok` lines after the first failure**, `scripts/validate-manifests.mjs:69` — `if (!failed) console.log(...)` is file-order-dependent cosmetics; valid manifests validated after a failing one don't print their `ok` line (exit code and per-file FAIL lines are unaffected). Harmless at current size. + +## Plan Item Traceability + +| # | Plan Requirement | Status | Implementation Location | Notes | +|--------|------------------|--------|-------------------------|-------| +| 1 | `.gitignore` copied + project-specific extension | ✅ | `.gitignore:1-84` | `.mimosa/` ignore verified via `git check-ignore`; `!.env.example` negation verified | +| 2 | `.editorconfig`, `.nvmrc`, `.env.example` created | ✅ | each file | `.nvmrc` = 22; `.env.example` adapted to "no env vars by design" | +| 3 | `.github/` full set (CI, CodeQL, dependabot, CODEOWNERS, 3 templates) | ✅ | `.github/` | All 3 YAML files parse (python yaml); all actions SHA-pinned | +| 4 | CI commands real, not invented | ✅ | `.github/workflows/ci.yml:39-41` | Single `validate` job running `scripts/validate-manifests.mjs`; lint/typecheck/test omitted per "do not invent commands" rule, noted in workflow comment | +| 5 | README canonical order + multi-tool positioning (NOT bulk tool) | ✅ | `README.md` | "A toolbox, not a monolith" + explicit design-choice paragraph; tool index + roadmap | +| 6 | R&D-purposes-only statement (user) | ✅ | `README.md` blockquote + `CLAUDE.md` positioning note | Placed directly under one-liner, before all content sections | +| 7 | AGPL-3.0 license (user) | ✅ | `LICENSE` (661 lines, canonical gnu.org text), badge + section in README | | +| 8 | CONTRIBUTING / SECURITY / CHANGELOG | ✅ | each file | All placeholders resolved; `{branch_prefix}` token found in QA sweep and fixed in-session | +| 9 | `library/` schema tree | ✅ | `library/` (15 files) | Copied verbatim per template | +| 10 | `/init` CLAUDE.md (user) | ✅ | `CLAUDE.md` | API facts cross-checked against `agent.js`/`popup.js` line-by-line (endpoints, limit 200, includeTriggers, headers, 120ms pause, 3×/400ms·n retry) — all match | +| 11 | Idempotency: existing files not clobbered | ✅ | `ghl-workflow-exporter/README.md` untouched | Evaluated file-by-file, per skill rule | +| 12 | `.mimosa/` untracked | ✅ | staged deletion + `.gitignore:73-74` | Was committed in `ae90a0e`; removal staged, files kept on disk | +| 13 | Verification report produced before "done" | ✅ | delivered in-session (three-part report) | | +| 14 | Ship Gate: security before quality | ✅ | `library/requirements/reports/2026-08-21-security-audit.md` | Security ran first, cleared with 0 Critical/High/Medium | + +## Files Changed + +- `.editorconfig` (A), editor defaults from template +- `.env.example` (A), documents that no env vars exist by design +- `.github/CODEOWNERS` (A), default + governance ownership to @legioncodeinc +- `.github/ISSUE_TEMPLATE/bug_report.md` (A), bug template with GHL-specific environment fields +- `.github/ISSUE_TEMPLATE/feature_request.md` (A), feature template +- `.github/PULL_REQUEST_TEMPLATE.md` (A), PR template with validate + smoke-test checklist +- `.github/dependabot.yml` (A), github-actions ecosystem; npm entry pre-written as comment +- `.github/workflows/ci.yml` (A), SHA-pinned manifest validation on push/PR to main +- `.github/workflows/codeql.yml` (A), weekly javascript-typescript scan +- `.gitignore` (A), Node baseline + .mimosa/, *.zip, *.crx, *.pem +- `.nvmrc` (A), Node 22 +- `CHANGELOG.md` (A), seeded 0.1.0 (2026-08-21) +- `CLAUDE.md` (A), codebase guide for AI agents (/init) +- `CONTRIBUTING.md` (A), conventions + local gate +- `LICENSE` (A), AGPL-3.0 canonical text +- `README.md` (A), multi-tool positioning, tool index, contract, roadmap, R&D notice +- `SECURITY.md` (A), private-vulnerability-reporting policy +- `library/` (A, 15 files), Library Schema v2 tree +- `library/requirements/reports/2026-08-21-security-audit.md` (A), Ship Gate 1 report +- `library/requirements/reports/repo-init/2026-08-21-qa-report.md` (A), this report +- `scripts/validate-manifests.mjs` (A), zero-dependency manifest validator +- `.mimosa/hook-state/sess_*.continue.json` (D), untracked local tool state diff --git a/library/requirements/reports/repo-init/2026-08-21-repo-health-audit.md b/library/requirements/reports/repo-init/2026-08-21-repo-health-audit.md new file mode 100644 index 0000000..4c9b694 --- /dev/null +++ b/library/requirements/reports/repo-init/2026-08-21-repo-health-audit.md @@ -0,0 +1,155 @@ +# GitHub Repo Health Audit Report + +**Repository:** legioncodeinc/ghl-toolset +**Audit date:** 2026-08-21 +**Data collection mode:** Local clone + gh CLI +**Coverage gaps:** None — branch protection, rulesets, and security settings verified via API. Note: docs/template files exist on the audited branch (this PR), not yet on `origin/main`; scores reflect branch state. +**Audited by:** github-repo-health-worker-bee + +--- + +## Overall Score: 67/100 + +| # | Dimension | Raw Score | Weight | Weighted | +|---|---|---|---|---| +| 1 | Branch protection / rulesets | 7/10 | 20% | 1.40 | +| 2 | Commit quality (Conventional Commits) | 3/10 | 15% | 0.45 | +| 3 | CODEOWNERS coverage | 8/10 | 15% | 1.20 | +| 4 | CI workflow density | 7/10 | 15% | 1.05 | +| 5 | Docs presence | 8/10 | 10% | 0.80 | +| 6 | Repository settings | 4/10 | 10% | 0.40 | +| 7 | Issue/PR templates | 10/10 | 8% | 0.80 | +| 8 | .gitignore coverage | 9/10 | 7% | 0.63 | +| | **Total** | | | **6.73** | + +--- + +## Branching Strategy (qualitative) + +**Observed strategy:** GitHub Flow (single long-lived `main`, PR-based changes) — now documented in CONTRIBUTING.md +**Documented:** Yes — CONTRIBUTING.md "Branching and commits" +**Branch inventory:** 1 branch (`main`), 0 open PR branches at audit time +**Assessment:** Correct strategy for a small toolset repo; the convention is documented going forward. + +--- + +## Branch Protection / Rulesets (Score: 7/10) + +**Enforcement mechanism:** GitHub Rulesets ("Main Protection", id 21131540, active, targets `~DEFAULT_BRANCH`) + +| Rule | Status | Notes | +|---|---|---| +| `pull_request` required | ✅ Enabled | `require_code_owner_review: true`, `required_review_thread_resolution: true`, `require_extra_approval_for_unattributed_changes: true` | +| `required_status_checks` | ❌ | CI is not required to pass before merge — a red CI can be merged over | +| `non_fast_forward` | ✅ | Force pushes blocked | +| `deletion` | ✅ | Branch deletion blocked | +| `dismiss_stale_reviews` | ⚠️ | `false` — new pushes don't dismiss approvals | +| `required_linear_history` | ❌ | Not set | +| `required_signatures` | ❌ | Not set | +| Approving review count | ⚠️ | 0 (code-owner requirement is the only approval gate) | + +**Operational risk (verify on first PR):** CODEOWNERS assigns everything to `@legioncodeinc` (an org). GitHub resolves CODEOWNERS owners as users or teams; a bare org handle may either be ignored (weakening the gate) or bind in unexpected ways. With a single-member org, a binding code-owner requirement plus "PR author cannot self-approve" can deadlock merges. If the first PR is blocked with "review required", adjust the ruleset (bypass for admins) or point CODEOWNERS at a team. + +--- + +## Commit Quality - Conventional Commits (Score: 3/10) + +| Metric | Value | +|---|---| +| CC-adherent commits (last 100) | 0/3 (0%) — "Initial commit", "Commit initial tools", "Moved files to tool specific" | +| Average subject line length | 24 chars ✅ | +| Generic/noise commits | 3 (all pre-convention, repo-birth commits) | +| Breaking changes documented | N/A | +| `commitlint` in CI | No | + +The convention is documented in CONTRIBUTING.md and the PR template; commits from this PR forward follow it. Rewriting 3 repo-birth commits is not worth a force-push; squash-merging this PR starts clean history. + +--- + +## CODEOWNERS (Score: 8/10) + +**Location:** `.github/CODEOWNERS` (on branch) +**Syntax errors:** None (no `\#`, `!`, or `[ ]` traps) +**Coverage:** 100% of paths (`*` default) + governance lockdown of `/.github/` +**Ownership type:** Org (see branch-protection note above on org-handle binding) + +--- + +## CI Workflow Density (Score: 7/10) + +| Workflow | Triggers | Lint | Type | Test | Build | Security | Timeout | In required checks | +|---|---|---|---|---|---|---|---|---| +| `ci.yml` | push, pull_request → main | n/a¹ | n/a¹ | n/a¹ | n/a | n/a | ❌ | ❌ | +| `codeql.yml` | push, PR, weekly cron | n/a | n/a | n/a | n/a | ✅ | ❌ | ❌ | + +¹ Stack-appropriate: dependency-free vanilla JS; the repo's entire gate is manifest validation, which CI runs. Lint/typecheck/test arrive with the first tooled tool. Both workflows are SHA-pinned with least-privilege permissions and concurrency cancellation. Missing `timeout-minutes` on all jobs. + +--- + +## Docs Presence (Score: 8/10) + +| File | Present | Notes | +|---|---|---| +| README.md | ✅ | Multi-tool positioning, tool index, contract, R&D notice | +| LICENSE | ✅ | AGPL-3.0 (registers on GitHub after push) | +| CONTRIBUTING.md | ✅ | Conventions + local gate | +| SECURITY.md | ✅ | Private vulnerability reporting channel | +| CODE_OF_CONDUCT.md | ❌ | Known gap; GitHub community-page "Add" flow can generate | + +--- + +## Repository Settings (Score: 4/10) + +| Setting | Status | +|---|---| +| Auto-delete head branches | ✅ | +| Allow merge commits | ⚠️ allowed | +| Allow squash merging | ✅ allowed (recommend: only this) | +| Allow rebase merging | ⚠️ allowed | +| Secret scanning | ❌ disabled | +| Push protection | ❌ disabled | +| Dependabot alerts | ❌ disabled (version-updates file is on branch; the alerts toggle is separate) | +| Code security (CodeQL default setup) | ❌ disabled — **blocks all code-scanning uploads on this private repo**; the committed `codeql.yml` is gated behind a `CODEQL_ENABLED` repo variable and skips until Code Security is enabled (paid GHAS decision for the org) | + +--- + +## Issue/PR Templates (Score: 10/10) + +| Item | Present | Substantive? | +|---|---|---| +| Bug report template | ✅ | ✅ GHL-specific environment fields, secret-handling warning | +| Feature request template | ✅ | ✅ | +| PR template | ✅ | ✅ validate + smoke-test checklist, CC type taxonomy | + +--- + +## .gitignore Coverage (Score: 9/10) + +**Detected stack:** Vanilla JS (Chrome MV3 extensions), zero dependencies +**Secret patterns:** ✅ `.env`/`.env.*` with `!.env.example` negation; `*.pem` (Web Store key) +**Build artifacts:** ✅ Node baseline (dist/build/out, caches, logs, framework dirs) +**Accidentally tracked files:** `.mimosa/` hook state was committed in `ae90a0e`; its removal is staged in this PR. No others. + +--- + +## Prioritized Remediation Plan + +| Priority | Finding | Impact | Effort | Action | +|---|---|---|---|---| +| 1 (4.0) | Secret scanning, push protection, Dependabot alerts, Code Security all disabled | 4 | 1 | Settings → Advanced Security. Note: on a **private** repo these are GHAS features with per-committer billing — an org cost decision. If enabled, also set repo variable `CODEQL_ENABLED=true` to activate the gated CodeQL workflow | +| 2 (4.0) | CI status checks not required before merge | 4 | 1 | After this PR merges and CI runs once: ruleset 21131540 → Add rule → Require status checks → `Validate extension manifests` | +| 3 (4.0) | CODEOWNERS org-handle binding unverified; possible merge deadlock | 4 | 1 | Watch first PR; if blocked, adjust ruleset bypass or point `*` at a team/personal handle | +| 4 (3.0) | All three merge methods allowed | 3 | 1 | Settings → General → Pull Requests → allow squash merge only | +| 5 (3.0) | Stale reviews not dismissed; 0-approval count | 3 | 1 | Ruleset → enable dismiss-stale-reviews; consider 1 approving review | +| 6 (2.0) | No `timeout-minutes` on workflow jobs | 2 | 1 | Add `timeout-minutes: 10` to each job (handoff: ci-release-worker-bee) | +| 7 (2.0) | CODE_OF_CONDUCT.md missing | 2 | 1 | Community page → Add → Covenant template | +| 8 (1.3) | 0% Conventional Commits in history | 4 | 3 | Adopted going forward via CONTRIBUTING + PR template; optional commitlint later | +| 9 (—) | `v0.1.0` tag not cut (CHANGELOG links 404) | 2 | 1 | Tag after merge (also tracked in QA report) | + +**Handoffs to other Bees:** +- `ci-release-worker-bee`: workflow `timeout-minutes`; lint/typecheck/test jobs when a tool adopts a build system +- `security-worker-bee`: none — no leaked secrets (verified in 2026-08-21 security audit); Settings toggles are human actions above + +--- + +*Ship Gate decision: nothing in this audit blocks commit/push. All remediation items are GitHub Settings actions or post-merge improvements.* diff --git a/scripts/validate-manifests.mjs b/scripts/validate-manifests.mjs new file mode 100644 index 0000000..9ce9021 --- /dev/null +++ b/scripts/validate-manifests.mjs @@ -0,0 +1,67 @@ +#!/usr/bin/env node +// Validates every Chrome extension manifest in this repo. Zero dependencies: +// runs on plain Node so CI needs no install step. Exits non-zero if any +// tool's manifest is invalid or references a file that does not exist. + +import { readdirSync, readFileSync, existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; + +const SKIP = new Set(['.git', 'node_modules', '.mimosa', 'library', '.github']); +let failed = false; + +function findManifests(dir, found = []) { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (SKIP.has(entry.name)) continue; + const full = join(dir, entry.name); + if (entry.isDirectory()) findManifests(full, found); + else if (entry.isFile() && entry.name === 'manifest.json') found.push(full); + } + return found; +} + +function fail(path, message) { + console.error('FAIL ' + path + ': ' + message); + failed = true; +} + +const manifests = findManifests(process.cwd()); + +if (!manifests.length) { + console.error('FAIL no manifest.json found: every tool in this set must be a loadable Chrome extension'); + process.exit(1); +} + +for (const path of manifests) { + let manifest; + try { + manifest = JSON.parse(readFileSync(path, 'utf8')); + } catch (err) { + fail(path, 'not valid JSON (' + err.message + ')'); + continue; + } + + if (manifest.manifest_version !== 3) { + fail(path, 'manifest_version must be 3, got ' + manifest.manifest_version); + } + if (!manifest.name || typeof manifest.name !== 'string') { + fail(path, 'name is required'); + } + if (!/^\d+\.\d+\.\d+/.test(String(manifest.version || ''))) { + fail(path, 'version must look like x.y.z, got ' + manifest.version); + } + + const popup = manifest.action && manifest.action.default_popup; + if (popup && !existsSync(join(dirname(path), popup))) { + fail(path, 'action.default_popup "' + popup + '" not found'); + } + + for (const icon of Object.values(manifest.icons || {})) { + if (!existsSync(join(dirname(path), icon))) { + fail(path, 'icon "' + icon + '" not found'); + } + } + + if (!failed) console.log('ok ' + path + ' (' + manifest.name + ' ' + manifest.version + ')'); +} + +process.exit(failed ? 1 : 0);