Commit Graph

4 Commits

Author SHA1 Message Date
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 90f14a6ca5
feat: notifier-composio plugin + integration tests for all plugins (#7)
* feat: implement notifier and terminal plugins (desktop, slack, webhook, iterm2, web)

Implement all 5 notification and terminal UI plugins for the agent
orchestrator, replacing stub files with full implementations of the
Notifier and Terminal interfaces from @agent-orchestrator/core.

Notifier plugins:
- notifier-desktop: OS notifications via osascript (macOS) / notify-send
  (Linux) with priority-based sound (urgent=sound, others=silent)
- notifier-slack: Slack Incoming Webhooks with Block Kit formatting,
  action buttons, PR links, CI status context blocks
- notifier-webhook: Generic HTTP POST with JSON payloads, configurable
  headers, and retry with backoff (default 2 retries)

Terminal plugins:
- terminal-iterm2: AppleScript-based iTerm2 tab management — detects
  existing tabs by profile name, creates/reuses tabs (ported from
  scripts/open-iterm-tab reference implementation)
- terminal-web: Web terminal session tracking for the dashboard's
  xterm.js frontend, providing URL generation and open-state tracking

All plugins follow the PluginModule pattern (manifest + create export)
and pass typecheck cleanly.

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

* test: add comprehensive vitest suites for all notifier and terminal plugins

Add 96 unit tests across 5 plugin packages covering:
- notifier-desktop: priority-based sound mapping, osascript/notify-send
  command generation, platform detection (macOS/Linux/unsupported),
  title formatting, error propagation
- notifier-slack: Block Kit message structure (header/section/context/
  divider blocks), priority emoji mapping, PR link and CI status
  rendering, action button generation (URL and callback variants),
  channel routing, post method
- notifier-webhook: JSON payload serialization, custom headers, retry
  logic (success after retry, exhausted retries, zero retries, network
  errors), timestamp ISO serialization
- terminal-iterm2: AppleScript command generation, tab reuse via profile
  name detection, new tab creation with tmux attach, runtimeHandle
  preference over session ID, batch openAll with delays, error fallback
- terminal-web: session open tracking, dashboard URL configuration,
  openAll batch registration, independent state per instance

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

* fix: address PR review feedback — security, retry logic, side effects

- Add AppleScript injection escaping in terminal-iterm2 and notifier-desktop
- Webhook: only retry on 429/5xx (not 4xx), add exponential backoff
- terminal-iterm2: fix isSessionOpen selecting tab (side effect), remove dead openNewWindow
- notifier-slack: type-guard event.data access, add URL validation
- notifier-webhook: add URL validation, remove unused config interfaces
- Update all tests to cover new behaviors (108 tests passing)

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

* fix: codex review — shell injection, iTerm2 name lookup, notify-send args, retry validation

- terminal-iterm2: shell-escape session names in tmux command (single-quote wrapping)
- terminal-iterm2: use `name of aSession` instead of `profile name` for tab lookup
- notifier-desktop: fix notify-send arg order (options before title/body)
- notifier-webhook: clamp retries/retryDelayMs to safe values (non-negative, finite)
- notifier-slack: sanitize action_id to [a-z0-9_] characters only

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

* chore: lint/format compliance after rebase on main

- Fix ESLint errors: remove unused WebTerminalConfig, ignore next-env.d.ts
- Run prettier on all files for consistent formatting
- Update pnpm-lock.yaml with new lint dependencies

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

* fix: codex review round 2 — printf injection, platform guard, config validation

- terminal-iterm2: use shell-escaped name in printf title (not just tmux target)
- terminal-iterm2: add platform guard — no-op with warning on non-macOS
- notifier-webhook: validate customHeaders are string:string before spreading
- notifier-desktop: validate sound config as boolean (reject string "false")

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

* chore: update lockfile after rebase on main

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

* feat: add notifier-composio plugin and integration tests for all plugins

Add a new notifier-composio plugin as the recommended notification
transport using Composio's unified API for Slack, Discord, and Gmail.
Create comprehensive integration tests (79 tests across 6 files) for
all notifier and terminal plugins, mocking only I/O boundaries.

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

* fix: address PR review feedback — security, retry logic, side effects

- Shell injection fix: double-escape (shell + AppleScript) in iTerm2 printf/tmux
- iTerm2 no-window crash: create window if none exists
- Composio graceful degradation: warn instead of throw when SDK missing
- Composio emailTo validation: require emailTo when defaultApp is gmail
- AbortSignal.timeout() replaces manual AbortController + {once: true} listener
- Discord channelName fallback for channel_id
- Slack empty action_id fallback
- Dashboard port: terminal-web default changed from 9847 to 3000
- escapeAppleScript deduplicated into core/utils.ts
- Consistent vitest version (^3.0.0) for composio plugin
- Remove duplicate eslint ignore entry
- Integration tests updated for shared event-factory helper
- Unit tests updated for new validation and escaping behavior

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

* chore: update lockfile after rebase on main

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

* fix: prevent double ao_ prefix in Slack action_id fallback

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

* fix: decouple Linux urgency from sound config, deduplicate validateUrl

- Linux --urgency=critical now driven by event priority, not sound config
- Moved validateUrl to core/utils.ts, imported by slack and webhook plugins

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

* fix: handle ESM ERR_MODULE_NOT_FOUND for composio-core detection

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

* fix: codex review round 2 — printf injection, platform guard, config validation

- Slack action_id: append index for uniqueness (two "Retry" buttons no
  longer collide)
- Composio Gmail subject: extract GMAIL_SUBJECT constant so notify() and
  post() use the same value
- Agent integration tests: require API key env vars before running to
  prevent CI timeout when secrets are not configured
- Update lockfile after rebase on main

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

* fix: parallelize integration tests and increase CI timeout

- Remove singleFork: true from vitest config — all integration test
  files use unique session prefixes so they're safe to run concurrently
- Increase CI timeout from 15 to 20 minutes as safety margin for agent
  tests that make real API calls

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

* fix: prevent unhandled promise rejection after timeout in composio notifier

Attach a no-op .catch() to the executeAction promise so that if the
timeout fires first and the action later rejects, it doesn't trigger
an unhandledRejection event.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:29:59 +05:30
prateek 8707faf11c
feat: implement SCM and tracker plugins (github, linear) (#4)
* feat: implement SCM and tracker plugins (github, linear)

Implement three plugin interfaces from packages/core/src/types.ts:

- scm-github: Full GitHub PR lifecycle via `gh` CLI — PR detection by
  branch, state tracking, CI checks, reviews, review decision, pending
  comments, automated bot comment detection (cursor[bot], codecov, etc.),
  and merge readiness (CI + reviews + conflicts + draft).

- tracker-github: GitHub Issues tracker via `gh` CLI — issue CRUD,
  completion check, branch naming (feat/issue-N), prompt generation,
  list/filter/update/create with label and assignee support.

- tracker-linear: Linear issue tracker via GraphQL API (LINEAR_API_KEY) —
  issue fetch, completion check, branch naming (feat/IDENTIFIER), prompt
  generation, list with team/state/label filters, state transitions via
  workflow state resolution, issue creation with teamId config.

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

* fix: address PR review — GraphQL injection, N+1 queries, timeouts, tests

- tracker-linear: use GraphQL variables in listIssues to prevent injection
- tracker-linear: add 30s HTTP timeout to linearQuery
- tracker-linear: fix issueUrl to use workspace slug from project config
- tracker-linear: add labels/assignee support to createIssue
- scm-github: rewrite getPendingComments to use GraphQL reviewThreads
  with real isResolved status instead of hardcoding false
- scm-github: simplify getAutomatedComments to single API call (N+1 fix)
- scm-github: add 30s timeout to gh CLI calls
- scm-github: fix parseDate to return epoch instead of fabricating dates
- scm-github: add repo format validation in detectPR
- scm-github: fix getCISummary to not count all-skipped as passing
- tracker-github: add 30s timeout to gh CLI calls
- tracker-github: fix createIssue to not use unsupported --json flag
- Add comprehensive vitest tests for scm-github and tracker-github

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

* fix: address Codex review — assignee lookup, GraphQL vars, status checks

Iteration 1 fixes (8 issues from Codex review):
- tracker-linear: remove invalid assigneeDisplayName, resolve assignee
  by display name to ID via users query after creation
- tracker-linear: add HTTP status code check in linearQuery
- tracker-linear: throw on missing workflow state in updateIssue
- scm-github: use GraphQL variables ($owner, $name, $number) in
  getPendingComments instead of string interpolation
- scm-github: guard against empty comment nodes in review threads
- scm-github: use mergeStateStatus in getMergeability (BEHIND, BLOCKED)
- tracker-github: default description to "" in createIssue

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

* fix: address PR review + lint — error context, GraphQL vars, mergeability

- scm-github/tracker-github: wrap gh() errors with command context + cause
- scm-github: use GraphQL variables ($owner, $name, $number) in
  getPendingComments to prevent injection
- scm-github: guard against empty review thread nodes
- scm-github: incorporate mergeStateStatus (BEHIND/BLOCKED) in getMergeability
- tracker-linear: add identifier vs UUID comment in updateIssue
- Fix ESLint preserve-caught-error violations
- Remove unused mockGhRaw in scm-github tests

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

* fix: add pagination to getAutomatedComments and increase thread limit

- getAutomatedComments: add --paginate flag to REST API call to fetch
  all review comments beyond the default 30-item first page
- getPendingComments: increase GraphQL reviewThreads limit from 100 to
  250 (GitHub's max for connections)

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

* fix: use per_page=100 instead of --paginate for JSON-safe pagination

Replace --paginate with -F per_page=100 in getAutomatedComments to
avoid concatenated JSON arrays that break JSON.parse on multi-page
responses. 100 is GitHub's max per_page value.

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

* fix: filter out resolved threads in getPendingComments

The method name implies it should only return unresolved/pending review
threads. The isResolved data was already fetched via GraphQL but was not
being filtered on, causing resolved threads to be included in results.

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

* test: add comprehensive tracker-linear test suite (53 tests)

Covers all Tracker interface methods: getIssue, isCompleted, issueUrl,
branchName, generatePrompt, listIssues, updateIssue, createIssue.
Mocks node:https request to simulate Linear GraphQL API responses.
Tests state mapping, error handling, assignee/label resolution, and
GraphQL variable injection.

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

* fix: address 6 bugbot review comments on PR #4

- GraphQL reviewThreads(first: 250) → first: 100 (GitHub max)
- noConflicts false when mergeable is UNKNOWN (not just CONFLICTING)
- updateIssue now handles labels and assignee (not just state/comment)
- Add res.on("error") handler in linearQuery to prevent crashes
- createIssue returns only actually-applied labels, not all requested
- Tests added/updated for all fixes (144 total across 3 plugins)

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

* fix: make Linear updateIssue labels additive to match GitHub behavior

Linear's issueUpdate replaces all labels, while GitHub's --add-label is
additive. Now fetches existing label IDs first and merges with new ones
before sending to issueUpdate, ensuring consistent behavior across both
tracker implementations.

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

* fix: default listIssues to open state in tracker-linear

When no state filter is specified, tracker-github defaults to "open"
but tracker-linear was returning all issues. Now defaults to excluding
completed/canceled issues, matching tracker-github behavior.

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

* feat: add Composio SDK support to tracker-linear

Allow users with a COMPOSIO_API_KEY to use the Linear tracker without
a separate LINEAR_API_KEY. The plugin auto-detects which key is available
and routes through either the direct Linear GraphQL API or Composio's
LINEAR_RUN_QUERY_OR_MUTATION tool. All existing queries and response
parsing are reused via a GraphQLTransport abstraction.

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

* fix: address 2 bugbot review comments on PR #4

1. getCIChecks now throws on error instead of silently returning [],
   preventing a fail-open where CI appears healthy when we can't fetch
   check status. getCISummary catches the error and returns "failing".

2. Handle GitHub's UNSTABLE mergeStateStatus (required checks failing)
   as a merge blocker in getMergeability.

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

* fix: prevent unhandled promise rejection in Composio timeout race

When the timeout wins Promise.race in the Composio transport, the
resultPromise is left without a rejection handler. If the SDK call
later rejects, it becomes an unhandled promise rejection that crashes
Node.js 20+ with --unhandled-rejections=throw.

Attach no-op .catch() to both resultPromise and timeoutPromise before
the race so whichever promise loses has its rejection silently handled.

Also adds plugin integration tests (core -> real plugins -> mocked gh CLI).

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

* fix: chain error cause in getCIChecks and fix core build

- getCIChecks catch now preserves the original error via { cause: err }
  instead of discarding it, matching the pattern used by the gh() helper
- Add tsconfig.build.json to core that excludes __tests__/ from build
  compilation, preventing TS5055 errors from circular workspace deps
  (integration tests import plugin packages that depend back on core)

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

* fix: remove circular devDeps, address bugbot comments

- Remove plugin devDependencies from core/package.json that created a
  circular dependency (core -> plugins -> core), breaking CI build order
- Document in_progress state as intentional no-op in tracker-github
  updateIssue (GitHub Issues only supports open/closed)
- Remove @composio/core optional peerDependency from tracker-linear;
  the dynamic import() already handles the missing package gracefully,
  and the peer dep was pulling composio + transitive deps into lockfile

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

* feat: add real integration test for tracker-linear against Linear API

Creates a test that exercises the full tracker-linear plugin lifecycle
against the real Linear API — createIssue, getIssue, isCompleted,
listIssues, updateIssue (comment, close, reopen), generatePrompt,
branchName, issueUrl. Each run creates a throwaway test issue and
trashes it in cleanup. Skipped when LINEAR_API_KEY is not set.

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

* ci: pass LINEAR_API_KEY and LINEAR_TEAM_ID to integration tests

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

* feat: support both LINEAR_API_KEY and COMPOSIO_API_KEY in integration test

The tracker-linear integration test now runs with either credential:
- LINEAR_API_KEY: direct Linear API (full cleanup via trash)
- COMPOSIO_API_KEY: via Composio SDK (cleanup falls back to closing)

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

* fix: don't pass COMPOSIO_API_KEY to CI integration tests

When both LINEAR_API_KEY and COMPOSIO_API_KEY are set, the plugin
prefers the Composio transport which requires @composio/core SDK.
The SDK isn't installed in CI, so use the direct LINEAR_API_KEY
transport which has no extra dependencies.

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

* fix: use import.meta.url in vitest config, default createIssue description

- Replace __dirname with import.meta.url-derived dirname in ESM config
- Default createIssue description to "" for defensive safety

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

* fix: map CANCELLED and ACTION_REQUIRED CI conclusions to failed

Terminal check conclusions (CANCELLED, ACTION_REQUIRED) were falling
through to "pending", causing the lifecycle manager to wait forever.
Also changed the default else branch to "failed" (fail-closed).

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:45:51 +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