Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
// Python 3.12 for the research/observer tooling under research/ (Poetry-managed).
"ghcr.io/devcontainers/features/python:1": {
"version": "3.12"
}
},
// go-task: the Taskfile.yml runner (`task --list`) for the build/package/run
// flows — one source of truth shared with WSL/host and CI.
"ghcr.io/devcontainers-extra/features/go-task:1": {}
},
// Persisted cache volume: survives rebuilds. bash history + npm/pnpm stores +
// corepack + Playwright browsers all live here (see remoteEnv below).
Expand Down Expand Up @@ -52,10 +55,13 @@
}
}
},
// 7801 = local bridge WebSocket/HTTP; 5280 = Vite PWA dev server (see docs/03-architecture.md).
// 7801 = local bridge; 5280 = Vite PWA dev; 7900 = dev gateway (task gateway:dev);
// 5285 = UI playground (task playground). See docs/03-architecture.md / docs/07.
"forwardPorts": [
7801,
5280
5280,
7900,
5285
],
"portsAttributes": {
"7801": {
Expand All @@ -65,6 +71,14 @@
"5280": {
"label": "CloakCode PWA (Vite)",
"onAutoForward": "notify"
},
"7900": {
"label": "CloakCode Gateway (dev)",
"onAutoForward": "silent"
},
"5285": {
"label": "CloakCode UI Playground (Vite)",
"onAutoForward": "notify"
}
},
// Persisted-cache wiring. The named volume (mounted at /.devcontainercache) survives
Expand Down
61 changes: 15 additions & 46 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,24 @@ jobs:
python -m pip install --upgrade pre-commit
pre-commit run --all-files --show-diff-on-failure --color=always

# Runs every package's tests with v8 coverage and FAILS on any regression below
# the per-package thresholds (85% statements/lines/functions, 75% branches) —
# this in-CI gate is the real coverage protection.
#
# We deliberately do NOT upload to GitHub Code Quality (the coverage-on-PRs
# feature). Per the public-preview changelog (2026-05-26, "Availability and
# pricing"), GitHub Code Quality is only available on GitHub Enterprise
# Cloud/Team — so it CANNOT be enabled on this public/free open-source repo:
# there is no setting to turn it on and the upload endpoint 404s. Uploading
# anyway would either fail CI or need a silent `fail-on-error: false` mask, which
# we won't carry. Revisit (add the actions/upload-code-coverage steps +
# `code-quality: write` permission) only if this repo ever moves under an
# Enterprise Cloud/Team org. See docs/06 field notes.
coverage:
name: Coverage → Code Quality
name: Coverage — 85% gate
runs-on: ubuntu-latest
permissions:
contents: read
code-quality: write # upload coverage to GitHub Code Quality (no third party)
steps:
# Check out the PR head (not the merge commit) so coverage line numbers map
# to the diff for the github-code-quality[bot] PR comment.
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Install pnpm
uses: pnpm/action-setup@v6
- name: Setup Node
Expand All @@ -119,45 +125,8 @@ jobs:
# Downstream tests import built workspace packages (e.g. the extension
# tests import @cloakcode/gateway's dist), so build everything first — like
# the node job. Then run every package's tests with v8 coverage: the
# per-package thresholds (85% statements/lines/functions, 75% branches) FAIL
# this job on a regression, and each run emits a Cobertura report.
# per-package thresholds FAIL this job on a regression.
- name: Test with coverage (enforces the 85% gate)
run: |
pnpm -r build
pnpm -r test:coverage
# Upload each package's Cobertura report. No third party — the
# github-code-quality[bot] posts the coverage summary on the pull request.
# fail-on-error: false so CI stays green until Code Quality is enabled in the
# repo's Code security settings (the action 404s the upload until then).
- name: Upload protocol coverage
if: ${{ !cancelled() }}
uses: actions/upload-code-coverage@v1.3.0
with:
file: packages/protocol/coverage/cobertura-coverage.xml
language: TypeScript
label: code-coverage/protocol
fail-on-error: false
- name: Upload gateway coverage
if: ${{ !cancelled() }}
uses: actions/upload-code-coverage@v1.3.0
with:
file: packages/gateway/coverage/cobertura-coverage.xml
language: TypeScript
label: code-coverage/gateway
fail-on-error: false
- name: Upload web coverage
if: ${{ !cancelled() }}
uses: actions/upload-code-coverage@v1.3.0
with:
file: packages/web/coverage/cobertura-coverage.xml
language: TypeScript
label: code-coverage/web
fail-on-error: false
- name: Upload extension coverage
if: ${{ !cancelled() }}
uses: actions/upload-code-coverage@v1.3.0
with:
file: packages/extension/coverage/cobertura-coverage.xml
language: TypeScript
label: code-coverage/extension
fail-on-error: false
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ pnpm -r test # Vitest
pnpm -r test:coverage # coverage gate (85% statements/lines/functions, 75% branches)
```

Common build/package/run flows are wrapped in a [`Taskfile.yml`](Taskfile.yml) — the dev container
installs [`task`](https://taskfile.dev), so run `task` (or `task --list`) to see them all:

| Task | What |
| --- | --- |
| `task build` · `task check` | build all · full gate (typecheck + lint + test) |
| `task package` | package everything — extension `.vsix` + assembled gateway |
| `task extension:install` / `extension:uninstall` | install / remove the packaged extension (+ its Copilot hook) |
| `task gateway:dev` · `task gateway:run -- --tunnel` | run the gateway (dev) · run the assembled hub with a tunnel |
| `task playground` · `task web:dev` | UI playground · PWA dev server |
| `task docker:gateway` | build the gateway image — **run from your WSL/host** (Docker isn't in the container) |

Try the research tools against your local Copilot sessions:

```bash
Expand Down
143 changes: 143 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# https://taskfile.dev — CloakCode task runner.
#
# One home for the build / package / run flows so the dev container, your WSL
# host, and CI never drift. `task` (or `task --list`) shows everything; pass args
# to a wrapped script after `--` (e.g. `task gateway:run -- --tunnel`).
#
# Docker is NOT in the dev container, so `task docker:gateway` is meant to run
# from your WSL/host (it needs only the repo + Docker). Everything else runs in
# the dev container.
version: "3"

vars:
# Gateway dev listen port (the dev-container F5 flow expects ws://<host>:7900).
GATEWAY_PORT: '{{.GATEWAY_PORT | default "7900"}}'
GATEWAY_DIST: "{{.TASKFILE_DIR}}/dist/gateway"
EXT_DIST: "{{.TASKFILE_DIR}}/dist/extension"

tasks:
default:
desc: List every task.
cmds:
- task --list
silent: true

# ── deps & quality gates ────────────────────────────────────────────────
install:
desc: Install workspace deps (pnpm, frozen lockfile).
cmds:
- pnpm install --frozen-lockfile

build:
desc: "① Build all packages (topological)."
cmds:
- pnpm -r build

test:
desc: Run all tests (Vitest).
cmds:
- pnpm -r test

test:coverage:
desc: Run all tests with the coverage gate (85% stmts/lines/funcs, 75% branches).
cmds:
- pnpm -r test:coverage

lint:
desc: Lint all packages (ESLint).
cmds:
- pnpm -r lint

typecheck:
desc: Type-check all packages (tsc --noEmit).
cmds:
- pnpm -r typecheck

check:
desc: Full TS gate — typecheck + lint + test (the Definition of Done).
cmds:
- pnpm -r typecheck
- pnpm -r lint
- pnpm -r test

check:py:
desc: Python gate for research/ — ruff + mypy (needs Poetry).
cmds:
- poetry run ruff check .
- poetry run mypy research

# ── package ─────────────────────────────────────────────────────────────
package:
desc: "② Build AND package everything — extension .vsix + assembled gateway."
cmds:
- task: package:extension
- task: package:gateway

package:extension:
desc: "③ Build + package the extension → dist/extension/ (cloakcode-<v>.vsix + install.sh / uninstall.sh)."
cmds:
- pnpm --filter @cloakcode/extension package

package:gateway:
desc: "④ Build + package the gateway → dist/gateway/ (main.mjs + web/ + run.sh), copy-ready."
cmds:
- pnpm --filter @cloakcode/gateway assemble

docker:gateway:
desc: "⑤ Build (+ smoke-test) the gateway Docker image. Needs Docker — run from WSL/host, NOT the dev container. Args after -- (e.g. build, --no-cache)."
cmds:
- bash scripts/docker-gateway.sh {{.CLI_ARGS}}

# ── run ─────────────────────────────────────────────────────────────────
gateway:dev:
desc: "⑥ Run the gateway from the workspace build (verbose), serving the local PWA — for dev."
env:
CLOAKCODE_WEB_DIR: "{{.TASKFILE_DIR}}/packages/web/dist"
CLOAKCODE_GATEWAY_PORT: "{{.GATEWAY_PORT}}"
CLOAKCODE_VERBOSE: "1"
cmds:
- pnpm --filter @cloakcode/protocol build
- pnpm --filter @cloakcode/web build
- pnpm --filter @cloakcode/gateway bundle
- node "{{.TASKFILE_DIR}}/packages/gateway/dist/main.mjs"

gateway:run:
desc: "⑦ Run the assembled gateway (from ④) via its run.sh. Flags after -- (e.g. task gateway:run -- --tunnel)."
deps:
- package:gateway
cmds:
- bash "{{.GATEWAY_DIST}}/run.sh" {{.CLI_ARGS}}

extension:install:
desc: "⑧ Package (③) then install the .vsix into VS Code (runs dist/extension/install.sh). CODE_BIN overrides the editor CLI."
deps:
- package:extension
cmds:
- bash "{{.EXT_DIST}}/install.sh"

extension:uninstall:
desc: "⑨ Uninstall the extension + its Copilot hook (runs dist/extension/uninstall.sh from ③). Pass --keep-hook after --."
cmds:
- bash "{{.EXT_DIST}}/uninstall.sh" {{.CLI_ARGS}}

playground:
desc: "⑩ Run the UI playground (Vite) — the real PWA against an in-browser fake bridge."
cmds:
- pnpm --filter @cloakcode/web-playground dev

# ── extra dev flows ─────────────────────────────────────────────────────
web:dev:
desc: Run the PWA dev server (Vite).
cmds:
- pnpm --filter @cloakcode/web dev

extension:dev:
desc: Run the bridge dev harness (dev-server.ts — the bridge with no extension host).
cmds:
- pnpm --filter @cloakcode/extension dev

clean:
desc: Remove build artifacts (dist/, package dist/, stray .vsix + install scripts).
cmds:
- rm -rf dist packages/*/dist
- rm -f packages/extension/*.vsix packages/extension/install.sh packages/extension/uninstall.sh
37 changes: 37 additions & 0 deletions docs/06-field-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,19 @@ Base: `~/.vscode-server/data/User/`
LTS**, so a blanket major-ignore would wrongly block the next LTS bumps. 25 is the last non-LTS
release (older odd 21/23 are EOL). When bumping the major manually, change **both** `FROM node:`
stages in `packages/gateway/Dockerfile` (build + runtime) together.
- **TypeScript stays on 5.x — the tsgo 7.x major is gated on typescript-eslint, not us (2026-07-23).**
`typescript@7.0.2` is now `latest` — the **Go-native ("tsgo") rewrite** with a different compiler
API. We can't adopt it while our type-aware linter **`typescript-eslint@8`** declares peer
`typescript: >=4.8.4 <6.1.0` (verified on the registry; `@typescript-eslint/typescript-estree`
carries the same cap): bumping past 5.x would break type-aware lint **and** introduce a
peer-dependency warning (a no-regression violation). So `.github/dependabot.yml` (npm ecosystem)
ignores **only the `typescript` majors** via `update-types: [version-update:semver-major]` — 5.x
minor/patch still auto-update. This is the **opposite** call from the Node-LTS note above (there we
ignore a _specific_ version, not the update-type) because here the block is the ecosystem peer cap,
not an LTS cadence: any TS major is gated until typescript-eslint widens its peer range. **Revisit
trigger:** the day a `typescript-eslint` release lands whose `typescript` peer covers the next TS
major — drop the ignore in that same change. Dependabot auto-closed the stale 5.9.3→7.0.2 PR once
the ignore hit `main`.
- **No duplicate tool-version pins — run the tool from the project's dep manager (2026-07-22).**
Dependabot has **no `pre-commit` ecosystem**, so a tool version living in a pre-commit `rev:` never
gets a Dependabot PR. If that same tool is _also_ pinned elsewhere (e.g. ruff in both
Expand Down Expand Up @@ -365,4 +378,28 @@ Base: `~/.vscode-server/data/User/`
Support/Code/User` / `~/.config/Code/User`), and `--user-data-dir` moves it again → a hardcoded
path finds **0 sessions**. Derive the root from **`context.globalStorageUri`** (sibling
`…/User/workspaceStorage`) instead. Both fixes are host-accurate with no `process.platform` check.
- **GitHub Code Quality coverage is PLAN-gated — unavailable on a public/free repo, so we do NOT
upload (2026-07-23).** The `coverage` job in [ci.yml](../.github/workflows/ci.yml) once uploaded
Cobertura reports via `actions/upload-code-coverage` so `github-code-quality[bot]` could post a PR
coverage summary. On `lsiddiquee/CloakCode` (a **personal / public / free** repo) every upload
**404s** and the setting to enable it is **nowhere in repo/profile settings** — because **GitHub
Code Quality is only available on GitHub Enterprise Cloud and Team**, per the public-preview
changelog's _Availability and pricing_ (2026-05-26; the product went GA 2026-07-20). It is **not** a
rollout wait and **not** a permission problem — the `code-quality: write` permission is
necessary-but-not-sufficient; the gate is the **account tier**. The `code-scanning` default/advanced
setup type is **irrelevant** to it: Code Quality (coverage) and CodeQL (code scanning) are
**separate features**, so **switching CodeQL to default setup does NOT unlock coverage** — don't do
it hoping to (we stay on **advanced** CodeQL: [codeql.yml](../.github/workflows/codeql.yml),
`javascript-typescript` + `security-extended`). **Decision (2026-07-23): the upload steps + the
`code-quality: write` permission were REMOVED** rather than left masked with `fail-on-error: false`
— a permanently-404ing step behind a silent mask is exactly the kind of hidden CI failure we don't
carry. The **real** protection stays: the in-CI `pnpm -r test:coverage` **85% gate**
(statements/lines/functions; 75% branches) FAILS the job on a regression, independent of any upload.
The job was renamed `Coverage → Code Quality` → **`Coverage — 85% gate`** to match reality; that
string is a **required status check** on `main`, so branch protection's required-checks list was
updated in the same change (rename the job ⇒ update branch protection or PRs hang on a check that
never reports). **Revisit trigger:** the day this repo lives under a GitHub Enterprise Cloud/Team
org (or Code Quality reaches personal plans) — enable Code Quality in the repo's Code security
settings, then re-add the `actions/upload-code-coverage` steps + `code-quality: write` permission
(no `fail-on-error: false` mask — a real upload failure should fail CI).
Details in docs/02.3 §4.27 + docs/02.4 §4.28.
18 changes: 18 additions & 0 deletions docs/07-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ pass in proto tcp from 192.168.65.0/24 to any port 3543 # Docker Desktop VM s
On every OS the firewall is the *fallback* — prefer loopback + a private tunnel, or a
virtual-interface bind, and keep the phone on the tunnel rather than a LAN IP.

## Build, package & run — `task`

Every build/package/run flow is wrapped in the repo's [`Taskfile.yml`](../Taskfile.yml) — the dev
container installs [`task`](https://taskfile.dev) (run `task --list`) so the container, your WSL
host, and CI never drift:

| Task | What it does |
| --- | --- |
| `task package:extension` | build + package the extension → `dist/extension/` (`.vsix` + `install.sh` / `uninstall.sh`) |
| `task extension:install` / `extension:uninstall` | install / remove the packaged extension (uninstall also drops the per-env Copilot hook) |
| `task package:gateway` | assemble the copy-ready gateway → `dist/gateway/` (`main.mjs` + `web/` + `run.sh`) |
| `task gateway:run -- --tunnel` | run the assembled gateway via its `run.sh` (opens a private tunnel) |
| `task gateway:dev` | run the gateway from the workspace build (verbose) for local iteration |
| `task docker:gateway` | build (+ smoke-test) the gateway image — **run from your WSL/host**; Docker isn't in the dev container |

`task docker:gateway` and `task gateway:run -- --tunnel` are the two you reach for when exposing a
hub; the sections below cover how to bind/forward it safely.

## Docker

The gateway ships as a container image that **bundles the `devtunnel` CLI**, so it can host the
Expand Down