speexor 0.1.0

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 (48) hide show
  1. package/API-REFERENCE.md +201 -0
  2. package/ARCHITECTURE.md +548 -0
  3. package/CHANGELOG.md +52 -0
  4. package/CODE-OF-CONDUCT.md +83 -0
  5. package/CONTRIBUTING.md +98 -0
  6. package/FAQ.md +105 -0
  7. package/LICENSE.md +21 -0
  8. package/PUBLISH.md +77 -0
  9. package/README.md +179 -0
  10. package/REFACTOR-LOG.md +40 -0
  11. package/ROADMAP.md +78 -0
  12. package/SECURITY.md +79 -0
  13. package/SUMMARY.md +46 -0
  14. package/TESTING.md +140 -0
  15. package/dist/agent-5D3BVWNK.js +37 -0
  16. package/dist/agent-5D3BVWNK.js.map +1 -0
  17. package/dist/chunk-2F66BZYJ.js +212 -0
  18. package/dist/chunk-2F66BZYJ.js.map +1 -0
  19. package/dist/chunk-5NA2TFPG.js +3 -0
  20. package/dist/chunk-5NA2TFPG.js.map +1 -0
  21. package/dist/chunk-B7WLHC4W.js +666 -0
  22. package/dist/chunk-B7WLHC4W.js.map +1 -0
  23. package/dist/chunk-SXALZEOJ.js +345 -0
  24. package/dist/chunk-SXALZEOJ.js.map +1 -0
  25. package/dist/cli/index.d.ts +1 -0
  26. package/dist/cli/index.js +287 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/core/index.d.ts +31 -0
  29. package/dist/core/index.js +4 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/index.d.ts +75 -0
  32. package/dist/index.js +205 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/plugins/index.d.ts +6 -0
  35. package/dist/plugins/index.js +3 -0
  36. package/dist/plugins/index.js.map +1 -0
  37. package/dist/types-0q_okI2g.d.ts +205 -0
  38. package/docs/PRD01.md +264 -0
  39. package/docs/PRD02.md +299 -0
  40. package/docs/PRD03.md +0 -0
  41. package/docs/PRD04.md +349 -0
  42. package/docs/PRD05.md +312 -0
  43. package/docs/SETUP.md +94 -0
  44. package/docs/TROUBLESHOOTING.md +113 -0
  45. package/examples/basic.yaml +61 -0
  46. package/package.json +102 -0
  47. package/schema/config.schema.json +119 -0
  48. package/speexor.config.yaml.example +30 -0
@@ -0,0 +1,548 @@
1
+ # Speexor Architecture
2
+
3
+ **Version:** 0.1.0 — **Package:** `@speexjs/speexor`
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ Speexor (Agent Orchestrator) is a plugin-based, agent-agnostic orchestrator for multi-AI coding agents. It manages the full lifecycle of autonomous coding agents across repositories — spawning agents in isolated worktrees, routing between provider backends, reacting to CI/PR events, and providing a real-time dashboard.
10
+
11
+ **Philosophy:**
12
+
13
+ - **Plugin-first architecture** — every capability is a pluggable module behind a typed interface.
14
+ - **Agent-agnostic** — the orchestrator does not care which coding agent runs. OpenCode, Claude Code, Aider, and Codex are all first-class citizens.
15
+ - **Isolation by design** — each agent works in its own `git worktree` with its own runtime session (tmux or subprocess).
16
+ - **Event-driven** — lifecycle events, tracker events, and reaction triggers flow through a shared EventBus.
17
+ - **Provider routing** — per-project configuration of primary and fallback agents with concurrency and cost limits.
18
+
19
+ ---
20
+
21
+ ## 2. High-Level Architecture
22
+
23
+ ```
24
+ ┌──────────────────────────────────────────────────────────────────────┐
25
+ │ CLI (Commander) │
26
+ │ start | agent spawn | list | stop | logs | config-help │
27
+ └──────────────────────────┬───────────────────────────────────────────┘
28
+
29
+
30
+ ┌──────────────────────────────────────────────────────────────────────┐
31
+ │ SpeexorLifecycle │
32
+ │ │
33
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │
34
+ │ │ Plugin │ │ Plugin │ │ Plugin │ │ Session Manager │ │
35
+ │ │ Registry │ │ Loader │ │ Context │ │ (in-memory Map) │ │
36
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────────────────────┘ │
37
+ │ │ │ │ │
38
+ │ ▼ ▼ ▼ │
39
+ │ ┌──────────────────────────────────────────────────────────────┐ │
40
+ │ │ EventBus │ │
41
+ │ │ (EventEmitter3 wrapper) │ │
42
+ │ └──────────────────────────────────────────────────────────────┘ │
43
+ └──────────────────────────────────────────────────────────────────────┘
44
+ │ │ │ │
45
+ ▼ ▼ ▼ ▼
46
+ ┌─────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐
47
+ │ Agent │ │ Runtime │ │Workspace │ │ Tracker │ SCM │
48
+ │ Adapters │ │ Adapters │ │ Adapters │ │ Adapters│Adapt. │
49
+ │ (4 impls) │ │ (2 impls)│ │ (1 impl) │ │ (1 impl)│(1impl)│
50
+ └─────────────┘ └──────────┘ └──────────┘ └──────────────────┘
51
+
52
+
53
+ ┌──────────────────────────────────────────────────────────────────────┐
54
+ │ Dashboard Server (Node.js HTTP) │
55
+ │ /api/status | /api/projects | /api/sessions | /api/health | / │
56
+ │ (4 JSON endpoints + single-page HTML dashboard, auto-refresh 5s) │
57
+ └──────────────────────────────────────────────────────────────────────┘
58
+ ```
59
+
60
+ The lifecycle is the central orchestrator. It holds a plugin registry (keyed by slot), an in-memory session map, and the EventBus. Plugins are loaded via `loadAllPlugins()`, registered by slot, and collaborate through the shared `PluginContext` (config + EventBus + logger).
61
+
62
+ ---
63
+
64
+ ## 3. Plugin Architecture
65
+
66
+ ### 3.1 Base Interface
67
+
68
+ Every plugin implements the `PluginModule` interface:
69
+
70
+ ```typescript
71
+ interface PluginModule {
72
+ name: string
73
+ version: string
74
+ type: PluginSlot
75
+ initialize(context: PluginContext): Promise<void>
76
+ destroy(): Promise<void>
77
+ }
78
+
79
+ interface PluginContext {
80
+ config: SpeexorConfig
81
+ eventBus: EventBus
82
+ logger: (msg: string) => void
83
+ }
84
+ ```
85
+
86
+ ### 3.2 Seven Plugin Slots
87
+
88
+ | Slot | Interface | Purpose |
89
+ |------|-----------|---------|
90
+ | `agent` | `AgentPlugin` | Spawn, communicate with, and kill coding agents |
91
+ | `runtime` | `RuntimePlugin` | Create/destroy terminal sessions (tmux or process) |
92
+ | `workspace` | `WorkspacePlugin` | Manage isolated git worktrees |
93
+ | `tracker` | `TrackerPlugin` | Fetch issues and subscribe to tracker events |
94
+ | `scm` | `SCMPlugin` | Branch, commit, PR, and CI operations |
95
+ | `notifier` | `NotifierPlugin` | Desktop notifications (macOS/Win/Linux) |
96
+ | `terminal` | `TerminalPlugin` | Interactive terminal attach/detach (interface only) |
97
+
98
+ ### 3.3 Slot Interface Definitions
99
+
100
+ **AgentPlugin**
101
+
102
+ ```typescript
103
+ interface AgentPlugin extends PluginModule {
104
+ type: 'agent'
105
+ spawn(task: AgentTask, runtime: RuntimeSession): Promise<AgentSession>
106
+ sendInput(sessionId: string, input: string): Promise<void>
107
+ getStatus(sessionId: string): Promise<AgentStatus>
108
+ kill(sessionId: string): Promise<void>
109
+ }
110
+ ```
111
+
112
+ **RuntimePlugin**
113
+
114
+ ```typescript
115
+ interface RuntimePlugin extends PluginModule {
116
+ type: 'runtime'
117
+ createSession(worktreePath: string): Promise<RuntimeSession>
118
+ destroySession(sessionId: string): Promise<void>
119
+ sendInput(sessionId: string, input: string): Promise<void>
120
+ getOutput(sessionId: string): Promise<string>
121
+ getLiveStream(sessionId: string): AsyncIterable<string>
122
+ getStatus(sessionId: string): Promise<'running' | 'stopped' | 'error'>
123
+ }
124
+ ```
125
+
126
+ **WorkspacePlugin**
127
+
128
+ ```typescript
129
+ interface WorkspacePlugin extends PluginModule {
130
+ type: 'workspace'
131
+ createWorktree(task: AgentTask): Promise<WorktreeSession>
132
+ removeWorktree(sessionId: string): Promise<void>
133
+ getWorktreePath(sessionId: string): string
134
+ listActive(): Promise<WorktreeSession[]>
135
+ cleanupStale(): Promise<string[]>
136
+ }
137
+ ```
138
+
139
+ **TrackerPlugin**
140
+
141
+ ```typescript
142
+ interface TrackerPlugin extends PluginModule {
143
+ type: 'tracker'
144
+ fetchIssues(filter?: TrackerFilter): Promise<TrackerIssue[]>
145
+ getIssue(id: string): Promise<TrackerIssue | null>
146
+ onEvent(handler: (event: TrackerEvent) => void): void
147
+ }
148
+ ```
149
+
150
+ **SCMPlugin**
151
+
152
+ ```typescript
153
+ interface SCMPlugin extends PluginModule {
154
+ type: 'scm'
155
+ createBranch(baseBranch: string, newBranch: string): Promise<void>
156
+ commitAndPush(branch: string, message: string): Promise<string>
157
+ createPullRequest(title: string, desc: string, head: string, base: string): Promise<PRInfo>
158
+ getPRStatus(prId: string): Promise<PRStatus>
159
+ getPRComments(prId: string): Promise<PRComment[]>
160
+ getCIRuns(prId: string): Promise<CIRun[]>
161
+ mergePR(prId: string, method?: 'merge' | 'squash' | 'rebase'): Promise<void>
162
+ onEvent(handler: (event: TrackerEvent) => void): void
163
+ }
164
+ ```
165
+
166
+ **NotifierPlugin**
167
+
168
+ ```typescript
169
+ interface NotifierPlugin extends PluginModule {
170
+ type: 'notifier'
171
+ notify(level: 'info' | 'warn' | 'error' | 'success', title: string, message: string): Promise<void>
172
+ }
173
+ ```
174
+
175
+ **TerminalPlugin**
176
+
177
+ ```typescript
178
+ interface TerminalPlugin extends PluginModule {
179
+ type: 'terminal'
180
+ attach(sessionId: string): Promise<void>
181
+ detach(sessionId: string): Promise<void>
182
+ write(sessionId: string, data: string): Promise<void>
183
+ onData(sessionId: string, handler: (data: string) => void): void
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 4. Plugin Implementations
190
+
191
+ The built-in registry (`src/plugins/index.ts`) includes 10 implementations loaded at startup:
192
+
193
+ | Plugin | Class | Slot | Dependencies |
194
+ |--------|-------|------|-------------|
195
+ | OpenCode Agent | `OpenCodeAgent` | agent | — |
196
+ | Claude Code Agent | `ClaudeCodeAgent` | agent | — |
197
+ | Aider Agent | `AiderAgent` | agent | — |
198
+ | Codex Agent | `CodexAgent` | agent | — |
199
+ | tmux Runtime | `TmuxRuntime` | runtime | `tmux -V` (Unix) |
200
+ | Process Runtime | `ProcessRuntime` | runtime | shell (Windows fallback) |
201
+ | Git Worktree | `GitWorktreeWorkspace` | workspace | `git` |
202
+ | GitHub Tracker | `GitHubTracker` | tracker | `gh` CLI |
203
+ | GitHub SCM | `GitHubSCM` | scm | `gh` CLI |
204
+ | Desktop Notifier | `DesktopNotifier` | notifier | osascript / PowerShell / notify-send |
205
+
206
+ **Runtime fallback logic:** The loader registers both `TmuxRuntime` and `ProcessRuntime`. `TmuxRuntime.initialize()` throws if `tmux` is not available; lifecycle initialization failure is caught per-plugin, allowing `ProcessRuntime` to serve as the fallback on Windows.
207
+
208
+ ---
209
+
210
+ ## 5. Data Flow
211
+
212
+ ### 5.1 Startup Sequence
213
+
214
+ ```
215
+ speexor start <repo-url>
216
+
217
+ ├─ 1. generateDefaultConfig(repo) → speexor.config.yaml
218
+
219
+ ├─ 2. loadConfig(cwd) → parse YAML → Zod validate → SpeexorConfig
220
+
221
+ ├─ 3. new SpeexorLifecycle(config)
222
+ │ │
223
+ │ ├─ createEventBus() (EventEmitter3)
224
+ │ └─ status = 'initializing'
225
+
226
+ ├─ 4. lifecycle.initialize()
227
+ │ └─ status = 'active'
228
+
229
+ ├─ 5. loadAllPlugins()
230
+ │ ├─ new OpenCodeAgent()
231
+ │ ├─ new ClaudeCodeAgent()
232
+ │ ├─ new AiderAgent()
233
+ │ ├─ new CodexAgent()
234
+ │ ├─ new TmuxRuntime()
235
+ │ ├─ new ProcessRuntime()
236
+ │ ├─ new GitWorktreeWorkspace()
237
+ │ ├─ new GitHubTracker()
238
+ │ ├─ new GitHubSCM()
239
+ │ └─ new DesktopNotifier()
240
+
241
+ ├─ 6. for each plugin → lifecycle.registerPlugin(plugin)
242
+ │ └─ plugin slot → Map<PluginSlot, PluginModule[]>
243
+
244
+ └─ 7. new DashboardServer(lifecycle, port).start()
245
+ └─ HTTP server listening on :3000
246
+ ```
247
+
248
+ ### 5.2 Agent Spawn Flow
249
+
250
+ ```
251
+ speexor agent spawn --task <id> [--agent opencode]
252
+
253
+ ├─ 1. loadConfig() → SpeexorConfig
254
+ ├─ 2. new SpeexorLifecycle(config).initialize()
255
+ ├─ 3. loadAllPlugins() → register all
256
+
257
+ ├─ 4. lifecycle.spawnAgent(task)
258
+ │ │
259
+ │ ├─ 4a. workspacePlugin.createWorktree(task)
260
+ │ │ ├─ git branch speexor/<task-id>
261
+ │ │ ├─ git worktree add .speexor/worktrees/<id> <branch>
262
+ │ │ └─ emit 'worktree:created'
263
+ │ │
264
+ │ ├─ 4b. runtimePlugin.createSession(worktreePath)
265
+ │ │ ├─ [tmux] tmux new-session -d -s speexor-<id> -c <path>
266
+ │ │ └─ [proc] spawn(shell, { cwd: <path> })
267
+ │ │
268
+ │ ├─ 4c. agentPlugin.spawn(task, runtimeSession)
269
+ │ │ ├─ creates AgentSession { id, taskId, provider, status, runtimeSessionId }
270
+ │ │ └─ emit 'session:created'
271
+ │ │
272
+ │ └─ return AgentSession
273
+
274
+ └─ 5. SessionStore persists session to .speexor/state.json
275
+ ```
276
+
277
+ ### 5.3 Agent Teardown Flow
278
+
279
+ ```
280
+ lifecycle.stopSession(sessionId)
281
+
282
+ ├─ 1. agentPlugin.kill(sessionId)
283
+ ├─ 2. runtimePlugin.destroySession(runtimeSessionId)
284
+ │ ├─ [tmux] tmux kill-session -t speexor-<id>
285
+ │ └─ [proc] SIGTERM → SIGKILL after 5s
286
+ ├─ 3. workspacePlugin.removeWorktree(worktreeId)
287
+ │ ├─ git worktree remove <path>
288
+ │ └─ git branch -D speexor/<task-id>
289
+ ├─ 4. sessions.delete(sessionId)
290
+ └─ 5. emit 'session:completed'
291
+ ```
292
+
293
+ ---
294
+
295
+ ## 6. Config System
296
+
297
+ ### 6.1 Format
298
+
299
+ YAML files, validated at startup with Zod. The loader searches these candidates in order:
300
+
301
+ 1. `speexor.config.yaml`
302
+ 2. `speexor.config.yml`
303
+ 3. `.speexor.yaml`
304
+ 4. `.speexor.yml`
305
+ 5. `speexor.yaml`
306
+
307
+ ### 6.2 Schema
308
+
309
+ ```yaml
310
+ version: "1" # literal, must be "1"
311
+ projects:
312
+ - name: my-project # string, required
313
+ repository: <url> # string, required
314
+ path: <local-path> # string, optional
315
+ branch: main # string, optional
316
+ provider:
317
+ primary: opencode # enum: opencode | claude-code | aider | codex
318
+ fallback: # array, optional
319
+ - claude-code
320
+ concurrentLimit: 3 # number (1-20), optional
321
+ costLimit: 1000 # number, optional (cents)
322
+ reactions: # optional
323
+ ci-failed: # reaction rule
324
+ auto: true
325
+ action: fix # enum: fix | notify | escalate | skip
326
+ retries: 3 # number (0-10)
327
+ escalateAfter: 30 # minutes (1-1440)
328
+ changes-requested:
329
+ auto: true
330
+ action: fix
331
+ retries: 2
332
+ escalateAfter: 60
333
+ approved-and-green:
334
+ auto: false
335
+ action: notify
336
+ retries: 0
337
+ escalateAfter: 0
338
+ plugins: # optional per-project plugin overrides
339
+ tracker: github-tracker
340
+ scm: github-scm
341
+ runtime: tmux-runtime
342
+ notifier: desktop-notifier
343
+ ```
344
+
345
+ ### 6.3 Default Reaction Rules
346
+
347
+ ```typescript
348
+ const DEFAULT_REACTION_RULES = {
349
+ 'ci-failed': { auto: true, action: 'fix', retries: 3, escalateAfter: 30 },
350
+ 'changes-requested': { auto: true, action: 'fix', retries: 2, escalateAfter: 60 },
351
+ 'approved-and-green':{ auto: false, action: 'notify', retries: 0, escalateAfter: 0 },
352
+ }
353
+ ```
354
+
355
+ If a project omits the `reactions` block, all three defaults are applied. Partial overrides merge with defaults per event type.
356
+
357
+ ---
358
+
359
+ ## 7. Reaction Engine
360
+
361
+ The `ReactionEngine` listens to `TrackerEvent` instances and executes configurable automated responses.
362
+
363
+ ### 7.1 Event Types
364
+
365
+ ```typescript
366
+ type TrackerEventType =
367
+ | 'issue-opened'
368
+ | 'issue-closed'
369
+ | 'ci-failed'
370
+ | 'ci-passed'
371
+ | 'changes-requested'
372
+ | 'approved'
373
+ ```
374
+
375
+ ### 7.2 Reaction Pipeline
376
+
377
+ ```
378
+ TrackerEvent
379
+
380
+
381
+ ReactionEngine.processEvent(event)
382
+
383
+ ├─ Find handlers matching event.type
384
+
385
+ ├─ For each handler:
386
+ │ │
387
+ │ ├─ Check retry count (key = issueId:eventType)
388
+ │ │
389
+ │ ├─ if retries >= rule.retries → escalate or skip
390
+ │ │
391
+ │ ├─ if !rule.auto → emit 'reaction:triggered' (notify)
392
+ │ │
393
+ │ └─ executeReaction(event, rule, project)
394
+ │ │
395
+ │ ├─ action = 'fix'
396
+ │ │ └─ autoFix() → create AgentTask → lifecycle.spawnAgent()
397
+ │ │
398
+ │ ├─ action = 'notify'
399
+ │ │ └─ emit 'reaction:triggered' with action 'notify'
400
+ │ │
401
+ │ ├─ action = 'escalate'
402
+ │ │ └─ emit 'reaction:triggered' with action 'escalate'
403
+ │ │
404
+ │ └─ action = 'skip'
405
+ │ └─ no-op
406
+
407
+ └─ Increment retry count
408
+ ```
409
+
410
+ ### 7.3 Retry Tracking
411
+
412
+ Retries are tracked per `issueId:eventType` key in a `Map<string, number>`. Once the configured retry limit is reached, further events of that type for the same issue are escalated or skipped.
413
+
414
+ ---
415
+
416
+ ## 8. Dashboard
417
+
418
+ ### 8.1 REST API
419
+
420
+ | Method | Endpoint | Description |
421
+ |--------|----------|-------------|
422
+ | `GET` | `/api/status` | Lifecycle status, project count, active sessions, uptime |
423
+ | `GET` | `/api/projects` | All configured projects with provider routing |
424
+ | `GET` | `/api/sessions` | Active agent sessions, worktrees, runtimes |
425
+ | `GET` | `/api/health` | Health check with memory usage and timestamp |
426
+ | `GET` | `/` or `/dashboard` | Single-page HTML dashboard |
427
+
428
+ All JSON endpoints return CORS headers (`Access-Control-Allow-Origin: *`).
429
+
430
+ ### 8.2 Frontend
431
+
432
+ The dashboard is served as an inline HTML string from `DashboardServer.serveDashboardHTML()`. It features:
433
+
434
+ - **Dark theme** (GitHub-inspired `#0d1117` background)
435
+ - **Stat cards**: project count, active agents, system status, uptime
436
+ - **Projects table**: name, repository, primary agent, fallback agents
437
+ - **Sessions table**: session ID, task, provider, status, start time
438
+ - **Auto-refresh**: polls all 3 JSON endpoints every 5 seconds via `setInterval`
439
+ - **No build step** — single HTML file with embedded `<style>` and `<script>`
440
+
441
+ ### 8.3 Architecture
442
+
443
+ ```
444
+ DashboardServer
445
+
446
+ ├─ Owns: http.Server, DashboardState, routes[]
447
+
448
+ ├─ Subscribes to lifecycle.eventBus
449
+ │ ├─ 'session:created' → DashboardState.addSession()
450
+ │ └─ 'session:completed' → DashboardState.removeSession()
451
+
452
+ ├─ DashboardState (in-memory)
453
+ │ ├─ SpeexorState { sessions, worktrees, runtimes, projects }
454
+ │ └─ listener pattern for reactive updates
455
+
456
+ └─ Routes:
457
+ ├─ /api/status → lifecycle.getStatus(), lifecycle.listSessions()
458
+ ├─ /api/projects → lifecycle.getConfig().projects
459
+ ├─ /api/sessions → lifecycle.listSessions()
460
+ ├─ /api/health → { status, timestamp, memory }
461
+ ├─ / → serveDashboardHTML()
462
+ └─ 404 → { error: 'Not found' }
463
+ ```
464
+
465
+ ---
466
+
467
+ ## 9. Session Management
468
+
469
+ ### 9.1 In-Memory (Runtime)
470
+
471
+ The `SpeexorLifecycle` maintains a `Map<string, AgentSession>` for active sessions. Sessions are created by `spawnAgent()` and removed by `stopSession()`.
472
+
473
+ ### 9.2 Persistent Store
474
+
475
+ The `SessionStore` class manages a JSON file at `.speexor/state.json`:
476
+
477
+ ```typescript
478
+ interface SpeexorState {
479
+ sessions: AgentSession[]
480
+ worktrees: WorktreeSession[]
481
+ runtimes: RuntimeSession[]
482
+ projects: ProjectConfig[]
483
+ }
484
+ ```
485
+
486
+ - Synchronous read/write (simplicity over performance)
487
+ - Auto-creates `.speexor/` directory on instantiation
488
+ - Methods: `addSession`, `updateSession`, `removeSession`, `addWorktree`, `removeWorktree`, `setProjects`, `clear`
489
+
490
+ ### 9.3 Directory Layout
491
+
492
+ ```
493
+ .speexor/
494
+ ├── state.json # Persistent session state
495
+ ├── worktrees/ # Git worktree targets
496
+ │ └── <task-id>/ # Isolated working directory per agent
497
+ ├── logs/ # Runtime session logs
498
+ │ └── <session-id>.log # stdio capture (ProcessRuntime only)
499
+ └── config.yaml (optional, manual)
500
+ ```
501
+
502
+ ---
503
+
504
+ ## 10. Extension Points
505
+
506
+ ### 10.1 Adding a New Agent Provider
507
+
508
+ Implement `AgentPlugin`, register in `loadAllPlugins()`:
509
+
510
+ ```typescript
511
+ export class DeepSeekAgent implements AgentPlugin {
512
+ name = 'deepseek-agent'
513
+ version = '0.1.0'
514
+ type = 'agent' as const
515
+ // implement spawn(), sendInput(), getStatus(), kill()
516
+ }
517
+ ```
518
+
519
+ Add the provider string to the `AgentProvider` union type and the Zod schema enum in `config.ts`.
520
+
521
+ ### 10.2 Adding a New Plugin Slot
522
+
523
+ 1. Define the interface in `src/core/types.ts` extending `PluginModule`
524
+ 2. Add the slot name to `PluginSlot` union type
525
+ 3. Implement the plugin class
526
+ 4. Register it in `loadAllPlugins()` — it will be auto-loaded by the lifecycle
527
+
528
+ ### 10.3 Custom Runtime
529
+
530
+ Implement `RuntimePlugin` for Docker, SSH, or Kubernetes sessions. The lifecycle uses `getFirstPlugin<RuntimePlugin>('runtime')`, so the first registered runtime wins.
531
+
532
+ ### 10.4 Custom Tracker/SCM
533
+
534
+ Implement `TrackerPlugin` for Jira, Linear, or GitLab issues. Implement `SCMPlugin` for GitLab or Bitbucket PRs. Both follow the same `onEvent(handler)` subscription pattern for event-driven reactions.
535
+
536
+ ---
537
+
538
+ ## Key Design Decisions
539
+
540
+ | Decision | Rationale |
541
+ |----------|-----------|
542
+ | EventBus over direct calls | Loose coupling — plugins and dashboard observe without knowing each other |
543
+ | `getFirstPlugin()` slot resolution | Single active adapter per slot per lifecycle; multiple registrations possible for fallback |
544
+ | YAML + Zod config | Strict validation with human-editable format |
545
+ | Synchronous JSON state file | Simplicity — avoids database dependency for session tracking |
546
+ | Inline HTML dashboard | Zero build step, zero dependencies for the UI |
547
+ | `gh` CLI dependency | Avoids GitHub API token management; delegates auth to `gh` |
548
+ | Reverse-order destroy | Dependencies destroyed before dependents (terminal → notifier → scm → tracker → workspace → runtime → agent) |
package/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to Speexor will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-06-30
9
+
10
+ ### Added
11
+ - Core orchestration lifecycle (SpeexorLifecycle) with plugin registry and session management
12
+ - 7-plugin slot architecture with TypeScript interfaces
13
+ - 4 AI agent adapters: OpenCode, Claude Code, Aider, Codex
14
+ - 2 runtime backends: tmux (Unix) and Process (Windows)
15
+ - Git worktree workspace isolation for parallel agent tasks
16
+ - GitHub tracker plugin for reading issues via gh CLI
17
+ - GitHub SCM plugin for PR/CI operations via gh CLI
18
+ - Desktop notification plugin (macOS, Windows, Linux)
19
+ - CLI with 6 commands: start, agent spawn, list, stop, logs, config-help
20
+ - YAML configuration system with Zod validation
21
+ - Built-in HTTP dashboard with REST API and HTML frontend
22
+ - Reaction engine for automated CI/PR feedback loops
23
+ - JSON-file session store for state persistence
24
+ - Event bus system (EventEmitter3) for inter-module communication
25
+ - In-memory reactive dashboard state management
26
+
27
+ ### Architecture
28
+ - PluginModule base interface with initialize/destroy lifecycle
29
+ - Provider routing with primary/fallback agent support
30
+ - Configurable reaction rules (ci-failed, changes-requested, approved-and-green)
31
+ - Graceful shutdown with SIGINT/SIGTERM handling
32
+
33
+ ## [0.2.0] — Planned
34
+
35
+ ### Added
36
+ - Recursive task decomposition (DAG-based multi-agent orchestration)
37
+ - Real-time WebSocket terminal streaming
38
+ - Cost/usage tracking per provider
39
+
40
+ ## [0.3.0] — Planned
41
+
42
+ ### Added
43
+ - Extension/marketplace system
44
+ - Plugin SDK (@speexor/sdk)
45
+ - Air-gapped mode support
46
+
47
+ ## [0.4.0] — Planned
48
+
49
+ ### Added
50
+ - Multi-host distributed agent execution
51
+ - Advanced observability (OpenTelemetry traces)
52
+ - Webhook integration for third-party tools
@@ -0,0 +1,83 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment:
18
+
19
+ * Demonstrating empathy and kindness toward other people
20
+ * Being respectful of differing opinions, viewpoints, and experiences
21
+ * Giving and gracefully accepting constructive feedback
22
+ * Accepting responsibility and apologizing to those affected by our mistakes
23
+ * Focusing on what is best not just for us as individuals, but for the overall community
24
+
25
+ Examples of unacceptable behavior:
26
+
27
+ * The use of sexualized language or imagery, and sexual attention or advances
28
+ * Trolling, insulting or derogatory comments, and personal or political attacks
29
+ * Public or private harassment
30
+ * Publishing others' private information without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
32
+
33
+ ## Enforcement Responsibilities
34
+
35
+ Project maintainers are responsible for clarifying and enforcing our standards of
36
+ acceptable behavior and will take appropriate and fair corrective action in
37
+ response to any behavior that they deem inappropriate, threatening, offensive,
38
+ or harmful.
39
+
40
+ ## Scope
41
+
42
+ This Code of Conduct applies within all community spaces, and also applies when
43
+ an individual is officially representing the community in public spaces.
44
+
45
+ ## Enforcement
46
+
47
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
48
+ reported to the project maintainers at opensource@superdevids.com.
49
+ All complaints will be reviewed and investigated promptly and fairly.
50
+
51
+ ## Enforcement Guidelines
52
+
53
+ Project maintainers will follow these Community Impact Guidelines:
54
+
55
+ ### 1. Correction
56
+ **Community Impact:** Use of inappropriate language or other behavior deemed
57
+ unprofessional or unwelcome.
58
+ **Consequence:** A private, written warning, providing clarity around the nature
59
+ of the violation and an explanation of why the behavior was inappropriate.
60
+
61
+ ### 2. Warning
62
+ **Community Impact:** A violation through a single incident or series of actions.
63
+ **Consequence:** A warning with consequences for continued behavior. No
64
+ interaction with the people involved for a specified period of time.
65
+
66
+ ### 3. Temporary Ban
67
+ **Community Impact:** A serious violation of community standards.
68
+ **Consequence:** A temporary ban from any sort of interaction or public
69
+ communication with the community for a specified period of time.
70
+
71
+ ### 4. Permanent Ban
72
+ **Community Impact:** Demonstrating a pattern of violation of community standards.
73
+ **Consequence:** A permanent ban from any sort of public interaction within the
74
+ community.
75
+
76
+ ## Attribution
77
+
78
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
79
+ version 2.1, available at
80
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
81
+
82
+ [homepage]: https://www.contributor-covenant.org
83
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html