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
49 changes: 49 additions & 0 deletions .github/workflows/freshness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: freshness

# Weekly research-freshness check. Re-crawls the GCP source corpus, compares
# content hashes against the stored audit trail, and opens an issue when a
# source has drifted — so the plugin never silently goes stale.

on:
schedule:
- cron: "0 6 * * 1" # Mondays 06:00 UTC
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
freshness:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Refresh corpus and detect drift
id: check
run: |
python3 scripts/research_crawl.py > crawl.log 2>&1 || true
if python3 scripts/freshness_check.py > freshness.log 2>&1; then
echo "drift=false" >> "$GITHUB_OUTPUT"
else
echo "drift=true" >> "$GITHUB_OUTPUT"
fi
tail -n 40 freshness.log || true

- name: Verify reference URLs still resolve
run: python3 scripts/check_links.py || true

- name: Open drift issue
if: steps.check.outputs.drift == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
BODY=$(printf 'The weekly freshness check detected drift in one or more GCP source docs.\n\n```\n%s\n```\n\nReview the changed sources and update the affected skills, then re-run `make crawl`.' "$(tail -n 60 freshness.log)")
gh issue create \
--title "Freshness: GCP source drift detected ($(date -u +%Y-%m-%d))" \
--body "$BODY" \
--label "freshness" || true
38 changes: 38 additions & 0 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: gate

# Runs the plugin validation gate on every PR and push to main.
# Mirrors the local pre-commit hook (make gate) so CI and local agree.

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install deps
run: pip install pyyaml pytest

- name: Validate skill contracts
run: python3 scripts/validate_skills.py

- name: Validate plugin packaging (Claude / AGY / Codex / Kimi)
run: python3 scripts/validate_plugin.py

- name: Lint Mermaid diagrams
run: python3 scripts/validate_mermaid.py

- name: Check reference URLs resolve
run: python3 scripts/check_links.py

- name: Smoke tests
run: python3 -m pytest tests/skill-smoke-tests/ -q
13 changes: 9 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all gate validate manifest preflight lint test check crawl crawl-dry hooks help
.PHONY: all gate validate manifest preflight lint mermaid test check crawl crawl-dry hooks help

PYTHON := python3
SCRIPTS := scripts
Expand All @@ -7,9 +7,9 @@ TESTS := tests/skill-smoke-tests
# ─── 3-minute validation gate (validate + manifest + lint + test) ───────────
# This is the pre-commit gate. Install the hook once with: make hooks

gate: validate manifest lint test
gate: validate manifest mermaid lint test
@echo ""
@echo "Gate passed: validate + manifest + lint + test"
@echo "Gate passed: validate + manifest + mermaid + lint + test"

# ─── Individual targets ──────────────────────────────────────────────────────

Expand All @@ -25,6 +25,10 @@ preflight:
@echo "==> Preflight: infer GCP context from environment (read-only)..."
@$(PYTHON) $(SCRIPTS)/preflight.py

mermaid:
@echo "==> Linting Mermaid diagrams (GitHub render-safety)..."
@$(PYTHON) $(SCRIPTS)/validate_mermaid.py

lint:
@echo "==> Checking reference URLs..."
@$(PYTHON) $(SCRIPTS)/check_links.py
Expand Down Expand Up @@ -62,9 +66,10 @@ help:
@echo ""
@echo "GoogleCloud Plugin — Makefile Targets"
@echo ""
@echo " make gate Pre-commit gate: validate + manifest + lint + test"
@echo " make gate Pre-commit gate: validate + manifest + mermaid + lint + test"
@echo " make validate Validate all SKILL.md frontmatter (contract check)"
@echo " make manifest Validate plugin is installable (Claude/AGY/Codex/Kimi)"
@echo " make mermaid Lint Mermaid diagrams for GitHub render-safety"
@echo " make lint Check all reference URLs resolve (HTTP 200)"
@echo " make test Run skill smoke tests via pytest"
@echo " make check Freshness check: hash drift detection vs live sources"
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
<p align="center">
<img src="assets/googlecloud-plugin-hero.png" alt="GoogleCloud Plugin — Human-led. Agent-powered. Built on Google Cloud." width="55%">
<img src="assets/images/googlecloud-plugin-by-jp.png" alt="googlecloud-plugin by JP — Open source. Agent-powered. Built for Google Cloud." width="100%">
</p>

# GoogleCloud Plugin

> **Human-led. Agent-powered. Built on Google Cloud.**

> **Install once, and your coding agent gains a full Google Cloud delivery team — a vendor-objective solution designer, a GCP architect, an agentic-systems architect, plus security, SRE, and QA — wired into a design-first, security-first delivery gate.**

Skill libraries for Google Cloud are everywhere. What they miss is the operating contract: who designs before anyone codes, who signs off on least-privilege before anything deploys, what "done" means when an agent ships to a billable cloud. This plugin ships that contract, with the GCP know-how bolted in.
Skill libraries for Google Cloud are everywhere. What they miss is the operating contract: who designs before anyone writes code, who signs off on least-privilege before anything deploys, what "done" means when an agent ships to a billable cloud. This plugin ships that contract, with the GCP know-how built in.

One install gives an agent (Claude, Codex, or Antigravity) five personas, eleven service skills, the MCP server map, and a research pipeline that keeps every reference current — because a plugin that silently goes stale is worse than no plugin at all.
One install gives an agent (Claude, Codex, or Antigravity) five personas, eleven service skills, the MCP server map, and a research pipeline that keeps every reference current. A plugin that goes stale in silence is worse than none.

The plugin is **GCP-only and opinionated by design**. It spans the estate a builder actually touches — from Firebase and Cloud Run to BigQuery and GKE, and up through the agent frameworks that now sit on top: ADK, and the MCP / A2A / AP2 protocol stack, deployed on the Gemini Enterprise Agent Platform. Refine it for your team; keep the contract coherent while you do.
The plugin is **GCP-only and opinionated by design**. It covers the estate a builder touches — Firebase, Cloud Run, BigQuery, GKEand the agent frameworks now built on top: ADK, and the MCP / A2A / AP2 protocol stack, deployed on the Gemini Enterprise Agent Platform. Refine it for your team. Keep the contract coherent as you do.

> **Speed is easy. Safe speed is engineered.**

Expand Down Expand Up @@ -89,13 +87,13 @@ sequenceDiagram

rect rgb(240, 253, 244)
note over Sec,Impl: Implementation — known-good GCP patterns, cost-warned
Impl->>Impl: 6. Build on google-cloud patterns; warn before every billable action
Impl->>Impl: 6. Build on google-cloud patterns, warn before every billable action
end

rect rgb(254, 249, 231)
note over Impl,QA: Quality Gate — receipts, not polish
Impl->>QA: 7. Submit candidate
QA-->>Ops: 8. make gate (validate + lint + test) + agent-eval — pass or BLOCK
QA-->>Ops: 8. make gate (all checks) + agent-eval — pass or BLOCK
end

rect rgb(224, 242, 254)
Expand Down Expand Up @@ -157,7 +155,7 @@ Freshness is a feature, not a chore: `make check` runs weekly and flags any GCP

## About the author

Created and maintained by **Jaroslav Pantsjoha (JP)** — Technical Director and Enterprise Agent Solution Architect at Cognizant, Google Developer Expert (Google Cloud), speaker, and author of *Building the Agentic Enterprise on Google Cloud* (Packt) and co-author of *Mastering Multi-Agent Systems on Google Cloud* (AVA Publishing).
Created and maintained by **Jaroslav Pantsjoha (JP)** — Technical Director and Enterprise Agent Solution Architect at Cognizant, Google Developer Expert (Google Cloud), speaker, and author of *Building the Agentic Enterprise on Google Cloud* (Packt).

I built this to make my own Google Cloud work repeatable, then to share it. The model is the easy part; the durable engineering is the harness around it — the skills, rules, MCP servers, gates, and evals. GCP patterns outlast the week's model release, so they are worth encoding once and reusing. This plugin is that baseline aimed squarely at Google Cloud — the GCP companion to my [`join-the-team`](https://github.com/jpantsjoha/ai-native-developer-experience) harness. Part of the **#HarnessEngineering** body of work and the Google Cloud / GDE community.

Expand All @@ -169,3 +167,7 @@ I built this to make my own Google Cloud work repeatable, then to share it. The
## Licence

Open source under the [MIT License](LICENSE) — free for personal and commercial use, modification, and redistribution.

---

*This is an independent, community project. It is not affiliated with, sponsored by, or endorsed by Google LLC. "Google Cloud", "GCP", "Gemini", "Vertex AI", and related names are trademarks of Google LLC, used here only to describe what the plugin works with.*
49 changes: 49 additions & 0 deletions docs/SUBMISSIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Submission Targets

Where to list `googlecloud-plugin` so builders can find and install it. Each entry has the destination, what it needs, and the status.

## 1. Claude Plugin Hub — primary (Claude Code)

- **Where:** https://www.claudepluginhub.com
- **Needs:** a public repo with `.claude-plugin/plugin.json` + `.claude-plugin/marketplace.json` (both present). Submit the repo URL.
- **Precedent:** `join-the-team` is listed here.
- **Install string once listed:**
```
/plugin marketplace add jpantsjoha/googlecloud-plugin
/plugin install googlecloud-plugin@googlecloud-plugin-marketplace
```
- **Status:** ready to submit.

## 2. Gemini CLI Extensions — Antigravity / Gemini

- **Where:** https://github.com/gemini-cli-extensions (community extensions org; `gcloud-mcp`, `dak` live here)
- **Needs:** `gemini-extension.json` (present) with `contextFileName` + inline `mcpServers` (present). Open a PR / request listing per that org's contribution guide.
- **Install once listed:**
```
agy plugin install https://github.com/jpantsjoha/googlecloud-plugin
```
- **Status:** validated locally (`agy plugin validate .` → 17 skills + 2 mcpServers). Ready to request listing.

## 3. awesome-claude-code — community list

- **Where:** https://github.com/hesreallyhim/awesome-claude-code
- **Needs:** a PR adding the repo under the plugins/skills section, one-line description.
- **Status:** ready — draft the PR entry.

## 4. MCP servers registry — optional (MCP angle)

- **Where:** https://github.com/modelcontextprotocol/servers
- **Needs:** only relevant if we surface the bundled MCP setup as a standalone entry; the plugin mainly *consumes* MCP servers rather than *being* one. Low priority.
- **Status:** optional.

## Pre-submission checklist

- [x] Public repo, MIT licensed
- [x] `.claude-plugin/plugin.json` + `marketplace.json` valid
- [x] `gemini-extension.json` valid; `agy plugin validate .` passes
- [x] `.kimi-plugin/plugin.json` + `AGENTS.md` (Codex) present
- [x] README with install blocks for all four harnesses
- [x] Repo description + 20 topics set
- [x] `make gate` green in CI (gate.yml)
- [x] "Not affiliated with Google" disclaimer in README
- [ ] Tag a release (`v0.1.0`) before submitting
115 changes: 115 additions & 0 deletions scripts/validate_mermaid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Lint Mermaid diagrams in Markdown for the errors GitHub's renderer rejects.

Dependency-free structural checks for ```mermaid fenced blocks. Focused on the
failure classes that actually break GitHub rendering (and that a URL linter
misses):

1. Semicolon in a sequenceDiagram message — ';' is a statement separator, so
text after it is parsed as a new (invalid) statement. This is the bug that
shipped in v0.1.0's README.
2. Unbalanced block keywords (rect/opt/alt/loop/par/critical/break) vs 'end'.
3. Message arrows referencing an undeclared participant/actor.

Exit 0: all diagrams parse-safe. Exit 1: one or more issues.
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).parent.parent
MD_FILES = ["README.md", "PLUGIN.md"]

BLOCK_OPENERS = ("rect", "opt", "alt", "loop", "par", "critical", "break", "box")
ARROW_RE = re.compile(r"^\s*([A-Za-z0-9_]+)\s*(?:-->>|->>|-->|->|--x|-x|--\)|-\))\s*[+-]?\s*([A-Za-z0-9_]+)\s*:(.*)$")
DECL_RE = re.compile(r"^\s*(?:participant|actor)\s+([A-Za-z0-9_]+)")


def extract_mermaid_blocks(md: Path) -> list[tuple[int, list[str]]]:
blocks: list[tuple[int, list[str]]] = []
lines = md.read_text().splitlines()
i = 0
while i < len(lines):
if lines[i].strip().startswith("```mermaid"):
start = i + 1
body: list[str] = []
i += 1
while i < len(lines) and not lines[i].strip().startswith("```"):
body.append(lines[i])
i += 1
blocks.append((start, body))
i += 1
return blocks


def lint_block(md: Path, start_line: int, body: list[str], errors: list[str]) -> None:
is_sequence = any(l.strip().startswith("sequenceDiagram") for l in body)
declared: set[str] = set()
block_depth = 0

for offset, raw in enumerate(body):
lineno = start_line + offset + 1 # 1-based file line
line = raw.strip()
if not line or line.startswith("%%"):
continue

d = DECL_RE.match(line)
if d:
declared.add(d.group(1))
continue

first = line.split()[0] if line.split() else ""
if first in BLOCK_OPENERS:
block_depth += 1
continue
if first == "end":
block_depth -= 1
if block_depth < 0:
errors.append(f"{md.name}:{lineno}: unmatched 'end' (no open block)")
block_depth = 0
continue

if is_sequence:
m = ARROW_RE.match(raw)
if m:
src, dst, msg = m.group(1), m.group(2), m.group(3)
declared.update([src, dst]) # implicit declaration is allowed
if ";" in msg:
errors.append(
f"{md.name}:{lineno}: ';' in sequence message — it is a statement "
f"separator and breaks rendering. Use ',' or ' — '. Text: '{msg.strip()}'"
)

if block_depth != 0:
errors.append(
f"{md.name}: mermaid block starting near line {start_line} has "
f"{block_depth} unclosed block(s) (rect/opt/alt/... without 'end')"
)


def main() -> None:
errors: list[str] = []
checked = 0
for name in MD_FILES:
md = ROOT / name
if not md.exists():
continue
for start, body in extract_mermaid_blocks(md):
checked += 1
lint_block(md, start, body, errors)

if checked == 0:
print("No mermaid diagrams found")
return

if errors:
for e in errors:
print(f"FAIL {e}")
print(f"\n{len(errors)} mermaid issue(s)", file=sys.stderr)
sys.exit(1)

print(f"{checked} mermaid diagram(s) parse-safe")


if __name__ == "__main__":
main()
Loading