@delorenj/pjangler 1.2.6 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +54 -16
- package/dist/mcp-server.js +66 -17
- package/package.json +1 -1
- package/templates/commonproject/AGENTS.md +3 -3
- package/templates/commonproject/README.md +12 -11
- package/templates/commonproject/copier.yml +24 -8
- package/templates/commonproject/mise.toml +3 -3
- package/templates/commonproject/template/.project.json.jinja +13 -6
- package/templates/hermes-agent/README.md +8 -8
- package/templates/hermes-agent/copier.yml +3 -4
- package/templates/hermes-agent/docs/architecture.md +4 -4
- package/templates/hermes-agent/docs/operations.md +2 -2
- package/templates/hermes-agent/docs/sentinel/README.md +6 -8
- package/templates/hermes-agent/docs/sentinel/architecture.md +2 -1
- package/templates/hermes-agent/docs/sentinel/development.md +13 -12
- package/templates/hermes-agent/docs/sentinel/providers.md +12 -32
- package/templates/hermes-agent/install-local.sh +6 -13
- package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +14 -37
- package/templates/hermes-agent/template/.scripts/70-systemd.sh +1 -0
- package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +6 -2
- package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md +2 -2
- package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md +1 -1
- package/templates/hermes-agent/template/.scripts/sentinel.prompt.md.jinja +5 -7
- package/templates/hermes-agent/template/SOUL.md.jinja +8 -7
- package/templates/hermes-agent/template/role.yaml.jinja +3 -6
- package/templates/hermes-agent/template/.scripts/40-plane.sh +0 -51
- package/templates/hermes-agent/template/.scripts/providers/linear.sh +0 -176
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
# 1. Renders agents/hermes/<role>/{role.yaml, SOUL.md, hermes, .scripts/}
|
|
13
13
|
# 2. Creates a per-agent runtime repo gh:delorenj/agent-hm-<repo>-<role>
|
|
14
14
|
# 3. Submodules it at agents/hermes/<role>/runtime/ (this is HERMES_HOME)
|
|
15
|
-
# 4. Creates
|
|
15
|
+
# 4. Creates or binds the configured ticket board
|
|
16
16
|
# 6. Prompts for a BotFather Telegram token, stores in runtime/.env
|
|
17
17
|
# 7. Installs a Bloodbank consumer + heartbeat timer (reconcile + checkpoint)
|
|
18
18
|
# 8. Appends to ~/.hermes/agents-registry.yaml
|
|
19
19
|
#
|
|
20
|
-
# Environment-specific defaults (Hermes binary path, runtime repo owner,
|
|
21
|
-
#
|
|
20
|
+
# Environment-specific defaults (Hermes binary path, runtime repo owner, ticket
|
|
21
|
+
# provider defaults, NATS endpoint, skills dir, …) are NOT hardcoded — they live in
|
|
22
22
|
# ~/.config/hermes-agent-template/config.toml, seeded from config.example.toml on
|
|
23
23
|
# first run. Edit that file to retarget the template for a different machine/user.
|
|
24
24
|
|
|
@@ -59,7 +59,6 @@ ticket_provider:
|
|
|
59
59
|
help: "Ticket system this agent's board lives in"
|
|
60
60
|
default: plane
|
|
61
61
|
choices:
|
|
62
|
-
"Linear": linear
|
|
63
62
|
"Plane": plane
|
|
64
63
|
"Trello": trello
|
|
65
64
|
|
|
@@ -92,11 +92,11 @@ happen to share a token, the second one's startup fails fast. The N×M cost
|
|
|
92
92
|
(N BotFather sessions per fleet) is the price we accept for zero custom
|
|
93
93
|
routing code.
|
|
94
94
|
|
|
95
|
-
## One
|
|
95
|
+
## One Ticket Board Per Repo
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
The configured ticket board is the natural unit of work isolation. The PM owns
|
|
98
|
+
one board per repo and routes worker agents through that shared source of truth,
|
|
99
|
+
so decisions, filters, and review state stay coherent.
|
|
100
100
|
|
|
101
101
|
## Bloodbank wiring
|
|
102
102
|
|
|
@@ -29,7 +29,7 @@ invocations).
|
|
|
29
29
|
| 10 hermes profile | `hermes profile create <repo>-<role> --clone --no-alias` + mirror skills/plugins/hooks from default + symlink canonical runtime skills (`delonet-conventions`, `delonet-dotenv`, `hermes-pm-template-maintenance`, `hindsight`, `subagent-driven-development`) from `/home/delorenj/.agents/skills`; PM roles also seed `VOX_URL` in profile `.env` | n/a |
|
|
30
30
|
| 20 runtime repo | Create gh:delorenj/agent-hm-<repo>-<role> (private), push scaffold from role-local `.runtime-scaffold/`, submodule-add into ./runtime/, symlink ~/.hermes/profiles/<id> → runtime; PM roles also link the Voxxy plugin and set `tts.provider: voxxy` | `SKIP_RUNTIME_REPO=1` |
|
|
31
31
|
| 30 telegram | Capture BotFather token, write to runtime/.env, enable hermes-telegram toolset | `SKIP_TELEGRAM=1` |
|
|
32
|
-
|
|
|
32
|
+
| 42 ticket provider | Create or bind the configured ticket board, patch identifier into role.yaml | `SKIP_PLANE=1` |
|
|
33
33
|
| 60 bloodbank | Install consumer (renders from scaffold w/ agent values), health-check NATS, install nats-py via uv if missing | `SKIP_BLOODBANK=1` |
|
|
34
34
|
| 70 systemd | Install user units: gateway, consumer, heartbeat timer (board-reconciliation sentinel pass + gated runtime checkpoint, one tick) | `SKIP_SYSTEMD=1` |
|
|
35
35
|
| 80 registry | Append entry to ~/.hermes/agents-registry.yaml | n/a |
|
|
@@ -187,7 +187,7 @@ systemctl --user disable --now hermes-${AGENT}-heartbeat.timer
|
|
|
187
187
|
# that's what you want!)
|
|
188
188
|
hermes profile delete ${AGENT}
|
|
189
189
|
|
|
190
|
-
# 3. Archive
|
|
190
|
+
# 3. Archive the ticket board if the provider supports archival
|
|
191
191
|
PROJECT_ID=$(python3 -c "import yaml,pathlib; print(yaml.safe_load(pathlib.Path.home().joinpath('.hermes/agents-registry.yaml').read_text())['agents']['${AGENT}']['plane']['project_id'])")
|
|
192
192
|
curl -X POST "https://plane.delo.sh/api/v1/workspaces/33god/projects/${PROJECT_ID}/archive/" \
|
|
193
193
|
-H "X-API-Key: ${PLANE_33GOD_API_KEY}"
|
|
@@ -26,7 +26,7 @@ that does the board-reconciliation pass and then a gated runtime checkpoint
|
|
|
26
26
|
`.scripts/sentinel/`.
|
|
27
27
|
|
|
28
28
|
It talks to the ticket board through a pluggable adapter, so the same engine
|
|
29
|
-
runs on
|
|
29
|
+
runs on Plane or Trello. The engine lives once in this template (the
|
|
30
30
|
single source of truth) and propagates to every deployment with
|
|
31
31
|
`copier update`.
|
|
32
32
|
|
|
@@ -37,8 +37,6 @@ The following is true as of June 1, 2026.
|
|
|
37
37
|
- The engine, the provider abstraction, and the autonomous adversarial-review
|
|
38
38
|
enforcement are built, syntax-clean, and validated offline with a mock
|
|
39
39
|
provider.
|
|
40
|
-
- The **Linear** adapter is verified live against a real board (a real
|
|
41
|
-
`DEL` team: `resolve`, `list_issues` returning 93 issues, and `get_issue`).
|
|
42
40
|
- The **Plane** adapter is verified live against a real workspace
|
|
43
41
|
(`resolve`, `list_issues`, `get_issue`, `comment`, and a
|
|
44
42
|
`transition` to `completed` on a disposable issue).
|
|
@@ -54,7 +52,7 @@ For a local, single-machine install (no GitHub runtime repo, no Telegram, no
|
|
|
54
52
|
NATS), use the one-command bootstrap. From inside the target project:
|
|
55
53
|
|
|
56
54
|
```bash
|
|
57
|
-
export PLANE_API_KEY=<key> # or
|
|
55
|
+
export PLANE_API_KEY=<key> # or TRELLO_KEY + TRELLO_TOKEN
|
|
58
56
|
curl -fsSL https://raw.githubusercontent.com/delorenj/hermes-agent-template/main/install-local.sh | sh
|
|
59
57
|
```
|
|
60
58
|
|
|
@@ -66,7 +64,7 @@ macOS). See [Development guide: local install](development.md#local-install-one-
|
|
|
66
64
|
<!-- prettier-ignore -->
|
|
67
65
|
> [!IMPORTANT]
|
|
68
66
|
> Full (non-local) provisioning is outward-facing. It can create a GitHub
|
|
69
|
-
> runtime repo, a Telegram bot, and a
|
|
67
|
+
> runtime repo, a Telegram bot, and a ticket board, and the Telegram step is
|
|
70
68
|
> interactive. Use `install-local.sh` or the `SKIP_*` flags described in
|
|
71
69
|
> [Development guide:
|
|
72
70
|
> provisioning](development.md#provisioning-the-pm-manual) for local or lean
|
|
@@ -81,7 +79,7 @@ All paths are relative to the repository root.
|
|
|
81
79
|
| `copier.yml` | Questions (`role`, `ticket_provider`) and the `_tasks` provisioning chain. |
|
|
82
80
|
| `template/role.yaml.jinja` | The rendered role manifest, including the `ticket_provider` binding and the `reconcile` knobs (`grace_hours`, `auto_review`). |
|
|
83
81
|
| `template/.scripts/lib/ticket-provider.sh` | The adapter dispatcher (`tp`). The engine's only seam to a ticket system. |
|
|
84
|
-
| `template/.scripts/providers/{
|
|
82
|
+
| `template/.scripts/providers/{plane,trello}.sh` | The provider adapters. |
|
|
85
83
|
| `template/.scripts/42-ticket-provider.sh` | Provisioning step that resolves or creates the board. |
|
|
86
84
|
| `template/.scripts/70-systemd.sh` | Provisioning step that installs the gateway, consumer, and the fused `heartbeat` timer (board-reconciliation sentinel pass + gated runtime checkpoint). |
|
|
87
85
|
| `install-local.sh` | One-command local install (no cloud, macOS + Linux). |
|
|
@@ -109,8 +107,8 @@ the agent to read at run time. Keep the two in sync when behavior changes.
|
|
|
109
107
|
|
|
110
108
|
The highest-value open work, in order:
|
|
111
109
|
|
|
112
|
-
1. Live-verify the **Trello** adapter against a real board (
|
|
113
|
-
|
|
110
|
+
1. Live-verify the **Trello** adapter against a real board (Plane is done).
|
|
111
|
+
See [Providers: verifying an
|
|
114
112
|
adapter](providers.md#verifying-an-adapter-against-a-live-board).
|
|
115
113
|
2. Confirm `install-local.sh` on a real macOS machine. The Linux path and the
|
|
116
114
|
Plane adapter are verified; the macOS `launchd` agent and `mkdir` lock get
|
|
@@ -71,7 +71,8 @@ function, `tp`, that dispatches to one provider implementation under
|
|
|
71
71
|
through the `ticket_provider` question and recorded in `role.yaml` under
|
|
72
72
|
`ticket_provider.name`.
|
|
73
73
|
|
|
74
|
-
This is what lets one engine serve
|
|
74
|
+
This is what lets one engine serve the supported ticket back ends: Plane and
|
|
75
|
+
Trello.
|
|
75
76
|
For the full contract and the per-provider details, see
|
|
76
77
|
[Providers](providers.md).
|
|
77
78
|
|
|
@@ -112,7 +112,7 @@ repository root instead of driving Copier by hand. From inside the target
|
|
|
112
112
|
project:
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
|
-
export PLANE_API_KEY=<key> # or
|
|
115
|
+
export PLANE_API_KEY=<key> # or TRELLO_KEY + TRELLO_TOKEN
|
|
116
116
|
curl -fsSL https://raw.githubusercontent.com/delorenj/hermes-agent-template/main/install-local.sh | sh
|
|
117
117
|
```
|
|
118
118
|
|
|
@@ -133,8 +133,8 @@ The script:
|
|
|
133
133
|
7. smoke-tests the board connection through the adapter.
|
|
134
134
|
|
|
135
135
|
Useful environment overrides (skip the prompts): `HAT_REPO`, `HAT_PROVIDER`,
|
|
136
|
-
`HAT_ROLES`, `HAT_PLANE_WORKSPACE`, `HAT_PLANE_PROJECT`, `
|
|
137
|
-
|
|
136
|
+
`HAT_ROLES`, `HAT_PLANE_WORKSPACE`, `HAT_PLANE_PROJECT`, `HAT_TRELLO_BOARD`,
|
|
137
|
+
and `HAT_DRY_RUN=1` to preview without changing anything.
|
|
138
138
|
|
|
139
139
|
This local path does not wire Telegram or email. Those are convenience layers in
|
|
140
140
|
the `pjangler` provisioner, not requirements for a working agent — talk to the
|
|
@@ -151,7 +151,7 @@ cd /path/to/your-project
|
|
|
151
151
|
copier copy gh:delorenj/hermes-agent-template ./agents/hermes/pm \
|
|
152
152
|
--data role=pm \
|
|
153
153
|
--data target_repo=<repo> \
|
|
154
|
-
--data ticket_provider=<
|
|
154
|
+
--data ticket_provider=<plane|trello>
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
The `_tasks` chain in `copier.yml` runs the numbered provisioning scripts in
|
|
@@ -162,7 +162,7 @@ order. Several of them reach outside the repository.
|
|
|
162
162
|
> Full provisioning is outward-facing and partly interactive.
|
|
163
163
|
> `20-runtime-repo.sh` creates a private GitHub repo with `gh repo create`,
|
|
164
164
|
> `30-telegram.sh` prompts for a BotFather token and blocks waiting for input,
|
|
165
|
-
> and `42-ticket-provider.sh` can create a
|
|
165
|
+
> and `42-ticket-provider.sh` can create a ticket board. Don't run full
|
|
166
166
|
> provisioning unattended.
|
|
167
167
|
|
|
168
168
|
Use the `SKIP_*` environment flags for a local or lean install. Each numbered
|
|
@@ -172,7 +172,7 @@ script checks its flag and skips cleanly.
|
|
|
172
172
|
| --- | --- |
|
|
173
173
|
| `SKIP_TELEGRAM` | The interactive BotFather token step. |
|
|
174
174
|
| `SKIP_RUNTIME_REPO` | Creating the GitHub runtime repo. |
|
|
175
|
-
| `SKIP_PLANE` | Creating
|
|
175
|
+
| `SKIP_PLANE` | Creating or linking the ticket board. |
|
|
176
176
|
| `SKIP_BLOODBANK` | Installing the BloodBank consumer. |
|
|
177
177
|
| `SKIP_SYSTEMD` | Installing `systemd` units (gateway, consumer, heartbeat timer). |
|
|
178
178
|
|
|
@@ -181,13 +181,14 @@ For example, a local install that creates no cloud resources:
|
|
|
181
181
|
```bash
|
|
182
182
|
SKIP_TELEGRAM=1 SKIP_RUNTIME_REPO=1 SKIP_PLANE=1 SKIP_BLOODBANK=1 \
|
|
183
183
|
copier copy gh:delorenj/hermes-agent-template ./agents/hermes/pm \
|
|
184
|
-
--data role=pm --data target_repo=<repo> --data ticket_provider=
|
|
184
|
+
--data role=pm --data target_repo=<repo> --data ticket_provider=trello
|
|
185
185
|
```
|
|
186
186
|
|
|
187
187
|
After provisioning, set the board binding in
|
|
188
|
-
`agents/hermes/pm/role.yaml`. For
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
`agents/hermes/pm/role.yaml`. For Trello, set `ticket_provider.board` to the
|
|
189
|
+
board id; for Plane, set `ticket_provider.workspace` and `ticket_provider.project`.
|
|
190
|
+
Make the provider key available to the heartbeat's environment: on Linux through
|
|
191
|
+
a `systemd` `EnvironmentFile` (for example
|
|
191
192
|
`~/.hermes/<agent_id>.env`); on macOS the `launchd` agent sources that same
|
|
192
193
|
per-agent env file, so write the key there.
|
|
193
194
|
|
|
@@ -256,8 +257,8 @@ These cost real debugging time. Watch for them.
|
|
|
256
257
|
|
|
257
258
|
The following work is open for the incoming agent, roughly in priority order.
|
|
258
259
|
|
|
259
|
-
1. **Live-verify the Trello adapter.**
|
|
260
|
-
(
|
|
260
|
+
1. **Live-verify the Trello adapter.** Plane is verified live
|
|
261
|
+
(including `transition` and `comment`). Trello is implemented against
|
|
261
262
|
the contract but unverified. Follow [Providers: verifying an
|
|
262
263
|
adapter](providers.md#verifying-an-adapter-against-a-live-board) with Trello
|
|
263
264
|
credentials, and fix any endpoint or field mismatches.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Provider adapters
|
|
2
2
|
|
|
3
3
|
This guide is the reference for the ticket-provider abstraction: the contract
|
|
4
|
-
every adapter implements, the
|
|
4
|
+
every adapter implements, the two adapters that ship today, and a
|
|
5
5
|
step-by-step process for adding a new provider or verifying an existing one
|
|
6
6
|
against a live board. Read [Architecture](architecture.md) first for how the
|
|
7
7
|
adapter fits into the engine.
|
|
@@ -43,12 +43,8 @@ maps each to its back end's concrete state.
|
|
|
43
43
|
|
|
44
44
|
## The adapters that ship today
|
|
45
45
|
|
|
46
|
-
The repository includes
|
|
46
|
+
The repository includes two supported adapters with different verification status.
|
|
47
47
|
|
|
48
|
-
- **Linear** (`providers/linear.sh`) is the reference implementation. It uses
|
|
49
|
-
the Linear GraphQL API. It's verified live against a real board. Linear's
|
|
50
|
-
`issue(id:)` field accepts both the UUID and the human identifier (for
|
|
51
|
-
example, `DEL-42`), so the engine can pass identifiers through.
|
|
52
48
|
- **Plane** (`providers/plane.sh`) uses the Plane REST API with `X-API-Key`
|
|
53
49
|
authentication. A Plane project maps to the board, a cycle maps to the
|
|
54
50
|
milestone, and `state.group` maps to the state type. It's verified live. Note
|
|
@@ -64,21 +60,6 @@ The repository includes three adapters with different verification status.
|
|
|
64
60
|
|
|
65
61
|
Set credentials in the environment and the board binding in `role.yaml`.
|
|
66
62
|
|
|
67
|
-
<details>
|
|
68
|
-
<summary>Linear</summary>
|
|
69
|
-
|
|
70
|
-
- Credentials: `LINEAR_API_KEY`.
|
|
71
|
-
- Binding:
|
|
72
|
-
|
|
73
|
-
```yaml
|
|
74
|
-
ticket_provider:
|
|
75
|
-
name: linear
|
|
76
|
-
team: DEL # Linear team key
|
|
77
|
-
project: "" # optional, scopes milestone and issue queries
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
</details>
|
|
81
|
-
|
|
82
63
|
<details>
|
|
83
64
|
<summary>Plane</summary>
|
|
84
65
|
|
|
@@ -114,8 +95,9 @@ Set credentials in the environment and the board binding in `role.yaml`.
|
|
|
114
95
|
|
|
115
96
|
To add a fourth provider, for example GitHub Issues or Jira, follow these steps.
|
|
116
97
|
|
|
117
|
-
1. Create `template/.scripts/providers/<name>.sh`. Start from `
|
|
118
|
-
|
|
98
|
+
1. Create `template/.scripts/providers/<name>.sh`. Start from `plane.sh` if it
|
|
99
|
+
uses REST with a single API key, or `trello.sh` if it has a board/list/card
|
|
100
|
+
model.
|
|
119
101
|
2. Implement every operation in [the contract](#the-contract). Return the exact
|
|
120
102
|
JSON shapes shown in the table. Map your back end's states to the five
|
|
121
103
|
normalized states.
|
|
@@ -127,8 +109,7 @@ To add a fourth provider, for example GitHub Issues or Jira, follow these steps.
|
|
|
127
109
|
5. Extend `template/role.yaml.jinja` with a provider-specific binding block
|
|
128
110
|
under the `{% if ticket_provider == '<name>' %}` branch.
|
|
129
111
|
6. Teach `42-ticket-provider.sh` how to resolve or create the board for your
|
|
130
|
-
provider, if it differs from the existing `
|
|
131
|
-
cases.
|
|
112
|
+
provider, if it differs from the existing `plane` and `trello` cases.
|
|
132
113
|
7. Validate and verify with the steps below.
|
|
133
114
|
|
|
134
115
|
<!-- prettier-ignore -->
|
|
@@ -158,7 +139,7 @@ Before you touch a live board, confirm the adapter parses and fails cleanly.
|
|
|
158
139
|
exit, with no Python traceback:
|
|
159
140
|
|
|
160
141
|
```bash
|
|
161
|
-
( unset
|
|
142
|
+
( unset PLANE_API_KEY; sh template/.scripts/providers/plane.sh resolve )
|
|
162
143
|
```
|
|
163
144
|
|
|
164
145
|
## Verifying an adapter against a live board
|
|
@@ -169,15 +150,15 @@ never closes or modifies a ticket, so it's safe to run.
|
|
|
169
150
|
|
|
170
151
|
```bash
|
|
171
152
|
# 1. Load the credential into the environment only (never print it).
|
|
172
|
-
export
|
|
173
|
-
~/.hermes/<agent>.env | head -1 | sed -E 's/^(export )?
|
|
153
|
+
export PLANE_API_KEY="$(grep -E '^(export )?PLANE_API_KEY=' \
|
|
154
|
+
~/.hermes/<agent>.env | head -1 | sed -E 's/^(export )?PLANE_API_KEY=//; s/^"//; s/"$//')"
|
|
174
155
|
|
|
175
156
|
# 2. Stage a role directory bound to the real board.
|
|
176
157
|
T=$(mktemp -d); RD="$T/agents/hermes/pm"
|
|
177
158
|
mkdir -p "$RD/.scripts/lib" "$RD/.scripts/providers"
|
|
178
159
|
cp template/.scripts/lib/ticket-provider.sh "$RD/.scripts/lib/"
|
|
179
|
-
cp template/.scripts/providers/
|
|
180
|
-
printf 'repo: demo\nrole: pm\nticket_provider:\n name:
|
|
160
|
+
cp template/.scripts/providers/plane.sh "$RD/.scripts/providers/"
|
|
161
|
+
printf 'repo: demo\nrole: pm\nticket_provider:\n name: plane\n workspace: 33god\n project: <project-uuid>\n' \
|
|
181
162
|
> "$RD/role.yaml"
|
|
182
163
|
|
|
183
164
|
# 3. Run the read operations.
|
|
@@ -199,5 +180,4 @@ verification.
|
|
|
199
180
|
## Read next
|
|
200
181
|
|
|
201
182
|
- [Development guide](development.md): the full edit, validate, and propagate
|
|
202
|
-
workflow, plus the open roadmap, which includes live-verifying
|
|
203
|
-
Trello.
|
|
183
|
+
workflow, plus the open roadmap, which includes live-verifying Trello.
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
#
|
|
16
16
|
# Environment overrides (skip the prompts):
|
|
17
17
|
# HAT_REPO=<name> project/repo name (default: basename of CWD)
|
|
18
|
-
# HAT_PROVIDER=
|
|
18
|
+
# HAT_PROVIDER=plane|trello (default: plane)
|
|
19
19
|
# HAT_ROLES="pm" roles to install (default: pm)
|
|
20
20
|
# HAT_DRY_RUN=1 print actions, change nothing
|
|
21
|
-
# Provider creds:
|
|
21
|
+
# Provider creds: PLANE_API_KEY+PLANE_BASE | TRELLO_KEY+TRELLO_TOKEN
|
|
22
22
|
set -eu
|
|
23
23
|
|
|
24
24
|
say() { printf '\033[36m%s\033[0m\n' "$*"; }
|
|
@@ -100,7 +100,7 @@ TOML
|
|
|
100
100
|
fi
|
|
101
101
|
|
|
102
102
|
# --- 4. Provider credentials + board binding ---------------------------------
|
|
103
|
-
ask HAT_PROVIDER "Ticket provider (
|
|
103
|
+
ask HAT_PROVIDER "Ticket provider (plane|trello)" "plane"
|
|
104
104
|
PROVIDER="$HAT_PROVIDER"
|
|
105
105
|
ask HAT_REPO "Project/repo name" "$(basename "$PROJECT_DIR" | tr '[:upper:]' '[:lower:]')"
|
|
106
106
|
REPO="$HAT_REPO"
|
|
@@ -109,7 +109,7 @@ PM_ENV="$HOME/.hermes/${REPO}-pm.env"
|
|
|
109
109
|
# These get written into the pm role.yaml binding after render.
|
|
110
110
|
# Pre-seed from optional env knobs so the install can run non-interactively.
|
|
111
111
|
TP_WORKSPACE="${HAT_PLANE_WORKSPACE:-}"; TP_PROJECT="${HAT_PLANE_PROJECT:-}"
|
|
112
|
-
|
|
112
|
+
TP_BOARD="${HAT_TRELLO_BOARD:-}"
|
|
113
113
|
case "$PROVIDER" in
|
|
114
114
|
plane)
|
|
115
115
|
ask PLANE_BASE "Plane base URL" "https://app.plane.so"
|
|
@@ -136,12 +136,6 @@ PY
|
|
|
136
136
|
CRED_LINES="PLANE_API_KEY=$PLANE_API_KEY
|
|
137
137
|
PLANE_BASE=$PLANE_BASE"
|
|
138
138
|
;;
|
|
139
|
-
linear)
|
|
140
|
-
: "${LINEAR_API_KEY:?Set LINEAR_API_KEY in your environment, then re-run}"
|
|
141
|
-
ask TP_TEAM "Linear team key (for example DEL)" ""
|
|
142
|
-
[ -n "$TP_TEAM" ] || die "team key is required"
|
|
143
|
-
CRED_LINES="LINEAR_API_KEY=$LINEAR_API_KEY"
|
|
144
|
-
;;
|
|
145
139
|
trello)
|
|
146
140
|
: "${TRELLO_KEY:?Set TRELLO_KEY in your environment, then re-run}"
|
|
147
141
|
: "${TRELLO_TOKEN:?Set TRELLO_TOKEN in your environment, then re-run}"
|
|
@@ -170,7 +164,7 @@ for ROLE in $ROLES; do
|
|
|
170
164
|
DEST="$PROJECT_DIR/agents/hermes/$ROLE"
|
|
171
165
|
# Scrub provider creds from copier's environment so 42-ticket-provider skips
|
|
172
166
|
# board CREATION; we bind to the existing board in step 6 instead.
|
|
173
|
-
run "env -u PLANE_API_KEY -u PLANE_33GOD_API_KEY
|
|
167
|
+
run "env -u PLANE_API_KEY -u PLANE_33GOD_API_KEY \
|
|
174
168
|
-u TRELLO_KEY -u TRELLO_TOKEN \
|
|
175
169
|
copier copy '$TEMPLATE_SRC' '$DEST' --trust --defaults --overwrite \
|
|
176
170
|
--data target_repo='$REPO' --data role='$ROLE' --data ticket_provider='$PROVIDER'"
|
|
@@ -180,7 +174,7 @@ done
|
|
|
180
174
|
PM_ROLE="$PROJECT_DIR/agents/hermes/pm/role.yaml"
|
|
181
175
|
if [ -f "$PM_ROLE" ] && [ "${HAT_DRY_RUN:-0}" != "1" ]; then
|
|
182
176
|
say "6. binding pm to your $PROVIDER board"
|
|
183
|
-
TP_WORKSPACE="$TP_WORKSPACE" TP_PROJECT="$TP_PROJECT"
|
|
177
|
+
TP_WORKSPACE="$TP_WORKSPACE" TP_PROJECT="$TP_PROJECT" \
|
|
184
178
|
TP_BOARD="$TP_BOARD" PROVIDER="$PROVIDER" python3 - "$PM_ROLE" <<'PY'
|
|
185
179
|
import os, re, sys, pathlib
|
|
186
180
|
p = pathlib.Path(sys.argv[1]); t = p.read_text()
|
|
@@ -190,7 +184,6 @@ def setleaf(text, key, val):
|
|
|
190
184
|
return new if n else text
|
|
191
185
|
t = setleaf(t, "workspace", os.environ.get("TP_WORKSPACE",""))
|
|
192
186
|
t = setleaf(t, "project", os.environ.get("TP_PROJECT",""))
|
|
193
|
-
t = setleaf(t, "team", os.environ.get("TP_TEAM",""))
|
|
194
187
|
t = setleaf(t, "board", os.environ.get("TP_BOARD",""))
|
|
195
188
|
p.write_text(t)
|
|
196
189
|
print(" bound:", {k:v for k,v in os.environ.items() if k.startswith("TP_") and v})
|
|
@@ -46,13 +46,13 @@ PY
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
# pj_write — merge board binding (optional) + this agent into .project.json.
|
|
49
|
-
# args: set_provider(0|1) provider board_id board_url workspace identifier
|
|
49
|
+
# args: set_provider(0|1) provider board_id board_url workspace identifier
|
|
50
50
|
pj_write() {
|
|
51
51
|
REPO="$REPO" REPO_ROOT="$REPO_ROOT" AGENT_ID="$AGENT_ID" ROLE="$ROLE" \
|
|
52
52
|
ROLE_DIR_REL="$ROLE_DIR_REL" PROJECT_DESC="${PROJECT_DESC:-}" \
|
|
53
53
|
python3 - "$PROJECT_JSON" "$@" <<'PY'
|
|
54
54
|
import sys, os, json, pathlib
|
|
55
|
-
(path, set_provider, provider, board_id, board_url, workspace, identifier
|
|
55
|
+
(path, set_provider, provider, board_id, board_url, workspace, identifier) = sys.argv[1:8]
|
|
56
56
|
p = pathlib.Path(path)
|
|
57
57
|
try:
|
|
58
58
|
d = json.loads(p.read_text())
|
|
@@ -72,7 +72,6 @@ if set_provider == "1":
|
|
|
72
72
|
if identifier: tp["identifier"] = identifier
|
|
73
73
|
if board_id: tp["board_id"] = board_id
|
|
74
74
|
if board_url: tp["board_url"] = board_url
|
|
75
|
-
if team: tp["team"] = team
|
|
76
75
|
ag = d.setdefault("agents", {})
|
|
77
76
|
ag[os.environ["AGENT_ID"]] = {"role": os.environ["ROLE"], "role_dir": os.environ["ROLE_DIR_REL"]}
|
|
78
77
|
p.write_text(json.dumps(d, indent=2) + "\n")
|
|
@@ -82,8 +81,8 @@ PY
|
|
|
82
81
|
|
|
83
82
|
# Mirror the binding into role.yaml so legacy consumers keep working.
|
|
84
83
|
mirror_to_role_yaml() {
|
|
85
|
-
# mirror_to_role_yaml <provider> <board_id> <board_url> <workspace> <identifier>
|
|
86
|
-
local provider="$1" bid="$2" burl="$3" ws="$4" ident="$5"
|
|
84
|
+
# mirror_to_role_yaml <provider> <board_id> <board_url> <workspace> <identifier>
|
|
85
|
+
local provider="$1" bid="$2" burl="$3" ws="$4" ident="$5"
|
|
87
86
|
yaml_set ticket_provider.name "$provider" 2>/dev/null || true
|
|
88
87
|
[ -n "$bid" ] && yaml_set ticket_provider.board_id "$bid" 2>/dev/null || true
|
|
89
88
|
[ -n "$burl" ] && yaml_set ticket_provider.board_url "$burl" 2>/dev/null || true
|
|
@@ -98,8 +97,8 @@ mirror_to_role_yaml() {
|
|
|
98
97
|
trello)
|
|
99
98
|
[ -n "$bid" ] && yaml_set ticket_provider.board "$bid" 2>/dev/null || true
|
|
100
99
|
;;
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
*)
|
|
101
|
+
die "unsupported ticket provider: $provider (expected plane|trello)"
|
|
103
102
|
;;
|
|
104
103
|
esac
|
|
105
104
|
}
|
|
@@ -111,7 +110,6 @@ SOT_BOARD_ID="$(pj ticket_provider.board_id)"
|
|
|
111
110
|
SOT_URL="$(pj ticket_provider.board_url)"
|
|
112
111
|
SOT_WS="$(pj ticket_provider.workspace)"
|
|
113
112
|
SOT_IDENT="$(pj ticket_provider.identifier)"
|
|
114
|
-
SOT_TEAM="$(pj ticket_provider.team)"
|
|
115
113
|
|
|
116
114
|
# role.yaml provider comes from copier --data (the operator's pjangler choice).
|
|
117
115
|
ROLE_PROVIDER="$(yaml_get ticket_provider.name)"
|
|
@@ -122,9 +120,10 @@ if [ -n "$SOT_BOARD_ID" ]; then
|
|
|
122
120
|
if [ -n "$ROLE_PROVIDER" ] && [ "$ROLE_PROVIDER" != "$PROVIDER" ]; then
|
|
123
121
|
warn "[42] requested provider '$ROLE_PROVIDER' but repo board is '$PROVIDER' (.project.json wins); binding to existing board"
|
|
124
122
|
fi
|
|
123
|
+
case "$PROVIDER" in plane|trello) ;; *) die "unsupported ticket provider in .project.json: $PROVIDER (expected plane|trello)" ;; esac
|
|
125
124
|
log "[42] binding $AGENT_ID to existing repo board (provider=$PROVIDER, id=$SOT_BOARD_ID)"
|
|
126
|
-
mirror_to_role_yaml "$PROVIDER" "$SOT_BOARD_ID" "$SOT_URL" "$SOT_WS" "$SOT_IDENT"
|
|
127
|
-
pj_write 0 "$PROVIDER" "" "" "" ""
|
|
125
|
+
mirror_to_role_yaml "$PROVIDER" "$SOT_BOARD_ID" "$SOT_URL" "$SOT_WS" "$SOT_IDENT"
|
|
126
|
+
pj_write 0 "$PROVIDER" "" "" "" "" # register agent only; board already recorded
|
|
128
127
|
mark_done 42-ticket-provider
|
|
129
128
|
exit 0
|
|
130
129
|
fi
|
|
@@ -143,45 +142,23 @@ NAME="$(printf '%s' "$REPO" | tr '_-' ' ' | python3 -c 'import sys; print(" ".j
|
|
|
143
142
|
DESC="Ticket board for $REPO"
|
|
144
143
|
|
|
145
144
|
case "$PROVIDER" in
|
|
146
|
-
linear)
|
|
147
|
-
if [[ -z "${LINEAR_API_KEY:-}" ]]; then
|
|
148
|
-
warn "[42] LINEAR_API_KEY not set; set role.yaml/.project.json ticket_provider.team and re-run ./.scripts/42-ticket-provider.sh"
|
|
149
|
-
pj_write 1 linear "" "" "" "$IDENT" "$SOT_TEAM"
|
|
150
|
-
mark_done 42-ticket-provider; exit 0
|
|
151
|
-
fi
|
|
152
|
-
OUT="$(tp resolve 2>/dev/null || true)"
|
|
153
|
-
BID="$(printf '%s' "$OUT" | python3 -c 'import sys,json
|
|
154
|
-
try: print(json.load(sys.stdin).get("board_id",""))
|
|
155
|
-
except Exception: print("")')"
|
|
156
|
-
BURL="$(printf '%s' "$OUT" | python3 -c 'import sys,json
|
|
157
|
-
try: print(json.load(sys.stdin).get("board_url",""))
|
|
158
|
-
except Exception: print("")')"
|
|
159
|
-
if [ -n "$BID" ]; then
|
|
160
|
-
mirror_to_role_yaml linear "$BID" "$BURL" "" "$IDENT" "$SOT_TEAM"
|
|
161
|
-
pj_write 1 linear "$BID" "$BURL" "" "$IDENT" "$SOT_TEAM"
|
|
162
|
-
else
|
|
163
|
-
warn "[42] linear resolve returned no board; set ticket_provider.team and re-run"
|
|
164
|
-
pj_write 1 linear "" "" "" "$IDENT" "$SOT_TEAM"
|
|
165
|
-
fi
|
|
166
|
-
;;
|
|
167
|
-
|
|
168
145
|
plane|trello)
|
|
169
146
|
KEYVAR=PLANE_API_KEY; [ "$PROVIDER" = trello ] && KEYVAR=TRELLO_KEY
|
|
170
147
|
if [[ -z "${!KEYVAR:-}" ]]; then
|
|
171
148
|
warn "[42] $KEYVAR not set; skipping board creation. Set creds and re-run ./.scripts/42-ticket-provider.sh"
|
|
172
|
-
pj_write 1 "$PROVIDER" "" "" "${SOT_WS:-$PLANE_WORKSPACE}" "$IDENT"
|
|
149
|
+
pj_write 1 "$PROVIDER" "" "" "${SOT_WS:-${PLANE_WORKSPACE:-}}" "$IDENT"
|
|
173
150
|
mark_done 42-ticket-provider; exit 0
|
|
174
151
|
fi
|
|
175
152
|
OUT="$(tp create_board "$NAME" "$IDENT" "$DESC")" || die "create_board failed for $PROVIDER"
|
|
176
153
|
BID="$(printf '%s' "$OUT" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("board_id",""))')"
|
|
177
154
|
BURL="$(printf '%s' "$OUT" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("board_url",""))')"
|
|
178
|
-
WS="${SOT_WS:-$PLANE_WORKSPACE}"
|
|
155
|
+
WS="${SOT_WS:-${PLANE_WORKSPACE:-}}"
|
|
179
156
|
[ "$PROVIDER" = trello ] && WS=""
|
|
180
|
-
mirror_to_role_yaml "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
|
|
181
|
-
pj_write 1 "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
|
|
157
|
+
mirror_to_role_yaml "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
|
|
158
|
+
pj_write 1 "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
|
|
182
159
|
;;
|
|
183
160
|
|
|
184
|
-
*) die "unknown ticket provider: $PROVIDER (expected
|
|
161
|
+
*) die "unknown ticket provider: $PROVIDER (expected plane|trello)" ;;
|
|
185
162
|
esac
|
|
186
163
|
|
|
187
164
|
mark_done 42-ticket-provider
|
|
@@ -33,6 +33,7 @@ Environment=HERMES_HOME=$RUNTIME
|
|
|
33
33
|
Environment=HERMES_OAUTH_FILE=$HERMES_OAUTH_FILE
|
|
34
34
|
Environment=CODEX_HOME=$CODEX_HOME
|
|
35
35
|
Environment=HERMES_KANBAN_DISPATCH_IN_GATEWAY=0
|
|
36
|
+
Environment=HERMES_KANBAN_SLASH_ENABLED=0
|
|
36
37
|
EnvironmentFile=-$RUNTIME/.env
|
|
37
38
|
ExecStart=$HERMES_BIN gateway run --replace
|
|
38
39
|
Restart=on-failure
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# shellcheck shell=bash
|
|
2
2
|
# Ticket-provider adapter dispatcher — the single seam between the heartbeat
|
|
3
|
-
# sentinel engine and a concrete ticket system (
|
|
3
|
+
# sentinel engine and a concrete ticket system (Plane | Trello).
|
|
4
4
|
#
|
|
5
5
|
# The engine NEVER calls a provider directly. It calls `tp <op> [args...]`,
|
|
6
6
|
# which dispatches to providers/<provider>.sh. Swapping providers is a one-line
|
|
@@ -64,7 +64,7 @@ PY
|
|
|
64
64
|
)"
|
|
65
65
|
[ -n "$name" ] && { printf '%s\n' "$name"; return 0; }
|
|
66
66
|
fi
|
|
67
|
-
printf '
|
|
67
|
+
printf 'plane\n'
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
# Directory holding provider implementations (sibling of this lib).
|
|
@@ -81,6 +81,10 @@ tp() {
|
|
|
81
81
|
|
|
82
82
|
local name impl
|
|
83
83
|
name="$(tp_provider_name)"
|
|
84
|
+
case "$name" in
|
|
85
|
+
plane|trello) ;;
|
|
86
|
+
*) echo "tp: unsupported ticket provider '$name' (expected plane|trello)" >&2; return 2 ;;
|
|
87
|
+
esac
|
|
84
88
|
impl="$(tp_providers_dir)/${name}.sh"
|
|
85
89
|
|
|
86
90
|
if [ ! -f "$impl" ]; then
|
package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md
CHANGED
|
@@ -13,7 +13,7 @@ actively tries to break the work, surface unmet acceptance criteria, hidden
|
|
|
13
13
|
regressions, and drift. On a clean adversarial verdict the loop treats the
|
|
14
14
|
ticket as done and moves on — through the ticket-provider adapter
|
|
15
15
|
(`tp transition <id> <state>`) — and emits a BloodBank decision event carrying
|
|
16
|
-
the full report. This works identically on
|
|
16
|
+
the full report. This works identically on Plane or Trello.
|
|
17
17
|
|
|
18
18
|
The operator's verification is **deferred** to end-of-product QA over the review
|
|
19
19
|
lane, backed by a queryable decision trail. A downstream regression rollback is
|
|
@@ -144,7 +144,7 @@ Write `<ISSUE>.review.md`; the script validates it:
|
|
|
144
144
|
```markdown
|
|
145
145
|
# Autonomous Review Report: <ISSUE>
|
|
146
146
|
## Issue
|
|
147
|
-
-
|
|
147
|
+
- Provider issue: <ISSUE>
|
|
148
148
|
- Review lane reason:
|
|
149
149
|
## Reviewer
|
|
150
150
|
- Reviewer agent: <independent-agent-id>
|
package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md
CHANGED
|
@@ -19,7 +19,7 @@ approve merges.
|
|
|
19
19
|
All board access goes through the adapter (`tp`, from
|
|
20
20
|
`.scripts/lib/ticket-provider.sh`) and reasons in normalized states:
|
|
21
21
|
`backlog | unstarted | started | in_review | completed`. Never call the provider
|
|
22
|
-
directly — the engine is identical across
|
|
22
|
+
directly — the engine is identical across Plane and Trello.
|
|
23
23
|
|
|
24
24
|
## Work-state feed
|
|
25
25
|
|
|
@@ -6,13 +6,11 @@ A cheap systemd heartbeat already decided this full pass is needed.
|
|
|
6
6
|
Working repo: the git root containing this role at `agents/hermes/{{ role }}/`.
|
|
7
7
|
Ticket provider: **{{ ticket_provider }}** (reached only through the adapter — see below).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
never close/move authoritative repo tickets.
|
|
9
|
+
The configured ticket provider is the authoritative repo ticket board/SOT for
|
|
10
|
+
this PM. Hermes local Kanban must NOT be an active durable task lifecycle
|
|
11
|
+
surface. Local Kanban may only run as an explicitly configured ephemeral child
|
|
12
|
+
execution queue and must never close/move authoritative repo tickets.
|
|
14
13
|
|
|
15
|
-
{% endif -%}
|
|
16
14
|
You are the **{{ target_repo }} PM**, running your continuous board-reconciliation
|
|
17
15
|
pass. Act autonomously, but stay inside the project contracts. Read
|
|
18
16
|
`agents/hermes/{{ role }}/SOUL.md` and the engine docs under
|
|
@@ -34,7 +32,7 @@ tp transition <id> <normalized-state> # backlog|unstarted|started|in_review|com
|
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
Reason in **normalized states**, not provider terms. This pass works identically
|
|
37
|
-
on
|
|
35
|
+
on Plane or Trello.
|
|
38
36
|
|
|
39
37
|
## Pass
|
|
40
38
|
|
|
@@ -55,17 +55,18 @@ read it before publishing a type you haven't published before.
|
|
|
55
55
|
{% if role == "pm" -%}
|
|
56
56
|
You are the **project manager**. You triage incoming requests from Telegram /
|
|
57
57
|
Bloodbank command lanes, decompose them into discrete tasks on the
|
|
58
|
-
|
|
58
|
+
configured ticket board, and route work to other agents in the fleet (e.g. the dev role
|
|
59
59
|
on `bloodbank.cmd.v1.agent.{{ target_repo }}-dev.task.assign`). You do not
|
|
60
60
|
write application code. You do not approve merges.
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
is removed from the PM's cli
|
|
65
|
-
|
|
62
|
+
The configured ticket provider (`{{ ticket_provider }}`) is the authoritative
|
|
63
|
+
repo ticket board/SOT for this PM. Hermes local Kanban must NOT be an active
|
|
64
|
+
durable task lifecycle surface. The kanban toolset is removed from the PM's cli
|
|
65
|
+
surface. Local Kanban may only run as an explicitly configured ephemeral child
|
|
66
|
+
execution queue and must never close/move authoritative repo tickets.
|
|
66
67
|
|
|
67
68
|
Default execution workflow for implementation delivery: use
|
|
68
|
-
`subagent-driven-development` in
|
|
69
|
+
`subagent-driven-development` in provider-ticket-orchestrated implementation mode
|
|
69
70
|
(WIP=1, spec review gate, quality review gate).
|
|
70
71
|
|
|
71
72
|
Decision events you commonly emit:
|
|
@@ -111,7 +112,7 @@ declared` event so the fleet knows what to route to you.
|
|
|
111
112
|
- **Hostnames**: Use `*.delo.sh` for external/cross-machine access (resolved
|
|
112
113
|
via Cloudflare Tunnel), `localhost` for same-host, Docker network service
|
|
113
114
|
names for container-to-container, Tailscale for private machine-to-machine.
|
|
114
|
-
- **
|
|
115
|
+
- **Ticketing**: Always include the configured provider's ticket reference in commit messages.
|
|
115
116
|
|
|
116
117
|
## Memory hygiene
|
|
117
118
|
|