@delorenj/pjangler 1.2.21 → 1.2.25

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.
Files changed (63) hide show
  1. package/dist/index.js +1852 -368
  2. package/dist/mcp-server.js +1782 -296
  3. package/package.json +14 -3
  4. package/templates/commonproject/copier.yml +3 -0
  5. package/templates/commonproject/template/.mise/scripts/provision-bmad-skills.py +480 -0
  6. package/templates/commonproject/template/.mise/scripts/sync-skills.py +449 -0
  7. package/templates/commonproject/template/mise.toml.jinja +9 -2
  8. package/templates/commonproject/tests/test_provision_bmad_skills.py +203 -0
  9. package/templates/commonproject/tests/test_sync_skills_topology.py +120 -0
  10. package/templates/hermes-agent/README.md +70 -29
  11. package/templates/hermes-agent/copier.yml +9 -6
  12. package/templates/hermes-agent/docs/architecture.md +75 -51
  13. package/templates/hermes-agent/docs/fleet-control-plane/architecture.md +11 -6
  14. package/templates/hermes-agent/docs/fleet-control-plane/epics-and-stories.md +2 -2
  15. package/templates/hermes-agent/docs/fleet-control-plane/prd.md +2 -3
  16. package/templates/hermes-agent/docs/operations.md +179 -81
  17. package/templates/hermes-agent/docs/runbooks/runtime-checkpoint-repair.md +31 -171
  18. package/templates/hermes-agent/docs/sentinel/README.md +10 -10
  19. package/templates/hermes-agent/docs/sentinel/architecture.md +6 -8
  20. package/templates/hermes-agent/docs/sentinel/development.md +12 -15
  21. package/templates/hermes-agent/docs/sentinel.md +1 -1
  22. package/templates/hermes-agent/install-local.sh +41 -4
  23. package/templates/hermes-agent/runtime-scaffold/README.md +4 -2
  24. package/templates/hermes-agent/scripts/degit-runtime.sh +192 -0
  25. package/templates/hermes-agent/scripts/fleet-prune-debris.sh +128 -0
  26. package/templates/hermes-agent/scripts/fleet-sync.sh +164 -7
  27. package/templates/hermes-agent/scripts/hermes-runtime-templatize.py +321 -0
  28. package/templates/hermes-agent/scripts/migrate-profile-scoped-chat-secrets.py +136 -0
  29. package/templates/hermes-agent/scripts/momo-unify-agent.py +224 -0
  30. package/templates/hermes-agent/scripts/repair-runtime-checkpoint.sh +7 -119
  31. package/templates/hermes-agent/template/.gitignore.jinja +5 -2
  32. package/templates/hermes-agent/template/.runtime-scaffold/README.md +4 -2
  33. package/templates/hermes-agent/template/.scripts/00-banner.sh +1 -1
  34. package/templates/hermes-agent/template/.scripts/01-config.sh +3 -0
  35. package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +12 -1
  36. package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +55 -129
  37. package/templates/hermes-agent/template/.scripts/30-telegram.sh +276 -22
  38. package/templates/hermes-agent/template/.scripts/31-slack.sh +362 -0
  39. package/templates/hermes-agent/template/.scripts/60-bloodbank.sh +8 -66
  40. package/templates/hermes-agent/template/.scripts/70-systemd.sh +44 -30
  41. package/templates/hermes-agent/template/.scripts/80-registry.sh +93 -13
  42. package/templates/hermes-agent/template/.scripts/99-summary.sh +1 -4
  43. package/templates/hermes-agent/template/.scripts/_lib.sh +66 -1
  44. package/templates/hermes-agent/template/.scripts/checkpoint.sh +3 -2
  45. package/templates/hermes-agent/template/.scripts/config.example.toml +10 -10
  46. package/templates/hermes-agent/template/.scripts/heartbeat.sh +1 -1
  47. package/templates/hermes-agent/template/SOUL.md.jinja +2 -2
  48. package/templates/hermes-agent/template/hermes.jinja +6 -3
  49. package/templates/hermes-agent/template/role.yaml.jinja +24 -13
  50. package/templates/hermes-agent/tests/test_bloodbank_consumer_contract.py +429 -130
  51. package/templates/hermes-agent/tests/test_fleet_upgrade_contract.py +176 -0
  52. package/templates/hermes-agent/tests/test_launcher_chat_secret_isolation.py +85 -0
  53. package/templates/hermes-agent/tests/test_local_runtime_provisioning.py +130 -0
  54. package/templates/hermes-agent/tests/test_operator_docs_local_runtime_contract.py +132 -0
  55. package/templates/hermes-agent/tests/test_profile_env_detach_contract.py +14 -0
  56. package/templates/hermes-agent/tests/test_profile_scoped_chat_secrets.py +89 -0
  57. package/templates/hermes-agent/tests/test_repository_privacy_contract.py +32 -0
  58. package/templates/hermes-agent/tests/test_slack_provisioning.py +344 -0
  59. package/templates/hermes-agent/tests/test_telegram_provisioning.py +475 -0
  60. package/templates/hermes-agent/.codegraph/daemon.pid +0 -6
  61. package/templates/hermes-agent/.omo/run-continuation/ses_0e5b28303ffeXxL53hZjKggXDW.json +0 -10
  62. package/templates/hermes-agent/runtime-scaffold/bloodbank-consumer.py +0 -181
  63. package/templates/hermes-agent/template/.runtime-scaffold/bloodbank-consumer.py +0 -181
@@ -1,181 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Bloodbank consumer for hermes agent {{agent_id}}.
4
-
5
- Subscribes to:
6
- bloodbank.evt.v1.repo.>
7
- bloodbank.cmd.v1.agent.>
8
-
9
- The Bloodbank subject identifies the fixed domain/entity/action route. Repo and
10
- agent identifiers stay in envelope data, so this consumer filters repo events
11
- by ``data.repo`` and agent commands by ``data.target_agent_id``.
12
-
13
- For each event/command received, writes it to the agent's notification queue at
14
- $HERMES_HOME/bloodbank-inbox/<timestamp>.json so the gateway can ingest it on
15
- the next tick. Reconnects on broker churn; idempotent across restarts.
16
-
17
- Generated by hermes-agent-template/runtime-scaffold/bloodbank-consumer.py and
18
- rendered with role-specific values at provision time. Re-render by re-running
19
- agents/hermes/{{role}}/.scripts/20-runtime-repo.sh — do NOT edit by hand.
20
- """
21
-
22
- import asyncio
23
- import json
24
- import os
25
- import pathlib
26
- import signal
27
- import sys
28
- import time
29
- import uuid
30
- from datetime import datetime, timezone
31
-
32
- try:
33
- import nats
34
- except ImportError:
35
- sys.stderr.write("nats-py not installed. install with: pip install nats-py\n")
36
- sys.exit(2)
37
-
38
-
39
- HERMES_HOME = pathlib.Path(os.environ.get("HERMES_HOME", str(pathlib.Path.home() / ".hermes")))
40
- INBOX_DIR = HERMES_HOME / "bloodbank-inbox"
41
- INBOX_DIR.mkdir(parents=True, exist_ok=True)
42
-
43
- NATS_HOST = os.environ.get("BLOODBANK_NATS_HOST", "127.0.0.1")
44
- NATS_PORT = int(os.environ.get("BLOODBANK_NATS_PORT", "4222"))
45
- NATS_URL = f"nats://{NATS_HOST}:{NATS_PORT}"
46
-
47
- AGENT_ID = "{{agent_id}}"
48
- REPO = "{{repo}}"
49
- ROLE = "{{role}}"
50
- PRODUCER = f"hermes-agent:{AGENT_ID}"
51
- SOURCE = f"hermes://agent/{AGENT_ID}"
52
-
53
- SUBJECTS = [
54
- "bloodbank.evt.v1.repo.>",
55
- "bloodbank.cmd.v1.agent.>",
56
- ]
57
-
58
- KIND_MARKERS = {"event": "evt", "command": "cmd", "reply": "rpy"}
59
-
60
-
61
- def _now():
62
- return datetime.now(timezone.utc).isoformat()
63
-
64
-
65
- def _subject_and_domain(ce_type, kind):
66
- parts = ce_type.split(".")
67
- if len(parts) != 5 or parts[0] != "bloodbank" or parts[1] != "v1":
68
- raise ValueError(f"type {ce_type!r} must match bloodbank.v1.<domain>.<entity>.<action>")
69
- if kind not in KIND_MARKERS:
70
- raise ValueError(f"unknown envelope kind {kind!r}")
71
- _vendor, version, domain, entity, action = parts
72
- return f"bloodbank.{KIND_MARKERS[kind]}.{version}.{domain}.{entity}.{action}", domain
73
-
74
-
75
- def build_envelope(ce_type, data, *, kind="event", correlationid=None, causationid=None):
76
- """Mirror of bloodbank/services/agent-hooks/core/envelope.build_envelope."""
77
- subject, domain = _subject_and_domain(ce_type, kind)
78
- env = {
79
- "specversion": "1.0",
80
- "type": ce_type,
81
- "id": str(uuid.uuid4()),
82
- "source": SOURCE,
83
- "subject": subject,
84
- "time": _now(),
85
- "datacontenttype": "application/json",
86
- "kind": kind,
87
- "producer": PRODUCER,
88
- "service": "hermes-agent",
89
- "domain": domain,
90
- "actor": {"type": "agent", "agent_id": AGENT_ID, "cli": "hermes"},
91
- "data": data,
92
- "correlationid": correlationid or str(uuid.uuid4()),
93
- "causationid": causationid or str(uuid.uuid4()),
94
- }
95
- if kind == "event":
96
- env["ordering_key"] = f"agent:{AGENT_ID}"
97
- return env
98
-
99
-
100
- def _is_for_consumer(subject, payload):
101
- """Route canonical subjects using identifiers carried in envelope data."""
102
- if not isinstance(payload, dict):
103
- return False
104
- parts = subject.split(".")
105
- if len(parts) != 6 or parts[0] != "bloodbank" or parts[2] != "v1":
106
- return False
107
-
108
- _vendor, kind, version, domain, entity, action = parts
109
- if kind not in KIND_MARKERS.values():
110
- return False
111
- if KIND_MARKERS.get(payload.get("kind")) != kind:
112
- return False
113
- if payload.get("subject") != subject:
114
- return False
115
- if payload.get("type") != f"bloodbank.{version}.{domain}.{entity}.{action}":
116
- return False
117
- data = payload.get("data")
118
- if not isinstance(data, dict):
119
- return False
120
- if kind == "evt" and domain == "repo":
121
- return data.get("repo") == REPO
122
- if kind == "cmd" and domain == "agent":
123
- return data.get("target_agent_id") == AGENT_ID
124
- return False
125
-
126
-
127
- async def main():
128
- nc = await nats.connect(NATS_URL, name=f"hermes-{AGENT_ID}")
129
- stop = asyncio.Event()
130
-
131
- async def on_msg(msg):
132
- try:
133
- payload = json.loads(msg.data.decode())
134
- except Exception:
135
- sys.stderr.write(f"[bloodbank-consumer:{AGENT_ID}] skipped unroutable payload on {msg.subject}\n")
136
- return
137
- if not _is_for_consumer(msg.subject, payload):
138
- return
139
- entry = {
140
- "received_at": _now(),
141
- "subject": msg.subject,
142
- "reply_to": msg.reply,
143
- "payload": payload,
144
- }
145
- fname = f"{int(time.time()*1000):013d}-{uuid.uuid4().hex[:8]}.json"
146
- tmp = INBOX_DIR / (fname + ".tmp")
147
- tmp.write_text(json.dumps(entry, indent=2))
148
- tmp.rename(INBOX_DIR / fname)
149
- sys.stderr.write(f"[bloodbank-consumer:{AGENT_ID}] {msg.subject} -> {fname}\n")
150
-
151
- for subj in SUBJECTS:
152
- await nc.subscribe(subj, cb=on_msg)
153
- sys.stderr.write(f"[bloodbank-consumer:{AGENT_ID}] subscribed: {subj}\n")
154
-
155
- # Announce presence
156
- online = build_envelope(
157
- "bloodbank.v1.agent.online.changed",
158
- {"agent_id": AGENT_ID, "repo": REPO, "role": ROLE, "state": "online"},
159
- )
160
- await nc.publish(online["subject"], json.dumps(online).encode())
161
-
162
- def _shutdown(*_):
163
- stop.set()
164
- signal.signal(signal.SIGTERM, _shutdown)
165
- signal.signal(signal.SIGINT, _shutdown)
166
-
167
- await stop.wait()
168
-
169
- offline = build_envelope(
170
- "bloodbank.v1.agent.online.changed",
171
- {"agent_id": AGENT_ID, "repo": REPO, "role": ROLE, "state": "offline"},
172
- )
173
- await nc.publish(offline["subject"], json.dumps(offline).encode())
174
- await nc.drain()
175
-
176
-
177
- if __name__ == "__main__":
178
- try:
179
- asyncio.run(main())
180
- except KeyboardInterrupt:
181
- pass