Commit Graph

33 Commits

Author SHA1 Message Date
prateek e450fed462
fix: upgrade @testing-library/jest-dom for vitest 2.x type compatibility (#21)
The v6.6.0 module augmentation for vitest's Assertion interface was
broken with vitest 2.x. Upgrading to v6.9.1 fixes the ~47 typecheck
errors (toBeInTheDocument, toHaveAttribute). Also adds vitest.d.ts to
ensure the type augmentation is included in tsc compilation.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:40:44 +05:30
prateek 343c8a65b5
feat: implement web dashboard with attention-zone UI and API routes (#1)
* feat: implement web dashboard with attention-zone UI, API routes, and SSE

Implements the Next.js 15 web dashboard for INT-1332 with:
- Attention-prioritized session cards (urgent/action/warning/ok/done zones)
- 6 API routes: sessions, spawn, send, kill, merge, SSE events
- 5 components: SessionCard, AttentionZone, PRStatus, CIBadge, Terminal
- Session detail page with PR merge readiness, CI checks, unresolved comments
- Tailwind CSS 4 dark theme matching the reference bash dashboard
- Mock data layer covering all attention states for development
- SSE endpoint for real-time lifecycle event streaming

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add vitest test suite for web dashboard (77 tests)

Add comprehensive tests covering API routes, component rendering, and
attention-level classification. Fix merge button visibility when no alerts present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address all PR review feedback

- Fix SSE memory leak: add cancel() handler to clear intervals on disconnect
- Add X-Accel-Buffering header for reverse proxy compatibility
- Add input validation on all API routes (validateString, validateIdentifier)
- Import core types (SessionStatus, ActivityState, CIStatus, etc.) from
  @agent-orchestrator/core instead of redeclaring them
- Fix hydration mismatch: render timestamp client-side only via useEffect
- Add error handling on fetch calls in Dashboard (check response.ok)
- Add cn() utility for conditional class composition
- Fix setTimeout leak: use useRef + useEffect cleanup in SessionCard
- Fix getAttentionLevel edge case: status-based checks outside PR block
- Add NaN check on parseInt in merge route
- Extract duplicated sizeLabel logic into shared getSizeLabel()
- Add TODO guard comment on mock-data.ts for production removal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Codex review feedback (iteration 1)

- Use strict /^\d+$/ validation on PR merge route (reject "432foo")
- Treat exited agents with non-terminal status as urgent (crashed agents)
- Add explicit getAttentionLevel mappings for review_pending, approved, cleanup
- Guard SSE update loop against empty sessions array
- Use encodeURIComponent on session details link
- Tighten mergeScore types to Pick<DashboardPR, ...>
- Add stripControlChars() and apply to send route for shell safety

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: apply ESLint and Prettier formatting after rebase on main

- Add next-env.d.ts to ESLint ignores (triple-slash reference)
- Merge duplicate imports using inline type syntax (no-duplicate-imports)
- Replace non-null assertions with proper null checks
- Apply Prettier formatting across all packages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Cursor Bugbot review findings

- Add reviewDecision "none" to warning zone in getAttentionLevel
- Change ACTION zone color from green to orange for proper priority signaling
- Fix SessionDetail date hydration mismatch with ClientDateCard pattern
- Add export const dynamic = "force-dynamic" to SSE route
- Add --color-accent-orange CSS variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Codex review iteration 2 findings

- Gate merge route on PR state (409 if not open) and draft status (422)
- Handle pr.state === "closed" in getAttentionLevel → done zone
- Reject messages that become empty after control char stripping
- Align SSEEvent types with actual emitted events (snapshot + activity)
- Add 6 new tests for edge cases (85 total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve typecheck error in test helper (NextRequest init type)

Cast RequestInit to NextRequest's ConstructorParameters to fix
type incompatibility between global RequestInit.signal (null allowed)
and Next.js RequestInit.signal (null not allowed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve remaining review threads (zone colors, draft PRs, ternary)

- WORKING zone uses green (not blue) per design spec
- ACTION zone uses orange, consistent across all components
- Draft PRs with reviewDecision "none" fall to ok (not warning)
- Remove redundant ternary in getAttentionLevel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add Playwright screenshot tooling for visual verification

Agents and developers can now capture headless Chromium screenshots of
the running dashboard. Includes dev server auto-start, default page
specs, and CLI arg parsing. Screenshots committed to branch for PR
visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add session detail screenshot after compact metadata redesign

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add screenshots for session detail redesign + zone reclassification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address bugbot findings — unused type, kill route validation, draft PR awareness

- Remove unused SSEEvent type alias from types.ts
- Add validateIdentifier() to kill route for session ID validation
- Skip "needs review" alert for draft PRs in SessionCard getAlerts()
- Show "draft" instead of "needs review" in PRTableRow for draft PRs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: session detail redesign + attention zone reclassification

Session Detail:
- Nav bar replacing standalone back link
- Meta chips (project/branch/issue) with GitHub links
- Humanized status labels and relative timestamps
- Unified PR card with stats row, issues list, inline CI checks
- Clickable file paths in unresolved comments

Attention Zones (reordered by human action urgency):
- merge: PRs ready to merge (highest ROI per second)
- respond: agents waiting for input (quick unblock)
- review: CI failures, changes requested, conflicts
- pending: waiting on reviewer or CI
- working: agents doing their thing
- done: merged or terminated

Also fixes:
- Add validateIdentifier() to send route for session ID
- Update all tests for new zone names

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve 3 remaining review threads

- Remove duplicate eslint ignore entry for next-env.d.ts
- Export checkStatusIcon and ciCheckSortOrder from CIBadge, import in
  SessionDetail instead of duplicating
- Add restore API route (was untracked)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore route validation, merged session guards

- Add validateIdentifier() to restore route (matches kill/send routes)
- Block restoring merged sessions with 409 response
- Hide kill button for merged sessions in top-row and expanded panel
- Expanded panel now shows restore OR terminate (not ternary fallback)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: exclude e2e from tsconfig include (fixes next build)

The e2e directory imports playwright which isn't resolvable during
next build. Since e2e files are standalone tooling (not app code),
exclude them from the main tsconfig include.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: unify CI check rendering via CICheckList layout prop

Add layout prop ("vertical"|"inline"|"expanded") to CICheckList,
remove duplicated InlineCIChecks from SessionDetail in favor of
reusing CICheckList with the appropriate layout mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add isDraft guard to unresolvedThreads pending check

Draft PRs with unresolved threads should fall through to "working",
not "pending". Adds the missing !pr.isDraft guard to match the
reviewDecision check on the next line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address 9 bugbot findings across e2e, SSE, and mock-data

- server.ts: kill child process on waitForServer failure, drain stdout
  to prevent backpressure
- screenshot.ts: validate Number() args for NaN with clear error messages
- events/route.ts: initialize interval variables as undefined
- mock-data.ts: exclude draft PRs from needsReview stat count

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent simultaneous restore and kill buttons in SessionCard

Add !isRestorable guard to kill button condition so the two buttons
are mutually exclusive. Restorable sessions show restore; non-restorable
exited sessions show kill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unreachable kill button from SessionCard top row

The kill button in the top row was dead code — isRestorable already
covers all activity === "exited" cases, so the !isRestorable && exited
condition could never be true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update tests to match SessionCard after kill button removal

Tests referenced "kill session" text that no longer exists. Exited
sessions show "restore session", not "kill session". Updated 4 tests
to use onRestore/restore session instead of onKill/kill session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:26:59 +05:30
prateek 7dd7de6249
fix: address Bugbot followup — stale paths in JSDoc, unused exports (#20)
- Remove developer-specific paths from JSDoc comments in integration tests
- Remove unused sessionExists and capturePane helper exports

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 13:30:25 +05:30
prateek 4b6d62d142
feat: agent plugins, OpenCode plugin, integration tests, CI (#5)
* feat: implement agent plugins (claude-code, codex, aider)

Implement the Agent interface from @agent-orchestrator/core for three
AI coding tools, enabling the orchestrator to launch, detect activity,
and introspect agent sessions.

Claude Code plugin (full implementation):
- JSONL session introspection (summary, session ID, cost, last message type)
- Activity detection via terminal output pattern matching
- Process detection via tmux pane TTY → ps lookup chain
- Launch command generation with permissions/model/prompt support

Codex and Aider plugins (functional stubs):
- Launch command generation with tool-specific flags
- Process detection via TTY lookup
- Stub introspection (no JSONL support yet)

Closes INT-1329

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review — shell escaping, activity detection, tests

- Replace JSON.stringify with POSIX shell escaping (single quotes) for
  all agent launch commands to prevent command injection
- Remove `unset CLAUDECODE &&` shell syntax from getLaunchCommand (breaks
  execFile); moved to getEnvironment as empty string
- Fix detectActivity returning "exited" for empty pane when process is
  confirmed alive — now returns "idle"
- Tighten BLOCKED_PATTERNS to specific error framing (^Error:, ENOENT:,
  APIError:, etc.) to avoid false positives on code output
- Check blocked patterns before input patterns to prevent "EACCES:
  permission denied" matching INPUT_PATTERNS on the word "permission"
- Remove pgrep fallback in findClaudeProcess (could match wrong session)
- Fix extractCost double-counting: prefer costUSD, fall back to
  estimatedCostUsd only when costUSD is absent
- Trim ACTIVE_PATTERNS to stable indicators only
- Add comprehensive vitest tests (127 tests across 3 plugins)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Codex review + lint fixes

- Use `ps -eo pid,tty,args` instead of `comm` for process detection so
  CLI wrappers (node, python) are correctly identified
- Coerce PID from handle.data with Number() + isFinite() to handle
  string-serialized PIDs
- Fix token double-counting: prefer `usage` object, only fall back to
  flat `inputTokens`/`outputTokens` when `usage` is absent
- Add `--` before Codex prompt to prevent prompts starting with `-`
  from being parsed as CLI flags
- Check blocked patterns before input patterns so "EACCES: permission
  denied" isn't misclassified as waiting_input
- Fix lint: merge duplicate imports, strict equality, remove non-null
  assertions
- Format with prettier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: multi-pane tmux, EPERM handling, strict process matching

Codex review iteration 1 fixes:
- Iterate all tmux pane TTYs instead of only the first — prevents
  false "exited" in multi-pane sessions
- Handle EPERM from process.kill(pid, 0) as "process exists" — EPERM
  means the process is alive but we lack permission to signal it
- Use word-boundary regex for process name matching — prevents false
  positives on similar names like "claude-code" or paths containing
  the substring
- Add tests for EPERM handling, multi-pane detection, and strict
  name matching (134 tests total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: exclude next-env.d.ts from eslint

Auto-generated Next.js type file triggers triple-slash-reference rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Bugbot review — JSON.parse guard, detectActivity pattern priority

- Guard parseJsonlFile against non-object JSON values (null, arrays,
  primitives) that would cause TypeError in downstream extractors
- Split null output (non-tmux → "active") from empty output (tmux → "idle")
  so non-tmux runtimes don't falsely report idle
- Reorder pattern matching: idle before blocked so stale errors in the
  tmux scrollback don't mask an idle prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract shellEscape to @agent-orchestrator/core

Move the duplicated POSIX shell-escaping utility from all three agent
plugins into packages/core/src/utils.ts and re-export from the package
entry point. Plugins now import { shellEscape } from the shared package.

Addresses Bugbot review comment on PR #5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add execFileAsync timeouts, use satisfies for plugin exports

- Add { timeout: 30_000 } to all execFileAsync calls (tmux, ps) per
  CLAUDE.md convention to prevent hanging on stuck processes
- Replace `const plugin: PluginModule<Agent>` with inline
  `export default { ... } satisfies PluginModule<Agent>` to preserve
  narrow literal types on manifest fields

Addresses Bugbot review comments on PR #5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename introspect → getSessionInfo, detectActivity uses JSONL

- Rename Agent.introspect() → getSessionInfo(), AgentIntrospection → AgentSessionInfo
- Claude Code detectActivity: replace tmux screen-scraping with JSONL-based
  detection (file mtime + last entry type)
- Remove terminal pattern constants (ACTIVE_PATTERNS, IDLE_PATTERNS, etc.)
- Update all tests: JSONL-based activity tests, renamed introspect blocks
- waiting_input/blocked states deferred to hooks-based implementation (#16)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add OpenCode agent plugin, integration tests, and CI workflow

- Add agent-opencode plugin with getLaunchCommand, detectActivity,
  isProcessRunning, getSessionInfo (27 unit tests)
- Add integration test suite for all 4 agents (claude-code, codex,
  aider, opencode) using real binaries in tmux sessions
- Add GitHub Actions CI workflow for integration tests
- Add test:integration script to root package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update core test mocks — introspect → getSessionInfo + isProcessing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Bugbot review — Windows paths, bytesRead, hardcoded paths

- toClaudeProjectPath: handle Windows backslashes and drive letters
- readLastJsonlEntry: use bytesRead to avoid null character corruption
- Remove duplicate next-env.d.ts eslint ignore entry
- Remove hardcoded developer-specific binary paths from integration tests
- Add isProcessing limitation comments with issue references (#17-19)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:28:42 +05:30
prateek 134bc6e177
Merge pull request #3 from ComposioHQ/feat/INT-1327
feat: implement core services (metadata, event-bus, tmux, session-manager, lifecycle-manager)
2026-02-14 07:24:07 +05:30
Prateek f2e0ac1867 fix: filter invalid session IDs in listMetadata to prevent downstream crashes
listMetadata() now validates filenames against VALID_SESSION_ID regex
before returning them, preventing readMetadataRaw() from throwing on
unexpected files in the sessions directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 06:30:13 +05:30
Prateek 660b8bb13d fix: restrict temp file permissions, validate session prefix format
- tmux sendKeys: write temp files with mode 0o600 instead of default umask
- config: validate sessionPrefix matches [a-zA-Z0-9_-]+ (Zod schema)
- config: sanitize derived prefix from project ID to match metadata ID rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 05:17:53 +05:30
Prateek 4028e5b77d fix: suppress immediate notification when send-to-agent reaction handles event
When a send-to-agent reaction was configured (e.g. for ci-failed), the
reactionHandledNotify flag stayed false, causing checkSession() to also
call notifyHuman() immediately — bypassing the retry/escalation workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 04:07:38 +05:30
Prateek 9e7a767730 fix: notify on significant transitions, preserve stuck state on probe failure
Two bugs fixed:

1. Transitions with non-info priority (e.g. merge.completed,
   review.approved, session.errored) now call notifyHuman() directly
   when no reaction config handles notification. Before, these
   transitions updated state silently with no human notification.

2. When agent.detectActivity() throws, sessions in stuck/needs_input
   state now preserve that state instead of being coerced to "working"
   by the fallback at the bottom of determineStatus().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 03:55:19 +05:30
Prateek 95fd47ef2a refactor: remove EventBus, simplify lifecycle manager
The event bus was over-engineered for a system with ~50 sessions. The
dashboard can read metadata files + poll GitHub directly. Remove the
EventBus interface, event-bus.ts module, and all emit/subscribe wiring
from session-manager and lifecycle-manager. The reaction engine (the
valuable part) stays — executeReaction now takes sessionId/projectId
directly instead of going through OrchestratorEvent.

Also fix listMetadata() to filter out directories (not just the
"archive" dir by name) so readMetadataRaw() can't crash on non-file
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 03:38:11 +05:30
Prateek 4c47a42b6e fix: use literal mode for tmux send-keys, prune stale lifecycle trackers
- Add -l flag to tmux send-keys for short messages so key-like payloads
  (e.g. "Enter", "Escape") are sent as text, not interpreted as keypresses
- Prune states and reactionTrackers maps in pollAll() for sessions that
  no longer appear in the session list, preventing unbounded memory growth
  and stale retry counters from applying to reused session IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:09:49 +05:30
Prateek 07b3a553f7 fix: allow notify reactions when auto is false, clean up reserved IDs on spawn failure
- Reactions with action: "notify" bypass the auto: false gate so
  merge-ready notifications fire for approved-and-green config
- All spawn failure paths now delete the reserved session metadata file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:00:58 +05:30
Prateek 65787f08e3 fix: use handle.runtimeName for plugin lookup, atomic session ID reservation
- kill() and send() prefer handle.runtimeName over project defaults
- spawn() uses O_EXCL atomic file creation to prevent concurrent ID collisions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:41:11 +05:30
Prateek a225bc17f5 fix: runtime plugin lookup, post-create guard, restart transition detection
- kill() uses handle.runtimeName to find the correct runtime plugin
- postCreate cleanup skips destroy when workspacePath is project root
- checkSession reads persisted metadata status on first poll after restart

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:16:50 +05:30
Prateek 88ea7d1491 fix: prevent project workspace deletion and agent config workspace leak
- kill() skips workspace.destroy when worktree matches project.path
- spawn() wraps getLaunchCommand/getEnvironment in workspace cleanup guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:55:07 +05:30
Prateek ad0b45006c fix: session recovery, reaction config merge, summary priority
- stuck/needs_input sessions recover to working when agent is active
- Project reaction overrides merge with global defaults (not replace)
- summary.all_complete events get info priority (not action)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:45:33 +05:30
Prateek 24391c874e fix: ignore next-env.d.ts in ESLint config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek f6f5e24e3c fix: add missing cleanup status to VALID_STATUSES set
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 4a804503b6 fix: execute all-complete reaction and guard workspace postCreate
- summary.all_complete reactions now execute in pollAll() after emission
- workspace.postCreate errors trigger workspace cleanup to prevent leaks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 2ecb011982 fix: address all review comments, lint/format, bugbot issues
Review fixes:
- metadata: sessionId validation to prevent path traversal
- event-bus: guard appendFileSync, remove unused import
- lifecycle-manager: fix reaction retry counting (track attempts,
  clear on status transition), allow retry on send failure instead
  of immediate escalation, detect killed sessions in polling loop,
  map session.killed to agent-exited reaction
- session-manager: validate status against union, clean up runtime
  and workspace on spawn failure, kill with default plugins when
  project config is missing

Lint/format fixes:
- Remove unused imports across all test files
- Replace `Function` type with typed callback in tmux tests
- Replace require() with dynamic import() in tests
- Replace dynamic delete with object spread in metadata
- Apply prettier formatting to all files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek c49649c2c1 fix: address codex review — path traversal, retry logic, type safety
- metadata: validate sessionId against ^[a-zA-Z0-9_-]+$ to prevent
  path traversal attacks
- lifecycle-manager: fix reaction retry/escalation to count attempts
  (not just successes), escalate after maxRetries attempts; map
  session.killed to agent-exited reaction (was unreachable session.exited)
- session-manager: validate status against known SessionStatus union
  (default to "spawning" for unknown values); clean up workspace on
  runtime creation failure to prevent resource leaks
- event-bus: guard appendFileSync in try/catch so disk errors don't
  crash event emitters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek d6ec95402d fix: address PR review — unsafe casts, tmux race, concurrent polling
- session-manager: replace readMetadata with readMetadataRaw (no unsafe casts),
  add safeJsonParse for JSON.parse guards, escapeRegex for session prefix,
  explicit branch priority, convert from this-based to closure-based methods
- tmux: named buffers (load-buffer -b / paste-buffer -b -d) to prevent
  global paste buffer race on concurrent sendKeys; send Escape before text
  to clear partial input
- lifecycle-manager: concurrent polling with Promise.allSettled instead of
  sequential for-of await, allCompleteEmitted guard to prevent repeated
  summary.all_complete events, polling re-entrancy guard, safe reaction
  config handling with action/auto checks
- tests: update tmux tests for Escape-first and named buffer changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek 2b9a28a031 test: add comprehensive unit tests for all core services
Add vitest test suite with 103 tests across 5 test files:

- metadata.test.ts (19 tests): read/write/update/delete/list, key=value
  parsing, comments, values with equals signs, archiving, dotfile exclusion
- event-bus.test.ts (22 tests): emit/on/off, wildcard listeners, handler
  error resilience, priority inference, JSONL persistence round-trip,
  filtered history with sessionId/projectId/type/priority/since/limit
- tmux.test.ts (23 tests): all tmux wrappers with mocked child_process,
  session listing/parsing, send-keys short vs load-buffer long text,
  capture-pane, kill, getPaneTTY
- session-manager.test.ts (23 tests): spawn pipeline with mocked plugins,
  session numbering, branch derivation, event emission, metadata writing,
  list with dead runtime detection, kill with cleanup, send via runtime
- lifecycle-manager.test.ts (16 tests): state transitions (spawning→working,
  killed, stuck, needs_input, ci_failed, merged, mergeable), reaction
  triggering, auto=false suppression, on/off handlers, getStates isolation

Also fixes inferPriority to handle merge.completed as "action" priority.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
Prateek de86054b9d feat: implement core services (metadata, event-bus, tmux, session-manager, lifecycle-manager)
Implement the 5 core service modules in packages/core/src/:

- metadata.ts: Flat-file key=value session metadata read/write matching
  existing bash script format. Supports read, write, update, delete
  (with archive), and list operations.

- event-bus.ts: In-process pub/sub with JSONL append-only persistence.
  Supports typed and wildcard listeners, priority inference from event
  type, filtered history queries, and a createEvent() helper.

- tmux.ts: Async wrappers around tmux commands using child_process.execFile
  (no shell injection). Handles long/multiline messages via load-buffer/
  paste-buffer. Covers list, create, send-keys, capture-pane, kill, and
  pane TTY discovery.

- session-manager.ts: Session CRUD that orchestrates Runtime + Agent +
  Workspace plugins. Full spawn pipeline (workspace → runtime → agent),
  list with live runtime checks, kill with cleanup, batch cleanup checking
  PR state + issue state + runtime liveness, and message delivery.

- lifecycle-manager.ts: State machine per session with configurable polling
  interval. Detects status transitions, emits events, triggers configured
  reactions (send-to-agent, notify, auto-merge) with retry counting and
  time-based escalation. Routes notifications by priority to configured
  notifier plugins.

All services implement interfaces defined in types.ts. Updated index.ts
to export all new modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:45 +05:30
prateek 0c535c98d7
fix: CI handles Next.js build separately, fix web tsconfig (#14)
* fix: CI workflow handles Next.js build separately, fix web tsconfig rootDir

- Remove rootDir from web tsconfig (conflicts with Next.js generated .next/types)
- Include .next/types/**/*.ts in web tsconfig
- CI: build non-web packages first, web build allowed to soft-fail
- CI: typecheck excludes web (typechecked by next build)
- CI: test excludes web build dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove || true from web build — bugbot caught it, web errors should fail CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: override rootDir in web tsconfig to "." for Next.js compat

Next.js generates .next/types/ files that must be under rootDir.
Base tsconfig sets rootDir to "src", web needs "." to include both
src/ and .next/types/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:41:58 +05:30
Prateek c8061ce03f chore: add ESLint, Prettier, CI workflow, and comprehensive CLAUDE.md conventions
- ESLint flat config with typescript-eslint strict rules
- Prettier config (double quotes, semicolons, 2-space indent)
- GitHub Actions CI: lint + typecheck + test on PRs
- Cursor BugBot config (.cursor/BUGBOT.md)
- Comprehensive CLAUDE.md with code conventions, security rules,
  plugin patterns, and common mistakes to avoid
- Fix unused param lint error in plugin-registry.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:01:52 +05:30
Prateek 5058c409d5 feat: scaffold TypeScript monorepo with all plugin interfaces
Phase 0 complete. Establishes:
- pnpm workspace with 18 packages (core, cli, web, 15 plugins)
- Complete type definitions in packages/core/src/types.ts defining
  all 8 plugin slot interfaces (Runtime, Agent, Workspace, Tracker,
  SCM, Notifier, Terminal) + core service interfaces
- YAML config loader with Zod validation and sensible defaults
- Plugin registry with built-in discovery
- CLAUDE.md with conventions for spawned agents

All agents can now branch from main and implement their assigned
packages against the interfaces defined in types.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:02:42 +05:30
Prateek 36b0792eb6 docs: add parallel implementation plan with 7-agent work breakdown
Maps dependency graph, identifies 7 truly independent work streams
after Phase 0 (scaffold + types), with Linear tickets and spawn
commands ready to go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:41:06 +05:30
Prateek 3eecca8460 docs: update architecture with push-first notification model
The human never polls. The system notifies the human. Two-tier event
handling: auto-handle routine issues silently, push notifications only
when human judgment is required. Escalation chains, priority-based
routing, and the dashboard is a drill-down tool, not the primary interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:38:00 +05:30
Prateek 74001b22d0 docs: add competitive research and architecture design artifacts
Comprehensive analysis of 16+ competing agent orchestration tools
(Gas Town, OpenHands, SWE-ReX, Par, claude-flow, etc.) and detailed
architecture design with 8-slot plugin system, session lifecycle state
machine, and human attention optimization patterns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:35:46 +05:30
Prateek 1601ad75c0 feat: add agent-orchestrator (ao) as a self-hosting project
- Create claude-ao-session: worktree-based session manager for ao
  (Linear tickets, GitHub PRs, AO_SESSION env var)
- Add ao|agent-orchestrator case to all shared scripts:
  claude-batch-spawn, claude-spawn, claude-status, claude-open-all,
  claude-review-check, claude-bugbot-fix
- Rewrite CLAUDE.orchestrator.md with full self-hosting instructions:
  commands reference, workflows, architecture, tips
- Session prefix: ao, metadata: ~/.ao-sessions/, worktrees: ~/.worktrees/ao/

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:44:17 +05:30
Prateek c16103fb4d rename CLAUDE.local.md to CLAUDE.orchestrator.md
This file contains the shared orchestrator instructions that ship with
the repo. It's loaded explicitly via --append-system-prompt-file when
launching an orchestrator session, rather than auto-discovered as a
CLAUDE.local.md. Users can still have their own CLAUDE.local.md for
personal overrides.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:34:24 +05:30
Prateek 0273e8f3d0 feat: initial commit with orchestrator scripts and dev instructions
Import all 18 production orchestrator scripts from ~/. These are direct
copies with hardcoded paths — generalization is the next step. Includes
CLAUDE.local.md with project overview, architecture notes, and roadmap.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 15:25:31 +05:30