cade-cli 0.11.0__tar.gz → 0.13.3__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 (161) hide show
  1. {cade_cli-0.11.0 → cade_cli-0.13.3}/PKG-INFO +34 -28
  2. {cade_cli-0.11.0 → cade_cli-0.13.3}/README.md +27 -20
  3. {cade_cli-0.11.0 → cade_cli-0.13.3}/pyproject.toml +11 -8
  4. cade_cli-0.13.3/src/cade_mcp_local/config.py +116 -0
  5. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/server.py +7 -3
  6. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/filesystem.py +18 -14
  7. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/utils.py +6 -2
  8. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/app.py +76 -14
  9. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/chat.py +26 -0
  10. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/context.py +24 -3
  11. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/cron.py +39 -19
  12. cade_cli-0.13.3/src/cadecoder/cli/commands/mem.py +176 -0
  13. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/model.py +112 -12
  14. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/persona.py +190 -24
  15. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/tools.py +110 -8
  16. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/config.py +47 -10
  17. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/constants.py +1 -8
  18. cade_cli-0.13.3/src/cadecoder/core/frozen.py +37 -0
  19. cade_cli-0.13.3/src/cadecoder/core/model_specs.py +194 -0
  20. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/runtime.py +1 -0
  21. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/context_window.py +4 -83
  22. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/orchestrator.py +4 -2
  23. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/tool_result_store.py +4 -2
  24. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/tool_schema_cache.py +34 -2
  25. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/providers/__init__.py +7 -2
  26. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/providers/anthropic.py +50 -6
  27. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/providers/base.py +8 -1
  28. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/providers/ollama.py +65 -23
  29. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/providers/openai.py +68 -37
  30. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/adapters/__init__.py +4 -1
  31. cade_cli-0.13.3/src/cadecoder/serve/adapters/desktop.py +408 -0
  32. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/allowlist.py +34 -6
  33. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/config.py +37 -2
  34. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/daemon.py +56 -26
  35. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/observability.py +4 -1
  36. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/worker.py +91 -11
  37. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/storage/personas.py +27 -0
  38. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/scheduler.py +90 -4
  39. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/serve/serve.toml.example +10 -1
  40. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/composite.py +41 -31
  41. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/search/service.py +61 -12
  42. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/session.py +102 -9
  43. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/slash.py +1 -0
  44. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/__init__.py +7 -1
  45. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/session.py +66 -7
  46. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/tts.py +15 -0
  47. cade_cli-0.11.0/src/cade_mcp_local/config.py +0 -68
  48. cade_cli-0.11.0/src/cadecoder/cli/commands/mem.py +0 -149
  49. {cade_cli-0.11.0 → cade_cli-0.13.3}/.gitignore +0 -0
  50. {cade_cli-0.11.0 → cade_cli-0.13.3}/LICENSE +0 -0
  51. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/__init__.py +0 -0
  52. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/__main__.py +0 -0
  53. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/_metadata.py +0 -0
  54. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/errors.py +0 -0
  55. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/resources.py +0 -0
  56. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/__init__.py +0 -0
  57. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/_read_cache.py +0 -0
  58. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/context.py +0 -0
  59. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/git.py +0 -0
  60. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/notifications.py +0 -0
  61. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/questions.py +0 -0
  62. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/results.py +0 -0
  63. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/schemas.py +0 -0
  64. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/search.py +0 -0
  65. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/shell.py +0 -0
  66. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/snippets.py +0 -0
  67. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/tasks.py +0 -0
  68. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/web_fetch.py +0 -0
  69. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/tools/web_search.py +0 -0
  70. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/__init__.py +0 -0
  71. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/arcade_client.py +0 -0
  72. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/cache.py +0 -0
  73. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/extract.py +0 -0
  74. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/markdown.py +0 -0
  75. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/preapproved.py +0 -0
  76. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/prompts.py +0 -0
  77. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/scrape.py +0 -0
  78. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/search/__init__.py +0 -0
  79. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/search/arcade.py +0 -0
  80. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/search/base.py +0 -0
  81. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cade_mcp_local/web/search/http.py +0 -0
  82. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/__init__.py +0 -0
  83. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ai/__init__.py +0 -0
  84. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ai/prompts.py +0 -0
  85. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/__init__.py +0 -0
  86. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/auth.py +0 -0
  87. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/__init__.py +0 -0
  88. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/account.py +0 -0
  89. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/auth.py +0 -0
  90. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/channels.py +0 -0
  91. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/hooks.py +0 -0
  92. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/mcp.py +0 -0
  93. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/serve.py +0 -0
  94. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/tasks.py +0 -0
  95. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/cli/commands/thread.py +0 -0
  96. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/__init__.py +0 -0
  97. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/errors.py +0 -0
  98. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/git.py +0 -0
  99. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/logging.py +0 -0
  100. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/names.py +0 -0
  101. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/paths.py +0 -0
  102. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/core/types.py +0 -0
  103. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/__init__.py +0 -0
  104. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/mcp_schema_index.py +0 -0
  105. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/execution/parallel.py +0 -0
  106. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/__init__.py +0 -0
  107. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/config.py +0 -0
  108. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/engine.py +0 -0
  109. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/__init__.py +0 -0
  110. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/callback.py +0 -0
  111. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/command.py +0 -0
  112. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/http.py +0 -0
  113. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/prompt.py +0 -0
  114. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
  115. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/matchers.py +0 -0
  116. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/registry.py +0 -0
  117. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/hooks/types.py +0 -0
  118. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/__init__.py +0 -0
  119. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/adapters/base.py +0 -0
  120. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/adapters/registry.py +0 -0
  121. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/adapters/stub.py +0 -0
  122. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/adapters/telegram.py +0 -0
  123. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/chunker.py +0 -0
  124. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/commands.py +0 -0
  125. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/elicitation.py +0 -0
  126. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/format.py +0 -0
  127. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/install.py +0 -0
  128. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/lock.py +0 -0
  129. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/progress_sink.py +0 -0
  130. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/router.py +0 -0
  131. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/secrets.py +0 -0
  132. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/serve/sinks.py +0 -0
  133. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/storage/__init__.py +0 -0
  134. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/storage/threads.py +0 -0
  135. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/__init__.py +0 -0
  136. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/channels.py +0 -0
  137. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/lock.py +0 -0
  138. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/notifications.py +0 -0
  139. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/store.py +0 -0
  140. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tasks/types.py +0 -0
  141. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/login_failed.html +0 -0
  142. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/login_success.html +0 -0
  143. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
  144. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
  145. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/templates/styles.css +0 -0
  146. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/__init__.py +0 -0
  147. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/__init__.py +0 -0
  148. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
  149. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/base.py +0 -0
  150. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/config.py +0 -0
  151. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/manager/mcp.py +0 -0
  152. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/search/__init__.py +0 -0
  153. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/search/discovered.py +0 -0
  154. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/tools/search/scoring.py +0 -0
  155. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/__init__.py +0 -0
  156. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/display.py +0 -0
  157. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/elicitation.py +0 -0
  158. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/ui/input.py +0 -0
  159. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/audio.py +0 -0
  160. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/cleanup.py +0 -0
  161. {cade_cli-0.11.0 → cade_cli-0.13.3}/src/cadecoder/voice/stt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cade-cli
3
- Version: 0.11.0
3
+ Version: 0.13.3
4
4
  Summary: Cade - The CLI Agent from Arcade.dev
5
5
  Project-URL: Homepage, https://arcade.dev
6
6
  Project-URL: Documentation, https://docs.arcade.dev
@@ -43,10 +43,10 @@ Classifier: Topic :: Software Development
43
43
  Classifier: Topic :: Software Development :: Code Generators
44
44
  Classifier: Typing :: Typed
45
45
  Requires-Python: >=3.11
46
- Requires-Dist: agent-library<1.0.0,>=0.12.0
46
+ Requires-Dist: agent-library<1.0.0,>=0.13.1
47
47
  Requires-Dist: anthropic<1.0.0,>=0.34.0
48
48
  Requires-Dist: arcade-core<5.0.0,>=4.7.0
49
- Requires-Dist: arcade-mcp-server<2.0.0,>=1.21.0
49
+ Requires-Dist: arcade-mcp-server<2.0.0,>=1.22.0
50
50
  Requires-Dist: arcade-tdk<4.0.0,>=3.8.0
51
51
  Requires-Dist: arcadepy<2.0.0,>=1.10.0
52
52
  Requires-Dist: authlib<2.0.0,>=1.6.0
@@ -58,15 +58,14 @@ Requires-Dist: keyring<26.0,>=24.0
58
58
  Requires-Dist: openai<2.0.0,>=1.0.0
59
59
  Requires-Dist: prompt-toolkit<4.0.0,>=3.0.52
60
60
  Requires-Dist: pydantic[email]<3.0.0,>=2.0.0
61
- Requires-Dist: pyperclip<2.0.0,>=1.8.0
62
61
  Requires-Dist: python-telegram-bot<22.0,>=21.0
63
- Requires-Dist: pyyaml<7.0.0,>=6.0
64
62
  Requires-Dist: rich<14.0.0,>=13.0.0
65
- Requires-Dist: sounddevice>=0.5.5
66
- Requires-Dist: tiktoken<1.0.0,>=0.11.0
67
63
  Requires-Dist: toml<1.0.0,>=0.10.0
68
64
  Requires-Dist: typer>0.10.0
69
65
  Requires-Dist: ulid==1.1
66
+ Requires-Dist: websockets<16.0,>=14.0
67
+ Provides-Extra: build
68
+ Requires-Dist: pyinstaller<7.0.0,>=6.16.0; extra == 'build'
70
69
  Provides-Extra: dev
71
70
  Requires-Dist: mypy<2.0.0,>=1.10.0; extra == 'dev'
72
71
  Requires-Dist: pytest-asyncio<1.0.0,>=0.24.0; extra == 'dev'
@@ -83,7 +82,7 @@ Provides-Extra: voice
83
82
  Requires-Dist: mlx-audio<0.4.0,>=0.2.0; extra == 'voice'
84
83
  Requires-Dist: mlx-whisper>=0.1.0; extra == 'voice'
85
84
  Requires-Dist: numpy>=1.24.0; extra == 'voice'
86
- Requires-Dist: sounddevice>=0.4.0; extra == 'voice'
85
+ Requires-Dist: sounddevice<1.0.0,>=0.5.5; extra == 'voice'
87
86
  Requires-Dist: webrtcvad>=2.0.10; extra == 'voice'
88
87
  Description-Content-Type: text/markdown
89
88
 
@@ -119,6 +118,8 @@ cade
119
118
  | pip | `pip install cade-cli` |
120
119
  | From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
121
120
 
121
+ Homebrew installs from a private release feed. Export `HOMEBREW_GITHUB_API_TOKEN` (a token with read access to `arcadeai-labs/cade`) before running `brew install`.
122
+
122
123
  **Prerequisites:** Python 3.11+, an Arcade account ([arcade.dev](https://arcade.dev)) for cloud tools, and an LLM provider key (`OPENAI_API_KEY` or `ANTHROPIC_API_KEY`). Skip the Arcade account with `--local-only` or `CADE_LOCAL_ONLY=1`.
123
124
 
124
125
  ```bash
@@ -126,6 +127,9 @@ cade login # Arcade Cloud OAuth (skip with --local-only)
126
127
  cade --version
127
128
  ```
128
129
 
130
+ Full install and first-run docs: [`docs/install.md`](./docs/install.md) and
131
+ [`docs/quickstart.md`](./docs/quickstart.md).
132
+
129
133
  ---
130
134
 
131
135
  ## Quick start
@@ -133,11 +137,11 @@ cade --version
133
137
  ```bash
134
138
  cade # interactive chat
135
139
  cade -m "What changed in HEAD?" # single message, then exit
136
- cat error.log | cade -m "What went wrong?" # pipe input
140
+ cade -m "What went wrong?" < error.log # prompt + file/stdin
137
141
  cade -r # resume the most recent thread
138
142
  cade resume "auth-rewrite" # resume a thread by name
139
143
  cade --persona reviewer # use a saved persona's system prompt
140
- cade --voice # speak / hear (voice mode)
144
+ cade --voice # speak / hear (requires cade-cli[voice])
141
145
  ```
142
146
 
143
147
  ### Top-level options
@@ -145,9 +149,9 @@ cade --voice # speak / hear (voice mode)
145
149
  | Flag | Description |
146
150
  |---|---|
147
151
  | `-r`, `--resume` | Resume most recent thread |
148
- | `-m`, `--message` | Single-message mode (reads stdin if no arg) |
152
+ | `-m`, `--message` | Single-message mode; piped stdin is appended to the prompt |
149
153
  | `-P`, `--persona` | Use a saved persona |
150
- | `-V`, `--voice` | Voice mode |
154
+ | `-V`, `--voice` | Voice mode; install `cade-cli[voice]` first |
151
155
  | `-v`, `--verbose` | Debug logging |
152
156
  | `--version` | Print version |
153
157
 
@@ -172,7 +176,7 @@ cade --voice # speak / hear (voice mode)
172
176
 
173
177
  Tools come from three sources and are addressed uniformly by their registered name:
174
178
 
175
- - **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash` — async with progress reporting), `Search`, `Git`, `Task*`, `Channel`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `Sleep`, `PinContext`, `PushNotification`.
179
+ - **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`), `Search`, `Git`, `Task*`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `PinContext`, `PushNotification`, `WebFetch`, and `WebSearch`.
176
180
  - **`Memory_*`** — agent-library backed, per-persona indexed knowledge store.
177
181
  - **Arcade Cloud + user MCP servers** — anything you register.
178
182
 
@@ -215,7 +219,7 @@ Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<co
215
219
 
216
220
  ## Headless daemon: `cade serve`
217
221
 
218
- Run Cade as a long-lived daemon that replies over messaging platforms (Telegram supported today; the adapter protocol is generic). Each chat gets its own persistent thread — persona, memory, and tools carry across messages.
222
+ Run Cade as a long-lived daemon that replies through adapters. Telegram is supported for personal bot workflows, and Desktop exposes a local WebSocket for native clients. Each conversation gets its own persistent thread — persona, memory, and tools carry across messages.
219
223
 
220
224
  ```bash
221
225
  cade serve init # wizard: bot token, allowed senders
@@ -226,9 +230,9 @@ cade serve status / stop # health, uptime; SIGTERM running daemo
226
230
 
227
231
  Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
228
232
 
229
- **Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
233
+ **Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[serve.tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
230
234
 
231
- The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input (rendered as Telegram inline keyboards), stream live progress (rendered as edited placeholder messages), and emit logs that flow into the daemon log. See [`docs/mcp.md`](./docs/mcp.md) for the protocol details, [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
235
+ The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input, stream live progress, and emit logs through whichever adapter is active. See [`docs/adapters/`](./docs/adapters/) for adapter details, [`docs/mcp.md`](./docs/mcp.md) for protocol details, and [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
232
236
 
233
237
  ---
234
238
 
@@ -252,17 +256,18 @@ See [`docs/channels.md`](./docs/channels.md) for the channels protocol; hooks an
252
256
  Cade works with any OpenAI-compatible endpoint. Three ways to wire one in (CLI flag → env → config, first match wins):
253
257
 
254
258
  ```bash
255
- # CLI flag
256
- cade chat --endpoint http://localhost:11434/v1 --model llama3
259
+ # CLI flag for Ollama. Use a model from `ollama list`.
260
+ cade chat -L --endpoint http://localhost:11434 --model qwen3:4b
257
261
 
258
- # Environment
259
- export OPENAI_BASE_URL="http://localhost:11434/v1"
260
- export OPENAI_API_KEY="ollama" # any non-empty value
261
- cade chat --model llama3
262
+ # Environment for one-shot mode
263
+ cade context create local-qwen --model qwen3:4b
264
+ cade context use local-qwen
265
+ export OLLAMA_BASE_URL="http://localhost:11434"
266
+ CADE_LOCAL_ONLY=1 cade -m "hello from a local model"
262
267
 
263
268
  # Config — ~/.cade/config/cade.toml
264
269
  [model_settings]
265
- host = "http://localhost:11434/v1"
270
+ host = "http://localhost:8000/v1"
266
271
  api_key = "ollama"
267
272
  ```
268
273
 
@@ -303,14 +308,15 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
303
308
  ```bash
304
309
  git clone https://github.com/arcadeai-labs/cade.git
305
310
  cd cade
306
- uv sync --all-extras --dev
307
- pytest # full suite
308
- ruff check src/ tests/ && ruff format src/ tests/
311
+ uv sync --extra dev
312
+ uv run pytest
313
+ uv run ruff check src/ tests/
309
314
  ```
310
315
 
311
316
  Style: Python 3.11+ with modern type hints (`dict`, `list`, `| None`); ruff for lint + format; pytest with `asyncio_mode = "auto"`. Public functions and classes get docstrings.
312
317
 
313
- PRs welcome open an issue first for anything substantial.
318
+ Build/release docs live in [`docs/development.md`](./docs/development.md). PRs
319
+ welcome — open an issue first for anything substantial.
314
320
 
315
321
  ---
316
322
 
@@ -318,7 +324,7 @@ PRs welcome — open an issue first for anything substantial.
318
324
 
319
325
  - [arcade.dev](https://arcade.dev) — product
320
326
  - [docs.arcade.dev](https://docs.arcade.dev) — API and tool catalog
321
- - [`docs/`](./docs/) — `mcp.md` (tool protocol), `configuration.md`, `channels.md`, `install.md`
327
+ - [`docs/`](./docs/) — quickstart, examples, install, config, MCP, channels, voice, development, and adapters
322
328
  - [Issues](https://github.com/arcadeai-labs/cade/issues) · [Releases](https://github.com/arcadeai-labs/cade/releases)
323
329
 
324
330
  ## License
@@ -30,6 +30,8 @@ cade
30
30
  | pip | `pip install cade-cli` |
31
31
  | From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
32
32
 
33
+ Homebrew installs from a private release feed. Export `HOMEBREW_GITHUB_API_TOKEN` (a token with read access to `arcadeai-labs/cade`) before running `brew install`.
34
+
33
35
  **Prerequisites:** Python 3.11+, an Arcade account ([arcade.dev](https://arcade.dev)) for cloud tools, and an LLM provider key (`OPENAI_API_KEY` or `ANTHROPIC_API_KEY`). Skip the Arcade account with `--local-only` or `CADE_LOCAL_ONLY=1`.
34
36
 
35
37
  ```bash
@@ -37,6 +39,9 @@ cade login # Arcade Cloud OAuth (skip with --local-only)
37
39
  cade --version
38
40
  ```
39
41
 
42
+ Full install and first-run docs: [`docs/install.md`](./docs/install.md) and
43
+ [`docs/quickstart.md`](./docs/quickstart.md).
44
+
40
45
  ---
41
46
 
42
47
  ## Quick start
@@ -44,11 +49,11 @@ cade --version
44
49
  ```bash
45
50
  cade # interactive chat
46
51
  cade -m "What changed in HEAD?" # single message, then exit
47
- cat error.log | cade -m "What went wrong?" # pipe input
52
+ cade -m "What went wrong?" < error.log # prompt + file/stdin
48
53
  cade -r # resume the most recent thread
49
54
  cade resume "auth-rewrite" # resume a thread by name
50
55
  cade --persona reviewer # use a saved persona's system prompt
51
- cade --voice # speak / hear (voice mode)
56
+ cade --voice # speak / hear (requires cade-cli[voice])
52
57
  ```
53
58
 
54
59
  ### Top-level options
@@ -56,9 +61,9 @@ cade --voice # speak / hear (voice mode)
56
61
  | Flag | Description |
57
62
  |---|---|
58
63
  | `-r`, `--resume` | Resume most recent thread |
59
- | `-m`, `--message` | Single-message mode (reads stdin if no arg) |
64
+ | `-m`, `--message` | Single-message mode; piped stdin is appended to the prompt |
60
65
  | `-P`, `--persona` | Use a saved persona |
61
- | `-V`, `--voice` | Voice mode |
66
+ | `-V`, `--voice` | Voice mode; install `cade-cli[voice]` first |
62
67
  | `-v`, `--verbose` | Debug logging |
63
68
  | `--version` | Print version |
64
69
 
@@ -83,7 +88,7 @@ cade --voice # speak / hear (voice mode)
83
88
 
84
89
  Tools come from three sources and are addressed uniformly by their registered name:
85
90
 
86
- - **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash` — async with progress reporting), `Search`, `Git`, `Task*`, `Channel`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `Sleep`, `PinContext`, `PushNotification`.
91
+ - **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`), `Search`, `Git`, `Task*`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `PinContext`, `PushNotification`, `WebFetch`, and `WebSearch`.
87
92
  - **`Memory_*`** — agent-library backed, per-persona indexed knowledge store.
88
93
  - **Arcade Cloud + user MCP servers** — anything you register.
89
94
 
@@ -126,7 +131,7 @@ Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<co
126
131
 
127
132
  ## Headless daemon: `cade serve`
128
133
 
129
- Run Cade as a long-lived daemon that replies over messaging platforms (Telegram supported today; the adapter protocol is generic). Each chat gets its own persistent thread — persona, memory, and tools carry across messages.
134
+ Run Cade as a long-lived daemon that replies through adapters. Telegram is supported for personal bot workflows, and Desktop exposes a local WebSocket for native clients. Each conversation gets its own persistent thread — persona, memory, and tools carry across messages.
130
135
 
131
136
  ```bash
132
137
  cade serve init # wizard: bot token, allowed senders
@@ -137,9 +142,9 @@ cade serve status / stop # health, uptime; SIGTERM running daemo
137
142
 
138
143
  Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
139
144
 
140
- **Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
145
+ **Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[serve.tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
141
146
 
142
- The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input (rendered as Telegram inline keyboards), stream live progress (rendered as edited placeholder messages), and emit logs that flow into the daemon log. See [`docs/mcp.md`](./docs/mcp.md) for the protocol details, [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
147
+ The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input, stream live progress, and emit logs through whichever adapter is active. See [`docs/adapters/`](./docs/adapters/) for adapter details, [`docs/mcp.md`](./docs/mcp.md) for protocol details, and [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
143
148
 
144
149
  ---
145
150
 
@@ -163,17 +168,18 @@ See [`docs/channels.md`](./docs/channels.md) for the channels protocol; hooks an
163
168
  Cade works with any OpenAI-compatible endpoint. Three ways to wire one in (CLI flag → env → config, first match wins):
164
169
 
165
170
  ```bash
166
- # CLI flag
167
- cade chat --endpoint http://localhost:11434/v1 --model llama3
171
+ # CLI flag for Ollama. Use a model from `ollama list`.
172
+ cade chat -L --endpoint http://localhost:11434 --model qwen3:4b
168
173
 
169
- # Environment
170
- export OPENAI_BASE_URL="http://localhost:11434/v1"
171
- export OPENAI_API_KEY="ollama" # any non-empty value
172
- cade chat --model llama3
174
+ # Environment for one-shot mode
175
+ cade context create local-qwen --model qwen3:4b
176
+ cade context use local-qwen
177
+ export OLLAMA_BASE_URL="http://localhost:11434"
178
+ CADE_LOCAL_ONLY=1 cade -m "hello from a local model"
173
179
 
174
180
  # Config — ~/.cade/config/cade.toml
175
181
  [model_settings]
176
- host = "http://localhost:11434/v1"
182
+ host = "http://localhost:8000/v1"
177
183
  api_key = "ollama"
178
184
  ```
179
185
 
@@ -214,14 +220,15 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
214
220
  ```bash
215
221
  git clone https://github.com/arcadeai-labs/cade.git
216
222
  cd cade
217
- uv sync --all-extras --dev
218
- pytest # full suite
219
- ruff check src/ tests/ && ruff format src/ tests/
223
+ uv sync --extra dev
224
+ uv run pytest
225
+ uv run ruff check src/ tests/
220
226
  ```
221
227
 
222
228
  Style: Python 3.11+ with modern type hints (`dict`, `list`, `| None`); ruff for lint + format; pytest with `asyncio_mode = "auto"`. Public functions and classes get docstrings.
223
229
 
224
- PRs welcome open an issue first for anything substantial.
230
+ Build/release docs live in [`docs/development.md`](./docs/development.md). PRs
231
+ welcome — open an issue first for anything substantial.
225
232
 
226
233
  ---
227
234
 
@@ -229,7 +236,7 @@ PRs welcome — open an issue first for anything substantial.
229
236
 
230
237
  - [arcade.dev](https://arcade.dev) — product
231
238
  - [docs.arcade.dev](https://docs.arcade.dev) — API and tool catalog
232
- - [`docs/`](./docs/) — `mcp.md` (tool protocol), `configuration.md`, `channels.md`, `install.md`
239
+ - [`docs/`](./docs/) — quickstart, examples, install, config, MCP, channels, voice, development, and adapters
233
240
  - [Issues](https://github.com/arcadeai-labs/cade/issues) · [Releases](https://github.com/arcadeai-labs/cade/releases)
234
241
 
235
242
  ## License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cade-cli"
3
- version = "0.11.0"
3
+ version = "0.13.3"
4
4
  description = "Cade - The CLI Agent from Arcade.dev"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -27,26 +27,23 @@ dependencies = [
27
27
  "typer>0.10.0",
28
28
  "pydantic[email]>=2.0.0,<3.0.0",
29
29
  "toml>=0.10.0,<1.0.0",
30
- "pyyaml>=6.0,<7.0.0",
31
30
  "openai>=1.0.0,<2.0.0",
32
31
  "anthropic>=0.34.0,<1.0.0",
33
32
  "ulid==1.1",
34
33
  "arcade-tdk>=3.8.0,<4.0.0",
35
- "arcade-mcp-server>=1.21.0,<2.0.0",
34
+ "arcade-mcp-server>=1.22.0,<2.0.0",
36
35
  "arcade-core>=4.7.0,<5.0.0",
37
36
  "arcadepy>=1.10.0,<2.0.0",
38
37
  "authlib>=1.6.0,<2.0.0",
39
- "pyperclip>=1.8.0,<2.0.0",
40
38
  "prompt-toolkit>=3.0.52,<4.0.0",
41
- "tiktoken>=0.11.0,<1.0.0",
42
39
  "httpx>=0.27.0,<1.0.0",
43
40
  "filelock>=3.0.0,<4.0.0",
44
- "agent-library>=0.12.0,<1.0.0",
45
- "sounddevice>=0.5.5",
41
+ "agent-library>=0.13.1,<1.0.0",
46
42
  "croniter>=2.0.0,<4.0.0",
47
43
  "python-telegram-bot>=21.0,<22.0",
48
44
  "keyring>=24.0,<26.0",
49
45
  "html2text>=2024.2.26",
46
+ "websockets>=14.0,<16.0",
50
47
  ]
51
48
 
52
49
  [project.urls]
@@ -69,10 +66,13 @@ dev = [
69
66
  "pytest-cov>=4.0.0,<5.0.0",
70
67
  "mypy>=1.10.0,<2.0.0",
71
68
  ]
69
+ build = [
70
+ "pyinstaller>=6.16.0,<7.0.0",
71
+ ]
72
72
  voice = [
73
73
  "mlx-audio>=0.2.0,<0.4.0",
74
74
  "mlx-whisper>=0.1.0",
75
- "sounddevice>=0.4.0",
75
+ "sounddevice>=0.5.5,<1.0.0",
76
76
  "numpy>=1.24.0",
77
77
  "webrtcvad>=2.0.10",
78
78
  ]
@@ -100,6 +100,9 @@ packages = ["src/cadecoder", "src/cade_mcp_local"]
100
100
  testpaths = ["tests"]
101
101
  asyncio_mode = "auto"
102
102
  asyncio_default_fixture_loop_scope = "function"
103
+ markers = [
104
+ "integration: tests that cross process or network boundaries without external services",
105
+ ]
103
106
 
104
107
  [tool.ruff]
105
108
  line-length = 100
@@ -0,0 +1,116 @@
1
+ """Runtime configuration for the local MCP server.
2
+
3
+ Configuration is resolved lazily at runtime, not at import time.
4
+ This allows proper testing and correct behavior when installed as a package.
5
+ """
6
+
7
+ import os
8
+ import pathlib
9
+ from functools import lru_cache
10
+
11
+ # Environment variable that disables filesystem path confinement. When set to a
12
+ # truthy value, filesystem tools may read/write paths outside the project root.
13
+ #
14
+ # This is intentionally a single on/off switch for now. A future iteration may
15
+ # replace it with a richer policy (e.g. an explicit list of additional allowed
16
+ # roots); keep `path_restrictions_enabled()` as the single read point so callers
17
+ # don't need to change when that happens.
18
+ PATH_RESTRICTIONS_ENV: str = "CADE_DISABLE_PATH_RESTRICTIONS"
19
+
20
+ _TRUTHY_VALUES: frozenset[str] = frozenset({"1", "true", "yes", "on"})
21
+
22
+
23
+ def path_restrictions_enabled() -> bool:
24
+ """Return whether filesystem tools must stay within the project root.
25
+
26
+ Restrictions are ON by default. They are lifted only when the
27
+ ``CADE_DISABLE_PATH_RESTRICTIONS`` environment variable is set to a truthy
28
+ value (``1``, ``true``, ``yes``, or ``on``, case-insensitive).
29
+
30
+ Returns:
31
+ True when path confinement should be enforced, False when tools may
32
+ access any path on the filesystem.
33
+
34
+ Examples:
35
+ >>> import os
36
+ >>> os.environ.pop("CADE_DISABLE_PATH_RESTRICTIONS", None)
37
+ >>> path_restrictions_enabled()
38
+ True
39
+ >>> os.environ["CADE_DISABLE_PATH_RESTRICTIONS"] = "1"
40
+ >>> path_restrictions_enabled()
41
+ False
42
+ """
43
+ raw = os.environ.get(PATH_RESTRICTIONS_ENV, "").strip().lower()
44
+ return raw not in _TRUTHY_VALUES
45
+
46
+
47
+ def disable_path_restrictions() -> None:
48
+ """Lift filesystem path confinement for this process and its children.
49
+
50
+ Sets ``CADE_DISABLE_PATH_RESTRICTIONS`` in the environment so that child
51
+ processes (notably the local-tools MCP subprocess, which inherits
52
+ ``os.environ``) and the serve sandbox hook all observe the override.
53
+
54
+ This is the single supported way to flip the toggle on from code, keeping
55
+ the environment-variable name encapsulated in this module.
56
+ """
57
+ os.environ[PATH_RESTRICTIONS_ENV] = "1"
58
+
59
+
60
+ @lru_cache(maxsize=1)
61
+ def get_project_root() -> pathlib.Path:
62
+ """Get the project root directory.
63
+
64
+ Resolution order:
65
+ 1. CADE_PROJECT_ROOT environment variable (set by parent process)
66
+ 2. Current working directory
67
+
68
+ This is cached for performance but can be reset with reset_config().
69
+
70
+ Returns:
71
+ Resolved path to the project root directory.
72
+ """
73
+ if root := os.environ.get("CADE_PROJECT_ROOT"):
74
+ return pathlib.Path(root).resolve()
75
+ return pathlib.Path.cwd().resolve()
76
+
77
+
78
+ def reset_config() -> None:
79
+ """Clear cached configuration.
80
+
81
+ Call this when:
82
+ - The working directory has changed
83
+ - CADE_PROJECT_ROOT environment variable has changed
84
+ - During testing to ensure isolation
85
+ """
86
+ get_project_root.cache_clear()
87
+
88
+
89
+ # Default ignore patterns for file listings and searches
90
+ DEFAULT_IGNORE_PATTERNS: list[str] = [
91
+ ".git",
92
+ "__pycache__",
93
+ "node_modules",
94
+ ".venv",
95
+ "venv",
96
+ ".mypy_cache",
97
+ ".pytest_cache",
98
+ "*.pyc",
99
+ ".DS_Store",
100
+ "*.egg-info",
101
+ "dist",
102
+ "build",
103
+ ".tox",
104
+ ".coverage",
105
+ "htmlcov",
106
+ ".idea",
107
+ ".vscode",
108
+ ".cursor",
109
+ ".claude",
110
+ ]
111
+
112
+ # File operation limits
113
+ MAX_LIST_DEPTH: int = 10
114
+ MAX_LIST_RESULTS: int = 1000
115
+ MAX_PREVIEW_BYTES: int = 100_000
116
+ DEFAULT_TIMEOUT: int = 60
@@ -13,8 +13,12 @@ os.environ.setdefault("LOGURU_LEVEL", "WARNING")
13
13
 
14
14
  from arcade_mcp_server import MCPApp
15
15
 
16
- from cade_mcp_local.resources import register_resources
17
- from cade_mcp_local.tools import (
16
+ from cadecoder.core.frozen import patch_frozen_return_detection
17
+
18
+ patch_frozen_return_detection()
19
+
20
+ from cade_mcp_local.resources import register_resources # noqa: E402
21
+ from cade_mcp_local.tools import ( # noqa: E402
18
22
  ask_user_question_tool,
19
23
  bash_tool,
20
24
  edit_tool,
@@ -69,7 +73,7 @@ def create_app() -> MCPApp:
69
73
  name="Local",
70
74
  transport="stdio",
71
75
  instructions=SERVER_INSTRUCTIONS,
72
- log_level="info",
76
+ log_level="INFO",
73
77
  )
74
78
 
75
79
  # Filesystem tools
@@ -19,6 +19,7 @@ from cade_mcp_local.config import (
19
19
  MAX_LIST_RESULTS,
20
20
  MAX_PREVIEW_BYTES,
21
21
  get_project_root,
22
+ path_restrictions_enabled,
22
23
  )
23
24
  from cade_mcp_local.errors import (
24
25
  FileOperationError,
@@ -47,22 +48,25 @@ def _read_text_file(file_path: pathlib.Path) -> str:
47
48
 
48
49
 
49
50
  def _write_text_file(file_path: pathlib.Path, content: str) -> None:
50
- """Write text content to a file, ensuring it's within project root."""
51
- project_root = get_project_root()
51
+ """Write text content to a file, ensuring it's within project root.
52
+
53
+ Confinement to the project root is skipped when path restrictions are
54
+ disabled (see `path_restrictions_enabled`).
55
+ """
52
56
  resolved = file_path.resolve()
53
- resolved_root = project_root.resolve()
54
57
 
55
- # Security check: must be within project root
56
- # Use string prefix check as fallback for symlink/worktree edge cases
57
- try:
58
- resolved.relative_to(resolved_root)
59
- except ValueError:
60
- # Fallback: compare resolved string paths
61
- if not str(resolved).startswith(str(resolved_root) + "/"):
62
- raise PathSecurityError(
63
- str(file_path),
64
- f"outside project root (resolved={resolved}, root={resolved_root})",
65
- )
58
+ if path_restrictions_enabled():
59
+ resolved_root = get_project_root().resolve()
60
+ # Security check: must be within project root.
61
+ # Use string prefix check as fallback for symlink/worktree edge cases.
62
+ try:
63
+ resolved.relative_to(resolved_root)
64
+ except ValueError:
65
+ if not str(resolved).startswith(str(resolved_root) + "/"):
66
+ raise PathSecurityError(
67
+ str(file_path),
68
+ f"outside project root (resolved={resolved}, root={resolved_root})",
69
+ )
66
70
 
67
71
  # Create parent directories if needed
68
72
  resolved.parent.mkdir(parents=True, exist_ok=True)
@@ -3,7 +3,11 @@
3
3
  import fnmatch
4
4
  import pathlib
5
5
 
6
- from cade_mcp_local.config import DEFAULT_IGNORE_PATTERNS, get_project_root
6
+ from cade_mcp_local.config import (
7
+ DEFAULT_IGNORE_PATTERNS,
8
+ get_project_root,
9
+ path_restrictions_enabled,
10
+ )
7
11
 
8
12
 
9
13
  def resolve_path(
@@ -46,7 +50,7 @@ def resolve_path(
46
50
  else:
47
51
  resolved = (base_dir / path).resolve()
48
52
 
49
- if confine_to_root:
53
+ if confine_to_root and path_restrictions_enabled():
50
54
  root = get_project_root().resolve()
51
55
  try:
52
56
  resolved.relative_to(root)