wcgw 2.8.7__tar.gz → 2.8.10__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.

Potentially problematic release.


This version of wcgw might be problematic. Click here for more details.

Files changed (170) hide show
  1. {wcgw-2.8.7 → wcgw-2.8.10}/PKG-INFO +1 -1
  2. {wcgw-2.8.7 → wcgw-2.8.10}/pyproject.toml +1 -1
  3. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/types.py +1 -1
  4. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/tools.py +43 -6
  5. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/anthropic_client.py +82 -10
  6. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_initialize.py +75 -0
  7. {wcgw-2.8.7 → wcgw-2.8.10}/uv.lock +1 -1
  8. {wcgw-2.8.7 → wcgw-2.8.10}/.github/workflows/python-publish.yml +0 -0
  9. {wcgw-2.8.7 → wcgw-2.8.10}/.github/workflows/python-tests.yml +0 -0
  10. {wcgw-2.8.7 → wcgw-2.8.10}/.github/workflows/python-types.yml +0 -0
  11. {wcgw-2.8.7 → wcgw-2.8.10}/.gitignore +0 -0
  12. {wcgw-2.8.7 → wcgw-2.8.10}/.gitmodules +0 -0
  13. {wcgw-2.8.7 → wcgw-2.8.10}/.python-version +0 -0
  14. {wcgw-2.8.7 → wcgw-2.8.10}/.vscode/settings.json +0 -0
  15. {wcgw-2.8.7 → wcgw-2.8.10}/Dockerfile +0 -0
  16. {wcgw-2.8.7 → wcgw-2.8.10}/LICENSE +0 -0
  17. {wcgw-2.8.7 → wcgw-2.8.10}/README.md +0 -0
  18. {wcgw-2.8.7 → wcgw-2.8.10}/gpt_action_json_schema.json +0 -0
  19. {wcgw-2.8.7 → wcgw-2.8.10}/gpt_instructions.txt +0 -0
  20. {wcgw-2.8.7 → wcgw-2.8.10}/openai.md +0 -0
  21. {wcgw-2.8.7 → wcgw-2.8.10}/src/__init__.py +0 -0
  22. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.git +0 -0
  23. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  25. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/workflows/main-checks.yml +0 -0
  26. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/workflows/publish-pypi.yml +0 -0
  27. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/workflows/pull-request-checks.yml +0 -0
  28. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.github/workflows/shared.yml +0 -0
  29. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.gitignore +0 -0
  30. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/.python-version +0 -0
  31. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/CODE_OF_CONDUCT.md +0 -0
  32. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/CONTRIBUTING.md +0 -0
  33. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/LICENSE +0 -0
  34. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/README.md +0 -0
  35. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/RELEASE.md +0 -0
  36. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/SECURITY.md +0 -0
  37. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/README.md +0 -0
  38. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/.python-version +0 -0
  39. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/README.md +0 -0
  40. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/__init__.py +0 -0
  41. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/__main__.py +0 -0
  42. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/mcp_simple_prompt/server.py +0 -0
  43. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-prompt/pyproject.toml +0 -0
  44. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/.python-version +0 -0
  45. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/README.md +0 -0
  46. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/__init__.py +0 -0
  47. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/__main__.py +0 -0
  48. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/mcp_simple_resource/server.py +0 -0
  49. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-resource/pyproject.toml +0 -0
  50. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/.python-version +0 -0
  51. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/README.md +0 -0
  52. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/__init__.py +0 -0
  53. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/__main__.py +0 -0
  54. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/mcp_simple_tool/server.py +0 -0
  55. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/examples/servers/simple-tool/pyproject.toml +0 -0
  56. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/pyproject.toml +0 -0
  57. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/__init__.py +0 -0
  58. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/client/__init__.py +0 -0
  59. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/client/__main__.py +0 -0
  60. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/client/session.py +0 -0
  61. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/client/sse.py +0 -0
  62. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/client/stdio.py +0 -0
  63. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/py.typed +0 -0
  64. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/__init__.py +0 -0
  65. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/__main__.py +0 -0
  66. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/models.py +0 -0
  67. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/session.py +0 -0
  68. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/sse.py +0 -0
  69. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/stdio.py +0 -0
  70. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/server/websocket.py +0 -0
  71. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/__init__.py +0 -0
  72. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/context.py +0 -0
  73. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/exceptions.py +0 -0
  74. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/memory.py +0 -0
  75. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/progress.py +0 -0
  76. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/session.py +0 -0
  77. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/src/mcp_wcgw/shared/version.py +0 -0
  78. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/__init__.py +0 -0
  79. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/client/__init__.py +0 -0
  80. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/client/test_session.py +0 -0
  81. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/client/test_stdio.py +0 -0
  82. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/conftest.py +0 -0
  83. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/server/__init__.py +0 -0
  84. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/server/test_session.py +0 -0
  85. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/server/test_stdio.py +0 -0
  86. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/shared/test_memory.py +0 -0
  87. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/tests/test_types.py +0 -0
  88. {wcgw-2.8.7 → wcgw-2.8.10}/src/mcp_wcgw_fork/uv.lock +0 -0
  89. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/__init__.py +0 -0
  90. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/__init__.py +0 -0
  91. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/common.py +0 -0
  92. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/computer_use.py +0 -0
  93. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/diff-instructions.txt +0 -0
  94. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/file_ops/diff_edit.py +0 -0
  95. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/file_ops/search_replace.py +0 -0
  96. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/mcp_server/Readme.md +0 -0
  97. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/mcp_server/__init__.py +0 -0
  98. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/mcp_server/server.py +0 -0
  99. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/memory.py +0 -0
  100. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/modes.py +0 -0
  101. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/repo_ops/display_tree.py +0 -0
  102. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/repo_ops/path_prob.py +0 -0
  103. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/repo_ops/paths_model.vocab +0 -0
  104. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/repo_ops/paths_tokens.model +0 -0
  105. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/repo_ops/repo_context.py +0 -0
  106. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/client/sys_utils.py +0 -0
  107. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/relay/serve.py +0 -0
  108. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/relay/static/privacy.txt +0 -0
  109. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw/types_.py +0 -0
  110. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/__init__.py +0 -0
  111. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/__main__.py +0 -0
  112. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/cli.py +0 -0
  113. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/openai_client.py +0 -0
  114. {wcgw-2.8.7 → wcgw-2.8.10}/src/wcgw_cli/openai_utils.py +0 -0
  115. {wcgw-2.8.7 → wcgw-2.8.10}/static/claude-ss.jpg +0 -0
  116. {wcgw-2.8.7 → wcgw-2.8.10}/static/computer-use.jpg +0 -0
  117. {wcgw-2.8.7 → wcgw-2.8.10}/static/example.jpg +0 -0
  118. {wcgw-2.8.7 → wcgw-2.8.10}/static/rocket-icon.png +0 -0
  119. {wcgw-2.8.7 → wcgw-2.8.10}/static/ss1.png +0 -0
  120. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/file_ops/test_diff_edit.py +0 -0
  121. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/file_ops/test_search_replace.py +0 -0
  122. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/repo_ops/__init__.py +0 -0
  123. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/repo_ops/test_display_tree.py +0 -0
  124. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/repo_ops/test_display_tree_simple.py +0 -0
  125. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/repo_ops/test_path_prob.py +0 -0
  126. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/repo_ops/test_repo_context.py +0 -0
  127. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_anthropic_client_utils.py +0 -0
  128. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_memory.py +0 -0
  129. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_openai_utils.py +0 -0
  130. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_basic.py +0 -0
  131. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_extended.py +0 -0
  132. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_file_ops.py +0 -0
  133. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_files.py +0 -0
  134. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_shell.py +0 -0
  135. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/test_tools_validation.py +0 -0
  136. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/__init__.py +0 -0
  137. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_command_validation.py +0 -0
  138. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_docker_operations.py +0 -0
  139. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_error_handling.py +0 -0
  140. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_error_handling_full.py +0 -0
  141. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_execute_bash.py +0 -0
  142. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_file_errors.py +0 -0
  143. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_file_operations.py +0 -0
  144. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_files/test1.py +0 -0
  145. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_files/test2.py +0 -0
  146. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_files/test_file.py +0 -0
  147. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_full_coverage.py +0 -0
  148. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_is_int.py +0 -0
  149. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_knowledge_transfer.py +0 -0
  150. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_large_blocks.py +0 -0
  151. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_prompt_update.py +0 -0
  152. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_render_terminal.py +0 -0
  153. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_terminal_output.py +0 -0
  154. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_terminal_output_full.py +0 -0
  155. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_terminal_output_incremental.py +0 -0
  156. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_terminal_output_raw.py +0 -0
  157. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_terminal_sequence.py +0 -0
  158. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_timeout_handling.py +0 -0
  159. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_timeout_state.py +0 -0
  160. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_user_interaction.py +0 -0
  161. {wcgw-2.8.7 → wcgw-2.8.10}/tests/client/tools/test_write_file.py +0 -0
  162. {wcgw-2.8.7 → wcgw-2.8.10}/tests/conftest.py +0 -0
  163. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_anthropic_client.py +0 -0
  164. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_basic.py +0 -0
  165. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_common.py +0 -0
  166. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_computer_use.py +0 -0
  167. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_computer_use_base.py +0 -0
  168. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_computer_use_shell.py +0 -0
  169. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_sys_utils.py +0 -0
  170. {wcgw-2.8.7 → wcgw-2.8.10}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 2.8.7
3
+ Version: 2.8.10
4
4
  Summary: Shell and coding agent on claude and chatgpt
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  authors = [{ name = "Aman Rusia", email = "gapypi@arcfu.com" }]
3
3
  name = "wcgw"
4
- version = "2.8.7"
4
+ version = "2.8.10"
5
5
  description = "Shell and coding agent on claude and chatgpt"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11, <3.13"
@@ -336,7 +336,7 @@ class CancellationNotification(Notification):
336
336
  long-running request.
337
337
  """
338
338
 
339
- method: Literal["cancelled"]
339
+ method: Literal["notifications/cancelled"]
340
340
  params: CancelledParams
341
341
 
342
342
 
@@ -6,8 +6,10 @@ import importlib.metadata
6
6
  import json
7
7
  import mimetypes
8
8
  import os
9
+ import platform
9
10
  import re
10
11
  import shlex
12
+ import subprocess
11
13
  import time
12
14
  import traceback
13
15
  import uuid
@@ -133,19 +135,43 @@ def ask_confirmation(prompt: Confirmation) -> str:
133
135
  PROMPT_CONST = "#" + "@wcgw@#"
134
136
 
135
137
 
136
- def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: # type: ignore
138
+ def is_mac() -> bool:
139
+ return platform.system() == "Darwin"
140
+
141
+
142
+ def get_tmpdir() -> str:
143
+ current_tmpdir = os.environ.get("TMPDIR", "")
144
+ if current_tmpdir or not is_mac():
145
+ return current_tmpdir
146
+ try:
147
+ # Fix issue while running ocrmypdf -> tesseract -> leptonica, set TMPDIR
148
+ # https://github.com/tesseract-ocr/tesseract/issues/4333
149
+ result = subprocess.check_output(
150
+ ["getconf", "DARWIN_USER_TEMP_DIR"],
151
+ text=True,
152
+ ).strip()
153
+ return result
154
+ except subprocess.CalledProcessError:
155
+ return "//tmp"
156
+ except Exception:
157
+ return ""
158
+
159
+
160
+ def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: # type: ignore[type-arg]
137
161
  cmd = "/bin/bash"
138
162
  if is_restricted_mode:
139
163
  cmd += " -r"
140
164
 
165
+ overrideenv = {**os.environ, "PS1": PROMPT_CONST, "TMPDIR": get_tmpdir()}
141
166
  try:
142
167
  shell = pexpect.spawn(
143
168
  cmd,
144
- env={**os.environ, **{"PS1": PROMPT_CONST}}, # type: ignore[arg-type]
169
+ env=overrideenv, # type: ignore[arg-type]
145
170
  echo=False,
146
171
  encoding="utf-8",
147
172
  timeout=TIMEOUT,
148
173
  cwd=initial_dir,
174
+ codec_errors="backslashreplace",
149
175
  )
150
176
  shell.sendline(
151
177
  f"export PROMPT_COMMAND= PS1={PROMPT_CONST}"
@@ -157,10 +183,11 @@ def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: #
157
183
 
158
184
  shell = pexpect.spawn(
159
185
  "/bin/bash --noprofile --norc",
160
- env={**os.environ, **{"PS1": PROMPT_CONST}}, # type: ignore[arg-type]
186
+ env=overrideenv, # type: ignore[arg-type]
161
187
  echo=False,
162
188
  encoding="utf-8",
163
189
  timeout=TIMEOUT,
190
+ codec_errors="backslashreplace",
164
191
  )
165
192
  shell.sendline(f"export PS1={PROMPT_CONST}")
166
193
  shell.expect(PROMPT_CONST, timeout=TIMEOUT)
@@ -256,7 +283,9 @@ class BashState:
256
283
  before = "\n".join(before_lines).strip()
257
284
  counts += 1
258
285
  if counts > 100:
259
- raise ValueError("Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it")
286
+ raise ValueError(
287
+ "Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it"
288
+ )
260
289
 
261
290
  try:
262
291
  return int(before)
@@ -273,7 +302,7 @@ class BashState:
273
302
  self._bash_command_mode.bash_mode == "restricted_mode",
274
303
  self._cwd,
275
304
  )
276
-
305
+
277
306
  self._pending_output = ""
278
307
 
279
308
  # Get exit info to ensure shell is ready
@@ -414,7 +443,9 @@ class BashState:
414
443
  index = self.shell.expect([self._prompt, pexpect.TIMEOUT], timeout=0.2)
415
444
  counts += 1
416
445
  if counts > 100:
417
- raise ValueError("Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it")
446
+ raise ValueError(
447
+ "Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it"
448
+ )
418
449
  console.print(f"Prompt updated to: {self._prompt}")
419
450
  return True
420
451
  return False
@@ -457,6 +488,12 @@ def initialize(
457
488
  folder_to_start = None
458
489
  if any_workspace_path:
459
490
  if os.path.exists(any_workspace_path):
491
+ if os.path.isfile(any_workspace_path):
492
+ # Set any_workspace_path to the directory containing the file
493
+ # Add the file to read_files_ only if empty to avoid duplicates
494
+ if not read_files_:
495
+ read_files_ = [any_workspace_path]
496
+ any_workspace_path = os.path.dirname(any_workspace_path)
460
497
  repo_context, folder_to_start = get_repo_context(any_workspace_path, 200)
461
498
 
462
499
  repo_context = f"---\n# Workspace structure\n{repo_context}\n---\n"
@@ -14,15 +14,17 @@ from anthropic import Anthropic
14
14
  from anthropic.types import (
15
15
  ImageBlockParam,
16
16
  MessageParam,
17
+ ModelParam,
17
18
  TextBlockParam,
18
19
  ToolParam,
19
20
  ToolResultBlockParam,
20
21
  ToolUseBlockParam,
21
22
  )
22
23
  from dotenv import load_dotenv
24
+ from pydantic import BaseModel
23
25
  from typer import Typer
24
26
 
25
- from wcgw.client.common import discard_input
27
+ from wcgw.client.common import CostData, discard_input
26
28
  from wcgw.client.memory import load_memory
27
29
  from wcgw.client.tools import (
28
30
  DoneFlag,
@@ -47,6 +49,14 @@ from wcgw.types_ import (
47
49
  WriteIfEmpty,
48
50
  )
49
51
 
52
+
53
+ class Config(BaseModel):
54
+ model: ModelParam
55
+ cost_limit: float
56
+ cost_file: dict[ModelParam, CostData]
57
+ cost_unit: str = "$"
58
+
59
+
50
60
  History = list[MessageParam]
51
61
 
52
62
 
@@ -150,7 +160,51 @@ def loop(
150
160
  first_message = ""
151
161
  waiting_for_assistant = history[-1]["role"] != "assistant"
152
162
 
153
- limit = 1
163
+ config = Config(
164
+ model="claude-3-5-sonnet-20241022",
165
+ cost_limit=0.1,
166
+ cost_unit="$",
167
+ cost_file={
168
+ # Claude 3.5 Haiku
169
+ "claude-3-5-haiku-latest": CostData(
170
+ cost_per_1m_input_tokens=0.80, cost_per_1m_output_tokens=4
171
+ ),
172
+ "claude-3-5-haiku-20241022": CostData(
173
+ cost_per_1m_input_tokens=0.80, cost_per_1m_output_tokens=4
174
+ ),
175
+ # Claude 3.5 Sonnet
176
+ "claude-3-5-sonnet-latest": CostData(
177
+ cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
178
+ ),
179
+ "claude-3-5-sonnet-20241022": CostData(
180
+ cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
181
+ ),
182
+ "claude-3-5-sonnet-20240620": CostData(
183
+ cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
184
+ ),
185
+ # Claude 3 Opus
186
+ "claude-3-opus-latest": CostData(
187
+ cost_per_1m_input_tokens=15.0, cost_per_1m_output_tokens=75.0
188
+ ),
189
+ "claude-3-opus-20240229": CostData(
190
+ cost_per_1m_input_tokens=15.0, cost_per_1m_output_tokens=75.0
191
+ ),
192
+ # Legacy Models
193
+ "claude-3-haiku-20240307": CostData(
194
+ cost_per_1m_input_tokens=0.25, cost_per_1m_output_tokens=1.25
195
+ ),
196
+ "claude-2.1": CostData(
197
+ cost_per_1m_input_tokens=8.0, cost_per_1m_output_tokens=24.0
198
+ ),
199
+ "claude-2.0": CostData(
200
+ cost_per_1m_input_tokens=8.0, cost_per_1m_output_tokens=24.0
201
+ ),
202
+ },
203
+ )
204
+
205
+ if limit is not None:
206
+ config.cost_limit = limit
207
+ limit = config.cost_limit
154
208
 
155
209
  tools = [
156
210
  ToolParam(
@@ -321,9 +375,15 @@ Saves provided description and file contents of all the relevant file paths or g
321
375
  while True:
322
376
  if cost > limit:
323
377
  system_console.print(
324
- f"\nCost limit exceeded. Current cost: {cost}, input tokens: {input_toks}, output tokens: {output_toks}"
378
+ f"\nCost limit exceeded. Current cost: {config.cost_unit}{cost:.4f}, "
379
+ f"input tokens: {input_toks}"
380
+ f"output tokens: {output_toks}"
325
381
  )
326
382
  break
383
+ else:
384
+ system_console.print(
385
+ f"\nTotal cost: {config.cost_unit}{cost:.4f}, input tokens: {input_toks}, output tokens: {output_toks}"
386
+ )
327
387
 
328
388
  if not waiting_for_assistant:
329
389
  if first_message:
@@ -335,13 +395,8 @@ Saves provided description and file contents of all the relevant file paths or g
335
395
  history.append(parse_user_message_special(msg))
336
396
  else:
337
397
  waiting_for_assistant = False
338
-
339
- cost_, input_toks_ = 0, 0
340
- cost += cost_
341
- input_toks += input_toks_
342
-
343
398
  stream = client.messages.stream(
344
- model="claude-3-5-sonnet-20241022",
399
+ model=config.model,
345
400
  messages=history,
346
401
  tools=tools,
347
402
  max_tokens=8096,
@@ -361,7 +416,24 @@ Saves provided description and file contents of all the relevant file paths or g
361
416
  with stream as stream_:
362
417
  for chunk in stream_:
363
418
  type_ = chunk.type
364
- if type_ in {"message_start", "message_stop"}:
419
+ if type_ == "message_start":
420
+ message_start = chunk.message
421
+ # Update cost based on token usage from the API response
422
+ input_tokens = message_start.usage.input_tokens
423
+ input_toks += input_tokens
424
+ cost += (
425
+ input_tokens
426
+ * config.cost_file[config.model].cost_per_1m_input_tokens
427
+ ) / 1_000_000
428
+ elif type_ == "message_stop":
429
+ message_stop = chunk.message
430
+ # Update cost based on output tokens
431
+ output_tokens = message_stop.usage.output_tokens
432
+ output_toks += output_tokens
433
+ cost += (
434
+ output_tokens
435
+ * config.cost_file[config.model].cost_per_1m_output_tokens
436
+ ) / 1_000_000
365
437
  continue
366
438
  elif type_ == "content_block_start" and hasattr(
367
439
  chunk, "content_block"
@@ -248,3 +248,78 @@ class TestInitialize(unittest.TestCase):
248
248
 
249
249
  # Verify task memory was still loaded despite state error
250
250
  self.assertIn("Following is the retrieved task:\ntest_memory", result)
251
+
252
+
253
+ def test_workspace_path_is_file(self):
254
+ """Test initialize when workspace path points to a file"""
255
+ # Create a test file
256
+ test_file = os.path.join(self.test_workspace, "test.py")
257
+ os.makedirs(os.path.dirname(test_file), exist_ok=True)
258
+ with open(test_file, "w") as f:
259
+ f.write("print('test')")
260
+
261
+ with (
262
+ patch("os.path.exists") as mock_exists,
263
+ patch("os.path.isfile") as mock_isfile,
264
+ patch("wcgw.client.tools.get_repo_context") as mock_get_context,
265
+ patch("wcgw.client.tools.read_files") as mock_read_files,
266
+ ):
267
+ mock_exists.return_value = True
268
+ mock_isfile.return_value = True
269
+ mock_get_context.return_value = (self.repo_context, os.path.dirname(test_file))
270
+ mock_read_files.return_value = f"``` {test_file}print('test')```"
271
+
272
+ # Call initialize with file path and no read_files_
273
+ result = initialize(
274
+ any_workspace_path=test_file,
275
+ read_files_=[],
276
+ task_id_to_resume="",
277
+ max_tokens=None,
278
+ mode=Modes.wcgw,
279
+ )
280
+
281
+ # Verify read_files was called with the file path
282
+ mock_read_files.assert_called_once_with([test_file], None)
283
+
284
+ # Verify repo context uses parent directory
285
+ mock_get_context.assert_called_once_with(os.path.dirname(test_file), 200)
286
+
287
+ # Verify file content is in output
288
+ self.assertIn(f"``` {test_file}print('test')", result)
289
+
290
+ # Verify cwd
291
+ self.assertIn(f"Initialized in directory (also cwd): {os.path.dirname(test_file)}\n", result)
292
+
293
+ def test_workspace_path_is_file_with_read_files(self):
294
+ """Test initialize when workspace path points to a file and read_files_ is provided"""
295
+ test_file = os.path.join(self.test_workspace, "test.py")
296
+ extra_file = os.path.join(self.test_workspace, "extra.py")
297
+
298
+ with (
299
+ patch("os.path.exists") as mock_exists,
300
+ patch("os.path.isfile") as mock_isfile,
301
+ patch("wcgw.client.tools.get_repo_context") as mock_get_context,
302
+ patch("wcgw.client.tools.read_files") as mock_read_files,
303
+ ):
304
+ mock_exists.return_value = True
305
+ mock_isfile.return_value = True
306
+ mock_get_context.return_value = (self.repo_context, os.path.dirname(test_file))
307
+ mock_read_files.return_value = f"``` {extra_file}extra content```"
308
+
309
+ # Call initialize with file path and explicit read_files_
310
+ result = initialize(
311
+ any_workspace_path=test_file,
312
+ read_files_=[extra_file],
313
+ task_id_to_resume="",
314
+ max_tokens=None,
315
+ mode=Modes.wcgw,
316
+ )
317
+
318
+ # Verify read_files was called with the provided read_files_ only
319
+ mock_read_files.assert_called_once_with([extra_file], None)
320
+
321
+ # Verify repo context uses parent directory
322
+ mock_get_context.assert_called_once_with(os.path.dirname(test_file), 200)
323
+
324
+ # Verify extra file content is in output
325
+ self.assertIn(f"``` {extra_file}extra content", result)
@@ -1042,7 +1042,7 @@ wheels = [
1042
1042
 
1043
1043
  [[package]]
1044
1044
  name = "wcgw"
1045
- version = "2.8.6"
1045
+ version = "2.8.9"
1046
1046
  source = { editable = "." }
1047
1047
  dependencies = [
1048
1048
  { name = "anthropic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes