forge-cpp-mcp 0.5.0__tar.gz → 0.5.1__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 (170) hide show
  1. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/PKG-INFO +16 -3
  2. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/README.md +15 -2
  3. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/process-details.js +3 -1
  4. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/app.js +3 -1
  5. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/result-view.js +1 -1
  6. forge_cpp_mcp-0.5.1/frontend/src/shared/tool-result.js +22 -0
  7. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/pyproject.toml +1 -1
  8. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/__init__.py +1 -1
  9. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/service.py +87 -37
  10. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/.gitignore +0 -0
  11. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/LICENSE +0 -0
  12. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/admin.html +0 -0
  13. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/clangd-result.html +0 -0
  14. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/cmake-build.html +0 -0
  15. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/cmake-configure.html +0 -0
  16. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/cmake-profiles.html +0 -0
  17. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/cmake-run-test.html +0 -0
  18. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/cmake-test.html +0 -0
  19. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/debugger-result.html +0 -0
  20. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/git-result.html +0 -0
  21. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/package-lock.json +0 -0
  22. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/package.json +0 -0
  23. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/process-details.html +0 -0
  24. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/process-overview.html +0 -0
  25. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/sandbox.html +0 -0
  26. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin-form.js +0 -0
  27. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin-resource.js +0 -0
  28. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin-sandbox.js +0 -0
  29. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin-widget.js +0 -0
  30. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin.css +0 -0
  31. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/admin.js +0 -0
  32. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/clangd-navigation-view.js +0 -0
  33. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/clangd-result.js +0 -0
  34. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/clangd-view.js +0 -0
  35. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-build.js +0 -0
  36. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-configure.js +0 -0
  37. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-profiles.js +0 -0
  38. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-run-test.js +0 -0
  39. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-test.js +0 -0
  40. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/cmake-view.js +0 -0
  41. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/debugger-result.js +0 -0
  42. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/git-result.js +0 -0
  43. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/git-view.js +0 -0
  44. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/git.css +0 -0
  45. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/process-overview.js +0 -0
  46. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/process-status.js +0 -0
  47. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/process.css +0 -0
  48. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/copy-icon.js +0 -0
  49. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/presentation.js +0 -0
  50. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/result-resources.js +0 -0
  51. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/shared/widget.css +0 -0
  52. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/source-view.js +0 -0
  53. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/toolsets.js +0 -0
  54. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/workspace-file.js +0 -0
  55. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/workspace-result.js +0 -0
  56. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/workspace-search.js +0 -0
  57. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/workspace-tree.js +0 -0
  58. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/src/workspace-view.js +0 -0
  59. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/tests/admin-widget.test.js +0 -0
  60. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/tests/result-resources.test.js +0 -0
  61. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/tests/result-view.test.js +0 -0
  62. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/tests/workspace.test.js +0 -0
  63. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/toolsets.html +0 -0
  64. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/vite.config.js +0 -0
  65. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/workspace-file.html +0 -0
  66. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/workspace-result.html +0 -0
  67. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/workspace-search.html +0 -0
  68. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/frontend/workspace-tree.html +0 -0
  69. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/hatch_build.py +0 -0
  70. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/admin/__init__.py +0 -0
  71. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/admin/errors.py +0 -0
  72. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/admin/history.py +0 -0
  73. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/admin/instance.py +0 -0
  74. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/admin/service.py +0 -0
  75. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/assets.py +0 -0
  76. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/__init__.py +0 -0
  77. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/errors.py +0 -0
  78. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/models.py +0 -0
  79. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/service.py +0 -0
  80. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/session.py +0 -0
  81. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/clangd/text.py +0 -0
  82. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/__init__.py +0 -0
  83. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/build_tree.py +0 -0
  84. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/errors.py +0 -0
  85. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/file_api.py +0 -0
  86. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/models.py +0 -0
  87. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/profiles.py +0 -0
  88. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/service.py +0 -0
  89. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/text.py +0 -0
  90. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/cmake/workspace.py +0 -0
  91. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/completion.py +0 -0
  92. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/__init__.py +0 -0
  93. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/errors.py +0 -0
  94. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/lldb_dap.py +0 -0
  95. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/models.py +0 -0
  96. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/service.py +0 -0
  97. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/session.py +0 -0
  98. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/debugger/text.py +0 -0
  99. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/diagnostics.py +0 -0
  100. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/__init__.py +0 -0
  101. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/errors.py +0 -0
  102. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/models.py +0 -0
  103. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/parsers.py +0 -0
  104. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/git/text.py +0 -0
  105. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/clangd.LICENSE +0 -0
  106. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/clangd.svg +0 -0
  107. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/cmake.svg +0 -0
  108. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/debugger.svg +0 -0
  109. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/git.svg +0 -0
  110. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/process.svg +0 -0
  111. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/toolchain.svg +0 -0
  112. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/workspace-edit.svg +0 -0
  113. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/workspace-file.svg +0 -0
  114. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/workspace-search.svg +0 -0
  115. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/icons/workspace.svg +0 -0
  116. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/local_ipc/__init__.py +0 -0
  117. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/local_ipc/errors.py +0 -0
  118. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/local_ipc/service.py +0 -0
  119. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/local_ipc/unix.py +0 -0
  120. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/local_ipc/windows.py +0 -0
  121. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/markdown.py +0 -0
  122. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/__init__.py +0 -0
  123. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/errors.py +0 -0
  124. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/host.py +0 -0
  125. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/models.py +0 -0
  126. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/service.py +0 -0
  127. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/text.py +0 -0
  128. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/process/transcript.py +0 -0
  129. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/progress.py +0 -0
  130. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/server.py +0 -0
  131. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/text.py +0 -0
  132. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/__init__.py +0 -0
  133. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/discovery.py +0 -0
  134. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/errors.py +0 -0
  135. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/loader.py +0 -0
  136. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/providers/__init__.py +0 -0
  137. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/providers/system.py +0 -0
  138. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/providers/user.py +0 -0
  139. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/providers/visual_studio.py +0 -0
  140. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/service.py +0 -0
  141. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/spec.py +0 -0
  142. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/__init__.py +0 -0
  143. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/clang.py +0 -0
  144. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/clang_cl.py +0 -0
  145. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/clangd.py +0 -0
  146. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/clangxx.py +0 -0
  147. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/cmake.py +0 -0
  148. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/cppvsdbg.py +0 -0
  149. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/ctest.py +0 -0
  150. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/gcc.py +0 -0
  151. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/gdb.py +0 -0
  152. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/git.py +0 -0
  153. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/gxx.py +0 -0
  154. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/link.py +0 -0
  155. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/lld.py +0 -0
  156. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/lld_link.py +0 -0
  157. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/lldb_dap.py +0 -0
  158. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/make.py +0 -0
  159. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/msbuild.py +0 -0
  160. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/msvc.py +0 -0
  161. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/toolchain/tools/ninja.py +0 -0
  162. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/__init__.py +0 -0
  163. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/diff.py +0 -0
  164. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/errors.py +0 -0
  165. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/metadata.py +0 -0
  166. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/models.py +0 -0
  167. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/path.py +0 -0
  168. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/providers.py +0 -0
  169. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/service.py +0 -0
  170. {forge_cpp_mcp-0.5.0 → forge_cpp_mcp-0.5.1}/src/forgemcp/workspace/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: forge-cpp-mcp
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: A safe MCP server for structured C++ project development workflows.
5
5
  Project-URL: Homepage, https://github.com/hardened-steel/ForgeMCP
6
6
  Project-URL: Repository, https://github.com/hardened-steel/ForgeMCP
@@ -87,6 +87,12 @@ without making additional tool calls. Process times are readable to seconds; JSO
87
87
  copying preserve the original precision. A toolsets list shows summaries, while a
88
88
  `toolset_get` result shows the details returned by that call.
89
89
 
90
+ When a host restores chat history without `structuredContent`, widgets can recover
91
+ the result from a single complete JSON object retained in a text content block,
92
+ including a fenced JSON block. This supports older JSON tool responses; current
93
+ plain-text responses alone cannot restore the structured result. Linked result
94
+ resources still disappear when the server restarts.
95
+
90
96
  ForgeMCP can directly run an explicitly selected test executable inside the
91
97
  project or storage root; it does not expose shell commands or external executable
92
98
  paths over MCP. Feature services use
@@ -107,7 +113,7 @@ and install it in a virtual environment:
107
113
 
108
114
  ```powershell
109
115
  python -m venv .venv
110
- .\.venv\Scripts\python.exe -m pip install .\forge_cpp_mcp-0.5.0-py3-none-any.whl
116
+ .\.venv\Scripts\python.exe -m pip install .\forge_cpp_mcp-0.5.1-py3-none-any.whl
111
117
  .\.venv\Scripts\forgemcp.exe --help
112
118
  ```
113
119
 
@@ -120,7 +126,7 @@ The distribution name is `forge-cpp-mcp`; the Python module and server command a
120
126
  After the package is published to PyPI, the install command can instead be:
121
127
 
122
128
  ```powershell
123
- .\.venv\Scripts\python.exe -m pip install forge-cpp-mcp==0.5.0
129
+ .\.venv\Scripts\python.exe -m pip install forge-cpp-mcp==0.5.1
124
130
  ```
125
131
 
126
132
  ## Setup from source
@@ -673,6 +679,13 @@ CMake alone determines compilation-context eligibility and notifies clangd throu
673
679
  its existing subscription. Source notifications combine Git dirty paths and
674
680
  committed tree changes; unknown changes use conservative invalidation. No external
675
681
  file watcher or cross-instance execution queue is added.
682
+ Git snapshots the existence and types of tracked and nonignored untracked paths
683
+ before and after mutations, retaining previous paths and explicitly selected file
684
+ paths. Index/ref changes and commits, including `nothing to commit`, keep clangd
685
+ sessions when working-file structure and CMake inputs remain unchanged. Actual
686
+ path/type changes or an unknown outcome can invalidate contexts. Snapshots read no
687
+ source contents, include at most 100,000 distinct paths, and do not track ignored-only
688
+ changes or empty directories outside the catalog.
676
689
 
677
690
  Git output is bounded to 4 MiB per command; narrow the request when the limit is
678
691
  exceeded. Each command has a 300-second deadline and each request a 620-second
@@ -58,6 +58,12 @@ without making additional tool calls. Process times are readable to seconds; JSO
58
58
  copying preserve the original precision. A toolsets list shows summaries, while a
59
59
  `toolset_get` result shows the details returned by that call.
60
60
 
61
+ When a host restores chat history without `structuredContent`, widgets can recover
62
+ the result from a single complete JSON object retained in a text content block,
63
+ including a fenced JSON block. This supports older JSON tool responses; current
64
+ plain-text responses alone cannot restore the structured result. Linked result
65
+ resources still disappear when the server restarts.
66
+
61
67
  ForgeMCP can directly run an explicitly selected test executable inside the
62
68
  project or storage root; it does not expose shell commands or external executable
63
69
  paths over MCP. Feature services use
@@ -78,7 +84,7 @@ and install it in a virtual environment:
78
84
 
79
85
  ```powershell
80
86
  python -m venv .venv
81
- .\.venv\Scripts\python.exe -m pip install .\forge_cpp_mcp-0.5.0-py3-none-any.whl
87
+ .\.venv\Scripts\python.exe -m pip install .\forge_cpp_mcp-0.5.1-py3-none-any.whl
82
88
  .\.venv\Scripts\forgemcp.exe --help
83
89
  ```
84
90
 
@@ -91,7 +97,7 @@ The distribution name is `forge-cpp-mcp`; the Python module and server command a
91
97
  After the package is published to PyPI, the install command can instead be:
92
98
 
93
99
  ```powershell
94
- .\.venv\Scripts\python.exe -m pip install forge-cpp-mcp==0.5.0
100
+ .\.venv\Scripts\python.exe -m pip install forge-cpp-mcp==0.5.1
95
101
  ```
96
102
 
97
103
  ## Setup from source
@@ -644,6 +650,13 @@ CMake alone determines compilation-context eligibility and notifies clangd throu
644
650
  its existing subscription. Source notifications combine Git dirty paths and
645
651
  committed tree changes; unknown changes use conservative invalidation. No external
646
652
  file watcher or cross-instance execution queue is added.
653
+ Git snapshots the existence and types of tracked and nonignored untracked paths
654
+ before and after mutations, retaining previous paths and explicitly selected file
655
+ paths. Index/ref changes and commits, including `nothing to commit`, keep clangd
656
+ sessions when working-file structure and CMake inputs remain unchanged. Actual
657
+ path/type changes or an unknown outcome can invalidate contexts. Snapshots read no
658
+ source contents, include at most 100,000 distinct paths, and do not track ignored-only
659
+ changes or empty directories outside the catalog.
647
660
 
648
661
  Git output is bounded to 4 MiB per command; narrow the request when the limit is
649
662
  exceeded. Each command has a 300-second deadline and each request a 620-second
@@ -1,5 +1,6 @@
1
1
  import { App, PostMessageTransport, applyDocumentTheme, applyHostFonts, applyHostStyleVariables } from "@modelcontextprotocol/ext-apps";
2
2
  import { timestamp } from "./shared/presentation.js";
3
+ import { restoreStructuredContent } from "./shared/tool-result.js";
3
4
  import { processStatus } from "./process-status.js";
4
5
  import "./shared/widget.css";
5
6
  import "./process.css";
@@ -170,7 +171,8 @@ const contextChanged = (context) => {
170
171
  }
171
172
  };
172
173
  app.addEventListener("toolinput", () => { details = undefined; render(); footer.textContent = "Reading process…"; });
173
- app.addEventListener("toolresult", (result) => {
174
+ app.addEventListener("toolresult", (message) => {
175
+ const result = restoreStructuredContent(message);
174
176
  if (result?.isError || !result?.structuredContent) {
175
177
  footer.textContent = "Process result unavailable.";
176
178
  return;
@@ -1,6 +1,7 @@
1
1
  import { App, PostMessageTransport, applyDocumentTheme, applyHostFonts, applyHostStyleVariables } from "@modelcontextprotocol/ext-apps";
2
2
  import { createResultView } from "./result-view.js";
3
3
  import { loadResultClangd, loadResultDiff, loadResultSkippedFiles } from "./result-resources.js";
4
+ import { restoreStructuredContent } from "./tool-result.js";
4
5
  import "./widget.css";
5
6
 
6
7
  /** Lifecycle bridge and immutable result-resource loading; never calls tools. */
@@ -27,7 +28,8 @@ export async function connectWidget({ toolName, describe, renderValue }) {
27
28
  };
28
29
  app.addEventListener("toolinput", invalidate(view.pending));
29
30
  app.addEventListener("toolinputpartial", invalidate(view.pending));
30
- app.addEventListener("toolresult", async (result) => {
31
+ app.addEventListener("toolresult", async (message) => {
32
+ const result = restoreStructuredContent(message);
31
33
  const current = ++generation;
32
34
  view.receive(result);
33
35
  const data = result?.structuredContent;
@@ -346,7 +346,7 @@ export function createResultView(root, { toolName, describe, renderValue }) {
346
346
  function receive(result) {
347
347
  if (disposed) return;
348
348
  clear("");
349
- if (!result || result.structuredContent === undefined || result.isError) {
349
+ if (!result || result.structuredContent == null || result.isError) {
350
350
  const texts = (result?.content ?? []).filter((item) => item.type === "text").map((item) => item.text);
351
351
  const message = result?.isError ? "Tool returned an error." : "No structured result was provided.";
352
352
  content.append(element("p", "fm-empty", message));
@@ -0,0 +1,22 @@
1
+ import { isObject } from "./presentation.js";
2
+
3
+ /** Recover one complete JSON object when a host replays only text content. */
4
+ export function restoreStructuredContent(result) {
5
+ if (!result || result.isError || result.structuredContent != null) return result;
6
+ let recovered;
7
+ for (const item of result.content ?? []) {
8
+ if (item.type !== "text" || typeof item.text !== "string") continue;
9
+ const text = item.text.trim();
10
+ const fenced = /^```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n```$/i.exec(text);
11
+ let value;
12
+ try {
13
+ value = JSON.parse(fenced ? fenced[1] : text);
14
+ } catch {
15
+ continue;
16
+ }
17
+ if (!isObject(value)) continue;
18
+ if (recovered !== undefined) return result;
19
+ recovered = value;
20
+ }
21
+ return recovered === undefined ? result : { ...result, structuredContent: recovered };
22
+ }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "forge-cpp-mcp"
7
- version = "0.5.0"
7
+ version = "0.5.1"
8
8
  description = "A safe MCP server for structured C++ project development workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.5.0"
5
+ __version__ = "0.5.1"
@@ -7,7 +7,8 @@ from __future__ import annotations
7
7
 
8
8
  import asyncio
9
9
  import re
10
- from collections.abc import AsyncGenerator, Awaitable, Callable, Sequence
10
+ import stat
11
+ from collections.abc import AsyncGenerator, Awaitable, Callable, Collection, Sequence
11
12
  from contextlib import asynccontextmanager
12
13
  from contextvars import ContextVar
13
14
  from dataclasses import dataclass, field
@@ -134,6 +135,7 @@ class GitService:
134
135
  COMMIT_URI = "forgemcp://git/commits/{revision}"
135
136
  COMMIT_FORMAT = "%H%x00%P%x00%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI%x00%B"
136
137
  MAX_BYTES = 4 * 1024 * 1024
138
+ MAX_SOURCE_PATHS = 100_000
137
139
 
138
140
  def __init__(
139
141
  self,
@@ -369,12 +371,13 @@ class GitService:
369
371
  self,
370
372
  arguments: Sequence[str],
371
373
  *,
372
- source_changes: bool = False,
374
+ source_paths: Sequence[WorkspacePath] = (),
373
375
  stdin: str | None = None,
374
376
  ) -> GitMutation:
375
377
  """Run a mutation once and reconcile source consumers even after partial failure."""
376
378
  before = await self.status()
377
- completed = False
379
+ sources = await self.source_snapshot(source_paths)
380
+ structural = True
378
381
  sync_error = None
379
382
  result = None
380
383
  after = GitStatus()
@@ -383,23 +386,29 @@ class GitService:
383
386
  context = await self.cmake.begin_source_change()
384
387
  try:
385
388
  result = await self.command(arguments, check=False, read_only=False, stdin=stdin)
386
- completed = True
387
389
  try:
388
390
  after = await self.status()
389
- changes = await self.source_files(before, after) if source_changes else []
391
+ structural = sources != await self.source_snapshot(sources)
392
+ changes = await self.source_files(before, after)
390
393
  except (GitError, ToolchainError):
391
394
  sync_error = (
392
- "Git command execution finished, but its resulting repository state "
393
- "could not be inspected; HEAD and conflicts are unknown. "
394
- "Do not repeat the mutation before checking git_status(). "
395
- "Inspect the returned process_ids with process_get(process_id=...)."
395
+ "Git command execution finished, but the resulting repository/source "
396
+ "state could not be fully verified. Treat unverified state as unknown. "
397
+ + (
398
+ "The Git command succeeded. Do not repeat the completed mutation. "
399
+ if result.return_code == 0
400
+ else "The Git command failed and may have partially changed the "
401
+ "repository. Do not repeat it before checking git_status(). "
402
+ )
403
+ + "Inspect git_status() and the returned process_ids with "
404
+ "process_get(process_id=...)."
396
405
  )
397
406
  finally:
398
407
  try:
399
408
  await self.cmake.finish_source_change(
400
409
  context,
401
- structural=source_changes,
402
- uncertain=not completed or changes is None,
410
+ structural=structural,
411
+ uncertain=result is None or changes is None,
403
412
  )
404
413
  await self.analysis.refresh_source_changes(changes)
405
414
  # A secondary synchronization error must not conceal a completed commit/push.
@@ -450,6 +459,62 @@ class GitService:
450
459
  ),
451
460
  )
452
461
 
462
+ async def source_snapshot(
463
+ self,
464
+ paths: Collection[WorkspacePath] = (),
465
+ ) -> dict[WorkspacePath, int | None]:
466
+ """Record physical kinds of Git-visible paths, retaining previous and explicit names.
467
+
468
+ Newly visible paths have an unknown previous kind and conservatively change the
469
+ snapshot without reading source text.
470
+ """
471
+ result = await self.command(
472
+ ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
473
+ )
474
+ selected = set(paths)
475
+ for raw in result.stdout.split(b"\0"):
476
+ if raw:
477
+ selected.add(parsers.qualified(raw))
478
+ if len(selected) > self.MAX_SOURCE_PATHS:
479
+ raise GitError(
480
+ f"Git source snapshot exceeds the {self.MAX_SOURCE_PATHS}-path limit. "
481
+ "Inspect git_status() and exclude generated or unrelated untracked files "
482
+ "with .gitignore before another Git mutation. "
483
+ f"Inspect process_get(process_id={result.process_id}) for the source catalog."
484
+ )
485
+ snapshot = {}
486
+ try:
487
+ for index, path in enumerate(selected):
488
+ snapshot[path] = self.source_kind(path)
489
+ if index % 256 == 0:
490
+ await asyncio.sleep(0)
491
+ except GitError as error:
492
+ raise GitError(
493
+ f"{error} Inspect process_get(process_id={result.process_id}) "
494
+ "for the source catalog."
495
+ ) from error
496
+ return snapshot
497
+
498
+ def source_kind(self, path: WorkspacePath) -> int | None:
499
+ """Inspect a project entry itself without following it or escaping through its parents."""
500
+ native = self.workspace.root / path.relative
501
+ try:
502
+ if not native.parent.resolve().is_relative_to(self.workspace.root):
503
+ raise GitError(
504
+ f"Cannot inspect source path {display_value(str(path))}: its parent "
505
+ "traverses a link outside project/. Inspect git_status() and ask the "
506
+ "operator to keep source parents inside the configured project."
507
+ )
508
+ metadata = native.lstat()
509
+ return stat.S_IFLNK if native.is_junction() else stat.S_IFMT(metadata.st_mode)
510
+ except (FileNotFoundError, NotADirectoryError):
511
+ return None
512
+ except OSError as error:
513
+ raise GitError(
514
+ f"Cannot inspect source path {display_value(str(path))}: "
515
+ f"{os_error_reason(error)}. Check access permissions and inspect git_status()."
516
+ ) from error
517
+
453
518
  async def source_files(self, before: GitStatus, after: GitStatus) -> list[FileChange]:
454
519
  """Combine dirty paths and committed tree changes without scanning the project."""
455
520
  affected = {entry.path for entry in [*before.entries, *after.entries]}
@@ -468,17 +533,7 @@ class GitService:
468
533
  affected.add(change.original_path)
469
534
  changes = []
470
535
  for path in sorted(affected):
471
- try:
472
- (self.workspace.root / path.relative).lstat()
473
- except FileNotFoundError:
474
- kind = 3
475
- except OSError as error:
476
- raise GitError(
477
- f"Cannot inspect changed source path {display_value(str(path))}: "
478
- f"{os_error_reason(error)}. Check access permissions and inspect git_status()."
479
- ) from error
480
- else:
481
- kind = 2
536
+ kind = 3 if self.source_kind(path) is None else 2
482
537
  changes.append(FileChange(path, kind))
483
538
  return changes
484
539
 
@@ -798,7 +853,7 @@ class GitService:
798
853
  async with self.request(ctx):
799
854
  result = await self.mutate(
800
855
  ["add", "--all", "--", *self.paths(paths)],
801
- source_changes=True,
856
+ source_paths=paths,
802
857
  )
803
858
  return tool_result(result, render(result))
804
859
 
@@ -822,7 +877,7 @@ class GitService:
822
877
  *selected,
823
878
  ]
824
879
  )
825
- result = await self.mutate(command, source_changes=True)
880
+ result = await self.mutate(command, source_paths=paths)
826
881
  return tool_result(result, render(result))
827
882
 
828
883
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=writes)
@@ -841,7 +896,6 @@ class GitService:
841
896
  )
842
897
  result = await self.mutate(
843
898
  ["commit", *(["--amend"] if amend else []), "--file=-"],
844
- source_changes=True,
845
899
  stdin=message,
846
900
  )
847
901
  return tool_result(result, render(result))
@@ -861,7 +915,7 @@ class GitService:
861
915
  )
862
916
  result = await self.mutate(
863
917
  ["restore", "--worktree", *options, "--", *self.paths(paths)],
864
- source_changes=True,
918
+ source_paths=paths,
865
919
  )
866
920
  return tool_result(result, render(result))
867
921
 
@@ -885,7 +939,7 @@ class GitService:
885
939
  command = ["branch", name, await self.revision(start_point, "start_point")]
886
940
  else:
887
941
  command = ["branch", "-D" if force else "-d", name]
888
- result = await self.mutate(command, source_changes=True)
942
+ result = await self.mutate(command)
889
943
  return tool_result(result, render(result))
890
944
 
891
945
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=writes)
@@ -906,7 +960,7 @@ class GitService:
906
960
  command = ["switch", "--no-guess", *(["-c"] if create else []), branch]
907
961
  if start_point is not None:
908
962
  command.append(await self.revision(start_point, "start_point"))
909
- result = await self.mutate(command, source_changes=True)
963
+ result = await self.mutate(command)
910
964
  return tool_result(result, render(result))
911
965
 
912
966
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=writes)
@@ -965,7 +1019,7 @@ class GitService:
965
1019
  "Use git_stash(action='list') to list available references."
966
1020
  )
967
1021
  command = ["stash", action, reference]
968
- changed = await self.mutate(command, source_changes=True)
1022
+ changed = await self.mutate(command)
969
1023
  result = GitStashResult(**changed.model_dump())
970
1024
  return tool_result(result, render(result))
971
1025
 
@@ -985,7 +1039,6 @@ class GitService:
985
1039
  "--prune" if prune else "--no-prune",
986
1040
  name,
987
1041
  ],
988
- source_changes=True,
989
1042
  )
990
1043
  return tool_result(result, render(result))
991
1044
 
@@ -1019,7 +1072,7 @@ class GitService:
1019
1072
  if branch is not None:
1020
1073
  await self.branch_name(branch, "branch", full=True)
1021
1074
  command.append("refs/heads/" + branch)
1022
- result = await self.mutate(command, source_changes=True)
1075
+ result = await self.mutate(command)
1023
1076
  return tool_result(result, render(result))
1024
1077
 
1025
1078
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=network)
@@ -1055,7 +1108,7 @@ class GitService:
1055
1108
  if set_upstream:
1056
1109
  command.append("--set-upstream")
1057
1110
  command.extend([name, f"HEAD:refs/heads/{target}"])
1058
- result = await self.mutate(command, source_changes=True)
1111
+ result = await self.mutate(command)
1059
1112
  return tool_result(result, render(result))
1060
1113
 
1061
1114
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=writes)
@@ -1072,7 +1125,7 @@ class GitService:
1072
1125
  if message is not None:
1073
1126
  command.extend(["--message", message])
1074
1127
  command.append(await self.revision(revision))
1075
- result = await self.mutate(command, source_changes=True)
1128
+ result = await self.mutate(command)
1076
1129
  return tool_result(result, render(result))
1077
1130
 
1078
1131
  @apps.tool(resource_uri=self.WIDGET.uri, icons=[icon], annotations=writes)
@@ -1081,7 +1134,6 @@ class GitService:
1081
1134
  async with self.request(ctx):
1082
1135
  result = await self.mutate(
1083
1136
  ["rebase", "--no-autostash", await self.revision(revision)],
1084
- source_changes=True,
1085
1137
  )
1086
1138
  return tool_result(result, render(result))
1087
1139
 
@@ -1108,7 +1160,6 @@ class GitService:
1108
1160
  *(["--mainline", str(mainline)] if mainline else []),
1109
1161
  *resolved,
1110
1162
  ],
1111
- source_changes=True,
1112
1163
  )
1113
1164
  return tool_result(result, render(result))
1114
1165
 
@@ -1136,7 +1187,6 @@ class GitService:
1136
1187
  *(["--mainline", str(mainline)] if mainline else []),
1137
1188
  *resolved,
1138
1189
  ],
1139
- source_changes=True,
1140
1190
  )
1141
1191
  return tool_result(result, render(result))
1142
1192
 
@@ -1223,7 +1273,7 @@ class GitService:
1223
1273
  selected.replace("_", "-"),
1224
1274
  f"--{action}",
1225
1275
  ]
1226
- result = await self.mutate(command, source_changes=True)
1276
+ result = await self.mutate(command)
1227
1277
  return tool_result(result, render(result))
1228
1278
 
1229
1279
  @mcp.resource(self.STATUS_URI, mime_type="text/plain", icons=[icon])
File without changes
File without changes