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
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true

[*.py]
indent_style = space
indent_size = 4
15 changes: 15 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Change and affected skills

Describe the user-visible change, sibling dependencies and compatibility impact.

## Evidence

Record commands, results and exact tested commit. Distinguish structural checks, runtime tests and visual review. Name checks not run and why.

## Boundaries

Confirm that activation keys, consent, private assets, approval gates and specialist entry points remain intact. Identify any intentional exception. No registry release, visibility change or live external action is implied.

## Family consistency

For shared changes, identify affected sibling PRs and the orchestrator drift-audit result. Do not claim coordinated merge until each PR is confirmed merged.
23 changes: 23 additions & 0 deletions .github/workflows/package-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Package contract
on: [push, pull_request]
permissions:
contents: read
jobs:
validate-package:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Test the package contract
run: python -m unittest discover -s tests -p 'test_package_*.py' -v
- name: Validate the full checkout
run: python scripts/validate_package.py
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# CompleteTech LLC Skills: maintainer instructions

Read [SKILL.md](SKILL.md) for specialist behavior, [ONBOARDING.md](ONBOARDING.md) for first use, [BRANDING.md](BRANDING.md) for presentation and handoffs, and [CONTRIBUTING.md](CONTRIBUTING.md) before changing this checkout.

Preserve the activation key, existing CLI paths, templates, dependencies, approval gates and consent boundaries. A common package does not require identical generators. Keep private assets private and use synthetic fixtures. Never scan personal logs, submit certificate data, install schedules, send messages or publish artifacts merely to test alignment.

Run `python -m unittest discover -s tests -p 'test_package_*.py' -v` and `python scripts/validate_package.py`, then the existing Quality checks. Do not disable failing checks or replace verified evidence with successful rendering. Describe any checks not run.

Shared files are maintained together through the orchestrator's catalog and drift audit. Update affected siblings in focused PRs; review exact heads and checks before merging. Registry publishing and visibility changes are separate actions. Never claim a PR exists or merged without its successful GitHub response.
30 changes: 30 additions & 0 deletions BRANDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# CompleteTech LLC Skills: branding and handoffs

[Start here](ONBOARDING.md) · [Asset rights](BRAND_ASSETS.md) · [Specialist instructions](SKILL.md)

## Resolve identity before rendering

An approved engagement brand takes precedence over a starter example. Preserve the operator's explicit settings; ask about unresolved identity instead of silently applying company branding. The ledger remains neutral by default. CompleteTech examples identify this library, not permission to represent another business.

Use the existing local `assets/logo.png` for authorized CompleteTech output. Do not synthesize replacement marks, fetch remote artwork, redistribute fonts, or transfer private seals/signatures to public repositories. The MIT code license does not replace the asset policy.

## Starter palette

These established library colors are a reference, not new runtime defaults:

| Token | Value |
|---|---|
| Accent | `#1E3A8A` |
| Ink | `#0F172A` |
| Soft accent | `#EEF2FF` |
| Muted text | `#64748B` |
| Border | `#E2E8F0` |
| Alternating row | `#F8FAFC` |

Keep specialist layouts: invoices, envelopes, certificates, proposals and dashboards have different constraints. Use existing renderer options, generator configuration or the ledger's explicit branding preset, as documented in this skill's README and `--help`. This document does not introduce a universal brand-import flag.

## Handoff to the next skill

Carry verified facts and their sources, artifact path/version, approved identity and local logo path, palette overrides, confidentiality, intended audience, approval owner/status, blockers and next specialist. Preserve these in the orchestrator's existing `project_state`; do not replace its workflow schema with an incompatible parallel state format.

Missing approval stays unknown or draft. Rendering, anonymization, a clean package check or a reused brand does not authorize sending, publication, billing, signing, attendance claims or launch. Review the actual output for clipping, contrast, placeholders and factual accuracy before approved use.
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Contributing to CompleteTech LLC Skills

[Start here](ONBOARDING.md) · [Branding and handoffs](BRANDING.md) · [Maintainer instructions](AGENTS.md) · [Specialist instructions](SKILL.md)

## Compatibility

Keep each skill independently usable. Preserve activation keys, root generator CLIs, templates, runtime dependencies, consent and approval gates. The ledger's standard-library core and neutral branding are intentional differences, not gaps to remove.

## Verification

From a full checkout using Python 3.12, the package CI baseline:

```bash
python -m unittest discover -s tests -p 'test_package_*.py' -v
python scripts/validate_package.py
```

Also run the existing quality and regression commands in [README.md](README.md). Package checks are read-only and do not execute specialist generators. They validate metadata, required files, local documentation links and PNG chunk integrity, not decoded artwork or document truth. Do not weaken original Quality checks to obtain a green result.

## Coordinated changes

Keep `skill-package.json`, onboarding and actual entry points synchronized. Shared validator, tests, CI, maintainer instructions and branding guidance must match the orchestrator's shared-file audit. Use focused PRs and report exact checks, failures and limitations. Respect branch protection, review requirements and changed heads.

Use synthetic fixtures and new output paths instead of overwriting committed previews. Never commit secrets, personal logs, real account mappings, client documents or unapproved quotes. Code remains under [LICENSE](LICENSE); branding remains under [BRAND_ASSETS.md](BRAND_ASSETS.md). Do not redistribute private assets or fonts. Registry releases and repository visibility changes require separate authorization.
64 changes: 64 additions & 0 deletions ONBOARDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Start here: agentic-email-skill

<p align="center"><img src="assets/logo.png" alt="CompleteTech LLC logo" width="260"></p>

**CompleteTech LLC Skills** — Email copy and sequences; never automatic sending.

[Overview](README.md) · [Agent instructions](SKILL.md) · [Contributing](CONTRIBUTING.md) · [Skill library](https://github.com/CompleteTech-LLC/agentic-services-orchestrator-skill/blob/main/references/skill-family.md)

## 1. Choose the right skill

This is a `catalog-renderer`. Its install/activation key is **`agentic-email-skill`**. Use the orchestrator when work spans multiple specialists; install only the specialists the engagement needs. Install the complete skill directory, including `SKILL.md`, references, scripts and assets, in the directory supported by your agent. Do not install only the Markdown file. Keep this skill separate from its siblings.

## 2. Get a full checkout and prepare Python

Python 3.12 is the shared CI baseline. Git is needed only for cloning. The full GitHub checkout includes the existing brand assets; text-only registry packages can omit binary logos/previews and are not the target of the checkout validator.

```bash
git clone https://github.com/CompleteTech-LLC/agentic-email-skill.git agentic-email-skill
cd agentic-email-skill
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python scripts/validate_package.py
```

On Windows PowerShell, create the environment with `py -3 -m venv .venv`, then use `.\.venv\Scripts\python.exe` instead of `python` in subsequent commands. Activation is optional; do not change machine execution policy for this skill. For an existing checkout, skip `git clone` and start at environment setup.

## 3. Run a safe demonstration

Run from the skill root. The commands below use bundled synthetic/example inputs, not client data. They can replace an earlier demonstration at the same output path; choose a new filename when retaining prior output.

```bash
python scripts/render_email.py --list
python scripts/render_pdf.py --markdown assets/examples/example.md --out output/onboarding-demo.pdf --logo assets/logo.png --title "Onboarding demonstration"
```

The catalog lists available templates; the second command renders the bundled demonstration Markdown to `output/onboarding-demo.pdf`. This is a rendering smoke test, not approval of its illustrative facts.

Open the generated output and inspect page breaks, tables, logo, text and any remaining placeholders. Package validation checks structure; it does not certify visual quality or factual correctness. Committed previews in `assets/examples/` are references, not destinations for your first run.

## 4. Use the skill with your agent

Read [SKILL.md](SKILL.md), select `agentic-email-skill` using your agent's skill activation mechanism, and provide verified inputs, the requested artifact, destination and required approval owners. Agent discovery paths differ by product; follow that product's installation documentation rather than assuming one global path. Do not grant broader permissions simply because multiple skills are installed together.

## Branding and handoff

Use the existing CompleteTech LLC logo and the practical, evidence-led voice described in [README.md](README.md). The code license does not grant logo, seal or signature rights: follow [BRAND_ASSETS.md](BRAND_ASSETS.md). Do not replace or republish private artwork or fonts.

For a multi-skill handoff, preserve the source facts, artifact paths, approval owner/status, blockers and next specialist in the orchestrator's existing `project_state` schema. Do not treat a generated artifact, anonymization or successful check as permission to send, publish, bill, sign, award or launch.

## Safety and permissions

The documented generation workflow is local. Installation may contact GitHub and package indexes, but generation does not send messages, move money, publish documents, grant approval or install background services. Review the specialist boundaries in [SKILL.md](SKILL.md) before using real inputs.

## Troubleshooting and verification

A missing module usually means the active interpreter differs from the one used for `pip`; use `python -m pip` in the same environment and this repository's `requirements.txt`. Missing logos or references usually indicate a partial/text-only installation or the wrong working directory; use a full checkout and run from its root. Do not download replacement branding from unrelated sources.

```bash
python -m unittest discover -s tests -p test_package_contract.py -v
python scripts/validate_package.py
```

These checks use the standard library and do not execute the listed generators, scan accounts, contact services or install schedules. Follow [CONTRIBUTING.md](CONTRIBUTING.md) and the repository's existing quality instructions for the additional runtime, rendering, parser and diagram checks.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**CompleteTech LLC Skills** · [Start here](ONBOARDING.md) · [Agent instructions](SKILL.md) · [Skill library](https://github.com/CompleteTech-LLC/agentic-services-orchestrator-skill/blob/main/references/skill-family.md) · [Contributing](CONTRIBUTING.md)

# Agentic Email Skill

<p align="center">
Expand Down
134 changes: 134 additions & 0 deletions scripts/validate_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""Read-only, standard-library validation of a full CompleteTech skill checkout."""
from __future__ import annotations
import argparse
import json
import re
import struct
import zlib
from pathlib import Path
from urllib.parse import unquote, urlsplit

FAMILY = "completetech-skills"
FIELDS = set("schema_version family repository skill_name kind entrypoints example_inputs network_mode private".split())
FILES = ("README.md", "SKILL.md", "LICENSE", "BRAND_ASSETS.md", "ONBOARDING.md", "CONTRIBUTING.md", "BRANDING.md", "AGENTS.md", "requirements.txt", "agents/openai.yaml")
NAVIGATION = ("CompleteTech LLC Skills", "[Start here](ONBOARDING.md)", "[Contributing](CONTRIBUTING.md)", "assets/logo.png")
KINDS = ("catalog-renderer", "config-generator", "orchestrator", "usage-ledger")
NETWORK_MODES = ("local", "operator-selected-hosts", "optional-receipts")


def unique_object(pairs: list[tuple[str, object]]) -> dict:
result: dict = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result


def local_file(root: Path, value: object) -> Path:
if not isinstance(value, str) or not value or any(ord(c) < 32 for c in value):
raise ValueError(f"invalid package path: {value!r}")
if "\\" in value or ":" in value or value.startswith("/") or any(p in ("", ".", "..") for p in value.split("/")):
raise ValueError(f"unsafe package path: {value!r}")
path = (root / value).resolve()
if not path.is_relative_to(root.resolve()) or not path.is_file():
raise ValueError(f"missing or escaping package file: {value}")
return path


def validate_png(path: Path) -> None:
"""Check bounded chunks and CRCs; decoding and visual review remain separate."""
with path.open("rb") as handle:
data = handle.read(16 * 1024 * 1024 + 1)
if len(data) > 16 * 1024 * 1024 or data[:8] != b"\x89PNG\r\n\x1a\n":
raise ValueError("invalid or oversized PNG")
offset, header, image = 8, False, False
while offset + 12 <= len(data):
length = struct.unpack_from(">I", data, offset)[0]
kind = data[offset + 4:offset + 8]
end = offset + length + 12
if end > len(data):
raise ValueError("truncated PNG chunk")
payload = data[offset + 8:end - 4]
if zlib.crc32(kind + payload) != struct.unpack_from(">I", data, end - 4)[0]:
raise ValueError("PNG checksum mismatch")
if not header and kind != b"IHDR":
raise ValueError("PNG must start with IHDR")
if kind == b"IHDR":
if header or length != 13:
raise ValueError("invalid PNG header")
width, height, depth, color, compression, filtering, interlace = struct.unpack(">IIBBBBB", payload)
depths = {0: (1, 2, 4, 8, 16), 2: (8, 16), 3: (1, 2, 4, 8), 4: (8, 16), 6: (8, 16)}
if not 0 < width < 2**31 or not 0 < height < 2**31 or depth not in depths.get(color, ()) or compression or filtering or interlace not in (0, 1):
raise ValueError("invalid PNG parameters")
header = True
elif kind == b"IDAT":
image = image or bool(length)
elif kind == b"IEND":
if length or not image or end != len(data):
raise ValueError("invalid PNG end")
return
offset = end
raise ValueError("incomplete PNG")


def validate(root: Path) -> list[str]:
errors: list[str] = []
try:
manifest = json.loads(local_file(root, "skill-package.json").read_text(encoding="utf-8"), object_pairs_hook=unique_object)
if not isinstance(manifest, dict) or set(manifest) != FIELDS:
raise ValueError("manifest fields must match schema 1")
name, repo = manifest["skill_name"], manifest["repository"]
if type(manifest["schema_version"]) is not int or manifest["schema_version"] != 1 or manifest["family"] != FAMILY:
errors.append("invalid schema_version or family")
if not isinstance(name, str) or not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", name) or len(name) > 64:
errors.append("invalid skill_name")
if not isinstance(repo, str) or repo not in (f"CompleteTech-LLC/{name}", f"CompleteTech-LLC/{name}-skill"):
errors.append("repository does not match skill_name")
if manifest["kind"] not in KINDS or manifest["network_mode"] not in NETWORK_MODES or type(manifest["private"]) is not bool:
errors.append("invalid kind, network_mode or private")
paths: list[object] = [*FILES, "assets/logo.png"]
if manifest["kind"] == "config-generator":
paths.append("config.ini")
for field in ("entrypoints", "example_inputs"):
values = manifest[field]
if not isinstance(values, list) or not values or not all(isinstance(v, str) for v in values):
errors.append(f"{field} must be a nonempty string array")
else:
if len(set(values)) != len(values):
errors.append(f"duplicate {field}")
paths.extend(values)
for value in paths:
try:
local_file(root, value)
except (OSError, ValueError, RuntimeError) as exc:
errors.append(str(exc))
skill = local_file(root, "SKILL.md").read_text(encoding="utf-8")
header = re.match(r"\A---\n(.*?)\n---(?:\n|\Z)", skill, re.S)
names = re.findall(r'''(?m)^name:\s*['"]?([a-z0-9-]+)['"]?\s*$''', header.group(1)) if header else []
if names != [name]:
errors.append("SKILL.md name must match manifest")
readme = local_file(root, "README.md").read_text(encoding="utf-8")
errors.extend(f"missing README navigation: {item}" for item in NAVIGATION if item not in readme)
validate_png(local_file(root, "assets/logo.png"))
for document in ("ONBOARDING.md", "CONTRIBUTING.md", "BRANDING.md", "AGENTS.md"):
text = local_file(root, document).read_text(encoding="utf-8")
for target in re.findall(r"\[[^\]]+\]\(([^)]+)\)", text):
parsed = urlsplit(target)
if target.startswith("#") or parsed.scheme in ("https", "http", "mailto"):
continue
if parsed.scheme or parsed.netloc:
raise ValueError(f"unsupported documentation link: {target}")
local_file(root, unquote(parsed.path))
except (OSError, UnicodeError, ValueError, RuntimeError, RecursionError) as exc:
errors.append(str(exc))
return errors


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1])
issues = validate(parser.parse_args().root)
print("\n".join(issues) if issues else "Package contract OK; no specialist commands executed")
raise SystemExit(bool(issues))
11 changes: 11 additions & 0 deletions skill-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"schema_version": 1,
"family": "completetech-skills",
"repository": "CompleteTech-LLC/agentic-email-skill",
"skill_name": "agentic-email-skill",
"kind": "catalog-renderer",
"entrypoints": ["scripts/render_email.py", "scripts/render_pdf.py"],
"example_inputs": ["assets/examples/example.md"],
"network_mode": "local",
"private": false
}
Loading
Loading