Commit Graph

8 Commits

Author SHA1 Message Date
harshitsinghbhandari f154f87add fix: merge upstream main and resolve conflicts for cursor agent
- Resolve @composio → @aoagents package renaming conflicts
- Add cursor agent to BUILTIN_PLUGINS in plugin-registry.ts
- Add cursor agent to AGENT_PLUGINS in detect-agent.ts
- Add cursor agent import and registration in plugins.ts
- Add cursor agent dependency and import in web services.ts
- Update cursor plugin package naming to @aoagents/ao-plugin-agent-cursor
- Add cursor agent to changeset linked group
- Fix test imports to use new @aoagents package naming

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 11:48:27 +05:30
harshitsinghbhandari 04f3a7c27f fix(cursor): register plugin in all discovery/resolution layers
Address PR review feedback from #1076 to make Cursor agent fully functional.

## Changes

### Plugin Registration (High Severity Fixes)
- **core/plugin-registry.ts**: Add cursor to BUILTIN_PLUGINS array for config-based resolution
- **cli/plugins.ts**: Import and register cursor in agentPlugins map for getAgent/getAgentByName
- **cli/detect-agent.ts**: Add cursor to AGENT_PLUGINS array for detectAvailableAgents()
- **web/services.ts**: Import and register cursor plugin for dashboard SessionManager/LifecycleManager
- **web/package.json**: Add @composio/ao-plugin-agent-cursor dependency

### Activity Detection Fix (Medium Severity)
- **cursor/index.ts**: Remove overly broad blocked detection patterns (error:/failed:)
  - Compiler errors, test failures, and linter output are normal tool output
  - Terminal-based detection can't distinguish between actionable agent errors and normal output
  - Follow Aider/OpenCode pattern: only Claude Code detects blocked (has native JSONL)
  - Added comment explaining why blocked detection is removed
- **cursor/index.test.ts**: Remove blocked detection test cases

## Why These Changes Are Needed

Before these fixes:
- `defaults.agent: cursor` in config would throw "Unknown agent plugin: cursor"
- `ao spawn --agent cursor` would fail
- Dashboard couldn't resolve cursor agent (SessionManager/LifecycleManager failures)
- `detectAvailableAgents()` never discovered Cursor
- False "blocked" states when agent encountered normal compiler errors

After these fixes:
- Cursor agent fully discoverable and usable via config and CLI
- Dashboard can spawn and manage Cursor sessions
- Activity detection matches other terminal-based agents (Aider, OpenCode)
- No false positives from normal tool output

## Testing
-  cursor plugin tests: 51/51 passing (reduced from 52 due to removed blocked tests)
-  core typecheck: clean
-  web typecheck: clean
-  CLI can import cursor plugin without errors

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 19:17:41 +05:30
Dhruv Sharma 1271ef8fbd fix: add missing default exports fallback and remove dead code
- Add top-level "default" key fallback in CLI's resolvePackageExportsEntry
  to match core's plugin-registry.ts (fixes local plugin resolution for
  packages using exports: { "default": "./dist/index.js" })
- Remove dead exports: MARKETPLACE_PLUGIN_CATALOG, printPluginList,
  getAgentFromRegistry, resolveInstalledPluginSpecifier
- Fix incomplete string escaping in plugin scaffold (backslash/newline)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 04:57:25 +05:30
Dhruv Sharma 9ba04391d9 use plugin store for shared loader paths 2026-03-31 04:02:25 +05:30
Harsh Batheja 4e2144d99e
feat: OpenCode session lifecycle and CLI controls (#315)
* feat: refine OpenCode session reuse strategy and cleanup

* fix: harden OpenCode session selection and lint errors

* refactor: centralize OpenCode reuse resolution flow

* fix: return 404 for missing session in message route

* feat: replace force remap with terminal reload control

* fix: protect project path from session kill cleanup

* fix: preserve fullscreen alignment without reload action

* fix: harden OpenCode session id handling and title reuse selection

* fix: show OpenCode reload control and remap before restart

* fix: preserve title-only OpenCode reuse with fallback mapping persistence

* fix: resolve remaining PR315 Bugbot findings

* fix: keep OpenCode discovery title-based without timestamp sorting

* fix: avoid enrichment race fallout in session listing

* fix: guard OpenCode discovery parse with array check

* fix: stabilize Linear comment integration check

* fix: harden OpenCode discovery and prompt option flow

* ux: clarify OpenCode terminal restart action

* fix: remap OpenCode session fresh on each restart

* fix: validate remap session ids before reuse

* fix: clean archived metadata only after purge

* docs: align OpenCode remap selection with title-based behavior

* fix: harden opencode cleanup and ignore local sisyphus state

* test: add timeout cleanup coverage for session enrichment

* fix: harden Linear integration helper against transient non-JSON errors

* fix: make linear integration assertions resilient to eventual consistency

* fix: remove unused fs import after rebase

* fix: address remaining Bugbot blockers for opencode session handling

* fix: avoid stale metadata overwrite during restore post-launch

* fix: forward subagent in orchestrator flows and defer reuse lookup

* fix: apply configured subagent fallback for session spawn

* fix: scope archived cleanup by project and delay archive restore write

* fix: normalize orchestrator strategy aliases in start display logic

* fix: centralize orchestrator strategy normalization in core

* fix: derive orchestrator reuse display from spawn result

* fix: keep cleanup results consistent across project-id collisions

* fix: namespace cleanup results when session IDs collide

* fix: harden GitHub issue stateReason fallback

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: avoid false failing CI state mapping

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: add tmux command timeouts

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: bound session API enrichment latency

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: repair scm-github merge resolution

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: repair lifecycle-manager test merge

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: delay archive metadata recreation until restore passes

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test: restore claim-pr session mocks

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix: address review findings for OpenCode lifecycle PR

- Fix stripControlChars to preserve newlines for reload commands
- Add SessionNotFoundError and use instanceof checks in API routes
- Document orchestratorSessionStrategy in YAML example
- Validate existingSessionId with asValidOpenCodeSessionId()
- Extract inline Node script to buildSessionLookupScript helper
- Create OpenCodeSessionManager interface for remap capability
- Create OpenCodeAgentConfig type for agent-specific config
- Change default orchestratorSessionStrategy from delete to reuse

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

* fix: guard reused session display without metadata

* chore: add agent config files to .gitignore

Agent configuration files (CLAUDE.md, AGENTS.md, IMPROVEMENTS.md, etc.) are personal and project-specific. They should not be committed to the repository.

Changes:
- Remove CLAUDE.md from git tracking
- Add agent config files to .gitignore
- Create .gitignore-template for reference

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

* chore: update gitignore for agent config folder structure

Reorganized agent configuration files:
- CLAUDE.md and AGENTS.md stay in root (agents read them there)
- Tracking files move to .opencode/ (IMPROVEMENTS.md, etc.)
- Optional Claude files in .claude/

Updated .gitignore to ignore folders instead of individual files.

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

* fix: tighten opencode remap and session discovery safeguards

* fix: address Bugbot findings in session manager

* fix: scope opencode discovery to opencode sessions

* fix: restore concurrent listing and strict permission literals

* fix: throw SessionNotFoundError, parallelize list enrichment, fix permissions type

- Session manager now throws SessionNotFoundError instead of plain Error
  for missing sessions, so web API routes correctly return 404 (not 500)
- Parallelize session enrichment in list() — was sequential, causing O(N)
  latency for N sessions with subprocess enrichment
- Fix AgentLaunchConfig.permissions type to accept legacy "skip" value
  (AgentPermissionInput instead of AgentPermissionMode)
- Add happy-path and validation tests for /api/sessions/:id/message route
- Update all test mocks to use SessionNotFoundError

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

* fix: route ao send through session manager

* feat: add purge option to orchestrator stop

* fix: register opencode agent in web services

* test: align web API missing-session coverage

* fix: match notifier config by plugin name

* refactor: dedupe session lookup and tmux buffer send flow

* test: update send lifecycle wait expectation

* fix: harden send routing and cleanup purge controls

---------

Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Prateek <karnalprateek@gmail.com>
2026-03-08 09:55:44 +05:30
prateek 21335db8af
feat: publish to npm under @composio scope (#32)
* feat: add npm publishing support with @composio scope

Set up Changesets for version management, add publish metadata to all 20
packages under the @composio scope, create an unscoped wrapper package
(@composio/agent-orchestrator) for global install, and add a GitHub
Actions release workflow.

- Rename all packages from @agent-orchestrator/* to @composio/ao-*
- Add @composio/agent-orchestrator wrapper (bin shim → @composio/ao-cli)
- Add license, repository, homepage, bugs, files, engines to all packages
- Add .npmrc (access=public), MIT LICENSE file
- Add .changeset/ config with linked versioning for all packages
- Add .github/workflows/release.yml (changesets publish CI)
- Add changeset, version-packages, release scripts to root

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: exclude private web package from release build

The release script now filters out @composio/ao-web, matching the
workflow's existing exclusion and preventing a Next.js build failure
from blocking npm publishing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-15 04:28:57 +05:30
prateek e5f0c08793
feat: enrich ao status with PR, CI, review, threads, and activity columns (#25)
* feat: enrich `ao status` with PR, CI, review, threads, and activity columns

The status command now displays a rich table with branch, PR number, CI
status, review decision, pending review threads, and agent activity state.
Data is fetched from the SCM plugin (GitHub) in parallel for performance.

Closes INT-1348

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

* fix: address Bugbot review — thread errors, PR fallback, activity mapping

- Thread errors: `.catch(() => null)` instead of `.catch(() => [])` so
  failed getPendingComments shows `-` not `0`
- PR fallback: extract PR number from metadata URL when SCM lookup fails
- Activity fallback: use metadata `status` when agent introspection is
  unavailable (working→active, idle→idle, stuck/errored→blocked)
- Activity mapping: treat `assistant`/`result` as idle, only `tool_use`
  and `user` as active — avoids marking completed responses as working

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:33:50 +05:30
prateek 06b5ec3267
feat: implement CLI with all commands (init, status, spawn, session, send, review-check, dashboard, open) (#6)
* feat: implement CLI with all commands (init, status, spawn, session, send, review-check, dashboard, open)

Implement the full `ao` CLI using Commander.js with 9 commands:

- ao init: Interactive setup wizard that creates agent-orchestrator.yaml
- ao status: Colored terminal table of all sessions (branch, activity, PR, CI)
- ao spawn <project> [issue]: Spawn single agent session (worktree + tmux + agent)
- ao batch-spawn <project> <issues...>: Batch spawn with duplicate detection
- ao session ls|kill|cleanup: Session management subcommands
- ao send <session> <message>: Smart message delivery with busy detection/retry
- ao review-check [project]: Scan PRs for review comments, trigger agent fixes
- ao dashboard: Start the Next.js web dashboard
- ao open [target]: Open session(s) in terminal tabs

Shared libraries:
- lib/shell.ts: Shell command helpers (tmux, git, gh wrappers)
- lib/metadata.ts: Flat metadata file read/write (backwards-compatible format)
- lib/format.ts: Terminal formatting (colors, tables, banners)

All commands code against core interfaces and flat metadata files,
matching the behavior of the reference bash scripts in scripts/.

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

* fix: address PR review — extract shared helpers, fix bugs, add tests

- Extract getTmuxSessions/getTmuxActivity to lib/shell.ts (was duplicated in 5 files)
- Fix readMetadata unsafe cast: return Partial<SessionMetadata> instead
- Fix dashboard webDir path: resolve from package root, not dist/
- Fix dashboard spawn error handling: add child.on("error")
- Fix getNextSessionNumber: escape regex metacharacters in prefix
- Fix fallback worktree creation: use existing branch name, not detached HEAD
- Fix post-create hooks: run before agent launch, not after
- Fix open --new-window: pass option through to openInTerminal
- Fix cleanup dry-run: separate summary message for dry-run mode
- Add Claude session introspection to status command (TTY→PID→CWD→JSONL)
- Add vitest test suite: 72 tests covering commands and lib utilities

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

* fix: address all PR review issues, add lint/format compliance

Review fixes:
- Extract getTmuxSessions/getTmuxActivity to lib/shell.ts (was duped in 5 files)
- Fix readMetadata: return Partial<SessionMetadata> instead of unsafe cast
- Fix dashboard webDir: use createRequire + __dirname for ESM compat
- Fix dashboard spawn: add child.on("error") handler
- Fix getNextSessionNumber: escape regex metacharacters in prefix
- Fix fallback worktree: use existing branch name, not detached HEAD
- Fix post-create hooks: run before agent launch, not after
- Fix open --new-window: pass option through to openInTerminal
- Fix cleanup dry-run: separate summary message for dry-run mode
- Add Claude session introspection to status (TTY→PID→CWD→JSONL)
- Add port validation in init wizard
- Add clarifying comment for tmux C-u send-keys

Lint/format:
- Add no-console override for CLI package in eslint config
- Fix duplicate imports (node:fs, @agent-orchestrator/core)
- Fix unused variable in send test
- Apply prettier formatting across all files

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

* fix: address codex review — fd leak, input validation, security hardening

- Wrap openSync/readSync in try/finally to prevent fd leak on error
- Add timeout NaN/<=0 guard in send command
- Add port validation (1-65535) in dashboard command
- Sanitize issueId for git-ref-safe branch names
- Add --project validation in session ls/cleanup
- Fix batch-spawn same-run duplicate detection
- Replace shell interpolation with safe ps + JS filtering
- Wrap temp file usage in try/finally for cleanup on error
- Read only tail of JSONL files to avoid large file loads

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

* fix: address remaining PR review comments

- Use `=== null` instead of falsy check for git worktree result (spawn.ts)
- Wrap spinner in try/catch so it stops on error (spawn.ts)
- Use exact match instead of substring for issue duplicate detection (metadata.ts)
- Check git worktree remove result before printing success (session.ts)
- Skip banner/headers/footer when --json is passed (status.ts)
- Add error handler on browser-open spawn (dashboard.ts)

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

* fix: address bugbot review — interrupt before send, error resilience, worktree validation

- review-check: send C-c + C-u to interrupt busy agent and clear input before
  delivering fix prompt, matching the reference bash script behavior
- review-check: wrap per-session send in try/catch so one failure doesn't abort
  remaining sessions
- spawn: check worktree creation return value and throw on failure instead of
  continuing with non-existent worktree path
- Update test mock for detached worktree to return success

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

* fix: address 4 bugbot findings — status type, TTY match, retry safety, comment counting

- spawn: write status "spawning" (not "starting") to match SessionStatus type
- status: use column-based exact TTY match to prevent pts/1 matching pts/10
- send: use null-safe tmux() helper in retry loop instead of throwing exec()
- review-check: use GraphQL reviewThreads to count only unresolved comments,
  preventing fix prompts on PRs with all threads resolved

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

* fix: GraphQL variable passing and env var name sanitization

- review-check: use -F flags for GraphQL variables instead of string
  interpolation to prevent injection via repo config values
- spawn: sanitize env var name by replacing non-alphanumeric chars with
  underscores (my-app → MY_APP_SESSION, not MY-APP_SESSION)

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

* fix: add -l flag to tmux send-keys and remove dead activityIndicator code

- Add literal (-l) flag to send-keys so user text isn't interpreted as
  tmux key names (e.g. "Enter", "Escape")
- Remove unused activityIndicator function from format.ts and its tests

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

* refactor: move agent-specific logic from CLI into agent plugins

detectActivity, introspect, and getLaunchCommand now live in the agent
plugins (claude-code, codex, aider) instead of being hardcoded in the
CLI commands. The Agent interface's detectActivity takes a plain string
of terminal output instead of a Session object, making plugins trivially
unit-testable.

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

* fix: use -f for string GraphQL vars and -l flag for tmux send-keys

Addresses two bugbot findings in review-check.ts:
1. Use -f (string) instead of -F (typed) for owner/name GraphQL
   variables to prevent numeric coercion of repo names.
2. Use -l (literal) flag with send-keys and separate Enter call,
   matching the established tmux safety pattern in send.ts.

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

* fix: guard empty terminal output in lifecycle-manager and deduplicate send.ts

- lifecycle-manager: skip detectActivity when terminal output is empty
  to prevent false "killed" state when runtime probe returns no data
- send.ts: collapse identical isBusy/isProcessing into single isActive
- tests: use non-empty mock terminal output so detectActivity is exercised

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

* fix: validate message before side effects, remove dead updateMetadataField

- send.ts: move message validation before idle-wait loop and C-u clear
  so running `ao send session` with no message exits immediately without
  touching the tmux session
- metadata.ts: remove exported updateMetadataField (unused in production)
- metadata.test.ts: remove corresponding tests

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

* fix: add -l flag and separate Enter for spawn prompt send-keys

Use literal flag (-l) when sending the initial prompt via tmux
send-keys in spawn.ts, and send Enter as a separate call. This
matches the pattern used in send.ts and review-check.ts and prevents
tmux from interpreting special characters in the issue ID.

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

* fix: strict session prefix matching and validate project before use

- session-utils: add matchesPrefix() using regex ^prefix-\d+$ to prevent
  cross-project misattribution with overlapping prefixes (e.g. "app" vs
  "app-v2")
- Replace all startsWith(`${prefix}-`) calls across status.ts,
  review-check.ts, session.ts, and open.ts with matchesPrefix()
- status.ts, review-check.ts: move project validation before the
  projects map construction so invalid project IDs exit before any
  access to undefined config

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

* fix: deduplicate escapeRegex and add -l flag to spawn send-keys

- Export escapeRegex from session-utils.ts, remove duplicate from spawn.ts
- Add -l (literal) flag to tmux send-keys for postCreate hooks and
  launch command, with Enter sent separately
- Prevents tmux from interpreting key names in user config or agent
  commands

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

* fix: update detectActivity to sync string signature across all plugins

After rebase on main, all agent plugins and their tests still had the
old async detectActivity(session: Session) signature. Updated to the
new sync detectActivity(terminalOutput: string): ActivityState across:
- agent-claude-code, agent-codex, agent-aider, agent-opencode plugins
- All plugin unit tests (test terminal output patterns, not process state)
- All integration tests (capture tmux pane output before calling)
- status.ts: use getSessionInfo() instead of nonexistent introspect()

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

* fix: integration tests expect idle (not exited) from detectActivity after process exit

detectActivity is a pure terminal-text classifier that returns "idle" for
empty/shell-prompt output. Process exit detection is handled by isProcessRunning.

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

* fix: check last-line prompt before historical activity patterns in detectActivity

Reorder classifyTerminalOutput to check the last line for a prompt
character before scanning the full buffer for activity indicators like
"Reading" or "Thinking". This prevents historical output in the capture
buffer from causing false "active" results when the agent is idle.

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

* fix: update pnpm-lock.yaml for web dashboard dependencies

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

* fix: preserve state on getOutput failure, check permission prompts before activity indicators

- lifecycle-manager: remove .catch(() => "") from getOutput so failures
  reach the outer catch block that preserves stuck/needs_input states
- classifyTerminalOutput: check waiting_input prompts against bottom of
  buffer before full-buffer active indicators, preventing historical
  "Thinking"/"Reading" text from overriding current permission prompts

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

* fix: detect agent exit via isProcessRunning, remove redundant active checks

- lifecycle-manager: replace dead "exited"/"blocked" branches with
  isProcessRunning check when detectActivity returns "idle" — correctly
  detects agent process exit inside a still-alive tmux session
- classifyTerminalOutput: remove redundant explicit active-indicator
  checks that returned the same "active" as the unconditional default

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

* fix: wrap killSession in try-catch so cleanup loop continues on failure

One session's kill failure (e.g. archiveMetadata fs error) no longer
aborts the entire cleanup loop, matching the pattern used by batch-spawn
and review-check.

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

* test: add comprehensive unit and integration tests for CLI

Unit tests (6 new files, 61 tests):
- session-utils: escapeRegex, matchesPrefix, findProjectForSession
- plugins: getAgent, getAgentByName lookup and error cases
- shell: exec, execSilent, tmux, git, gh, getTmuxSessions, getTmuxActivity
- review-check: GraphQL review checking, dry-run, fix prompt delivery
- open: target resolution (all/project/session), fallback, --new-window
- init: rejects existing config file

Integration tests (2 new files, 8 tests):
- spawn-send-kill: real tmux session create, send-keys, kill, graceful re-kill
- session-ls: multi-session listing, per-session capture, activity timestamps

CLI vitest config updated with explicit thread pool parallelization.
Total CLI tests: 68 → 129. Total integration tests: 20 → 28.

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

* fix: address 5 bugbot findings — symlink type, timeout cleanup, file validation, repo format

- spawn.ts: pass `type` param to symlinkSync based on lstat (dir vs file)
- spawn.ts: document TOCTOU gap in getNextSessionNumber (tmux rejects dupes)
- dashboard.ts: store browser timeout and clear it on child exit
- send.ts: wrap file read in try-catch with user-friendly error
- review-check.ts: validate owner/name split before GraphQL query

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:14:27 +05:30