Add extra padding (keyboardHeight + 10) so the input comfortably
clears the keyboard suggestions/accessory bar on all devices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KeyboardAvoidingView was causing two issues: scrolling the page to the
top on focus, and not restoring the layout when dismissing the keyboard.
New approach uses Keyboard.addListener to track keyboard height and
applies it as paddingBottom on the message bar directly. This works
reliably on both iOS and Android regardless of softInputMode.
- Removed softwareKeyboardLayoutMode: "pan" from app.json
- Replaced KAV with plain View + dynamic keyboard padding
- Reverted SpawnSession/Settings screens to simple iOS-only KAV
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump Android keyboardVerticalOffset from 80 to 140 to account for the
suggestions/clipboard bar that sits above the keyboard on many devices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set android.softwareKeyboardLayoutMode to "pan" in app.json to prevent
conflict between system adjustResize and KeyboardAvoidingView
- Use behavior="padding" consistently on both platforms
- Add keyboardVerticalOffset on Android to account for nav header
- Applied fix across all screens with keyboard input
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set KeyboardAvoidingView behavior to "height" on Android (was undefined)
- Add scrollToEnd on TextInput focus so content scrolls up with keyboard
- Add keyboardShouldPersistTaps="handled" for better tap behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add required `type: TIME_INTERVAL` to notification trigger for SDK 53
- Remove phantom statuses from SessionStatus/ActivityState not in core
(removed "running" from SessionStatus; "starting", "thinking", "working"
from ActivityState)
- Handle unhandled promise rejections from scheduleNotification in
useSessionNotifications hook with void + .catch()
- Await scheduleNotification in SettingsScreen test handlers and show
error alert on failure instead of premature "Sent"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Exclude packages/mobile/** from root eslint config (mobile uses Expo/CJS toolchain)
- Align TERMINAL_STATUSES and NON_RESTORABLE_STATUSES with packages/core/src/types.ts
- Fix isRestorable() to use isTerminal() check matching core logic
- Change notification trigger from null to { seconds: 1 } for Android background task compat
- Add isPRRateLimited guard to merge button to prevent merge when API rate limited
- Await scheduleNotification in background task to ensure notifications are delivered
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds missing action features to match the web dashboard:
- Merge PR button: green button when PR is mergeable, calls /api/prs/:id/merge
- CI checks list: individual checks with status icons and links
- "Ask to fix CI" button: sends "Please fix the failing CI checks" to agent
- Unresolved review comments: per-comment card with author, path, body
- "Ask Agent to Fix" per comment: sends context-specific message with file path
- Alert cards: CI failures, merge conflicts, changes requested warnings
- Spawn Session screen: create new sessions with project ID and optional issue ID
- "+" button on home screen header to spawn sessions
- PR title is now tappable (opens in browser)
- Color-coded CI/review/mergeability status values
- PR blockers list display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds `packages/mobile` — an Expo SDK 53 React Native app for monitoring
agent sessions from a phone.
Features:
- Home screen: Kanban-style session list sorted by attention level
- Session detail: metadata, PR link, CI status, branch info
- Terminal screen: full xterm.js terminal via WebView (port 14801)
- Settings screen: configurable backend URL (LAN IP or ngrok)
- Push notifications: background polling for attention-level changes
Improvements over PR #235:
- Fixed terminal onmessage handler to filter JSON control messages
(resize echoes no longer render as garbage text)
- Removed duplicate terminal.html (single source of truth in
terminal-html.ts)
- Fixed isDone check to include "cleanup" status using isTerminal()
Tech: Expo SDK 53, React Navigation 6, React Native WebView.
Two server connections: REST API (port 3000) + WebSocket terminal (14801).
The mobile package is excluded from the pnpm workspace to avoid
interfering with the monorepo toolchain.
Closes#265
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle "invalid issue format" error for ad-hoc spawn
gh CLI returns "invalid issue format" when the issue argument is free-text
rather than a valid issue number/URL. This error was not matched by
isIssueNotFoundError, causing ad-hoc spawns to fail instead of gracefully
falling through to ad-hoc mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: sanitize ad-hoc issue text into valid git branch names
When tracker lookup fails (ad-hoc mode), the raw free-text was used
directly as a branch name, causing git to reject it. Now:
- Only use tracker.branchName() when the issue was actually resolved
- Slugify ad-hoc text (lowercase, replace non-alphanumeric with hyphens,
trim, cap at 60 chars) to produce valid branch names
e.g. "fix the cold start issue" → feat/fix-the-cold-start-issue
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: preserve casing for branch-safe issue IDs, only slugify free-text
The slug sanitization was lowercasing all issue IDs without a resolved
issue (e.g. INT-9999 → int-9999). Now only applies sanitization when
the issueId contains characters invalid for git branch names.
Adds tests for slug sanitization, casing preservation, truncation,
empty-slug fallback, and isIssueNotFoundError patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: assert tracker.branchName not called for unresolved issues
Adds assertion to existing test that tracker.branchName is NOT called
when the issue wasn't found. Adds end-to-end test for "invalid issue
format" error flowing through spawn with free-text slugification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: reject '..' in branch-safe check, verify generatePrompt skipped
The isBranchSafe regex allowed '..' which is invalid in git refs.
Also asserts that tracker.generatePrompt is not called when the
issue wasn't resolved, and adds a test for the '..' edge case.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: trim dashes after truncation, not before
Bugbot correctly noted that .slice(0, 60) after dash-trim can
reintroduce a trailing dash. Reorder so trim runs after slice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Prateek <karnalprateek@gmail.com>
* docs: add demo video and article links to README
Add tweet screenshot for the video demo prominently after the intro,
and link to the full article thread. Replaces the TODO placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: use article screenshot for README article link
Replace text-only article link with clickable screenshot showing
the article title, preview image, and engagement stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: replace demo video screenshot with higher quality version
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Default dangerouslySkipPermissions to true for autonomous agent operation
Spawned agents need to run CLI commands without permission prompts. Changed
the agentConfig.permissions schema default from undefined to "skip" so
--dangerously-skip-permissions is included by default. The orchestrator
session is hardcoded to "skip" regardless of config since it must always
run ao CLI commands autonomously.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Default agentConfig to {} so permissions default propagates
When agentConfig is absent from YAML, .optional() leaves it undefined,
so project.agentConfig?.permissions evaluates to undefined instead of
"skip". Change to .default({}) so Zod always constructs the object and
applies the inner permissions default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: skip orchestrator sessions during cleanup
The cleanup command could kill the orchestrator's own tmux session
(named {prefix}-orchestrator) because it has no PR or issue, making
it eligible for the "dead runtime" cleanup heuristic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add role metadata for orchestrator session identification
Store role=orchestrator in metadata during spawnOrchestrator() and
check it in cleanup() for explicit identification. Keeps the name
fallback (endsWith -orchestrator) for pre-existing sessions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: preserve role in restore path, add integration test
- restore() now preserves the role field when recreating metadata
from archive (previously dropped, breaking role-based detection)
- Add integration test: orchestrator skipped while sibling session
with same closed issue gets killed (verifies real tracker plugin)
- Verified: both unit and integration tests fail without the guard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: persist role field in metadata, isolate role test from name fallback
Bugbot caught two real issues:
- writeMetadata() and readMetadata() never serialized the role field,
so role=orchestrator was silently dropped on disk
- The role-based test used "app-orchestrator" as session name, so the
name fallback caught it — role check was never actually exercised
Fix: add role to metadata read/write, rename test session to "app-99"
so only the role metadata path can protect it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: keep Claude Code interactive after initial prompt
Claude Code's -p flag runs in one-shot mode (exits after responding),
which prevents follow-up messages via `ao send`. Instead, launch Claude
interactively and deliver the initial prompt post-launch via
runtime.sendMessage().
Adds `promptDelivery` property to the Agent interface so each agent
plugin can declare whether prompts should be inlined in the launch
command or sent after the agent starts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make post-launch prompt delivery non-fatal and add test coverage
- Move sendMessage call outside the try/catch that destroys the session.
A prompt delivery failure should not kill a running agent — user can
retry with `ao send`.
- Add tests: no-prompt + post-launch agent, sendMessage failure resilience,
5s delay verification, systemPrompt/systemPromptFile alongside omitted -p.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add integration test for prompt delivery (proves bug and fix)
Two real-Claude integration tests that contrast:
1. `-p` mode: Claude exits after responding (the bug)
2. Interactive + sendMessage: Claude stays alive, follow-up works (the fix)
Runs in CI with ANTHROPIC_API_KEY. Skips when prerequisites missing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): skip interactive test without auth, improve TUI readiness detection
The integration test failed in CI because interactive Claude requires
full login auth (not just ANTHROPIC_API_KEY). Skip the interactive suite
when `claude auth status` reports not logged in.
Also fix local flakiness: replace blind 5s sleep with polling for
Claude's TUI prompt character (❯) before sending the first message,
and increase scrollback capture from 200 to 500 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): skip interactive test in CI, fix TUI readiness detection
The interactive test ran in CI despite hasInteractiveAuth() — Claude
reports logged in when ANTHROPIC_API_KEY is set, but interactive mode
requires OAuth. Use `!process.env.CI` as the skip condition instead.
Also fix waitForTuiReady false positive: the OAuth screen's
"Paste code here if prompted >" matched the `>` regex. Now checks
the last non-empty line for Claude's specific ❯ prompt character,
and bails early if the OAuth/login screen is detected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Merge the two centered divs into one and add <br><br><br> between
the demo CTA button and the article screenshot below it.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
GitHub collapses <br> surrounded by blank lines in HTML blocks.
Placing <br><br> on the same line without surrounding blank lines
keeps them in raw HTML mode where they render correctly.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace <p> </p> (too much margin) with a 1px transparent GIF
at height=8 for precise, subtle spacing.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
GitHub markdown collapses <br> inside <div> blocks. Use <p> </p>
which GitHub reliably renders as vertical space.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: make "See it in action" heading bigger (h2)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add vertical spacing between screenshots and buttons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add demo video and article links to README
Add tweet screenshot for the video demo prominently after the intro,
and link to the full article thread. Replaces the TODO placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: use article screenshot for README article link
Replace text-only article link with clickable screenshot showing
the article title, preview image, and engagement stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: replace demo video screenshot with higher quality version
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: vibrant 3D gradient CTA buttons for README
- Replace flat black shields.io badges with custom SVG buttons
- Purple gradient for "Watch the Demo" with play icon
- Coral gradient for "Read the Full Article" with book icon
- Both have 3D shine effect (top highlight, bottom shadow)
- High contrast on both GitHub light and dark mode
- Update article screenshot with cleaner version
- Images remain clickable (already wrapped in <a> tags)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: remove white borders from CTA buttons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: use manually captured PNG buttons instead of SVGs
Playwright screenshots introduce 1-2px border artifacts on SVG img
elements (microsoft/playwright#35014). Use clean manually-captured
PNG screenshots instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add demo video and article links to README
Add tweet screenshot for the video demo prominently after the intro,
and link to the full article thread. Replaces the TODO placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: use article screenshot for README article link
Replace text-only article link with clickable screenshot showing
the article title, preview image, and engagement stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: replace demo video screenshot with higher quality version
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update article screenshot and make CTAs more prominent
- Replace article screenshot with cleaner version showing title/preview
- Replace small <sub> text links with shields.io badge-style buttons
for "Watch the demo on X" and "Read the full article on X"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add demo video and article links to README
Add tweet screenshot for the video demo prominently after the intro,
and link to the full article thread. Replaces the TODO placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: use article screenshot for README article link
Replace text-only article link with clickable screenshot showing
the article title, preview image, and engagement stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: replace demo video screenshot with higher quality version
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a 30-second TTL cache for session file lookups so that
getActivityState, getSessionInfo, and getRestoreCommand called in the
same refresh cycle reuse the result instead of re-scanning
~/.codex/sessions/ independently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use the Codex session file's mtime as a proxy for agent activity.
Codex continuously appends to its rollout JSONL while working, so:
- recently modified file → active
- stale file (past threshold) → idle
- process not running → exited
This replaces the previous null return with real activity detection,
using the same threshold constant (DEFAULT_READY_THRESHOLD_MS = 5min)
as the Claude Code plugin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap entire handleApprovalRequest body in try/catch so a throwing
"approval" listener doesn't cause an unhandled promise rejection
- Remove wasClosedBefore variable that was always false (the closed
check on entry guarantees it), simplifying the connect catch block
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract duplicated approval-policy and model/reasoning flag logic from
getLaunchCommand and getRestoreCommand into shared appendApprovalFlags
and appendModelFlags helpers. Single source of truth for flag mapping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move this.emit("error", err) after the pending-rejection loop in
handleProcessError, matching handleProcessExit's order. Without this,
emit("error") with no listeners throws synchronously, skipping
cleanup of pending requests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three tests relied on the empty default stream coincidentally matching
expected values instead of actually exercising the streaming parser.
Added setupMockStream(content) and stronger assertions to verify the
parser correctly handles missing model, missing token events, and
missing threadId.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "jsonrpc": "2.0" to all JSON-RPC requests, notifications, and
approval responses for spec compliance
- Place flags before positional threadId in resume command for CLI
parser compatibility (codex resume --flags <threadId>)
- Add test assertions for jsonrpc field and flag ordering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use lstat instead of stat in collectJsonlFiles so symlinks to
directories are never followed (isDirectory returns false for symlinks)
- Add MAX_SESSION_SCAN_DEPTH=4 cap as defense-in-depth
(Codex uses YYYY/MM/DD structure, max 3 levels)
- Update tests to mock lstat for directory checks separately from stat
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove additive cached_tokens/reasoning_tokens from cost calculation
(they are subsets of input_tokens/output_tokens per OpenAI API convention)
- Wrap entire connect() body in try/catch so connecting flag resets if
spawn() throws synchronously (EMFILE, ENOMEM)
- Add test for spawn-throws-synchronously recovery
- Update cost assertions to reflect correct non-additive token counting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reset closed flag after failed connect() so client can retry
- Wrap binary resolve guard in try/finally to clear on rejection
- Fix duplicate import and unused variable lint errors in tests
- Add test: client retries connect after transient handshake failure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes 6 issues flagged by Cursor Bugbot on PR #188:
1. sessionFileMatchesCwd reads only first 4 KB via open()+read() instead of
loading entire rollout file into memory (Medium)
2. Binary resolution race condition: added promise guard to prevent
concurrent resolveCodexBinary() calls (Low)
3. Concurrent connect() calls: added connecting guard to prevent
orphaned child processes (High)
4. connect() failure cleanup: wrap initialize() in try/catch that
calls close() on failure (Medium)
5. readline cleanup on process exit/error: close readline in both
handleProcessExit and handleProcessError (Medium)
6. stderr pipe drain: call stderr.resume() to prevent child blocking
when stderr buffer fills (High)
All 177 tests pass, typecheck clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update test expectations to match the corrected Codex CLI flags:
- --full-auto instead of --dangerously-bypass-approvals-and-sandbox
- -c developer_instructions=... instead of --system-prompt (inline)
- -c model_instructions_file=... instead of --system-prompt $(cat) (file)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace --dangerously-bypass-approvals-and-sandbox with --full-auto
(keeps workspace sandbox, auto-approves — safer default for automation)
- Replace nonexistent --system-prompt flag with Codex config overrides:
-c developer_instructions=... for inline prompts
-c model_instructions_file=... for file-based prompts
- Remove redundant "active" pattern checks that duplicate the default
return value (addresses BugBot dead-code finding)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
readMetadata() was not returning the `agent` field even though
writeMetadata() persisted it. This meant typed consumers got
undefined for session.agent while raw readers worked fine.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When --agent override is used at spawn time, the agent name was not stored
in session metadata. The lifecycle manager and session enrichment always
resolved the agent from project config defaults, causing mismatched process
detection (e.g. looking for "claude" instead of "codex") and incorrect
"killed" status on the first poll cycle.
- Add `agent` field to SessionMetadata type and writeMetadata
- Store `plugins.agent.name` in metadata during spawn
- Pass stored agent name to resolvePlugins in list/get/restore paths
- Read `session.metadata["agent"]` in lifecycle manager's determineStatus
- Add tests for metadata persistence with and without override
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Session manager: agent override uses correct agent, unknown agent throws, default used without override
- CLI spawn: --agent flag passthrough with and without issue ID
- Plugin registry: multiple agent plugins registered via loadBuiltins importFn
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The \$ escapes in bash script template literals are intentional to
prevent JS template interpolation. Added eslint-disable/enable block.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allows starting sessions with a different agent without changing config:
ao spawn ao --agent codex
ao spawn ao --agent codex INT-1234
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clearer H1 and tagline.
- H1 now describes what the project is (orchestration layer for parallel agents)
- Tagline calls out git worktree isolation and autonomous CI/review handling
No structural changes.
Co-authored-by: Sujay Choubey <sujaychoubey@Sujays-MacBook-Pro-4.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The static file is fully superseded by the auto-generated orchestrator
prompt in packages/core/src/orchestrator-prompt.ts, which gets injected
dynamically via `ao start`. Also clean up stale references in comments
and architecture docs.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: redesign README based on 16-repo competitive research
Restructured README following patterns from lazygit, starship, Aider,
Swarm, LangGraph, and other high-star projects. Key changes:
- Centered hero with one-line tagline and custom metric badges
- Quick Start within first 20 lines (code-first)
- "How It Works" with numbered flow instead of abstract description
- Plugin architecture table (our differentiator) kept prominent
- Config example showing the reactions system (the "aha" feature)
- "Why Agent Orchestrator?" competitive positioning section
- Reduced from 234 to ~130 lines — link to docs for depth
Research artifacts: github.com/ComposioHQ/agent-orchestrator/releases/tag/metrics-v1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use valid HTML structure for README hero section
Replace invalid <h1> inside <p> with <div> + markdown heading.
Drop dynamic stars badge (shields.io intermittently fails).
Use markdown badge syntax instead of raw HTML img tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove unrelated scm-github changes from PR
These files were accidentally included from pre-existing unstaged
changes on main. Restoring to main state so PR only contains README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add GitHub stars badge to README
Follow the standard pattern used by popular open-source projects
(CrewAI, lazygit, starship, etc.) using shields.io dynamic badge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>