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
16 changes: 7 additions & 9 deletions .github/workflows/publish-github-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,12 @@ jobs:
set -euo pipefail

NOTES=$(mktemp)
{
echo "Automated release for ${RELEASE_TAG}."
echo
echo "Published packages:"
echo "- npm: @cafitac/hermit-agent@${VERSION}"
echo "- PyPI: cafitac-hermit-agent==${VERSION}"
} > "$NOTES"
python3 scripts/render_release_notes.py \
--tag "$RELEASE_TAG" \
--version "$VERSION" \
--repo "$REPOSITORY" \
--reason "tag-triggered release publication" \
--out "$NOTES"

if gh release view "$RELEASE_TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
gh release edit "$RELEASE_TAG" \
Expand All @@ -90,7 +89,6 @@ jobs:
--repo "$REPOSITORY" \
--verify-tag \
--title "$RELEASE_TAG" \
--notes-file "$NOTES" \
--generate-notes
--notes-file "$NOTES"

echo "Created GitHub Release ${RELEASE_TAG}."
23 changes: 12 additions & 11 deletions .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,21 +308,23 @@ jobs:
RELEASE_TAG: ${{ needs.determine_version.outputs.release_tag }}
RELEASE_REASON: ${{ needs.classify_release.outputs.reason }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Create or update GitHub Release
shell: bash
run: |
set -euo pipefail

NOTES=$(mktemp)
{
echo "Automated release for ${RELEASE_TAG}."
echo
echo "Release classifier: ${RELEASE_REASON}"
echo
echo "Published packages:"
echo "- npm: @cafitac/hermit-agent@${NEXT}"
echo "- PyPI: cafitac-hermit-agent==${NEXT}"
} > "$NOTES"
python3 scripts/render_release_notes.py \
--tag "$RELEASE_TAG" \
--version "$NEXT" \
--repo "${{ github.repository }}" \
--reason "$RELEASE_REASON" \
--out "$NOTES"

if gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then
gh release edit "$RELEASE_TAG" \
Expand All @@ -337,8 +339,7 @@ jobs:
--repo "${{ github.repository }}" \
--verify-tag \
--title "$RELEASE_TAG" \
--notes-file "$NOTES" \
--generate-notes
--notes-file "$NOTES"

echo "Created GitHub Release ${RELEASE_TAG}."

Expand Down
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
- Refreshed the README opening section to position Hermit as a distinct open-source executor layer for Claude Code and Codex rather than a generic terminal UI.
- Added a comparison section that explains why teams would pair Hermit with Claude Code or Codex instead of treating it as a replacement orchestrator.
- Added explicit "who Hermit is for / not for" guidance so the landing page qualifies the intended audience instead of only describing features.
- Added a reusable `docs/open-source-positioning.md` copy deck for repository descriptions, social preview messaging, release framing, audience-fit guidance, and topic candidates.
- Added a reusable `docs/open-source-positioning.md` copy deck for repository descriptions, social preview messaging, release framing, audience-fit guidance, topic candidates, and hero-asset references.
- Added a final social-preview asset set: editable SVG, ready-to-upload PNG export, and a local review page under `docs/assets/` for GitHub/social-preview iteration.
- Added a dedicated `docs/assets/hermit-readme-hero.svg` so the README explains the planner/executor split with a reusable visual instead of terminal ASCII.
- Added `docs/social-preview-ops.md` so maintainers have a concrete review/export/upload checklist for the GitHub social preview image.
- Added `docs/release-notes-template.md` so release blurbs and GitHub Releases can reuse the same planner/executor positioning without improvising each time.
- Reordered the README badge row around release health and package availability, and tightened the hero copy so the landing page reads closer to the final social-preview message.
- Reordered the README badge row around release health and package availability, tightened the hero copy, and swapped the ASCII diagram for a polished SVG hero that matches the social-preview tone.
- Updated package and repository-facing descriptions to emphasize the MCP executor + cheaper execution lane story instead of the outdated Codex-first fallback wording.
- Tightened the public metadata around predictable local / flat-rate defaults so the repository pitch matches the current install and routing policy.

Expand All @@ -23,6 +24,7 @@
- Added a dedicated `Publish GitHub Release` workflow on `v*` tags for manual or external tag pushes, while the main release workflow also creates the GitHub Release directly so auto-published releases do not depend on cross-workflow tag triggers.
- Added release-workflow concurrency plus idempotent npm publish, tag-push, and GitHub Release checks so reruns do not accidentally create duplicate artifacts.
- Fixed release write-back to use the configured push token correctly, and kept the protected-`main` fallback that opens a sync PR when direct write-back is rejected.
- GitHub Release notes are now rendered from `scripts/render_release_notes.py` so the auto-publish path and the tag/manual fallback both follow the same release-note template and planner/executor framing.

### Install and model-selection UX
- Switched the primary onboarding flow to `npm install -g @cafitac/hermit-agent` followed by `hermit`, with guided setup offered from startup when Claude Code or Codex integration is incomplete.
Expand Down
13 changes: 2 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,7 @@

> Hermit adds a dedicated MCP execution lane under Claude Code or Codex: keep the premium orchestrator for judgment, and route the repetitive repo work to predictable local or flat-rate models.

```
┌──────────────┐
│ Claude Code │──┐
│ (planner) │ │ ┌──────────────┐ any OpenAI-compatible ┌───────┐
└──────────────┘ ├───▶│ HermitAgent │ ────────────────────────▶ │ LLM │
│ │ (executor) │ └───────┘
┌──────────────┐ │ └──────────────┘
│ Codex │──┘ local / flat-rate by default
│ (planner) │
└──────────────┘
```
![Hermit README hero](docs/assets/hermit-readme-hero.svg)

Claude Code or Codex stays in charge of planning, interviewing, and review. Hermit takes the mechanical path: file edits, test runs, refactors, commits, and MCP-executed follow-through on predictable local or flat-rate execution models. The switch is one word in a slash command: `/foo` → `/foo-hermit`.

Expand Down Expand Up @@ -164,6 +154,7 @@ MIT — see [LICENSE](LICENSE).
- [docs/open-source-positioning.md](docs/open-source-positioning.md) — short public-facing copy for descriptions, releases, and future social previews
- [docs/release-notes-template.md](docs/release-notes-template.md) — reusable release-note framing that matches Hermit's planner/executor positioning
- [docs/social-preview-ops.md](docs/social-preview-ops.md) — how to review, export, and upload the GitHub social-preview image
- [docs/assets/hermit-readme-hero.svg](docs/assets/hermit-readme-hero.svg) — README hero graphic for the planner/executor split
- [docs/assets/hermit-social-preview.svg](docs/assets/hermit-social-preview.svg) — final editable social-preview asset for repo cards and launch posts
- [docs/assets/hermit-social-preview.png](docs/assets/hermit-social-preview.png) — ready-to-upload GitHub social-preview export
- [docs/assets/hermit-social-preview-review.html](docs/assets/hermit-social-preview-review.html) — local review page for checking the social-preview composition before export
Expand Down
41 changes: 41 additions & 0 deletions docs/assets/hermit-readme-hero.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/open-source-positioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ Hermit ships the mechanical follow-through.
One MCP executor layer.
Cheaper execution across multiple orchestrators.

## Homepage / hero copy variants

### Hero visual assets
- README hero: `docs/assets/hermit-readme-hero.svg`
- Social preview card: `docs/assets/hermit-social-preview.svg`
- Social preview export: `docs/assets/hermit-social-preview.png`

Use the README hero for in-page explanation of the planner/executor split, and use the social-preview card for repository thumbnails, launch posts, and Open Graph-style previews.

## Social preview asset

- Final editable asset: `docs/assets/hermit-social-preview.svg`
Expand Down
123 changes: 123 additions & 0 deletions scripts/render_release_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
from __future__ import annotations

import argparse
import os
import re
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = ROOT / "docs/release-notes-template.md"


def git(*args: str) -> str:
return subprocess.check_output(["git", *args], cwd=ROOT, text=True).strip()


def safe_git(*args: str) -> str:
try:
return git(*args)
except Exception:
return ""


def detect_previous_tag(current_tag: str) -> str:
tags = [t.strip() for t in safe_git("tag", "--list", "v*").splitlines() if t.strip()]
tags = [t for t in tags if t != current_tag]
def key(tag: str):
parts = tag.lstrip("v").split(".")
return tuple(int(p) if p.isdigit() else 0 for p in parts)
tags.sort(key=key)
return tags[-1] if tags else ""


def recent_subjects(previous_tag: str) -> list[str]:
if previous_tag:
text = safe_git("log", "--format=%s", f"{previous_tag}..HEAD")
else:
text = safe_git("log", "-n", "5", "--format=%s")
subjects = [line.strip() for line in text.splitlines() if line.strip()]
cleaned: list[str] = []
seen: set[str] = set()
for subject in subjects:
if subject.startswith("chore: release v"):
continue
if subject in seen:
continue
seen.add(subject)
cleaned.append(subject)
return cleaned[:5]


def sentence_case(subject: str) -> str:
subject = re.sub(r"\s*\(#\d+\)$", "", subject).strip()
if not subject:
return subject
return subject[0].upper() + subject[1:]


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--tag", required=True)
parser.add_argument("--version", required=True)
parser.add_argument("--repo", required=True)
parser.add_argument("--reason", default="automated main-branch release")
parser.add_argument("--out", required=True)
args = parser.parse_args()

current_tag = args.tag
version = args.version
repo = args.repo
reason = args.reason
previous_tag = detect_previous_tag(current_tag)
subjects = recent_subjects(previous_tag)
headline = subjects[0] if subjects else f"Automated release for {current_tag}"
summary = sentence_case(headline)

template_text = TEMPLATE.read_text()
opening_lines = re.findall(r"^- (.+)$", template_text.split("## Reusable opening lines", 1)[1].split("## Reusable closing lines", 1)[0], re.M)
closing_lines = re.findall(r"^- (.+)$", template_text.split("## Reusable closing lines", 1)[1], re.M)
opening = opening_lines[0] if opening_lines else "Hermit keeps planner judgment premium while pushing repetitive repo work into a dedicated MCP executor lane."
closing = closing_lines[0] if closing_lines else "The planner stays premium; the repo mechanics stay efficient."

bullets = []
if subjects:
for subject in subjects[:3]:
bullets.append(f"- {sentence_case(subject)}")
else:
bullets.append(f"- Automated release for {current_tag}.")

if previous_tag:
range_line = f"Changes since {previous_tag}."
else:
range_line = "Changes from the latest main-branch release payload."

body = f"""## Summary
{summary}

## What changed
{chr(10).join(bullets)}
- Published packages: npm @cafitac/hermit-agent@{version} and PyPI cafitac-hermit-agent=={version}.

## Why it matters
{opening} This release was classified as: {reason}. {range_line}

## Operator notes
- Release tag: {current_tag}
- If protected main blocks workflow write-back, the publish workflow opens a metadata-only sync PR automatically.
- README, changelog, and package metadata stay aligned through the same release path.

## Assets
- GitHub Release: https://github.com/{repo}/releases/tag/{current_tag}
- npm: https://www.npmjs.com/package/@cafitac/hermit-agent
- PyPI: https://pypi.org/project/cafitac-hermit-agent/
- README: https://github.com/{repo}#readme

> {closing}
"""
Path(args.out).write_text(body)


if __name__ == "__main__":
main()