coding-agent-adapters 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,6 +94,27 @@ Adapters detect prompts that block the session and require user action:
94
94
  | Codex | Directory trust, tool approval, update available, model migration, CWD selection |
95
95
  | Aider | File operations, shell commands, git init, pip install, destructive operations |
96
96
 
97
+ ### Task Completion Detection
98
+
99
+ Each adapter implements `detectTaskComplete(output)` to recognize when the CLI has finished a task and returned to its idle prompt. This is more specific than `detectReady()` — it matches high-confidence completion indicators (duration summaries, explicit done messages) that short-circuit the LLM stall classifier in pty-manager.
100
+
101
+ | Adapter | Completion Indicators | Source Patterns |
102
+ |---------|----------------------|----------------|
103
+ | Claude | Turn duration (`Cooked for 3m 12s`, custom verb) + `❯` prompt | `claude_completed_turn_duration` |
104
+ | Gemini | `◇ Ready` window title, `Type your message` composer | `gemini_ready_title` |
105
+ | Codex | `Worked for 1m 05s` separator + `›` prompt | `codex_completed_worked_for_separator`, `codex_ready_prompt` |
106
+ | Aider | `Aider is waiting for your input`, mode prompts with edit/cost markers | `aider_completed_llm_response_ready` |
107
+
108
+ ```typescript
109
+ const claude = new ClaudeAdapter();
110
+ claude.detectTaskComplete('Cooked for 3m 12s\n❯ '); // true
111
+ claude.detectTaskComplete('Reading 5 files…'); // false
112
+
113
+ const aider = new AiderAdapter();
114
+ aider.detectTaskComplete('Applied edit to main.ts\nTokens: 1234\ncode> '); // true
115
+ aider.detectTaskComplete('Waiting for claude-sonnet-4-20250514'); // false
116
+ ```
117
+
97
118
  ### Exit Detection
98
119
 
99
120
  Adapters detect when a CLI session has ended:
@@ -390,6 +411,11 @@ export class CursorAdapter extends BaseCodingAdapter {
390
411
  return /cursor>\s*$/m.test(output);
391
412
  }
392
413
 
414
+ detectTaskComplete(output: string): boolean {
415
+ // High-confidence: task summary + idle prompt
416
+ return /completed in \d+s/.test(output) && /cursor>\s*$/m.test(output);
417
+ }
418
+
393
419
  parseOutput(output: string): ParsedOutput | null {
394
420
  return { type: 'response', content: output.trim(), isComplete: true, isQuestion: output.includes('?') };
395
421
  }
package/dist/index.cjs CHANGED
@@ -56,6 +56,8 @@ var CLAUDE_TOOL_CATEGORIES = {
56
56
  // file_write
57
57
  Write: "file_write",
58
58
  Edit: "file_write",
59
+ MultiEdit: "file_write",
60
+ NotebookEdit: "file_write",
59
61
  // shell
60
62
  Bash: "shell",
61
63
  BashOutput: "shell",
@@ -747,6 +749,29 @@ var ClaudeAdapter = class extends BaseCodingAdapter {
747
749
  }
748
750
  return super.detectBlockingPrompt(output);
749
751
  }
752
+ /**
753
+ * Detect task completion for Claude Code.
754
+ *
755
+ * High-confidence pattern: turn duration summary + idle prompt.
756
+ * Claude Code shows "<Verb> for Xm Ys" (e.g. "Cooked for 3m 12s")
757
+ * when a turn completes, followed by the ❯ input prompt.
758
+ *
759
+ * Patterns from: AGENT_LOADING_STATUS_PATTERNS.json
760
+ * - claude_completed_turn_duration
761
+ * - claude_completed_turn_duration_custom_verb
762
+ */
763
+ detectTaskComplete(output) {
764
+ const stripped = this.stripAnsi(output);
765
+ const hasDuration = /[A-Z][A-Za-z' -]{2,40}\s+for\s+\d+(?:h\s+\d{1,2}m\s+\d{1,2}s|m\s+\d{1,2}s|s)/.test(stripped);
766
+ const hasIdlePrompt = /❯\s*$/.test(stripped);
767
+ if (hasDuration && hasIdlePrompt) {
768
+ return true;
769
+ }
770
+ if (hasIdlePrompt && stripped.includes("for shortcuts")) {
771
+ return true;
772
+ }
773
+ return false;
774
+ }
750
775
  detectReady(output) {
751
776
  const stripped = this.stripAnsi(output);
752
777
  if (/trust.*directory|do you want to|needs? your permission/i.test(stripped)) {
@@ -1004,6 +1029,26 @@ var GeminiAdapter = class extends BaseCodingAdapter {
1004
1029
  }
1005
1030
  return super.detectBlockingPrompt(output);
1006
1031
  }
1032
+ /**
1033
+ * Detect task completion for Gemini CLI.
1034
+ *
1035
+ * High-confidence patterns:
1036
+ * - "◇ Ready" window title signal (OSC sequence, may survive ANSI stripping)
1037
+ * - "Type your message" composer placeholder after agent output
1038
+ *
1039
+ * Patterns from: AGENT_LOADING_STATUS_PATTERNS.json
1040
+ * - gemini_ready_title
1041
+ */
1042
+ detectTaskComplete(output) {
1043
+ const stripped = this.stripAnsi(output);
1044
+ if (/◇\s+Ready/.test(stripped)) {
1045
+ return true;
1046
+ }
1047
+ if (/type.?your.?message/i.test(stripped)) {
1048
+ return true;
1049
+ }
1050
+ return false;
1051
+ }
1007
1052
  detectReady(output) {
1008
1053
  const stripped = this.stripAnsi(output);
1009
1054
  if (/type.?your.?message/i.test(stripped)) {
@@ -1314,6 +1359,32 @@ var CodexAdapter = class extends BaseCodingAdapter {
1314
1359
  }
1315
1360
  return super.detectBlockingPrompt(output);
1316
1361
  }
1362
+ /**
1363
+ * Detect task completion for Codex CLI.
1364
+ *
1365
+ * High-confidence patterns:
1366
+ * - "Worked for Xm Ys" separator after work-heavy turns
1367
+ * - "› Ask Codex to do anything" ready prompt
1368
+ *
1369
+ * Patterns from: AGENT_LOADING_STATUS_PATTERNS.json
1370
+ * - codex_completed_worked_for_separator
1371
+ * - codex_ready_prompt
1372
+ */
1373
+ detectTaskComplete(output) {
1374
+ const stripped = this.stripAnsi(output);
1375
+ const hasWorkedFor = /Worked\s+for\s+\d+(?:h\s+\d{2}m\s+\d{2}s|m\s+\d{2}s|s)/.test(stripped);
1376
+ const hasReadyPrompt = /›\s+Ask\s+Codex\s+to\s+do\s+anything/.test(stripped);
1377
+ if (hasWorkedFor && hasReadyPrompt) {
1378
+ return true;
1379
+ }
1380
+ if (hasReadyPrompt) {
1381
+ return true;
1382
+ }
1383
+ if (hasWorkedFor && /›\s+/m.test(stripped)) {
1384
+ return true;
1385
+ }
1386
+ return false;
1387
+ }
1317
1388
  detectReady(output) {
1318
1389
  const stripped = this.stripAnsi(output);
1319
1390
  if (/do.?you.?trust.?the.?contents/i.test(stripped) || /sign.?in.?with.?chatgpt/i.test(stripped) || /update.?available/i.test(stripped) || /enable.?full.?access/i.test(stripped) || /choose.?working.?directory/i.test(stripped)) {
@@ -1718,6 +1789,31 @@ var AiderAdapter = class extends BaseCodingAdapter {
1718
1789
  }
1719
1790
  return super.detectBlockingPrompt(output);
1720
1791
  }
1792
+ /**
1793
+ * Detect task completion for Aider.
1794
+ *
1795
+ * High-confidence patterns:
1796
+ * - "Aider is waiting for your input" notification (bell message)
1797
+ * - Edit-format mode prompts (ask>, code>, architect>) after output
1798
+ *
1799
+ * Patterns from: AGENT_LOADING_STATUS_PATTERNS.json
1800
+ * - aider_completed_llm_response_ready
1801
+ */
1802
+ detectTaskComplete(output) {
1803
+ const stripped = this.stripAnsi(output);
1804
+ if (/Aider\s+is\s+waiting\s+for\s+your\s+input/.test(stripped)) {
1805
+ return true;
1806
+ }
1807
+ const hasPrompt = /(?:ask|code|architect)(?:\s+multi)?>\s*$/m.test(stripped);
1808
+ if (hasPrompt) {
1809
+ const hasEditMarkers = /Applied edit to|Commit [a-f0-9]+|wrote to|Updated/i.test(stripped);
1810
+ const hasTokenUsage = /Tokens:|Cost:/i.test(stripped);
1811
+ if (hasEditMarkers || hasTokenUsage) {
1812
+ return true;
1813
+ }
1814
+ }
1815
+ return false;
1816
+ }
1721
1817
  detectReady(output) {
1722
1818
  const stripped = this.stripAnsi(output);
1723
1819
  if (/login to openrouter/i.test(stripped) || /open this url in your browser/i.test(stripped) || /waiting up to 5 minutes/i.test(stripped)) {