modastack 0.2.0__tar.gz

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 (139) hide show
  1. modastack-0.2.0/.claude/hooks/session-state.sh +52 -0
  2. modastack-0.2.0/.claude/memory/project_dispatch.md +22 -0
  3. modastack-0.2.0/.claude/plans/github-app-manifest.md +225 -0
  4. modastack-0.2.0/.claude/settings.json +7 -0
  5. modastack-0.2.0/.github/workflows/deploy-event-server.yml +36 -0
  6. modastack-0.2.0/.github/workflows/publish-pypi.yml +41 -0
  7. modastack-0.2.0/.gitignore +16 -0
  8. modastack-0.2.0/.idea/.gitignore +5 -0
  9. modastack-0.2.0/.idea/encodings.xml +6 -0
  10. modastack-0.2.0/.modastack.yaml +23 -0
  11. modastack-0.2.0/CHANGELOG.md +63 -0
  12. modastack-0.2.0/CLAUDE.md +248 -0
  13. modastack-0.2.0/PKG-INFO +33 -0
  14. modastack-0.2.0/README.md +430 -0
  15. modastack-0.2.0/VERSION +1 -0
  16. modastack-0.2.0/bootstrap.sh +15 -0
  17. modastack-0.2.0/dashboard/__init__.py +0 -0
  18. modastack-0.2.0/dashboard/app.py +219 -0
  19. modastack-0.2.0/dashboard/data.py +240 -0
  20. modastack-0.2.0/dashboard/templates/index.html +412 -0
  21. modastack-0.2.0/deploy/INSTALL.md +592 -0
  22. modastack-0.2.0/deploy/auto-deploy.sh +106 -0
  23. modastack-0.2.0/deploy/install.sh +760 -0
  24. modastack-0.2.0/deploy/modastack.service +15 -0
  25. modastack-0.2.0/deploy/setup-ec2.sh +175 -0
  26. modastack-0.2.0/deploy/update-webhooks.sh +88 -0
  27. modastack-0.2.0/docs/CUSTOM_WORKFLOWS.md +345 -0
  28. modastack-0.2.0/docs/LINEAR_SETUP.md +91 -0
  29. modastack-0.2.0/docs/SKILL_PACKS.md +105 -0
  30. modastack-0.2.0/docs/SLACK_SETUP.md +111 -0
  31. modastack-0.2.0/docs/WORKFLOWS.md +305 -0
  32. modastack-0.2.0/docs/specs/85-worktree-relocation.md +263 -0
  33. modastack-0.2.0/event-server/.editorconfig +12 -0
  34. modastack-0.2.0/event-server/.gitignore +167 -0
  35. modastack-0.2.0/event-server/.prettierrc +6 -0
  36. modastack-0.2.0/event-server/package-lock.json +2891 -0
  37. modastack-0.2.0/event-server/package.json +18 -0
  38. modastack-0.2.0/event-server/src/deployment-session.ts +166 -0
  39. modastack-0.2.0/event-server/src/index.ts +383 -0
  40. modastack-0.2.0/event-server/test/env.d.ts +3 -0
  41. modastack-0.2.0/event-server/test/index.spec.ts +72 -0
  42. modastack-0.2.0/event-server/test/tsconfig.json +8 -0
  43. modastack-0.2.0/event-server/tsconfig.json +42 -0
  44. modastack-0.2.0/event-server/vitest.config.mts +11 -0
  45. modastack-0.2.0/event-server/worker-configuration.d.ts +13720 -0
  46. modastack-0.2.0/event-server/wrangler.jsonc +28 -0
  47. modastack-0.2.0/modastack/__init__.py +0 -0
  48. modastack-0.2.0/modastack/__version__.py +5 -0
  49. modastack-0.2.0/modastack/board_setup.py +86 -0
  50. modastack-0.2.0/modastack/browser.py +324 -0
  51. modastack-0.2.0/modastack/cli.py +2218 -0
  52. modastack-0.2.0/modastack/config.py +342 -0
  53. modastack-0.2.0/modastack/doctor.py +70 -0
  54. modastack-0.2.0/modastack/github_issues.py +166 -0
  55. modastack-0.2.0/modastack/history.py +370 -0
  56. modastack-0.2.0/modastack/manager/__init__.py +0 -0
  57. modastack-0.2.0/modastack/manager/events/__init__.py +6 -0
  58. modastack-0.2.0/modastack/manager/events/consumer.py +284 -0
  59. modastack-0.2.0/modastack/manager/events/event_client.py +429 -0
  60. modastack-0.2.0/modastack/manager/events/slack_responder.py +91 -0
  61. modastack-0.2.0/modastack/manager/session.py +430 -0
  62. modastack-0.2.0/modastack/monitors/__init__.py +20 -0
  63. modastack-0.2.0/modastack/monitors/checks.py +144 -0
  64. modastack-0.2.0/modastack/monitors/registry.py +183 -0
  65. modastack-0.2.0/modastack/monitors/scheduler.py +214 -0
  66. modastack-0.2.0/modastack/monitors/schema.py +109 -0
  67. modastack-0.2.0/modastack/prompts/__init__.py +14 -0
  68. modastack-0.2.0/modastack/prompts/agent_base.md +40 -0
  69. modastack-0.2.0/modastack/prompts/agents/engineer.md +492 -0
  70. modastack-0.2.0/modastack/prompts/manager_base.md +172 -0
  71. modastack-0.2.0/modastack/prompts/manager_engineering.md +152 -0
  72. modastack-0.2.0/modastack/prompts/resolver.py +40 -0
  73. modastack-0.2.0/modastack/relay.py +83 -0
  74. modastack-0.2.0/modastack/scanner.py +47 -0
  75. modastack-0.2.0/modastack/sdk.py +219 -0
  76. modastack-0.2.0/modastack/session.py +277 -0
  77. modastack-0.2.0/modastack/setup.py +141 -0
  78. modastack-0.2.0/modastack/subagent.py +1065 -0
  79. modastack-0.2.0/modastack/tmux.py +287 -0
  80. modastack-0.2.0/modastack/workflow/__init__.py +8 -0
  81. modastack-0.2.0/modastack/workflow/orchestrator.py +571 -0
  82. modastack-0.2.0/modastack/workflow/schema.py +91 -0
  83. modastack-0.2.0/modastack/workflow/state.py +187 -0
  84. modastack-0.2.0/modastack/workflow/triggers.py +89 -0
  85. modastack-0.2.0/modastack/workflow/variables.py +201 -0
  86. modastack-0.2.0/monitors/defaults.yaml +16 -0
  87. modastack-0.2.0/pack.json +60 -0
  88. modastack-0.2.0/pyproject.toml +55 -0
  89. modastack-0.2.0/scripts/test_executor_coding.py +175 -0
  90. modastack-0.2.0/scripts/test_executor_live.py +152 -0
  91. modastack-0.2.0/specs/agd-9-review-readme.md +84 -0
  92. modastack-0.2.0/specs/mds-21-stall-detection.md +223 -0
  93. modastack-0.2.0/specs/mds-22-remote-deployment.md +495 -0
  94. modastack-0.2.0/specs/mds-24-web-dashboard.md +214 -0
  95. modastack-0.2.0/specs/mds-25-self-updating.md +240 -0
  96. modastack-0.2.0/tests/__init__.py +0 -0
  97. modastack-0.2.0/tests/integration/__init__.py +0 -0
  98. modastack-0.2.0/tests/integration/test_agent_cli.py +67 -0
  99. modastack-0.2.0/tests/integration/test_consult.py +116 -0
  100. modastack-0.2.0/tests/integration/test_consumer_startup.py +110 -0
  101. modastack-0.2.0/tests/integration/test_event_pipeline.py +220 -0
  102. modastack-0.2.0/tests/integration/test_full_lifecycle.py +318 -0
  103. modastack-0.2.0/tests/integration/test_inject.py +162 -0
  104. modastack-0.2.0/tests/integration/test_orchestrator.py +189 -0
  105. modastack-0.2.0/tests/integration/test_spawn_background.py +94 -0
  106. modastack-0.2.0/tests/test_browser.py +241 -0
  107. modastack-0.2.0/tests/test_cli.py +241 -0
  108. modastack-0.2.0/tests/test_config.py +153 -0
  109. modastack-0.2.0/tests/test_consult.py +114 -0
  110. modastack-0.2.0/tests/test_consumer.py +172 -0
  111. modastack-0.2.0/tests/test_credentials.py +62 -0
  112. modastack-0.2.0/tests/test_dashboard.py +221 -0
  113. modastack-0.2.0/tests/test_event_client.py +250 -0
  114. modastack-0.2.0/tests/test_github_issues.py +230 -0
  115. modastack-0.2.0/tests/test_inject.py +158 -0
  116. modastack-0.2.0/tests/test_inject_capture.py +103 -0
  117. modastack-0.2.0/tests/test_manager_sdk.py +192 -0
  118. modastack-0.2.0/tests/test_monitors.py +255 -0
  119. modastack-0.2.0/tests/test_orchestrator.py +443 -0
  120. modastack-0.2.0/tests/test_registry.py +128 -0
  121. modastack-0.2.0/tests/test_relay.py +87 -0
  122. modastack-0.2.0/tests/test_self_update.py +333 -0
  123. modastack-0.2.0/tests/test_session.py +97 -0
  124. modastack-0.2.0/tests/test_session_extended.py +133 -0
  125. modastack-0.2.0/tests/test_setup.py +97 -0
  126. modastack-0.2.0/tests/test_slack_reply.py +232 -0
  127. modastack-0.2.0/tests/test_slack_responder.py +202 -0
  128. modastack-0.2.0/tests/test_subagent.py +314 -0
  129. modastack-0.2.0/tests/test_subagent_blocking.py +1210 -0
  130. modastack-0.2.0/tests/test_triggers.py +217 -0
  131. modastack-0.2.0/workflows/adhoc.yaml +12 -0
  132. modastack-0.2.0/workflows/build-failure.yaml +14 -0
  133. modastack-0.2.0/workflows/examples/content-review.yaml +121 -0
  134. modastack-0.2.0/workflows/examples/research.yaml +99 -0
  135. modastack-0.2.0/workflows/issue-lifecycle.yaml +64 -0
  136. modastack-0.2.0/workflows/pr-feedback.yaml +16 -0
  137. modastack-0.2.0/workflows/pr-merged.yaml +14 -0
  138. modastack-0.2.0/workflows/self-update.yaml.disabled +47 -0
  139. modastack-0.2.0/workflows/stall-recovery.yaml +15 -0
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+ # Write Claude Code hook events to the manager activity log.
3
+ # On Stop: also relay the assistant response to Slack via the relay config.
4
+ python3 -c "
5
+ import sys, json, time, os
6
+
7
+ data = json.load(sys.stdin)
8
+ entry = {
9
+ 'event': data['hook_event_name'],
10
+ 'ts': time.time(),
11
+ 'session_id': data.get('session_id', ''),
12
+ }
13
+
14
+ # Write activity log
15
+ log_dir = os.path.expanduser('~/.modastack/manager')
16
+ os.makedirs(log_dir, exist_ok=True)
17
+ with open(os.path.join(log_dir, 'activity.jsonl'), 'a') as f:
18
+ f.write(json.dumps(entry) + '\n')
19
+
20
+ # On Stop: relay assistant response to Slack only if a Slack message triggered this turn
21
+ if data['hook_event_name'] == 'Stop':
22
+ msg = data.get('last_assistant_message', '')
23
+ marker = os.path.expanduser('~/.modastack/manager/slack_reply_pending')
24
+ if msg and os.path.exists(marker):
25
+ os.unlink(marker)
26
+ try:
27
+ import yaml
28
+ config_path = os.path.expanduser('~/.modastack/config.yaml')
29
+ with open(config_path) as cf:
30
+ config = yaml.safe_load(cf) or {}
31
+ slack = config.get('slack', {})
32
+ token = slack.get('bot_token', '')
33
+ channel = slack.get('dm_channel', '') or 'D0B51JP1N4C'
34
+ if token:
35
+ import urllib.request, re
36
+ text = msg
37
+ # Markdown → Slack mrkdwn
38
+ text = re.sub(r'^#{1,6}\s+(.+)$', r'*\1*', text, flags=re.MULTILINE)
39
+ text = re.sub(r'\*\*(.+?)\*\*', r'*\1*', text)
40
+ text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'<\2|\1>', text)
41
+ if len(text) > 3000:
42
+ text = text[:3000] + '\n_(truncated)_'
43
+ payload = json.dumps({'channel': channel, 'text': text}).encode()
44
+ req = urllib.request.Request(
45
+ 'https://slack.com/api/chat.postMessage',
46
+ data=payload,
47
+ headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},
48
+ )
49
+ urllib.request.urlopen(req, timeout=5)
50
+ except Exception:
51
+ pass
52
+ "
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: modastack project state
3
+ description: Current state and decisions for the modastack project — cron-based dispatch loop with Linear as primary interaction channel
4
+ type: project
5
+ ---
6
+
7
+ Building a cron-based agent dispatch system at ~/dev/modastack.
8
+
9
+ **Why:** User wants to understand agentic development from first principles by building their own orchestration layer, taking design decisions from OpenClaw and Hermes.
10
+
11
+ **Key decisions made:**
12
+ - Linear + Linear comments as the sole interaction channel (Slack deferred to later)
13
+ - Cron every 1 minute (cheap cycle, just HTTP calls)
14
+ - Python + pip/venv
15
+ - Per-repo .modastack.yaml for portability
16
+ - Named credentials in ~/.modastack/credentials.yaml for multi-workspace
17
+ - Skills discovery layer (auto-detects gstack or any skill pack)
18
+ - BLOCKED state: agent posts question as Linear comment, polls for reply each cycle
19
+ - GitHub repo: underminedsk/modastack (public)
20
+ - GitHub account for this project: underminedsk
21
+
22
+ **How to apply:** When working on this project, use the underminedsk GitHub account. Linear is the primary channel — no Slack integration yet. Keep the architecture simple (cron, JSON state file, no daemon).
@@ -0,0 +1,225 @@
1
+ # Plan: Centralized Event Server + GitHub App
2
+
3
+ Replace the current polling-and-direct-webhook architecture with a centralized
4
+ event server on Cloudflare Workers. One GitHub App receives all webhook events.
5
+ Modastack deployments connect outbound via WebSocket to subscribe to their repos'
6
+ events, with automatic catch-up on missed events after downtime.
7
+
8
+ ## Problem
9
+
10
+ Three problems, one solution:
11
+
12
+ 1. **Webhook permissions** — moda-bot can't install webhooks because it lacks admin
13
+ access. A centralized GitHub App gets webhook permissions via installation.
14
+ 2. **Event loss during downtime** — when modastack restarts or crashes, all events
15
+ during the gap are lost. The central server buffers events and replays on reconnect.
16
+ 3. **Inbound port requirement** — current webhook server requires a public IP with
17
+ open ports. Outbound WebSocket connections work behind NAT/firewalls.
18
+
19
+ ## Architecture
20
+
21
+ ```
22
+ GitHub ──webhook──┐
23
+ Linear ──webhook──┤
24
+ Slack ──webhook───┤
25
+
26
+ Cloudflare Worker (event-server)
27
+
28
+ ├── receives webhooks, assigns sequence IDs
29
+ ├── stores events in KV (48h buffer)
30
+ └── Durable Object per deployment
31
+
32
+ ▼ WebSocket
33
+ modastack deployment
34
+
35
+ └── "last_seen: 57" → replays 58, 59, 60... → live stream
36
+ ```
37
+
38
+ ## Components
39
+
40
+ ### 1. Cloudflare Worker: `moda-event-server`
41
+
42
+ A single Worker with three responsibilities:
43
+
44
+ **Webhook ingestion (HTTP routes):**
45
+ - `POST /webhooks/github` — receives GitHub App webhook events
46
+ - `POST /webhooks/linear` — receives Linear webhook events
47
+ - `POST /webhooks/slack` — receives Slack event API payloads
48
+
49
+ Each incoming event gets:
50
+ - A globally monotonic sequence ID (per-deployment namespace)
51
+ - Stored in KV with TTL of 48 hours
52
+ - Forwarded to the Durable Object for each matching deployment
53
+
54
+ **Deployment registration (HTTP routes):**
55
+ - `POST /deployments` — register a new deployment, returns API key
56
+ - `GET /deployments/:id/subscribe` — upgrade to WebSocket
57
+
58
+ **Event routing logic:**
59
+ - GitHub: route by `installation.id` or `repository.full_name`
60
+ - Linear: route by workspace ID or team key
61
+ - Slack: route by workspace/bot token identifier
62
+
63
+ ### 2. Durable Object: `DeploymentSession`
64
+
65
+ One DO per registered deployment. Responsibilities:
66
+
67
+ - **Subscription state** — which repos/orgs/Linear teams this deployment cares about
68
+ - **Cursor tracking** — last-acknowledged event ID per deployment
69
+ - **WebSocket management** — holds the live connection to the deployment
70
+ - **Replay on reconnect** — when a deployment reconnects with `last_seen: N`,
71
+ fetch events N+1..latest from KV and send them in order before switching to live
72
+
73
+ **Event delivery contract:**
74
+ - Events delivered in sequence order, no gaps
75
+ - Deployment sends `{"ack": 73}` to advance its cursor
76
+ - If WebSocket is disconnected, events buffer in KV (up to 48h)
77
+ - On reconnect, full replay from cursor position
78
+
79
+ ### 3. GitHub App: `Modastack` (centralized)
80
+
81
+ One GitHub App registered under the moda-labs org:
82
+
83
+ **Permissions:**
84
+ - `contents: write` — read/write repo contents (for PRs, branches)
85
+ - `issues: write` — manage issues and labels
86
+ - `pull_requests: write` — create/update PRs, request reviews
87
+ - `checks: read` — read CI status
88
+ - Webhook events: `issues`, `issue_comment`, `pull_request`,
89
+ `pull_request_review`, `check_run`, `workflow_run`
90
+
91
+ **Webhook URL:** `https://moda-events.<domain>/webhooks/github`
92
+
93
+ **Installation flow:**
94
+ 1. User runs `modastack setup`
95
+ 2. Opens `https://github.com/apps/modastack/installations/new`
96
+ 3. User selects org and repos to grant access
97
+ 4. GitHub sends `installation` webhook to the event server
98
+ 5. Event server auto-registers the deployment's subscription
99
+
100
+ **Token generation stays local.** Each modastack deployment has a copy of the
101
+ app's private key (provisioned during `modastack setup`). It generates its own
102
+ installation tokens via JWT for GitHub API calls (creating PRs, managing issues).
103
+ The central server only handles webhook receipt and forwarding — it never needs
104
+ to call GitHub's API on behalf of deployments.
105
+
106
+ ### 4. Modastack client changes
107
+
108
+ Replace the current webhook server + polling architecture in `modastack/manager/events/`
109
+ with an outbound WebSocket client:
110
+
111
+ **New file: `modastack/manager/events/event_client.py`**
112
+ - Connects to `wss://moda-events.<domain>/deployments/:id/subscribe`
113
+ - Authenticates with deployment API key
114
+ - Sends `last_seen` cursor on connect (persisted in `~/.modastack/cursor.json`)
115
+ - Receives events, feeds them into the existing event bus
116
+ - Acks events after successful processing
117
+ - Auto-reconnects with exponential backoff
118
+
119
+ **Modified: `modastack/manager/events/consumer.py`**
120
+ - `run()` starts the WebSocket client instead of (or alongside) pollers
121
+ - Events from the WebSocket feed into the same bus/batching pipeline
122
+ - Existing manager session injection unchanged
123
+
124
+ **Removed (or made optional):**
125
+ - `webhook_server.py` — no longer needed; events come via WebSocket
126
+ - GitHub/Linear pollers in `pollers.py` — replaced by webhooks through the
127
+ central server. Slack Socket Mode can stay as-is or move to central server.
128
+
129
+ **Kept as fallback:**
130
+ - `gh` CLI auth for GitHub API calls (creating PRs, etc.)
131
+ - Local polling mode via `modastack start` (no `--webhooks`) for users who
132
+ don't want the central server
133
+
134
+ ## KV Schema
135
+
136
+ ```
137
+ events:{deployment_id}:{sequence_id} → {event JSON} TTL: 48h
138
+ cursor:{deployment_id} → {last_acked_id}
139
+ deployments:{api_key} → {deployment config}
140
+ subscriptions:{owner/repo} → [deployment_id, ...]
141
+ ```
142
+
143
+ ## Setup Flow (updated)
144
+
145
+ ```bash
146
+ modastack setup <repo>
147
+ ```
148
+
149
+ 1. Detect GitHub org from remote URL
150
+ 2. Check if Modastack GitHub App is installed on that org
151
+ - If not: open browser to install URL, wait for confirmation
152
+ 3. Register deployment with central event server (gets API key)
153
+ 4. Save API key + deployment ID to `~/.modastack/config.yaml`
154
+ 5. Configure repo subscriptions on the event server
155
+ 6. Existing setup steps: generate .modastack.yaml, install skills, hooks
156
+
157
+ ## Auth Model
158
+
159
+ | Action | Auth method |
160
+ |---|---|
161
+ | Receive webhook events | Central server → deployment via WebSocket (API key) |
162
+ | Create PRs, manage issues | Deployment generates installation token locally (app private key) |
163
+ | Register deployment | One-time API key from central server |
164
+ | Install GitHub App on org | User clicks install in browser (GitHub handles auth) |
165
+
166
+ ## Key Design Decisions
167
+
168
+ - **Central server is dumb relay.** It receives, stores, and forwards. It never
169
+ calls external APIs. All GitHub/Linear API calls happen on the deployment side.
170
+ This keeps the Worker simple and stateless (except for KV/DO).
171
+ - **48-hour event buffer.** Covers weekends, maintenance windows, EC2 crashes.
172
+ After 48h, the deployment falls back to polling-based reconciliation (scan
173
+ Linear/GitHub for current state), which already exists.
174
+ - **Private key distributed to deployments.** Each deployment gets a copy during
175
+ setup. This means deployments can make GitHub API calls independently. The
176
+ alternative (central server proxies all API calls) adds latency and complexity.
177
+ - **Sequence IDs per deployment namespace.** Not global. Each deployment gets its
178
+ own monotonic counter. Simpler, no cross-deployment ordering needed.
179
+ - **`gh` CLI stays as fallback** for GitHub API calls. Existing users don't break.
180
+
181
+ ## What This Replaces
182
+
183
+ | Current | New |
184
+ |---|---|
185
+ | `webhook_server.py` (inbound HTTP) | WebSocket client (outbound) |
186
+ | GitHub polling in `pollers.py` | GitHub webhooks via central server |
187
+ | Linear polling in `pollers.py` | Linear webhooks via central server |
188
+ | `moda-bot` user account + PAT | Modastack GitHub App + installation tokens |
189
+ | Events lost on restart | 48h buffer with replay |
190
+ | Public IP required | Works behind NAT |
191
+
192
+ ## Implementation Sequence
193
+
194
+ ### Phase 1: Central event server (Cloudflare Worker)
195
+ 1. Scaffold Worker project with Durable Objects + KV
196
+ 2. GitHub webhook ingestion endpoint
197
+ 3. Deployment registration + API key management
198
+ 4. Durable Object: WebSocket handling, cursor tracking, replay
199
+ 5. Deploy to Cloudflare
200
+
201
+ ### Phase 2: GitHub App
202
+ 1. Register Modastack app under moda-labs org
203
+ 2. Configure permissions and webhook URL → central server
204
+ 3. Install on moda-labs org
205
+ 4. Test: webhook events arrive at central server
206
+
207
+ ### Phase 3: Modastack client
208
+ 1. New `event_client.py` — WebSocket client with reconnect + replay
209
+ 2. Wire into consumer.py event loop
210
+ 3. Update `modastack setup` to register with central server
211
+ 4. Update `modastack start` to use WebSocket mode by default
212
+ 5. Keep polling mode as `modastack start --local` fallback
213
+
214
+ ### Phase 4: Linear + Slack integration
215
+ 1. Add Linear webhook ingestion to central server
216
+ 2. Add Slack event API endpoint (or keep Socket Mode local)
217
+ 3. Route Linear events to deployments by team key
218
+ 4. Remove polling loops from modastack client
219
+
220
+ ## Testing
221
+
222
+ - Unit tests: event serialization, cursor logic, subscription matching
223
+ - Integration test: Worker receives webhook → DO buffers → client replays
224
+ - Miniflare for local Worker testing
225
+ - Manual end-to-end: install app, push to repo, verify event arrives at deployment
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "deny": [
4
+ "mcp__claude_ai_Venn__*"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,36 @@
1
+ name: Deploy Event Server
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "event-server/**"
9
+
10
+ jobs:
11
+ deploy:
12
+ name: Deploy Worker
13
+ runs-on: ubuntu-latest
14
+ defaults:
15
+ run:
16
+ working-directory: event-server
17
+ steps:
18
+ - uses: actions/checkout@v5
19
+
20
+ - uses: actions/setup-node@v5
21
+ with:
22
+ node-version: "22"
23
+ cache: "npm"
24
+ cache-dependency-path: event-server/package-lock.json
25
+
26
+ - run: npm ci
27
+
28
+ - run: npx vitest run
29
+
30
+ - name: Deploy to Cloudflare
31
+ uses: cloudflare/wrangler-action@v4
32
+ with:
33
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
34
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
35
+ workingDirectory: event-server
36
+ command: deploy
@@ -0,0 +1,41 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.13"
21
+
22
+ - name: Install build tools
23
+ run: pip install build
24
+
25
+ - name: Build package
26
+ run: python -m build
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
30
+
31
+ - name: Extract version from tag
32
+ id: version
33
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
34
+
35
+ - name: Trigger Homebrew formula update
36
+ uses: peter-evans/repository-dispatch@v3
37
+ with:
38
+ token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
39
+ repository: moda-labs/homebrew-modastack
40
+ event-type: pypi-release
41
+ client-payload: '{"version": "${{ steps.version.outputs.version }}"}'
@@ -0,0 +1,16 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ .pytest_cache/
6
+ *.pyc
7
+ .env
8
+ .claude/settings.local.json
9
+ .claude/projects/
10
+ worktrees/
11
+ .dispatch/
12
+ .dispatch.yaml
13
+ .gstack/
14
+ .modastack/
15
+ .context/
16
+ worktrees/
@@ -0,0 +1,5 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Encoding">
4
+ <file url="PROJECT" charset="UTF-8" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,23 @@
1
+ task_tracking:
2
+ system: github-issues
3
+ project: ENG
4
+ trigger_labels:
5
+ - agent
6
+ skip_labels:
7
+ - blocked
8
+ - human-only
9
+ complexity:
10
+ trivial: label:typo OR label:docs OR label:config
11
+ medium: default
12
+ heavy: label:feature OR label:refactor OR estimate>3
13
+ agent:
14
+ tool: claude
15
+ skills:
16
+ - review
17
+ - ship
18
+ max_parallel: 2
19
+ verify:
20
+ test_command: pytest
21
+ review_required: true
22
+ auto_merge: true
23
+ credentials: modastack
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ### Added
6
+ - Auto-resolve merge conflicts: `monitor/pr.conflict_detected` now triggers the manager to auto-spawn an engineer (instead of just noting it) that follows a new `merge-conflict` skill — merge the base branch, resolve conflicts honoring both sides, verify build/tests, and push. If the conflict needs a human decision, the engineer comments on the PR and exits non-zero, and the manager escalates to the human via Slack (#117)
7
+
8
+ ## 0.4.1 — 2026-06-01
9
+
10
+ ### Added
11
+ - Engineer lifecycle events: `modastack spawn` and workflow-managed engineers now emit `engineer/session.started`, `engineer/session.completed`, and `engineer/session.failed` to the event bus, so the manager can narrate engineer activity without polling (#103)
12
+ - Events post fire-and-forget over HTTP (`POST /api/event`) on a daemon thread, reusing the same path monitor checks use, so delivery never blocks or breaks an engineer run
13
+ - Manager event formatter now surfaces `phase`, `duration`, `summary`, and `error` fields from lifecycle events
14
+
15
+ ## 0.4.0 — 2026-06-01
16
+
17
+ ### Added
18
+ - Background monitoring system: scheduled polling tasks that fill webhook gaps by detecting conditions and injecting synthetic events into the manager's event stream (#100)
19
+ - Three-tier monitor storage (built-in `monitors/defaults.yaml` → user `~/.modastack/monitors.yaml` → repo `.modastack.yaml`), merged with later tiers overriding by `name` and repo-level `enabled: false` opt-out
20
+ - Built-in default monitors: PR conflict check (15m) and stale-PR check (1h), both working out of the box
21
+ - `modastack monitor add/list/pause/remove` CLI for managing monitors across tiers
22
+ - Native check runners (`pr_conflicts`, `stale_prs`) with per-condition deduplication; description-only monitors fall back to manager interpretation
23
+
24
+ ## 0.3.3 — 2026-05-27
25
+
26
+ ### Added
27
+ - Documentation: composable skills principle, workflow resolution chain (repo > user > default), and event normalization table (GitHub Issues + Linear to task.* format)
28
+
29
+ ## 0.3.2.1 — 2026-05-27
30
+
31
+ ### Fixed
32
+ - README phase routing table and handoff example now use the correct `implement_complete` phase name (was `implementation_complete`)
33
+
34
+ ## 0.3.2 — 2026-05-26
35
+
36
+ ### Added
37
+ - Mermaid flowchart diagrams in README: event flow, issue lifecycle, skill composition, and deploy pipeline
38
+
39
+ ## 0.3.1 — 2026-05-26
40
+
41
+ ### Changed
42
+ - CLI help text for `workflow` and `history` subcommands now includes descriptions and usage examples
43
+
44
+ ## 0.2.2 — 2026-05-23
45
+
46
+ ### Added
47
+ - Stall detection: heartbeat tracking via output hashing detects sessions idle >5 min (nudge) or >10 min (kill)
48
+ - Permission prompt detection: sessions blocked on interactive approval are identified and reported
49
+ - Process liveness checks: dead claude processes inside live tmux sessions emit `worker.process_dead`
50
+ - Auto-routing: manager prompt now routes engineers to the next phase based on handoff state
51
+
52
+ ## 0.2.1 — 2026-05-23
53
+
54
+ - Self-updating: version check poller, Slack notification, user-approved update
55
+ - Slack threading fix — conversations inline, only proactive updates threaded
56
+
57
+ ## 0.2.0 — 2026-05-20
58
+
59
+ - Event-driven architecture with persistent manager session
60
+ - Linear + GitHub Issues task tracking
61
+ - Slack Socket Mode for real-time events
62
+ - Engineer lifecycle: pickup, spec, implement, prepare-pr, feedback
63
+ - Orphan session detection