reverse-api-engineer 0.4.4__tar.gz → 0.4.5__tar.gz

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.
Files changed (144) hide show
  1. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/CHANGELOG.md +8 -3
  2. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/PKG-INFO +1 -1
  3. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/pyproject.toml +1 -1
  4. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/auto_engineer.py +80 -93
  5. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/engineer.py +80 -94
  6. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/uv.lock +1 -1
  7. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/.claude/settings.local.json +0 -0
  8. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/.claude-plugin/marketplace.json +0 -0
  9. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/.gitignore +0 -0
  10. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/.python-version +0 -0
  11. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/CLAUDE.md +0 -0
  12. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/CONTRIBUTING.md +0 -0
  13. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/INTERVIEW.md +0 -0
  14. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/LICENSE +0 -0
  15. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/PROMPT_STASH.md +0 -0
  16. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/README.md +0 -0
  17. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/RELEASING.md +0 -0
  18. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/assets/reverse-api-banner.svg +0 -0
  19. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/assets/reverse-api-engineer.gif +0 -0
  20. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/assets/reverse-api-logo.svg +0 -0
  21. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/.claude/settings.local.json +0 -0
  22. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/components.json +0 -0
  23. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/package-lock.json +0 -0
  24. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/package.json +0 -0
  25. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/packed/reverse-api-engineer-chrome.zip +0 -0
  26. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/postcss.config.js +0 -0
  27. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/_locales/en/messages.json +0 -0
  28. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/icons/icon-128.png +0 -0
  29. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/icons/icon-16.png +0 -0
  30. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/icons/icon-32.png +0 -0
  31. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/icons/icon-48.png +0 -0
  32. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/public/manifest.json +0 -0
  33. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/background/service-worker.ts +0 -0
  34. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/agent-action.tsx +0 -0
  35. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/chat-input.tsx +0 -0
  36. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/icons.tsx +0 -0
  37. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/markdown-renderer.tsx +0 -0
  38. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/mode-selector.tsx +0 -0
  39. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/plan.tsx +0 -0
  40. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/session-selector.tsx +0 -0
  41. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/terminal.tsx +0 -0
  42. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/components/ui/code-block.tsx +0 -0
  43. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/content/codegen-recorder.ts +0 -0
  44. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/index.css +0 -0
  45. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/shared/capture.ts +0 -0
  46. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/shared/native-host.ts +0 -0
  47. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/shared/storage.ts +0 -0
  48. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/shared/types.ts +0 -0
  49. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/sidepanel/index.html +0 -0
  50. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/sidepanel/main.tsx +0 -0
  51. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/sidepanel/side-panel.tsx +0 -0
  52. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/src/vite-env.d.ts +0 -0
  53. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/store-assets/screenshot-1280x800.png +0 -0
  54. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/tailwind.config.js +0 -0
  55. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/tsconfig.json +0 -0
  56. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/chrome-extension/vite.config.ts +0 -0
  57. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/INDEX.md +0 -0
  58. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/QUICKSTART.md +0 -0
  59. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/README.md +0 -0
  60. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/SUMMARY.md +0 -0
  61. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/api_client.py +0 -0
  62. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/extract_job_fields.py +0 -0
  63. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/main.py +0 -0
  64. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/quick_example.py +0 -0
  65. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/apple/requirements.txt +0 -0
  66. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/API_SUMMARY.txt +0 -0
  67. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/QUICKSTART.md +0 -0
  68. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/README.md +0 -0
  69. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/api_client.py +0 -0
  70. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/example_usage.py +0 -0
  71. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ashby/requirements.txt +0 -0
  72. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/autoscout24/README.md +0 -0
  73. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/autoscout24/SUMMARY.md +0 -0
  74. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/autoscout24/api_client.py +0 -0
  75. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ikea/README.md +0 -0
  76. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/ikea/api_client.py +0 -0
  77. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/mintlify/README.md +0 -0
  78. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/mintlify/api_client.py +0 -0
  79. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/API_ANALYSIS_SUMMARY.md +0 -0
  80. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/README.md +0 -0
  81. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/api_client.py +0 -0
  82. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/example_fetch_all_jobs.py +0 -0
  83. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/quick_start.py +0 -0
  84. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/examples/uber/requirements.txt +0 -0
  85. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/llm-docs/OPENCODE_API_SUMMARY.md +0 -0
  86. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/llm-docs/opencode-api.json +0 -0
  87. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/.claude-plugin/plugin.json +0 -0
  88. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/CHANGELOG.md +0 -0
  89. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/LICENSE +0 -0
  90. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/README.md +0 -0
  91. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/agents/api-reverse-engineer.md +0 -0
  92. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/commands/agent.md +0 -0
  93. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/commands/capture.md +0 -0
  94. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/commands/engineer.md +0 -0
  95. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/commands/manual.md +0 -0
  96. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/CHANGELOG.md +0 -0
  97. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/LICENSE +0 -0
  98. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/SKILL.md +0 -0
  99. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/references/AUTH_PATTERNS.md +0 -0
  100. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/references/HAR_ANALYSIS.md +0 -0
  101. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/scripts/har_analyze.py +0 -0
  102. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/scripts/har_filter.py +0 -0
  103. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/scripts/har_utils.py +0 -0
  104. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/scripts/har_validate.py +0 -0
  105. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/plugins/reverse-api-engineer/skills/reverse-engineering-api/templates/api_client.py +0 -0
  106. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/scripts/clean_build.sh +0 -0
  107. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/__init__.py +0 -0
  108. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/action_recorder.py +0 -0
  109. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/base_engineer.py +0 -0
  110. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/browser.py +0 -0
  111. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/cli.py +0 -0
  112. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/collector.py +0 -0
  113. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/collector_ui.py +0 -0
  114. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/config.py +0 -0
  115. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/copilot_engineer.py +0 -0
  116. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/messages.py +0 -0
  117. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/native_host.py +0 -0
  118. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/opencode_engineer.py +0 -0
  119. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/opencode_ui.py +0 -0
  120. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/playwright_codegen.py +0 -0
  121. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/pricing.py +0 -0
  122. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/session.py +0 -0
  123. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/sync.py +0 -0
  124. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/tui.py +0 -0
  125. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/src/reverse_api/utils.py +0 -0
  126. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/__init__.py +0 -0
  127. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/conftest.py +0 -0
  128. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_action_recorder.py +0 -0
  129. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_auto_engineer.py +0 -0
  130. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_base_engineer.py +0 -0
  131. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_collector.py +0 -0
  132. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_collector_ui.py +0 -0
  133. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_config.py +0 -0
  134. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_engineer.py +0 -0
  135. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_init.py +0 -0
  136. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_messages.py +0 -0
  137. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_native_host.py +0 -0
  138. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_opencode_engineer.py +0 -0
  139. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_opencode_ui.py +0 -0
  140. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_pricing.py +0 -0
  141. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_session.py +0 -0
  142. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_sync.py +0 -0
  143. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_tui.py +0 -0
  144. {reverse_api_engineer-0.4.4 → reverse_api_engineer-0.4.5}/tests/test_utils.py +0 -0
@@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [0.4.4] - 2026-03-15
8
+ ## [0.4.5] - 2026-03-15
9
9
 
10
10
  ### Fixed
11
- - **Stream closed errors (#51)**: Fixed "Error in hook callback hook_0: Stream closed" by clearing inherited `CLAUDECODE` env var from CLI subprocess and extending stream close timeout
12
- - **Async generator cleanup**: Avoid early return inside `query()` loop to prevent cancel-scope errors
11
+ - **Stream closed errors (#51)**: Reverted from `query()` to `ClaudeSDKClient` which maintains a persistent bidirectional connection, eliminating "Error in hook callback hook_0: Stream closed" errors. The original AUQ fix (v0.4.3) unnecessarily switched APIs only the `can_use_tool` callback signature needed updating
12
+ - **CLAUDECODE env var leak**: Clear inherited `CLAUDECODE` env var from CLI subprocess to prevent nested session interference when running inside Claude Code
13
13
  - **CLI stderr noise**: Filter minified JS stack traces into a single clean error line (use `DEBUG=1` for full output)
14
14
 
15
15
  ### Changed
@@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
17
  - **Agent mode**: No longer prompts for URL (agent navigates autonomously)
18
18
  - **Header UI**: Version and task labels now use mode-specific colors (agent=coral, engineer=blue, collector=gold)
19
19
 
20
+ ## [0.4.4] - 2026-03-15
21
+
22
+ ### Fixed
23
+ - **Stream closed errors (#51)**: Partial fix using `query()` with env var workarounds (superseded by v0.4.5)
24
+
20
25
  ## [0.4.3] - 2026-03-12
21
26
 
22
27
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reverse-api-engineer
3
- Version: 0.4.4
3
+ Version: 0.4.5
4
4
  Summary: A tool to capture browser traffic for API reverse engineering
5
5
  Project-URL: Homepage, https://github.com/kalil0321/reverse-api-engineer
6
6
  Project-URL: Repository, https://github.com/kalil0321/reverse-api-engineer
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reverse-api-engineer"
3
- version = "0.4.4"
3
+ version = "0.4.5"
4
4
  description = "A tool to capture browser traffic for API reverse engineering"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -11,14 +11,13 @@ import httpx
11
11
  from claude_agent_sdk import (
12
12
  AssistantMessage,
13
13
  ClaudeAgentOptions,
14
- HookMatcher,
14
+ ClaudeSDKClient,
15
15
  PermissionResultAllow,
16
16
  ResultMessage,
17
17
  TextBlock,
18
18
  ToolPermissionContext,
19
19
  ToolResultBlock,
20
20
  ToolUseBlock,
21
- query,
22
21
  )
23
22
 
24
23
  from .engineer import ClaudeEngineer
@@ -257,105 +256,93 @@ Your final response should confirm the files were created and provide a brief su
257
256
  ],
258
257
  }
259
258
 
260
- # Required: dummy hook keeps the stream open for can_use_tool
261
- async def _dummy_hook(input_data: dict[str, Any], tool_use_id: str | None, context: Any) -> dict[str, Any]:
262
- return {"continue_": True}
263
-
264
- async def _prompt_stream():
265
- yield {
266
- "type": "user",
267
- "message": {"role": "user", "content": self._build_auto_prompt()},
268
- }
269
-
270
259
  options = ClaudeAgentOptions(
271
260
  mcp_servers={"playwright": mcp_config},
261
+ permission_mode="bypassPermissions",
272
262
  can_use_tool=self._handle_tool_permission,
273
- hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[_dummy_hook])]},
274
263
  cwd=str(self.scripts_dir.parent.parent), # Project root
275
264
  model=self.model,
276
- env={"CLAUDECODE": "", "CLAUDE_CODE_STREAM_CLOSE_TIMEOUT": "1800000"},
265
+ env={"CLAUDECODE": ""},
277
266
  stderr=self._handle_cli_stderr,
278
267
  )
279
268
 
280
- final_result: dict[str, Any] | None = None
281
-
282
269
  try:
283
- # Do not break/return inside this loop — the SDK requires the
284
- # async generator to be fully consumed to avoid cancel-scope errors.
285
- async for message in query(prompt=_prompt_stream(), options=options):
286
- # Check for usage metadata
287
- if hasattr(message, "usage") and isinstance(message.usage, dict):
288
- self.usage_metadata.update(message.usage)
289
-
290
- if isinstance(message, AssistantMessage):
291
- last_tool_name = None
292
- for block in message.content:
293
- if isinstance(block, ToolUseBlock):
294
- last_tool_name = block.name
295
- self.ui.tool_start(block.name, block.input)
296
- self.message_store.save_tool_start(block.name, block.input)
297
- elif isinstance(block, ToolResultBlock):
298
- is_error = block.is_error if block.is_error else False
299
-
300
- # Extract output from ToolResultBlock
301
- output = None
302
- if hasattr(block, "content"):
303
- output = block.content
304
- elif hasattr(block, "result"):
305
- output = block.result
306
- elif hasattr(block, "output"):
307
- output = block.output
308
-
309
- tool_name = last_tool_name or "Tool"
310
- self.ui.tool_result(tool_name, is_error, output)
311
- self.message_store.save_tool_result(tool_name, is_error, str(output) if output else None)
312
- elif isinstance(block, TextBlock):
313
- self.ui.thinking(block.text)
314
- self.message_store.save_thinking(block.text)
315
-
316
- elif isinstance(message, ResultMessage):
317
- if message.is_error:
318
- self.ui.error(message.result or "Unknown error")
319
- self.message_store.save_error(message.result or "Unknown error")
320
- else:
321
- script_path = str(self.scripts_dir / self._get_client_filename())
322
- local_path = str(self.local_scripts_dir / self._get_client_filename()) if self.local_scripts_dir else None
323
- self.ui.success(script_path, local_path)
324
-
325
- # Calculate estimated cost if we have usage data
326
- if self.usage_metadata:
327
- input_tokens = self.usage_metadata.get("input_tokens", 0)
328
- output_tokens = self.usage_metadata.get("output_tokens", 0)
329
- cache_creation_tokens = self.usage_metadata.get("cache_creation_input_tokens", 0)
330
- cache_read_tokens = self.usage_metadata.get("cache_read_input_tokens", 0)
331
-
332
- from .pricing import calculate_cost
333
-
334
- cost = calculate_cost(
335
- model_id=self.model,
336
- input_tokens=input_tokens,
337
- output_tokens=output_tokens,
338
- cache_creation_tokens=cache_creation_tokens,
339
- cache_read_tokens=cache_read_tokens,
340
- )
341
- self.usage_metadata["estimated_cost_usd"] = cost
342
-
343
- self.ui.console.print(" [dim]Usage:[/dim]")
344
- if input_tokens > 0:
345
- self.ui.console.print(f" [dim] input: {input_tokens:,} tokens[/dim]")
346
- if cache_creation_tokens > 0:
347
- self.ui.console.print(f" [dim] cache creation: {cache_creation_tokens:,} tokens[/dim]")
348
- if cache_read_tokens > 0:
349
- self.ui.console.print(f" [dim] cache read: {cache_read_tokens:,} tokens[/dim]")
350
- if output_tokens > 0:
351
- self.ui.console.print(f" [dim] output: {output_tokens:,} tokens[/dim]")
352
- self.ui.console.print(f" [dim] total cost: ${cost:.4f}[/dim]")
353
-
354
- final_result = {
355
- "script_path": script_path,
356
- "usage": self.usage_metadata,
357
- }
358
- self.message_store.save_result(final_result)
270
+ async with ClaudeSDKClient(options=options) as client:
271
+ await client.query(self._build_auto_prompt())
272
+
273
+ async for message in client.receive_response():
274
+ if hasattr(message, "usage") and isinstance(message.usage, dict):
275
+ self.usage_metadata.update(message.usage)
276
+
277
+ if isinstance(message, AssistantMessage):
278
+ last_tool_name = None
279
+ for block in message.content:
280
+ if isinstance(block, ToolUseBlock):
281
+ last_tool_name = block.name
282
+ self.ui.tool_start(block.name, block.input)
283
+ self.message_store.save_tool_start(block.name, block.input)
284
+ elif isinstance(block, ToolResultBlock):
285
+ is_error = block.is_error if block.is_error else False
286
+
287
+ output = None
288
+ if hasattr(block, "content"):
289
+ output = block.content
290
+ elif hasattr(block, "result"):
291
+ output = block.result
292
+ elif hasattr(block, "output"):
293
+ output = block.output
294
+
295
+ tool_name = last_tool_name or "Tool"
296
+ self.ui.tool_result(tool_name, is_error, output)
297
+ self.message_store.save_tool_result(tool_name, is_error, str(output) if output else None)
298
+ elif isinstance(block, TextBlock):
299
+ self.ui.thinking(block.text)
300
+ self.message_store.save_thinking(block.text)
301
+
302
+ elif isinstance(message, ResultMessage):
303
+ if message.is_error:
304
+ self.ui.error(message.result or "Unknown error")
305
+ self.message_store.save_error(message.result or "Unknown error")
306
+ return None
307
+ else:
308
+ script_path = str(self.scripts_dir / self._get_client_filename())
309
+ local_path = str(self.local_scripts_dir / self._get_client_filename()) if self.local_scripts_dir else None
310
+ self.ui.success(script_path, local_path)
311
+
312
+ if self.usage_metadata:
313
+ input_tokens = self.usage_metadata.get("input_tokens", 0)
314
+ output_tokens = self.usage_metadata.get("output_tokens", 0)
315
+ cache_creation_tokens = self.usage_metadata.get("cache_creation_input_tokens", 0)
316
+ cache_read_tokens = self.usage_metadata.get("cache_read_input_tokens", 0)
317
+
318
+ from .pricing import calculate_cost
319
+
320
+ cost = calculate_cost(
321
+ model_id=self.model,
322
+ input_tokens=input_tokens,
323
+ output_tokens=output_tokens,
324
+ cache_creation_tokens=cache_creation_tokens,
325
+ cache_read_tokens=cache_read_tokens,
326
+ )
327
+ self.usage_metadata["estimated_cost_usd"] = cost
328
+
329
+ self.ui.console.print(" [dim]Usage:[/dim]")
330
+ if input_tokens > 0:
331
+ self.ui.console.print(f" [dim] input: {input_tokens:,} tokens[/dim]")
332
+ if cache_creation_tokens > 0:
333
+ self.ui.console.print(f" [dim] cache creation: {cache_creation_tokens:,} tokens[/dim]")
334
+ if cache_read_tokens > 0:
335
+ self.ui.console.print(f" [dim] cache read: {cache_read_tokens:,} tokens[/dim]")
336
+ if output_tokens > 0:
337
+ self.ui.console.print(f" [dim] output: {output_tokens:,} tokens[/dim]")
338
+ self.ui.console.print(f" [dim] total cost: ${cost:.4f}[/dim]")
339
+
340
+ result: dict[str, Any] = {
341
+ "script_path": script_path,
342
+ "usage": self.usage_metadata,
343
+ }
344
+ self.message_store.save_result(result)
345
+ return result
359
346
 
360
347
  except Exception as e:
361
348
  error_msg = str(e)
@@ -374,7 +361,7 @@ Your final response should confirm the files were created and provide a brief su
374
361
  self.ui.console.print("\n[dim]Make sure Claude Code CLI is installed: npm install -g @anthropic-ai/claude-code[/dim]")
375
362
  return None
376
363
 
377
- return final_result
364
+ return None
378
365
 
379
366
 
380
367
  class OpenCodeAutoEngineer(OpenCodeEngineer):
@@ -8,14 +8,13 @@ from typing import Any
8
8
  from claude_agent_sdk import (
9
9
  AssistantMessage,
10
10
  ClaudeAgentOptions,
11
- HookMatcher,
11
+ ClaudeSDKClient,
12
12
  PermissionResultAllow,
13
13
  ResultMessage,
14
14
  TextBlock,
15
15
  ToolPermissionContext,
16
16
  ToolResultBlock,
17
17
  ToolUseBlock,
18
- query,
19
18
  )
20
19
 
21
20
  from .base_engineer import BaseEngineer
@@ -46,105 +45,92 @@ class ClaudeEngineer(BaseEngineer):
46
45
  self.ui.start_analysis()
47
46
  self.message_store.save_prompt(self._build_analysis_prompt())
48
47
 
49
- # Required: dummy hook keeps the stream open for can_use_tool
50
- # See: https://platform.claude.com/docs/en/agent-sdk/user-input
51
- async def _dummy_hook(input_data: dict[str, Any], tool_use_id: str | None, context: Any) -> dict[str, Any]:
52
- return {"continue_": True}
53
-
54
- async def _prompt_stream():
55
- yield {
56
- "type": "user",
57
- "message": {"role": "user", "content": self._build_analysis_prompt()},
58
- }
59
-
60
48
  options = ClaudeAgentOptions(
49
+ permission_mode="acceptEdits",
61
50
  can_use_tool=self._handle_tool_permission,
62
- hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[_dummy_hook])]},
63
51
  cwd=str(self.scripts_dir.parent.parent), # Project root
64
52
  model=self.model,
65
- env={"CLAUDECODE": "", "CLAUDE_CODE_STREAM_CLOSE_TIMEOUT": "1800000"},
53
+ env={"CLAUDECODE": ""},
66
54
  stderr=self._handle_cli_stderr,
67
55
  )
68
56
 
69
- final_result: dict[str, Any] | None = None
70
-
71
57
  try:
72
- # Do not break/return inside this loop — the SDK requires the
73
- # async generator to be fully consumed to avoid cancel-scope errors.
74
- async for message in query(prompt=_prompt_stream(), options=options):
75
- # Check for usage metadata in message if applicable
76
- if hasattr(message, "usage") and isinstance(message.usage, dict):
77
- self.usage_metadata.update(message.usage)
78
-
79
- if isinstance(message, AssistantMessage):
80
- last_tool_name = None
81
- for block in message.content:
82
- if isinstance(block, ToolUseBlock):
83
- last_tool_name = block.name
84
- self.ui.tool_start(block.name, block.input)
85
- self.message_store.save_tool_start(block.name, block.input)
86
- elif isinstance(block, ToolResultBlock):
87
- is_error = block.is_error if block.is_error else False
88
-
89
- # Extract output from ToolResultBlock
90
- output = None
91
- if hasattr(block, "content"):
92
- output = block.content
93
- elif hasattr(block, "result"):
94
- output = block.result
95
- elif hasattr(block, "output"):
96
- output = block.output
97
-
98
- tool_name = last_tool_name or "Tool"
99
- self.ui.tool_result(tool_name, is_error, output)
100
- self.message_store.save_tool_result(tool_name, is_error, str(output) if output else None)
101
- elif isinstance(block, TextBlock):
102
- self.ui.thinking(block.text)
103
- self.message_store.save_thinking(block.text)
104
-
105
- elif isinstance(message, ResultMessage):
106
- if message.is_error:
107
- self.ui.error(message.result or "Unknown error")
108
- self.message_store.save_error(message.result or "Unknown error")
109
- else:
110
- script_path = str(self.scripts_dir / self._get_client_filename())
111
- local_path = str(self.local_scripts_dir / self._get_client_filename()) if self.local_scripts_dir else None
112
- self.ui.success(script_path, local_path)
113
-
114
- # Calculate estimated cost if we have usage data
115
- if self.usage_metadata:
116
- input_tokens = self.usage_metadata.get("input_tokens", 0)
117
- output_tokens = self.usage_metadata.get("output_tokens", 0)
118
- cache_creation_tokens = self.usage_metadata.get("cache_creation_input_tokens", 0)
119
- cache_read_tokens = self.usage_metadata.get("cache_read_input_tokens", 0)
120
-
121
- from .pricing import calculate_cost
122
-
123
- cost = calculate_cost(
124
- model_id=self.model,
125
- input_tokens=input_tokens,
126
- output_tokens=output_tokens,
127
- cache_creation_tokens=cache_creation_tokens,
128
- cache_read_tokens=cache_read_tokens,
129
- )
130
- self.usage_metadata["estimated_cost_usd"] = cost
131
-
132
- self.ui.console.print(" [dim]Usage:[/dim]")
133
- if input_tokens > 0:
134
- self.ui.console.print(f" [dim] input: {input_tokens:,} tokens[/dim]")
135
- if cache_creation_tokens > 0:
136
- self.ui.console.print(f" [dim] cache creation: {cache_creation_tokens:,} tokens[/dim]")
137
- if cache_read_tokens > 0:
138
- self.ui.console.print(f" [dim] cache read: {cache_read_tokens:,} tokens[/dim]")
139
- if output_tokens > 0:
140
- self.ui.console.print(f" [dim] output: {output_tokens:,} tokens[/dim]")
141
- self.ui.console.print(f" [dim] total cost: ${cost:.4f}[/dim]")
142
-
143
- final_result = {
144
- "script_path": script_path,
145
- "usage": self.usage_metadata,
146
- }
147
- self.message_store.save_result(final_result)
58
+ async with ClaudeSDKClient(options=options) as client:
59
+ await client.query(self._build_analysis_prompt())
60
+
61
+ async for message in client.receive_response():
62
+ if hasattr(message, "usage") and isinstance(message.usage, dict):
63
+ self.usage_metadata.update(message.usage)
64
+
65
+ if isinstance(message, AssistantMessage):
66
+ last_tool_name = None
67
+ for block in message.content:
68
+ if isinstance(block, ToolUseBlock):
69
+ last_tool_name = block.name
70
+ self.ui.tool_start(block.name, block.input)
71
+ self.message_store.save_tool_start(block.name, block.input)
72
+ elif isinstance(block, ToolResultBlock):
73
+ is_error = block.is_error if block.is_error else False
74
+
75
+ output = None
76
+ if hasattr(block, "content"):
77
+ output = block.content
78
+ elif hasattr(block, "result"):
79
+ output = block.result
80
+ elif hasattr(block, "output"):
81
+ output = block.output
82
+
83
+ tool_name = last_tool_name or "Tool"
84
+ self.ui.tool_result(tool_name, is_error, output)
85
+ self.message_store.save_tool_result(tool_name, is_error, str(output) if output else None)
86
+ elif isinstance(block, TextBlock):
87
+ self.ui.thinking(block.text)
88
+ self.message_store.save_thinking(block.text)
89
+
90
+ elif isinstance(message, ResultMessage):
91
+ if message.is_error:
92
+ self.ui.error(message.result or "Unknown error")
93
+ self.message_store.save_error(message.result or "Unknown error")
94
+ return None
95
+ else:
96
+ script_path = str(self.scripts_dir / self._get_client_filename())
97
+ local_path = str(self.local_scripts_dir / self._get_client_filename()) if self.local_scripts_dir else None
98
+ self.ui.success(script_path, local_path)
99
+
100
+ if self.usage_metadata:
101
+ input_tokens = self.usage_metadata.get("input_tokens", 0)
102
+ output_tokens = self.usage_metadata.get("output_tokens", 0)
103
+ cache_creation_tokens = self.usage_metadata.get("cache_creation_input_tokens", 0)
104
+ cache_read_tokens = self.usage_metadata.get("cache_read_input_tokens", 0)
105
+
106
+ from .pricing import calculate_cost
107
+
108
+ cost = calculate_cost(
109
+ model_id=self.model,
110
+ input_tokens=input_tokens,
111
+ output_tokens=output_tokens,
112
+ cache_creation_tokens=cache_creation_tokens,
113
+ cache_read_tokens=cache_read_tokens,
114
+ )
115
+ self.usage_metadata["estimated_cost_usd"] = cost
116
+
117
+ self.ui.console.print(" [dim]Usage:[/dim]")
118
+ if input_tokens > 0:
119
+ self.ui.console.print(f" [dim] input: {input_tokens:,} tokens[/dim]")
120
+ if cache_creation_tokens > 0:
121
+ self.ui.console.print(f" [dim] cache creation: {cache_creation_tokens:,} tokens[/dim]")
122
+ if cache_read_tokens > 0:
123
+ self.ui.console.print(f" [dim] cache read: {cache_read_tokens:,} tokens[/dim]")
124
+ if output_tokens > 0:
125
+ self.ui.console.print(f" [dim] output: {output_tokens:,} tokens[/dim]")
126
+ self.ui.console.print(f" [dim] total cost: ${cost:.4f}[/dim]")
127
+
128
+ result: dict[str, Any] = {
129
+ "script_path": script_path,
130
+ "usage": self.usage_metadata,
131
+ }
132
+ self.message_store.save_result(result)
133
+ return result
148
134
 
149
135
  except Exception as e:
150
136
  self.ui.error(str(e))
@@ -152,7 +138,7 @@ class ClaudeEngineer(BaseEngineer):
152
138
  self.ui.console.print("\n[dim]Make sure Claude Code CLI is installed: npm install -g @anthropic-ai/claude-code[/dim]")
153
139
  return None
154
140
 
155
- return final_result
141
+ return None
156
142
 
157
143
 
158
144
  # Keep old class name for backwards compatibility
@@ -2158,7 +2158,7 @@ wheels = [
2158
2158
 
2159
2159
  [[package]]
2160
2160
  name = "reverse-api-engineer"
2161
- version = "0.4.3"
2161
+ version = "0.4.4"
2162
2162
  source = { editable = "." }
2163
2163
  dependencies = [
2164
2164
  { name = "aiohttp" },