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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ Format: [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), [Semantic Vers

## [Unreleased]

## [0.2.0] - 2026-10-11

### Hinzugef眉gt / Added

- Befehl `system-agent`: versionierte, gehashte Systemagent-Vorlage (`githubbot-core.system-agent/v1`)
mit Agent-Studio-Blueprint-Feldern, nur bei installiertem Paket vorhanden /
`system-agent` command: versioned, hashed system agent template with agent-studio blueprint fields,
present only where the package is installed.

## [0.1.0] - 2026-10-11

### Hinzugef眉gt / Added
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pull-request summaries, read-only health/traffic/star reports, a preview/apply c
write, a plugin SPI and signed exact-SHA review receipts. Pure Python standard library, no runtime
dependencies.

> Status: alpha (0.1.0). The CLI, the JSON result envelope and SPI version 1 are the contract;
> Status: alpha (0.2.0). The CLI, the JSON result envelope and SPI version 1 are the contract;
> everything else may still change.

## Features
Expand Down Expand Up @@ -62,8 +62,9 @@ githubbot-core duplicates --input issues.json --threshold 0.8
# Pull-request summary (+ checks from installed plugins)
githubbot-core review-pr --repo owner/repo --pr 12

# Plugins
# Plugins and system agent template
githubbot-core plugins
githubbot-core --json system-agent
githubbot-core run <plugin> [--plugin-arg ...] [--apply]
```

Expand All @@ -85,6 +86,15 @@ The Python API is the package itself: `githubbot_core.triage.IssueTriage`,
`githubbot_core.actions.preview/apply`, `githubbot_core.health.RepoHealthCollector`,
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.

### System agent template

`githubbot-core --json system-agent` prints the neutral agent template shipped with the package
(schema `githubbot-core.system-agent/v1`): `version` (package version), `sha256` of the canonical
template, `capability`, the effective `permissions` and a `blueprint` with the field names of the
agent-studio blueprint payload (persona, role, contractus, governance, skills). The template
exists only where the package is installed. Consumers bind an agent to `version` + `sha256` and must
not overwrite an existing agent configuration when a newer template appears.

## Configuration and permissions

The packaged defaults (`githubbot_core/default_config.json`) are extended with `--config file.json`
Expand Down
14 changes: 12 additions & 2 deletions README_de.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Duplikaterkennung, Pull-Request-Zusammenfassungen, lesende Berichte zu Gesundhei
Sternen, ein Vorschau-/Ausf眉hrungsvertrag f眉r jede Schreibaktion, eine Plugin-Schnittstelle (SPI)
und signierte Exact-SHA-Review-Belege. Reine Python-Standardbibliothek, keine Laufzeitabh盲ngigkeiten.

> Status: Alpha (0.1.0). Verbindlich sind CLI, JSON-Ergebnish眉lle und SPI-Version 1; alles andere
> Status: Alpha (0.2.0). Verbindlich sind CLI, JSON-Ergebnish眉lle und SPI-Version 1; alles andere
> kann sich noch 盲ndern.

## Funktionen
Expand Down Expand Up @@ -62,8 +62,9 @@ githubbot-core duplicates --input issues.json --threshold 0.8
# Pull-Request-Zusammenfassung (+ Pr眉fungen installierter Plugins)
githubbot-core review-pr --repo owner/repo --pr 12

# Plugins
# Plugins und Systemagent-Vorlage
githubbot-core plugins
githubbot-core --json system-agent
githubbot-core run <plugin> [--plugin-arg ...] [--apply]
```

Expand All @@ -86,6 +87,15 @@ Die Python-API ist das Paket selbst: `githubbot_core.triage.IssueTriage`,
`githubbot_core.actions.preview/apply`, `githubbot_core.health.RepoHealthCollector`,
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.

### Systemagent-Vorlage

`githubbot-core --json system-agent` gibt die neutrale, mit dem Paket ausgelieferte Agentenvorlage aus
(Schema `githubbot-core.system-agent/v1`): `version` (Paketversion), `sha256` der kanonischen Vorlage,
`capability`, die wirksamen `permissions` und ein `blueprint` mit den Feldnamen des
Agent-Studio-Blueprint-Payloads (Persona, Rolle, Contractus, Governance, Skills). Die Vorlage
existiert nur dort, wo das Paket installiert ist. Konsumenten binden einen Agenten an `version` +
`sha256` und 眉berschreiben bei einer neueren Vorlage keine bestehende Agentenkonfiguration.

## Konfiguration und Rechte

Die mitgelieferten Standardwerte (`githubbot_core/default_config.json`) werden durch
Expand Down
2 changes: 2 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Languages: en, de
- Python API: `githubbot_core.triage`, `githubbot_core.actions`, `githubbot_core.health`,
`githubbot_core.traffic`, `githubbot_core.stars`, `githubbot_core.pr_review`,
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.
- System agent template: `githubbot-core --json system-agent` (schema
`githubbot-core.system-agent/v1`, version + sha256, agent-studio blueprint fields).
- Plugin SPI v1: entry-point group `githubbot_core.plugins`, factory returning an object with
`name`, `spi_version`, `describe()`, optional `plan(ctx)` and `pr_policy_checks(pr, files, diff)`.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ version = { attr = "githubbot_core.__version__" }
where = ["src"]

[tool.setuptools.package-data]
githubbot_core = ["default_config.json", "templates/*.yml"]
githubbot_core = ["default_config.json", "system_agent.json", "templates/*.yml"]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
2 changes: 1 addition & 1 deletion src/githubbot_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
a plugin SPI and signed exact-SHA review receipts. Standard library only.
"""

__version__ = "0.1.0"
__version__ = "0.2.0"
15 changes: 15 additions & 0 deletions src/githubbot_core/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from __future__ import annotations

import argparse
import hashlib
import json
import shutil
import sys
Expand Down Expand Up @@ -114,6 +115,18 @@ def cmd_plugins(args, config, transport) -> int:
return EXIT_OK


def cmd_system_agent(args, config, transport) -> int:
template = json.loads(resources.files("githubbot_core").joinpath("system_agent.json").read_text(encoding="utf-8"))
canonical = json.dumps(template, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
data = {"schema": template["_meta"]["schema"], "id": template["id"], "version": __version__,
"sha256": hashlib.sha256(canonical.encode("utf-8")).hexdigest(),
"capability": {"package": "githubbot-core", "version": __version__, "spi_version": plugins.SPI_VERSION},
"blueprint": template["blueprint"], "permissions": config.get("permissions", {})}
text = f"system agent {data['id']} {data['version']} sha256 {data['sha256']}"
_emit(args, "system-agent", True, "read", data, [], text)
return EXIT_OK


def cmd_classify(args, config, transport) -> int:
classifier = IssueClassifier(config)
results = [classifier.classify(issue) for issue in _read_issues(Path(args.input))]
Expand Down Expand Up @@ -240,6 +253,8 @@ def build_parser() -> argparse.ArgumentParser:
sub.add_parser("info", help="version, contract and plugin overview").set_defaults(func=cmd_info)
sub.add_parser("doctor", help="check gh CLI, credentials and permissions").set_defaults(func=cmd_doctor)
sub.add_parser("plugins", help="list installed plugins").set_defaults(func=cmd_plugins)
sub.add_parser("system-agent", help="versioned system agent template of this package").set_defaults(
func=cmd_system_agent)

p = sub.add_parser("classify", help="classify issues from a JSON file (offline)")
p.add_argument("--input", required=True)
Expand Down
20 changes: 20 additions & 0 deletions src/githubbot_core/system_agent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"_meta": {
"schema": "githubbot-core.system-agent/v1",
"description": "Neutral system agent template shipped with this package. It exists only where githubbot-core is installed; read it with `githubbot-core --json system-agent`. `blueprint` uses the field names of the agent-studio blueprint payload. Consumers may change persona, skills, governance and model; a newer template version must not silently overwrite an existing agent configuration."
},
"id": "githubbot",
"blueprint": {
"name": "githubbot",
"title": "GitHub Maintenance Agent",
"kind": "agent",
"description": "Issue triage, duplicate detection, pull-request summaries and read-only health, traffic and star reports. Every write is a previewed plan that is applied only with explicit approval.",
"persona_role": "github-maintainer",
"persona_prompt": "You are the GitHub maintenance agent of githubbot-core. You triage issues, find duplicates, summarise pull requests and produce read-only health, traffic and star reports for the repositories assigned to you. Every write is first shown as a preview plan; you apply a plan only when a current, explicit approval names its plan_sha256, and you never widen the configured permissions. A review verdict is valid only for the exact head SHA it was made on. If a source cannot be read, report the error instead of treating it as empty.",
"skills": [],
"animus_type": "cli",
"modus": "casualis",
"contractus": {"turns": 15},
"governance": {"profile": "read_only_research", "tool_whitelist": ["read_files", "search_content"]}
}
}
4 changes: 4 additions & 0 deletions tests/test_consumer_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def test_installed_package_cli_without_private_extension(tmp_path: Path) -> None
envelope = json.loads(info.stdout)
assert envelope["ok"] is True and envelope["data"]["plugins"] == []

agent = _run([str(cli), "--json", "system-agent"], cwd=work) # template ships as package data
assert agent.returncode == 0, agent.stderr
assert json.loads(agent.stdout)["data"]["blueprint"]["name"] == "githubbot"

origin = _run([str(python), "-c", "import githubbot_core, sys; print(githubbot_core.__file__)"], cwd=work)
assert "site-packages" in origin.stdout, origin.stdout # not the source tree

Expand Down
50 changes: 50 additions & 0 deletions tests/test_system_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""System agent template: versioned, hashed, shaped like the agent-studio blueprint payload."""
from __future__ import annotations

import hashlib
import json
import re
from importlib import resources

from githubbot_core import __version__, cli

# Rules of the consuming agent-studio save/materialize path (blueprint service).
NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$")
KINDS = {"agent", "role", "skill", "workflow", "service", "contractus"}
ANIMUS = {"api", "cli", "subscription", "local"}
EXECUTABLE_MODUS = {"casualis", "usus"}


def _system_agent(capsys, *extra: str) -> dict:
assert cli.main([*extra, "--json", "system-agent"]) == 0
envelope = json.loads(capsys.readouterr().out)
assert envelope["ok"] is True and envelope["mode"] == "read"
return envelope["data"]


def test_template_identity_is_version_plus_canonical_hash(capsys):
data = _system_agent(capsys)
template = json.loads(resources.files("githubbot_core").joinpath("system_agent.json").read_text(encoding="utf-8"))
canonical = json.dumps(template, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
assert data["schema"] == "githubbot-core.system-agent/v1"
assert data["id"] == "githubbot" and data["version"] == __version__
assert data["sha256"] == hashlib.sha256(canonical.encode("utf-8")).hexdigest()
assert data["capability"] == {"package": "githubbot-core", "version": __version__, "spi_version": 1}


def test_blueprint_is_accepted_by_the_agent_studio_payload_rules(capsys):
bp = _system_agent(capsys)["blueprint"]
assert NAME.fullmatch(bp["name"]) and bp["name"] == bp["name"].lower()
assert bp["kind"] in KINDS and bp["kind"] == "agent"
assert bp["persona_prompt"].strip() and len(bp["title"]) <= 120
assert bp["animus_type"] in ANIMUS and bp["modus"] in EXECUTABLE_MODUS
assert isinstance(bp["skills"], list) and all(NAME.fullmatch(s) for s in bp["skills"])
assert isinstance(bp["contractus"], dict) and isinstance(bp["governance"], dict)
assert "is_template" not in bp # system templates are registered by the consumer, never self-declared


def test_permissions_are_the_effective_narrowed_configuration(capsys, tmp_path):
layer = tmp_path / "narrow.json"
layer.write_text(json.dumps({"permissions": {"enabled": []}}), encoding="utf-8")
assert _system_agent(capsys)["permissions"]["enabled"] == ["issue.add_labels", "issue.comment"]
assert _system_agent(capsys, "--config", str(layer))["permissions"]["enabled"] == []
Loading