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: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## Unreleased

- Integrated native verification corrective loop:
- Created `manual-skills/create-verification-skill/references/verification-loop.md` with explicit When, Corrective action, and Evidence ledger protocols (requiring mechanical `FACT:` rows and banning test weakening).
- Updated `manual-skills/create-verification-skill/SKILL.md` to require executing the corrective loop before claiming PASS and copying it into generated skill failure handling.
- Added Corrective loop section to `rules/01-verification.md` referencing the verification loop protocol and anti-test-weakening constraints.
- Added exploratory visual layout shift (CLS) observation recipe in `skills/playwright-qa/references/cls.md` and linked reference in `skills/playwright-qa/SKILL.md` while strictly preserving the 200-newline budget.
- Documented `/chrome-devtools-axi` diagnostic entry point for observed layout shifts via `127.0.0.1:9223`.
- Added 18-second brag launch video card pattern to HyperFrames:
- Created `skills/hyperframes/references/brag.md` specifying an 18-second, 60fps, 1080p deterministic video recipe across 4 narrative beats (Hook & Problem, Solution Reveal, Capability Highlight, Call to Action) with local-only assets and target output `brag-output/brag.mp4`.
- Updated `skills/hyperframes/SKILL.md` and `skills/hyperframes/NOTICE.md` to reference the brag card recipe.
- Updated `rules/00-routing.md` under `video_html` and `README.md` to route short launch video cards to `hyperframes` via `references/brag.md`.
- Implemented optional Crawl4AI web extraction MCP:
- Registered `crawl4ai` as an optional `FOREIGN_ON_DEMAND` remote MCP (`http://127.0.0.1:11235/mcp`; `--cloud` using `https://api.crawl4ai.com/mcp` with `{env:CRAWL4AI_KEY}`) via `opencode-he crawl4ai enable [--cloud]` and `opencode-he crawl4ai disable`.
- Added strict doctor validation in `lib/doctor.py`: enforces `127.0.0.1:11235` local binding, rejects `0.0.0.0` or invalid URLs, prevents raw secret keys on cloud endpoints, and reports `OPTIONAL_ABSENT` when unconfigured.
- Added CLI options in `lib/cli.py` and enablement handlers in `lib/install.py`.
- Added doctor unit tests in `tests/test_doctor.py` covering missing, zero-bind, invalid URL, cloud missing token, cloud raw secret, valid local, valid cloud, and enable/disable flows.
- Documented configuration and boundaries in `docs/mcp.md`, `docs/CATALOG-FREEZE.md`, `docs/source-wave.md`, `docs/troubleshooting.md`, and `README.md` (documenting Scrapling as an unmanaged pointer and retaining Agent-Reach as rejected).
- Maintained frozen catalog of 62 skills (47 model-invoked, 15 manual slash commands) with zero new skill names. Product version remains 0.1.4.

## 0.1.4 — 2026-09-23

- Upgraded `codebase-memory-mcp` pin to v0.11.0 with SHA-256 verified portable tarball download, and added automatic `--format json` argument propagation in `lib/cbm.py` for reliable JSON extraction across project listing and status commands.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Default: repository evidence first. Then at most one specialist.
| Scroll-led storytelling | `scroll-craft` |
| Scroll-driven 3D / camera world | `scroll-world` |
| Procedural Three.js object from image | `img2threejs` |
| Deterministic HTML composition video | `hyperframes` |
| Deterministic HTML composition video | `hyperframes` (18s brag card via `references/brag.md`) |
| Demo video aplikasi & narasi ID | `id-demo-video` (`/demo-video`) |
| Browser | `playwright-qa` (isolated verification edge; not builder self-attest) → `browser-act` → `chrome-devtools-axi` → `click-path-audit` |
| Documents (PDF/DOCX/answer/extract/review) | `smartdoc` |
Expand Down Expand Up @@ -178,6 +178,7 @@ Optional:
- `reticle` — `opencode-he reticle enable` registers Reticle as a local perception server (`npx -y @reticlehq/server mcp`). `FOREIGN_ON_DEMAND`. Server package is FSL-1.1-ALv2 (competing-use clause); SDK packages (Apache-2.0) are not vendored. Never an auto-implementer; default verification remains `playwright-qa` / `chrome-devtools-axi`. `opencode-he reticle disable` removes only that server key. Absent is not a `doctor` failure; a malformed entry fails closed.
- `ui-skills` — `opencode-he ui-skills enable` registers UI Skills (`https://www.ui-skills.com/mcp`) as an optional remote MCP server. `FOREIGN_ON_DEMAND` for design-skill lookup only. Product UI remains Design Bank + Impeccable + Design V2 atoms + shadcn; `BANK_MISS` never generates from a random ui-skills document. `opencode-he ui-skills disable` removes only that server key. Absent is not a `doctor` failure; a malformed entry fails closed.
- `markitdown` — `opencode-he markitdown enable` registers MarkItDown as a local stdio ingest converter (`uvx --from markitdown-mcp==0.1.8 markitdown-mcp`). `FOREIGN_ON_DEMAND`. Local trusted agents only; never `--http` / `0.0.0.0` / docker bind-all. Output is Markdown data; SmartDoc keeps contract/QA/render. `opencode-he markitdown disable` removes only that server key. Absent is not a `doctor` failure; a malformed entry fails closed.
- `crawl4ai` — `opencode-he crawl4ai enable` registers Crawl4AI as an optional web content extraction remote MCP (`http://127.0.0.1:11235/mcp`; `--cloud` registers `https://api.crawl4ai.com/mcp` with `{env:CRAWL4AI_KEY}`). `FOREIGN_ON_DEMAND`. Docker users bind `127.0.0.1:11235`, never `0.0.0.0`. Web content extraction only; not an exploratory QA tool (`playwright-qa` remains default). `opencode-he crawl4ai disable` removes only that server key. Absent is not a `doctor` failure; a malformed entry (or `0.0.0.0`) fails closed.
- `jev-mcp` — TypeSafe Jev / `jkudish/jev-mcp` is intentionally SKIPPED as a required runtime MCP; core verification and done-gates operate offline without external server dependencies.
- `exa` — `FOREIGN_ON_DEMAND`; installer never adds, removes, or overwrites it

Expand Down
2 changes: 1 addition & 1 deletion docs/CATALOG-FREEZE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This contract defines the immutable boundary and governance for the OpenCodeHigh
- **Retired in this wave and not to be revived**: `ask-matt`, `grilling`, `wait-what`, `matt-implement`.
- **Kept on purpose**: `wizard` (target-app bash wizard), `codebase-design` (new module), `/improve-codebase-architecture` (scan + HTML report).
- **Name collision remains**: `install-anti-slop` = Oxlint; UI/copy filter lives in `impeccable` taste-gate + `humanizer`; `/unslop` = `humanizer`.
- **FOREIGN_ON_DEMAND stays out of the overlay**: `ECC`, `noodle`, `serena`, `stitch`, `reticle`, `ui-skills` MCP, `markitdown` MCP, `exa`, `Caliper`, `SkillEvaluator`. `doctor` must not fail when they are absent.
- **FOREIGN_ON_DEMAND stays out of the overlay**: `ECC`, `noodle`, `serena`, `stitch`, `reticle`, `ui-skills` MCP, `markitdown` MCP, `crawl4ai` MCP, `exa`, `Caliper`, `SkillEvaluator`. `doctor` must not fail when they are absent.
- **No new allowlist name without retiring one existing name in the same change.**
- **No padding back to 64.**
- **No `/how`, `/poteto-mode`, `/antislop`, `taste-skill`, `axi-core`, `human-atlas`, `awesome-design-md` vendor.**
Expand Down
4 changes: 4 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Optional:
- `reticle` — `opencode-he reticle enable` (local stdio via `npx -y @reticlehq/server mcp`; perception only, never auto-implementer)
- `ui-skills` — `opencode-he ui-skills enable` (remote HTTP `https://www.ui-skills.com/mcp`; design-skill lookup only)
- `markitdown` — `opencode-he markitdown enable` (local stdio via `uvx --from markitdown-mcp==0.1.8 markitdown-mcp`; Markdown ingest only)
- `crawl4ai` — `opencode-he crawl4ai enable` (remote HTTP `http://127.0.0.1:11235/mcp`; cloud via `--cloud` with `{env:CRAWL4AI_KEY}`)
- `exa` — foreign; never add/remove/overwrite

Merge is parse-aware. Comment-free JSON is rewritten with `json.dumps`. JSONC with comments is patched surgically (owned MCP keys only). If surgical merge cannot be verified, install fails closed instead of destroying comments.
Expand All @@ -33,8 +34,11 @@ Doctor reports `CONFIGURED` for owned MCP entries present in config. That is not

`opencode-he markitdown enable` configures MarkItDown as an optional local stdio ingest MCP (`uvx --from markitdown-mcp==0.1.8 markitdown-mcp`). It is `FOREIGN_ON_DEMAND`. Official server is for local trusted agents only; never `--http`, never bind `0.0.0.0`, never docker bind-all. The converter is not vendored into `lib/`. Missing `uvx` is documented in the skill (CLI/`pipx`/`enable`); enable still writes the stdio command like reticle. `opencode-he markitdown disable` surgically removes only the markitdown server key. Absent is not a doctor failure; a malformed entry (including `--http` / `0.0.0.0`) fails closed.

`opencode-he crawl4ai enable` configures Crawl4AI as an optional web content extraction remote MCP (`http://127.0.0.1:11235/mcp`). It is `FOREIGN_ON_DEMAND` for content extraction, not exploratory browser QA (which remains `playwright-qa`). For Docker users, bind strictly to `127.0.0.1:11235` (e.g. `docker run -p 127.0.0.1:11235:11235 ...`); never bind `0.0.0.0`. OpenCodeHighEnd does not launch or manage the container. With `--cloud`, it configures `https://api.crawl4ai.com/mcp` using `{env:CRAWL4AI_KEY}` without writing secrets to disk. `opencode-he crawl4ai disable` surgically removes only the crawl4ai server key. Absent is not a doctor failure; binding to `0.0.0.0` or invalid URLs fails closed.

## Evaluated, Skipped & Rejected

- **Scrapling** — Evaluated against Crawl4AI and documented as an unmanaged alternative pointer; not registered as an MCP server, CLI command, or skill to avoid redundant surface.
- **TypeSafe Jev (`jev-mcp`)** — TypeSafe Jev / `jkudish/jev-mcp` was evaluated and is intentionally **SKIPPED** as a required runtime MCP. It is not vendored and not bundled in core MCPs. If ever manually configured by a user, it remains optional `FOREIGN_ON_DEMAND` only with `{env:TYPESAFE_API_KEY}`. Core verification, done-gates, and evidence ledgers operate fully offline without external Jev services.
- **Agent-Reach (`Panniantong/Agent-Reach`)** — Agent-Reach was evaluated and is strictly **REJECTED** as a core MCP or skill. It is not an alternative to Playwright QA eyes, carries ToS/cookie/account risks, and relies on Exa which is already designated `FOREIGN_ON_DEMAND`. Never register Agent-Reach as a core or required tool.
- **TypeSafe MCP (`itsmostafa/typesafe-mcp`)** — Evaluated and **REJECTED** as an extra core MCP. Core MCPs remain strictly `codebase-memory-mcp`, `context7`, and `shadcn`.
2 changes: 2 additions & 0 deletions docs/source-wave.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ Recorded per Phase 0 contract.
| [cathrynlavery/diagram-design](https://github.com/cathrynlavery/diagram-design) | `dc1ace47b99a` (v2.6.33) | Editorial HTML/SVG diagram design. MIT. | **PIN_ONLY** | `skills/diagram-design` | Pinned commit `dc1ace47b99a` in `vendor/sources.json`. Skill body unchanged. |
| [blader/humanizer](https://github.com/blader/humanizer) | `3.0.0` | AI prose humanizing and slop removal. MIT. | **PIN_ONLY** | `skills/humanizer` | Pinned v3.0.0 in `vendor/sources.json`. Skill body unchanged. |
| [semgrep / gitleaks / osv-scanner](https://github.com) | `semgrep` 1.177.0, `gitleaks` 8.30.1, `osv-scanner` 2.6.0 | Host security scanners. | **PIN_ONLY** | `skills/full-audit-keamanan` | Host scanner version pins recorded in `vendor/sources.json`. |
| [unclecode/crawl4ai](https://github.com/unclecode/crawl4ai) | upstream ref | LLM-friendly web crawler & scraper MCP. Apache-2.0. | **FOREIGN_ON_DEMAND** | `docs/mcp.md` | Optional remote MCP (`http://127.0.0.1:11235/mcp`, cloud via `--cloud`). Not vendored. Content extraction only, not exploratory QA eyes (`playwright-qa`). Bind strictly 127.0.0.1, never 0.0.0.0. |
| [D4Vinci/Scrapling](https://github.com/D4Vinci/Scrapling) | upstream ref | Undetectable web scraping library. | **POINTER_ONLY** | `docs/mcp.md` | Evaluated against Crawl4AI. Kept as documentation pointer only; not registered as MCP, CLI command, or skill. |
2 changes: 2 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

Doctor `OPTIONAL_ABSENT` is not a core failure. `DEGRADED` is non-fatal unless `doctor --strict`. `EMPTY Design V2` means no user bank yet — not a failure. `DEGRADED_FTS` means JSONL search works without SQLite FTS.

`FAIL mcp:crawl4ai` — Crawl4AI must bind to `http://127.0.0.1:11235/mcp` (or cloud `https://api.crawl4ai.com/mcp` with `{env:CRAWL4AI_KEY}`). Binding to `0.0.0.0`, using raw secret keys, or using non-standard URLs triggers a FAIL. Reconfigure with `opencode-he crawl4ai enable` (or `--cloud`).

`doctor --deep` exit 1 with `NOT_CHECKED` — `opencode mcp list` failed or was empty; core MCP is not proven live.

Restart OpenCode after install.
10 changes: 10 additions & 0 deletions lib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
cmd_install,
cmd_restore,
cmd_restore_list,
cmd_crawl4ai_disable,
cmd_crawl4ai_enable,
cmd_markitdown_disable,
cmd_markitdown_enable,
cmd_reticle_disable,
Expand Down Expand Up @@ -119,6 +121,10 @@ def build_parser() -> argparse.ArgumentParser:
uis = sub.add_parser("ui-skills", help="optional UI Skills remote MCP")
uis.add_argument("action", choices=["enable", "disable"])

c4 = sub.add_parser("crawl4ai", help="optional Crawl4AI web extraction MCP")
c4.add_argument("action", choices=["enable", "disable"])
c4.add_argument("--cloud", action="store_true", help="use cloud endpoint with CRAWL4AI_KEY instead of local container")

sd = sub.add_parser("smartdoc", help="document profiles, extract, status")
add_smartdoc_cli(sd)
sb = sub.add_parser("smartbook", help="reusable SmartBook lifecycle")
Expand Down Expand Up @@ -190,6 +196,10 @@ def main(argv: list[str] | None = None) -> int:
if args.action == "enable":
return cmd_ui_skills_enable()
return cmd_ui_skills_disable()
if cmd == "crawl4ai":
if args.action == "enable":
return cmd_crawl4ai_enable(cloud=args.cloud)
return cmd_crawl4ai_disable()
if cmd == "smartdoc":
return dispatch_smartdoc(args)
if cmd == "smartbook":
Expand Down
37 changes: 33 additions & 4 deletions lib/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ def mcp_status_map() -> dict[str, str]:
try:
data = jsonc.load_path(cfg)
except (OSError, json.JSONDecodeError, ValueError):
return {k: "FAIL" for k in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "exa")}
return {k: "FAIL" for k in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "crawl4ai", "exa")}
mcp = data.get("mcp") or {}
if not isinstance(mcp, dict):
return {k: "FAIL" for k in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "exa")}
return {k: "FAIL" for k in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "crawl4ai", "exa")}
servers = jsonc.mcp_servers_from_config(data)
owned = {"codebase-memory-mcp", "context7", "shadcn"}
optional = {"serena", "stitch", "reticle", "ui-skills", "markitdown", "exa"}
for name in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "exa"):
optional = {"serena", "stitch", "reticle", "ui-skills", "markitdown", "crawl4ai", "exa"}
for name in ("codebase-memory-mcp", "context7", "shadcn", "serena", "stitch", "reticle", "ui-skills", "markitdown", "crawl4ai", "exa"):
spec = servers.get(name)
if spec is None:
out[name] = "OPTIONAL_ABSENT" if name in optional else "FAIL"
Expand Down Expand Up @@ -190,6 +190,35 @@ def mcp_status_map() -> dict[str, str]:
continue
out[name] = "CONFIGURED"
continue
if name == "crawl4ai":
typ = spec.get("type")
url = spec.get("url")
cmd = spec.get("command")
joined = " ".join(str(part) for part in cmd) if isinstance(cmd, list) else str(cmd or "")
url_str = str(url or "")
if "0.0.0.0" in url_str or "0.0.0.0" in joined:
out[name] = "FAIL"
continue
if typ != "remote":
out[name] = "FAIL"
continue
if url not in ("http://127.0.0.1:11235/mcp", "https://api.crawl4ai.com/mcp"):
out[name] = "FAIL"
continue
headers = spec.get("headers")
if url == "https://api.crawl4ai.com/mcp":
if not isinstance(headers, dict) or not headers:
out[name] = "FAIL"
continue
if not all("{env:CRAWL4AI_KEY}" in str(v) for v in headers.values()):
out[name] = "FAIL"
continue
else:
if headers is not None and not isinstance(headers, dict):
out[name] = "FAIL"
continue
out[name] = "CONFIGURED"
continue
if name not in owned:
out[name] = "FOREIGN"
continue
Expand Down
32 changes: 31 additions & 1 deletion lib/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ def take(src: Path, dest: Path) -> None:
"modelInvokedSkills": meta["model"],
"manualSkills": meta["manual"],
"ownedMcp": list(OWNED_MCP),
"optionalMcp": ["serena", "stitch", "reticle", "ui-skills", "markitdown", "exa"],
"optionalMcp": ["serena", "stitch", "reticle", "ui-skills", "markitdown", "crawl4ai", "exa"],
"designBank": {
"root": bank_root,
"source": bank_source,
Expand Down Expand Up @@ -1509,3 +1509,33 @@ def cmd_ui_skills_enable() -> int:
def cmd_ui_skills_disable() -> int:
return _optional_mcp_disable("ui-skills")


def cmd_crawl4ai_enable(cloud: bool = False) -> int:
if cloud and not os.environ.get("CRAWL4AI_KEY", "").strip():
die("CRAWL4AI_KEY environment variable is empty (set CRAWL4AI_KEY to use --cloud)")
if cloud:
spec: dict[str, object] = {
"type": "remote",
"url": "https://api.crawl4ai.com/mcp",
"headers": {
"Authorization": "Bearer {env:CRAWL4AI_KEY}",
},
"disabled": False,
}
else:
spec = {
"type": "remote",
"url": "http://127.0.0.1:11235/mcp",
"disabled": False,
}
return _optional_mcp_enable(
"crawl4ai",
spec,
already_present_msg="crawl4ai MCP already present; not overwriting (run `crawl4ai disable` first to change endpoint)",
)


def cmd_crawl4ai_disable() -> int:
return _optional_mcp_disable("crawl4ai")


2 changes: 1 addition & 1 deletion manual-skills/create-verification-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Create `.opencode/skills/verify-<app>/features/README.md` plus one file per user

## 4. Prove the generated skill before handing it over

Run its own instructions end to end once: launch, doctor, drive ONE mapped feature (one is enough; the map exists so later runs can cover the rest), capture evidence, clean up. After cleanup, confirm the evidence still exists at the named location — a cleanup that eats the proof fails this step. Fix what fails, and run the generated cleanup after every failed iteration too, so broken attempts don't strand processes and ports. A generated skill that was never executed is a **DRAFT**, not a PASS. Do not claim PASS until that live run succeeded.
Run its own instructions end to end once: launch, doctor, drive ONE mapped feature (one is enough; the map exists so later runs can cover the rest), capture evidence, clean up. After cleanup, confirm the evidence still exists at the named location — a cleanup that eats the proof fails this step. Fix what fails, and run the generated cleanup after every failed iteration too, so broken attempts don't strand processes and ports. Enforce the corrective loop in [`references/verification-loop.md`](references/verification-loop.md): if an assertion fails or evidence is missing, fix the product or the harness; never weaken tests, skip assertions, or drop checks to force a PASS. Copy this loop into the generated skill's failure section. A generated skill that was never executed is a **DRAFT**, not a PASS. Do not claim PASS until that live run succeeded.

## 5. Offer the maintenance loop

Expand Down
Loading
Loading