loadConfig() already expands ~ in project paths via expandPaths(),
so manual replacement is unnecessary. Use resolve(proj.path) directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When multiple projects are configured and no project argument is given,
`ao start` and `ao stop` now match the current working directory against
project paths before erroring. This lets users run `ao start` from within
a project directory without specifying the project name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users can now install with:
npm install -g @composio/ao
Instead of the longer:
npm install -g @composio/agent-orchestrator
- Rename packages/agent-orchestrator → packages/ao
- Update package.json name and directory field
- Update all references in README, SETUP.md, changeset config, and CLI error messages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix autoDetectProject path matching: expand ~ before comparing project
paths to cwd, so `path: ~/my-repo` matches `/Users/user/my-repo`
- Fix addProjectToConfig: detect duplicate directory names and auto-suffix
instead of silently overwriting existing project entries
- Fix agent detect() in all 4 plugins: replace `which` (Unix-only) with
direct `--version` invocation for cross-platform compatibility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace CPU-burning spin loops in running-state.ts with async setTimeout
and jittered backoff; make all exports async
- Fix TOCTOU race in acquireLock by extracting tryAcquire helper with
retry loop instead of force-remove-and-retry-once
- Hoist dynamic imports in addProjectToConfig/choice-2 to static imports
- Add try/finally around readline in detectAgentRuntime
- Validate session prefix uniqueness in "Start new orchestrator" menu
- Add ConfigNotFoundError class to @composio/ao-core, replace fragile
string matching in ao start with instanceof check
- Fix setup.sh to recommend `ao start` instead of deprecated `ao init`
- Fix start-all.ts: resolve next binary with fallback, wait for children
on SIGTERM instead of immediate process.exit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove redundant project-existence check after autoDetectProject
- Wrap SIGTERM in try/catch for restart flow (dead process case)
- Remove unused setCallerContext export from caller-context.ts
- Fix unused vi import lint error in init.test.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add vi.mock() for running-state, caller-context, detect-env,
detect-agent, and project-detection modules imported by start.ts
- Without these mocks, vitest threads never exit (open handles)
- Remove init.test.ts test that triggered dynamic import of start.js
- Relax waitForPortAndOpen assertion (port resolution depends on
full dashboard startup which is mocked out)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix spawn hint in start.ts to use new single-arg syntax
- Remove dead addProjectOnly() export (add-project fully deleted)
- Make detect-agent.ts handle both named and default export shapes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When users run `ao spawn <project> <issue>`, show a yellow warning
explaining the new syntax (`ao spawn <issue>`) instead of Commander's
raw "too many arguments" error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- spawn now takes only `[issue]` — project is always auto-detected
- batch-spawn now takes only `<issues...>` — no project prefix
- Commander rejects extra positional args automatically
- Update orchestrator prompt to remove projectId from spawn example
- Rewrite spawn tests to use auto-detected project (single project in config)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite init.test.ts to match simplified deprecated init command
(old tests referenced removed --output, --auto, --smart flags)
- Add eslint-disable for consistent-type-imports in direct-terminal-ws.ts
(node-pty is optional, must use import() type annotations)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change != null to !== undefined && !== null in caller-context.ts and
session-manager.ts (3 locations) to satisfy eqeqeq lint rule
- Add displayName field to all 4 agent plugin test manifest assertions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused getCallerType import from start.ts
- Fix ao stop orphaning dashboard: always run stopDashboard via lsof
after killing parent PID, since SIGTERM may not propagate to child
- Revert spawn single-project special case back to always matching
project ID (backward compat)
- Update README: replace ao init --interactive and ao add-project
references with ao start equivalents, update spawn syntax
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add advisory lockfile (O_EXCL) to running-state.ts to prevent TOCTOU
race when multiple ao start processes run concurrently
- Wait up to 5s for old process to exit after SIGTERM in Override menu,
escalate to SIGKILL if needed — prevents port conflict on restart
- Guard autoCreateConfig against overwriting existing config files —
returns existing config instead of silently replacing it
- Fix spawn single-arg disambiguation: in single-project configs, always
treat the arg as an issue ID (user never needs to specify project)
- Clean up running.json by deleting file instead of writing "null"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace require() with execFileSync in all 4 agent plugin detect()
functions — fixes ReferenceError in ESM-only environments
- Remove non-existent -p flag from orchestrator prompt spawn/batch-spawn
docs — prevents orchestrator agent from generating broken commands
- Return actual port from runStartup() and pass to register() — fixes
running.json storing wrong port when auto-escalation picks a free port
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduces onboarding to `npm install -g @composio/ao && ao start`.
- Absorb init and add-project logic into `ao start` with auto-config
creation, environment detection, and project type detection
- Add single-instance tracking via running.json with already-running
interactive menu (human) and info+exit (agent)
- Add caller context detection (human/orchestrator/agent) via TTY and
AO_CALLER_TYPE env var
- Add plugin-based agent runtime detection — no hardcoded binary paths,
each plugin exports detect() and displayName
- Simplify `ao spawn` to just `ao spawn <issue>` with auto-detected
project (backward compat: `ao spawn <project> <issue>` still works)
- Set AO_CALLER_TYPE, AO_PROJECT_ID, AO_CONFIG_PATH, AO_PORT env vars
on all spawned sessions (worker, orchestrator, restore)
- Add `ao config-help` subcommand for config schema reference
- Deprecate `ao init` to thin wrapper, fully remove `ao add-project`
- Register/unregister in running.json on start/stop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Publish @composio/ao-web dashboard as npm package (removed private flag,
added files field, production entry point, node-pty made optional)
- CLI auto-detects dev vs production mode for dashboard startup
- Use local next binary instead of npx in production start-all.ts
- findWebDir() throws with install-specific guidance instead of returning
broken path
- Fix CI-silent failure: setup.sh and ao-update.sh exit 1 on non-interactive
npm link failure
- Deduplicate detectDefaultBranch into shared cli/lib/git-utils.ts
- Add EACCES permission guidance to README.md and SETUP.md
- Move resolveProjectIdForSessionId to @composio/ao-core
- Update design doc with problems #8-13, changes #9-12, known limitations
section, and expanded test plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every command now prints what to run next and from where:
- setup.sh → tells user to cd to project dir and run ao init
- ao init → tells user to run ao start (with directory context)
- ao add-project → tells user to run ao start and ao spawn
- ao start → tells user to run ao spawn <project> <issue>
Eliminates confusion about running commands in the wrong directory
(e.g., ao init inside agent-orchestrator instead of the project repo).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- `ao init` now auto-detects project with zero prompts by default
- Old 13-prompt wizard moved to `ao init --interactive`
- Removed dead `--smart` placeholder flag
- `ao start` auto-finds next free port if configured port is busy
instead of crashing with an error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix npm link EACCES permission error in setup.sh, ao-update.sh, and ao-doctor.sh
by auto-retrying with sudo when interactive
- Add `ao add-project <path>` command that auto-detects git remote, default branch,
project type, and generates agent rules — appends to existing config in one step
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add end-to-end observability across core, web, and terminal
Instrument lifecycle/API/websocket flows with correlation-aware metrics and operator health surfaces so the system can self-diagnose and escalate failures.
* fix: realign session restore set and unblock claim PR typecheck
* fix(web): restore project-filtered sessions route after main merge
* fix(core): remove unused orchestrator import to unblock lint
* fix(core): always record lifecycle poll failures and remove dead review branches
* fix(web): harden websocket metrics and reuse SSE observers
* fix(web): preserve primary session API errors when services bootstrap fails
* fix(web): use full orchestrator config type in SSE observer helper
* fix(web): restore project-scoped SSE and guard observability error paths
* fix(web): address remaining Bugbot review gaps
* fix(web): align SSE project attribution and share session project resolver
* fix(web): require request arg for SSE route and align tests
* fix(web): record websocket error disconnects as failures
* fix(web): use path alias for session project resolver import
* fix(web): include active connection count in disconnect metrics
Keep orchestrator control sessions visible in CLI status without counting them as worker sessions, and preserve explicit roles in JSON output for multi-project tooling.
* feat: wire lifecycle manager, backlog auto-claim, and dashboard overhaul
- Start LifecycleManager in dashboard server (30s polling) so reactions
actually fire: CI failures, review comments, merge conflicts are now
auto-forwarded to agents
- Add backlog auto-claim poller (60s interval) that watches for issues
labeled `agent:backlog` and auto-spawns agent sessions up to max
concurrent limit (5)
- Add tabbed dashboard UI: Board (kanban), Backlog (issue queue), PRs
- Add issue creation form in dashboard — creates GitHub issues with
`agent:backlog` label for immediate agent pickup
- Add API routes: /api/backlog, /api/issues, /api/setup-labels
- Pass notifier config through plugin registry (slack webhook fix)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add task decomposition layer (classify → decompose → recurse)
Adds LLM-driven recursive task decomposition upstream of session spawning.
Complex issues are broken into atomic subtasks before agents start working.
Each agent receives lineage context (where it fits in the hierarchy) and
sibling awareness (what parallel agents are doing).
Core changes:
- New decomposer module (core/src/decomposer.ts) — classify, decompose,
plan tree, lineage formatting, using Claude API
- Extended SessionSpawnConfig with lineage/siblings fields
- Prompt builder Layer 4: decomposition context (hierarchy + siblings)
- ProjectConfig.decomposer config section with Zod validation
- Tracker plugin: added removeLabels support for label management
CLI:
- `ao spawn <project> <issue> --decompose` flag
- `--max-depth <n>` option for decomposition depth
- Spawns multiple sessions with lineage context for composite tasks
Backlog poller:
- Respects project.decomposer.enabled for auto-decomposition
- Posts plan as issue comment when requireApproval=true
- Auto-spawns subtasks with lineage when requireApproval=false
Config example:
projects:
my-app:
decomposer:
enabled: true
maxDepth: 3
requireApproval: true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add verification gate — issues stay open until human confirms fix
PR merge no longer auto-closes GitHub issues. Instead:
1. On PR merge: issue labeled `merged-unverified`, stays open
2. Human checks staging, then runs `ao verify <issue>` to close
3. Or `ao verify <issue> --fail` to flag verification failure
Changes:
- services.ts: labelIssuesForVerification() replaces closeIssuesForMergedSessions()
- New CLI command: `ao verify` (verify/fail/list modes)
- New API route: GET/POST /api/verify
- Dashboard: new Verify tab with one-click verify/fail buttons
- ao status: shows count of issues awaiting verification
- Idle session detection + auto-nudge reaction
- Use TERMINAL_STATUSES in batch-spawn dedup check
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: rename decomposerConfig to avoid variable shadowing
Addresses Bugbot medium severity issue where inner variable
shadowed outer from getServices().
* fix: update pnpm-lock.yaml for new @anthropic-ai/sdk dependency
* fix: resolve remaining merge conflicts and syntax errors
- Remove leftover conflict markers in types.ts
- Remove orphaned code in services.ts
- Fix semicolon to comma in config.ts
- Remove unused import in verify.ts
* fix: address final Bugbot issues
- requireApproval path now exits early with continue to prevent
fall-through to in-progress label and session spawned comment
- remove packages/core/package-lock.json (pnpm workspace should only
use root pnpm-lock.yaml)
* fix: idle sessions now transition back to working
When agent resumes activity after being idle, the status correctly
transitions to 'working' instead of remaining stuck in 'idle' state.
* fix(backlog): remove agent:backlog label when claiming issues
When claiming issues from the backlog, the poller now removes the
agent:backlog label in addition to adding agent:in-progress. This
prevents duplicate work if all spawned sessions reach terminal status
and the poller rediscovers the issue.
* fix(test): use Set for TERMINAL_STATUSES mock
The mock for TERMINAL_STATUSES was an array, but the real export is a
ReadonlySet. Changed to use a Set so tests with non-empty sessions won't
crash when calling .has().
* fix(web): resolve backlog/dashboard regressions after branch sync
* fix(web): align dashboard events hook and SSE test mocks
* fix(notifier-openclaw): apply exponential delay from retry index
* fix(integration-tests): align openclaw retry delay expectation
* fix(web): keep dashboard header stats in sync
* fix(openclaw): keep first retry at base delay
---------
Co-authored-by: Agent <agent@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
Co-authored-by: Harsh <harsh@example.com>
* fix(core): enforce single-owner PR claim consolidation
* refactor: remove dead `takeover` option from claimPR
Since PR consolidation is now automatic (single-owner enforcement),
the `--takeover` flag became dead code. Users passing it got no
error but it had zero effect.
This commit:
- Removes takeover from ClaimPROptions interface
- Removes --takeover flag from spawn and session CLI commands
- Updates orchestrator-prompt.ts examples
- Updates tests to reflect automatic consolidation
Tests: ao-core 403 passing, ao-cli 189 passing (spawn+session)
* fix: remove stale --takeover from Quick Start example
Remove remaining --takeover reference in orchestrator prompt Quick Start section.
* feat(core): document and test asymmetric PR ownership model
- Add JSDoc to claimPR documenting RULE A (exclusive PR->Agent) and
RULE B (Agent->Many PRs) ownership contract
- Add tests for:
- Same session claiming multiple PRs sequentially (RULE B)
- Idempotent re-claim by same owner
- Stale/dead prior owner handoff
- Exclusive PR ownership enforcement (RULE A)
139 tests passing
---------
Co-authored-by: Harsh <harsh@Ubuntu-24-Forrest.lan>
* fix: pause workers on model limits and stabilize session visibility
Detect model limit exhaustion, pause project worker operations until reset, and expose pause state in dashboard/API. Also harden killed-session cleanup and SSE session reconciliation so sessions do not appear ghost-active or disappear until reload.
* fix: satisfy lint in rate-limit pause probe
* fix: address bugbot feedback for pause handling
* fix(lifecycle): prevent infinite re-pause loop for duration-based rate limits
Duration-based rate limits (e.g., 'usage limit reached for N hours') were
causing infinite re-pause loops because they always calculate reset time as
Date.now() + duration, which extends the pause on every poll cycle if the
message remains in terminal output.
Now checks for existing active pause before setting a new one:
- Skips override if same session already has active pause
- Preserves longer pauses from other sessions
Fixes infinite loop described in PR #367 review comment.
* fix(core): export global pause constants to prevent duplication
Export GLOBAL_PAUSE_*_KEY constants and parsePauseUntil utility from
@composio/ao-core so web package can import them instead of hardcoding.
This prevents silent breakage if key values ever change - now there's
a single source of truth.
Addresses review comment on PR #367.
* fix(web): globalPause as first-class state in SSE event flow
globalPause is now part of the same reducer/event flow as sessions,
not derived from provider-specific output text in the UI.
Changes:
- useSessionEvents: manage globalPause alongside sessions in reducer state
- Dashboard: consume globalPause from hook instead of SSR-only prop
- types: re-export GlobalPauseState from shared lib (provider-agnostic contract)
- Tests: 15 new tests proving banner appears/disappears from state updates
alone, regardless of agent model/plugin (Claude Code, OpenCode, Codex)
Design requirements satisfied:
- First-class state in same reducer/event flow as sessions
- Key names sourced from shared core contract via export/import
- Provider-neutral: no Anthropic/OpenAI string coupling in control logic
- State-driven: banner visibility from reducer state updates via SSE
Addresses Bugbot finding: Dashboard pause banner never updates after
initial render (eba24a0b-9e4c-47e3-91c9-7d10be01e3cf)
* fix: remove unused import in test file
* fix(cli): consistent purge default for session kill and stop commands
The kill method's default changed from opt-in (=== true) to opt-out (!== false).
Both session kill and stop commands now use the same logic:
purgeOpenCode = opts.purgeSession === true ? true : opts.keepSession !== true
This ensures consistent behavior across all kill paths.
Adds --keep-session flag to both commands.
Adds regression tests for provider-agnostic behavior verified.
Addresses Bugbot finding: orchestrator start cleanup inconsistent
with new purge default (2bc2535f-b2d1-4f64-96d6-150f97ed8564)
* fix: address bugbot follow-ups in send and opencode discovery
* fix(test): update stale integration test expectation for opencode bootstrap
The getLaunchCommand implementation now uses a robust bootstrap pattern
that captures the session ID between 'opencode run' and 'exec opencode --session'.
Updated test to check both parts separately instead of expecting a contiguous
string that no longer exists.
* fix: avoid NaN in sort comparator when both timestamps are missing
The comparator (b.updatedAt ?? -Infinity) - (a.updatedAt ?? -Infinity)
produces NaN when both timestamps are missing because -Infinity - (-Infinity)
is NaN in IEEE 754. A comparator returning NaN violates ECMA-262's
'consistent comparison function' requirement, making sort results
implementation-defined.
Fixed by adding equality check before subtraction:
- If both timestamps are equal (including both -Infinity), return 0
- Otherwise return the difference
* feat: add PR claim flow for agent sessions
* feat: support PR claim during spawn
* fix: address PR review feedback
* feat: add lifecycle worker automation
* fix: prevent duplicate messages on unconfirmed delivery and deduplicate review prompt
sendWithConfirmation now treats unconfirmed delivery as a soft success
since the message was already sent, preventing the dispatch hash from
staying stale and causing duplicate sends on the next poll cycle.
review-check command now sources its prompt from the lifecycle reaction
config instead of hardcoding it, keeping both paths aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pass AO_CONFIG_PATH to spawned lifecycle worker and log duplicate-worker early exit
The spawned lifecycle worker now receives AO_CONFIG_PATH in its
environment so it finds the correct config regardless of CWD. Also
added a log message when exiting early due to an existing worker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent lifecycle worker from clearing metadata on SCM fetch failures
SCM methods (getPendingComments, getAutomatedComments) silently returned []
on error, causing maybeDispatchReviewBacklog to treat fetch failures as
"no comments" and clear all tracking metadata every poll cycle. The worker
appeared to do nothing because it kept resetting its own state.
- SCM methods now propagate errors instead of returning []
- maybeDispatchReviewBacklog distinguishes null (fetch failed, skip) from
[] (confirmed empty, safe to clear)
- pollAll() logs errors instead of silently swallowing them
- Added heartbeat logging and stdout flush before process.exit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add debug breadcrumbs when review comment fetch fails
Logs a console.debug message when getPendingComments or
getAutomatedComments fails, making it clear the lifecycle loop is
preserving metadata due to a fetch failure rather than genuinely
having no comments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: scope lifecycle worker polling to its project and add missing test
pollAll() now passes the worker's projectId to sessionManager.list(),
so each per-project lifecycle worker only polls its own sessions. This
prevents duplicate SCM API calls and race conditions in multi-project
configs.
Also fixed misleading test name and added a test verifying that
--no-dashboard alone still starts the lifecycle worker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove dead confirmation check and respect project-level reaction overrides
Removed the unreachable "could not be confirmed" guard from the retry
logic since sendWithConfirmation now returns silently on unconfirmed
delivery.
review-check now resolves the prompt per-session by checking
project-level reaction overrides before falling back to the global
config, matching lifecycle worker behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: close TOCTOU race in lifecycle worker spawn and unexport getRegistry
Write the child PID from the parent immediately after spawn, closing
the window where a concurrent ensureLifecycleWorker could pass the
"not running" check and spawn a duplicate worker.
Also removed the unnecessary export on getRegistry since it has no
external consumers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: always include ao base prompt on spawn
* fix: clear heartbeat on shutdown and add initial delay to confirmation loop
Clear the heartbeat interval in the shutdown handler to prevent it
firing during the stream-flush window after lifecycle.stop().
Move the sleep in sendWithConfirmation to before each check (including
the first), so the runtime has time to reflect the message before
the first confirmation attempt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve lint errors in lifecycle and session manager
- Replace dynamic delete with object reconstruction in lifecycle-manager
- Add { cause } to re-thrown errors in session-manager for preserve-caught-error rule
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: auto-detect free port in ao start <url> config generation
When generating a new config via `ao start <url>`, auto-detect a free
port instead of hardcoding 3000. Reuses `findFreePort` (extracted to
shared web-dir.ts from init.ts). When port was auto-selected, skip the
strict preflight port check and silently find another free port if
needed (race condition).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add polling to listIssues integration test for Linear API consistency
The listIssues test was failing because Linear's API has eventual
consistency — a just-created issue may not appear in list queries
immediately. Other tests in this file already use pollUntilEqual for
the same reason. Use pollUntil to retry until the issue appears.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restore coupling between MAX_PORT_SCAN and findFreePort
MAX_PORT_SCAN was decoupled from findFreePort after extraction to
web-dir.ts — init.ts kept a local constant only used in messages while
findFreePort had its own default, and start.ts hardcoded 99. Export
MAX_PORT_SCAN from web-dir.ts as the single source of truth, use it as
findFreePort's default parameter, and import it in both callers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
HTTPS clone fails on private repos without credentials. Now tries
three strategies in order:
1. gh repo clone (handles GitHub auth via gh auth token)
2. git clone with SSH URL (works with SSH keys)
3. git clone with HTTPS URL (public repos fallback)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: URL start handles multi-project configs and cross-platform browser open
When `ao start <url>` loads an existing config with multiple projects,
resolve the correct project by matching the URL's owner/repo against
each project's `repo` field instead of failing with "Multiple projects".
Also fix browser open to use platform-appropriate command (open/xdg-open/start)
instead of hardcoded macOS `open`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use shallow clone (--depth 1) for faster repo onboarding
Full clones of large repos can take minutes. Shallow clone with
depth 1 fetches only the latest commit, making `ao start <url>`
near-instant for any repo size.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use cmd.exe /c start for Windows browser open
`start` is a cmd.exe builtin, not a standalone executable — spawn
would fail with ENOENT. Run via cmd.exe /c with empty title arg.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add `ao start <url>` one-command project onboarding
When `ao start` receives a repo URL instead of a project ID, it now:
1. Parses the URL (supports GitHub, GitLab, Bitbucket — HTTPS and SSH)
2. Clones the repo (or reuses if already present with matching remote)
3. Auto-generates agent-orchestrator.yaml with sensible defaults:
- SCM/tracker plugins inferred from URL host
- Default branch detected from local refs
- Project type detected (language, package manager)
- Post-create install commands set based on package manager
4. Starts the orchestrator + dashboard as usual
New core module `config-generator.ts` handles URL parsing, SCM detection,
project info detection, and config generation. All logic is in core (not
CLI) for reusability. 36 unit tests cover URL parsing, SCM detection,
default branch detection, project info, config generation, and clone
target resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address bugbot review feedback
1. Non-JS postCreate fix: Only set postCreate install commands for JS
package managers (pnpm/yarn/bun/npm). Rust/Go/Python projects no
longer get an incorrect "npm install" command.
2. Deduplicate startup logic: Extract shared `runStartup()` helper used
by both URL-mode and normal-mode start flows. Eliminates ~100 lines
of duplicated dashboard+orchestrator startup code.
3. SSH URL matching: Normalize SSH URLs to HTTPS format in
`isRepoAlreadyCloned()` so repos cloned via SSH
(git@github.com:owner/repo) are correctly detected as matching.
4. SCM/tracker always explicit: Always set scm and tracker fields in
generated config so `applyProjectDefaults()` doesn't silently
override with github defaults for non-GitHub repos.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: sanitize repo names for project IDs and session prefixes
Address remaining bugbot comments:
- Add sanitizeProjectId() to handle dots/special chars in repo names
(e.g. "my.app" → "my-app" instead of failing Zod validation)
- Preserve original case for generateSessionPrefix() so CamelCase
detection works (e.g. "DevOS" → prefix "dos", not "dev")
- Strip invalid chars before prefix generation to prevent dots
leaking into sessionPrefix
Also update README and SETUP docs with `ao start <url>` quick
onboarding flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: auto-open browser to orchestrator page on `ao start`
Opens the orchestrator session page (/sessions/{id}) instead of the
root dashboard, since the Kanban board is empty on first startup
with no worker sessions yet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: replace fixed 3s delay with port polling for browser open
Poll the dashboard port via TCP connection attempts (300ms intervals,
30s timeout) instead of a hardcoded setTimeout. Opens the browser only
once the server is actually accepting connections — deterministic
regardless of how long Next.js takes to compile.
Applied to both `ao start` and `ao dashboard` commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add CLI tests for start/stop commands
12 new tests covering:
- Project resolution: single project, explicit arg, missing project,
multi-project ambiguity, empty config
- URL argument: reuse existing clone, git clone flow, existing config
detection, clone failure error handling
- Port polling: waitForPortAndOpen polls isPortAvailable before opening
browser (proves deterministic behavior vs fixed delay)
- Stop command: session kill + dashboard stop, graceful missing session
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: deduplicate waitForPortAndOpen into shared utility
Move waitForPortAndOpen to lib/web-dir.ts (alongside isPortAvailable)
so both start.ts and dashboard.ts share a single cancellation-aware
implementation. start.ts now uses AbortSignal like dashboard.ts —
polling stops immediately if the dashboard process exits early.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds scripts/try-pr.sh — a helper to switch the global 'ao' command to
any session's worktree for manual testing, then restore back to main.
Usage:
bash scripts/try-pr.sh <session-id> # CLI/core/plugins only
bash scripts/try-pr.sh <session-id> --with-web # also builds + starts dashboard
bash scripts/try-pr.sh --restore # switch back to main
Also fixes isPortAvailable() to use a connect-based probe instead of
bind-based. The old approach had false positives on macOS when Next.js
listens on :: (IPv6 wildcard) — binding 127.0.0.1 succeeded even though
the port was taken. A TCP connect correctly detects any listener
regardless of which address it's bound to.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Check ao-core/dist/index.js relative to webDir's node_modules instead
of using require.resolve which fails under npm link. Also removes
unused existsSync/resolve/createRequire imports that caused lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
.next/BUILD_ID only exists after next build (production) but the
dashboard runs with next dev. Check @composio/ao-core resolve instead,
which is the actual cause of module resolution errors when packages
are not compiled.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Preflight errors in batch-spawn were unhandled, causing unformatted
rejection output. Now caught with chalk.red formatting and
process.exit(1), matching the single spawn handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract preflight checks from spawnSession into runSpawnPreflight and
call it once upfront in both registerSpawn and registerBatchSpawn. A
missing prerequisite now fails fast with one clear error instead of
repeating N times across the batch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Consistent error handling: all preflight functions now throw instead
of mixing process.exit and throw, so callers can catch and clean up
- checkGhAuth distinguishes "not installed" (ENOENT) from "not
authenticated" by checking gh --version first
- Move checkBuilt() after package.json existence check in start.ts so
missing web package shows "Run: pnpm install" not "Run: pnpm build"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The comment claimed connect-based detection works for IPv6 listeners,
but the probe only connects to 127.0.0.1 (IPv4). Updated the comment
to accurately document this limitation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused `createServer` import from web-dir.ts (only `Socket`
is used) — fixes the lint error in CI
- Mock `isPortAvailable` in the "uses port 3000" test so it doesn't
depend on port 3000 actually being free on the host machine
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>