tweek 0.2.0__tar.gz → 0.2.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 (164) hide show
  1. {tweek-0.2.0/tweek.egg-info → tweek-0.2.1}/PKG-INFO +1 -1
  2. {tweek-0.2.0 → tweek-0.2.1}/pyproject.toml +1 -1
  3. {tweek-0.2.0 → tweek-0.2.1}/tests/test_installer_improvements.py +20 -29
  4. {tweek-0.2.0 → tweek-0.2.1}/tweek/__init__.py +1 -1
  5. {tweek-0.2.0 → tweek-0.2.1}/tweek/cli.py +124 -25
  6. tweek-0.2.1/tweek-openclaw-plugin/node_modules/flatted/python/flatted.py +149 -0
  7. {tweek-0.2.0 → tweek-0.2.1/tweek.egg-info}/PKG-INFO +1 -1
  8. {tweek-0.2.0 → tweek-0.2.1}/tweek.egg-info/SOURCES.txt +1 -0
  9. {tweek-0.2.0 → tweek-0.2.1}/LICENSE +0 -0
  10. {tweek-0.2.0 → tweek-0.2.1}/README.md +0 -0
  11. {tweek-0.2.0 → tweek-0.2.1}/setup.cfg +0 -0
  12. {tweek-0.2.0 → tweek-0.2.1}/tests/test_approval_queue.py +0 -0
  13. {tweek-0.2.0 → tweek-0.2.1}/tests/test_audit.py +0 -0
  14. {tweek-0.2.0 → tweek-0.2.1}/tests/test_break_glass.py +0 -0
  15. {tweek-0.2.0 → tweek-0.2.1}/tests/test_cli.py +0 -0
  16. {tweek-0.2.0 → tweek-0.2.1}/tests/test_cli_helpers.py +0 -0
  17. {tweek-0.2.0 → tweek-0.2.1}/tests/test_config_manager.py +0 -0
  18. {tweek-0.2.0 → tweek-0.2.1}/tests/test_credential_scanner.py +0 -0
  19. {tweek-0.2.0 → tweek-0.2.1}/tests/test_diagnostics.py +0 -0
  20. {tweek-0.2.0 → tweek-0.2.1}/tests/test_enforcement.py +0 -0
  21. {tweek-0.2.0 → tweek-0.2.1}/tests/test_feedback.py +0 -0
  22. {tweek-0.2.0 → tweek-0.2.1}/tests/test_heuristic_scorer.py +0 -0
  23. {tweek-0.2.0 → tweek-0.2.1}/tests/test_language_detection.py +0 -0
  24. {tweek-0.2.0 → tweek-0.2.1}/tests/test_licensing.py +0 -0
  25. {tweek-0.2.0 → tweek-0.2.1}/tests/test_llm_local.py +0 -0
  26. {tweek-0.2.0 → tweek-0.2.1}/tests/test_llm_reviewer.py +0 -0
  27. {tweek-0.2.0 → tweek-0.2.1}/tests/test_local_model.py +0 -0
  28. {tweek-0.2.0 → tweek-0.2.1}/tests/test_log_bundle.py +0 -0
  29. {tweek-0.2.0 → tweek-0.2.1}/tests/test_logging.py +0 -0
  30. {tweek-0.2.0 → tweek-0.2.1}/tests/test_logging_enhanced.py +0 -0
  31. {tweek-0.2.0 → tweek-0.2.1}/tests/test_mcp_clients.py +0 -0
  32. {tweek-0.2.0 → tweek-0.2.1}/tests/test_mcp_proxy.py +0 -0
  33. {tweek-0.2.0 → tweek-0.2.1}/tests/test_mcp_server.py +0 -0
  34. {tweek-0.2.0 → tweek-0.2.1}/tests/test_openclaw_integration.py +0 -0
  35. {tweek-0.2.0 → tweek-0.2.1}/tests/test_overrides.py +0 -0
  36. {tweek-0.2.0 → tweek-0.2.1}/tests/test_path_boundary.py +0 -0
  37. {tweek-0.2.0 → tweek-0.2.1}/tests/test_pattern_families.py +0 -0
  38. {tweek-0.2.0 → tweek-0.2.1}/tests/test_patterns.py +0 -0
  39. {tweek-0.2.0 → tweek-0.2.1}/tests/test_plugin_scoping.py +0 -0
  40. {tweek-0.2.0 → tweek-0.2.1}/tests/test_post_tool_use.py +0 -0
  41. {tweek-0.2.0 → tweek-0.2.1}/tests/test_prompt_injection_patterns.py +0 -0
  42. {tweek-0.2.0 → tweek-0.2.1}/tests/test_protect_command.py +0 -0
  43. {tweek-0.2.0 → tweek-0.2.1}/tests/test_proxy_detection.py +0 -0
  44. {tweek-0.2.0 → tweek-0.2.1}/tests/test_rate_limiter.py +0 -0
  45. {tweek-0.2.0 → tweek-0.2.1}/tests/test_redaction.py +0 -0
  46. {tweek-0.2.0 → tweek-0.2.1}/tests/test_screening_context.py +0 -0
  47. {tweek-0.2.0 → tweek-0.2.1}/tests/test_session_analyzer.py +0 -0
  48. {tweek-0.2.0 → tweek-0.2.1}/tweek/_keygen.py +0 -0
  49. {tweek-0.2.0 → tweek-0.2.1}/tweek/audit.py +0 -0
  50. {tweek-0.2.0 → tweek-0.2.1}/tweek/cli_helpers.py +0 -0
  51. {tweek-0.2.0 → tweek-0.2.1}/tweek/cli_model.py +0 -0
  52. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/__init__.py +0 -0
  53. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/allowed_dirs.yaml +0 -0
  54. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/families.yaml +0 -0
  55. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/manager.py +0 -0
  56. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/patterns.yaml +0 -0
  57. {tweek-0.2.0 → tweek-0.2.1}/tweek/config/tiers.yaml +0 -0
  58. {tweek-0.2.0 → tweek-0.2.1}/tweek/diagnostics.py +0 -0
  59. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/__init__.py +0 -0
  60. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/break_glass.py +0 -0
  61. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/feedback.py +0 -0
  62. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/overrides.py +0 -0
  63. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/post_tool_use.py +0 -0
  64. {tweek-0.2.0 → tweek-0.2.1}/tweek/hooks/pre_tool_use.py +0 -0
  65. {tweek-0.2.0 → tweek-0.2.1}/tweek/integrations/__init__.py +0 -0
  66. {tweek-0.2.0 → tweek-0.2.1}/tweek/integrations/openclaw.py +0 -0
  67. {tweek-0.2.0 → tweek-0.2.1}/tweek/integrations/openclaw_server.py +0 -0
  68. {tweek-0.2.0 → tweek-0.2.1}/tweek/licensing.py +0 -0
  69. {tweek-0.2.0 → tweek-0.2.1}/tweek/logging/__init__.py +0 -0
  70. {tweek-0.2.0 → tweek-0.2.1}/tweek/logging/bundle.py +0 -0
  71. {tweek-0.2.0 → tweek-0.2.1}/tweek/logging/json_logger.py +0 -0
  72. {tweek-0.2.0 → tweek-0.2.1}/tweek/logging/security_log.py +0 -0
  73. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/__init__.py +0 -0
  74. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/approval.py +0 -0
  75. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/approval_cli.py +0 -0
  76. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/clients/__init__.py +0 -0
  77. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/clients/chatgpt.py +0 -0
  78. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/clients/claude_desktop.py +0 -0
  79. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/clients/gemini.py +0 -0
  80. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/proxy.py +0 -0
  81. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/screening.py +0 -0
  82. {tweek-0.2.0 → tweek-0.2.1}/tweek/mcp/server.py +0 -0
  83. {tweek-0.2.0 → tweek-0.2.1}/tweek/memory/__init__.py +0 -0
  84. {tweek-0.2.0 → tweek-0.2.1}/tweek/memory/queries.py +0 -0
  85. {tweek-0.2.0 → tweek-0.2.1}/tweek/memory/safety.py +0 -0
  86. {tweek-0.2.0 → tweek-0.2.1}/tweek/memory/schemas.py +0 -0
  87. {tweek-0.2.0 → tweek-0.2.1}/tweek/memory/store.py +0 -0
  88. {tweek-0.2.0 → tweek-0.2.1}/tweek/platform/__init__.py +0 -0
  89. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/__init__.py +0 -0
  90. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/base.py +0 -0
  91. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/__init__.py +0 -0
  92. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/gdpr.py +0 -0
  93. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/gov.py +0 -0
  94. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/hipaa.py +0 -0
  95. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/legal.py +0 -0
  96. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/pci.py +0 -0
  97. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/compliance/soc2.py +0 -0
  98. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/__init__.py +0 -0
  99. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/continue_dev.py +0 -0
  100. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/copilot.py +0 -0
  101. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/cursor.py +0 -0
  102. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/openclaw.py +0 -0
  103. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/detectors/windsurf.py +0 -0
  104. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/git_discovery.py +0 -0
  105. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/git_installer.py +0 -0
  106. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/git_lockfile.py +0 -0
  107. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/git_registry.py +0 -0
  108. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/git_security.py +0 -0
  109. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/__init__.py +0 -0
  110. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/anthropic.py +0 -0
  111. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/azure_openai.py +0 -0
  112. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/bedrock.py +0 -0
  113. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/google.py +0 -0
  114. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/providers/openai.py +0 -0
  115. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/scope.py +0 -0
  116. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/__init__.py +0 -0
  117. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/heuristic_scorer.py +0 -0
  118. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/llm_reviewer.py +0 -0
  119. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/local_model_reviewer.py +0 -0
  120. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/pattern_matcher.py +0 -0
  121. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/rate_limiter.py +0 -0
  122. {tweek-0.2.0 → tweek-0.2.1}/tweek/plugins/screening/session_analyzer.py +0 -0
  123. {tweek-0.2.0 → tweek-0.2.1}/tweek/proxy/__init__.py +0 -0
  124. {tweek-0.2.0 → tweek-0.2.1}/tweek/proxy/addon.py +0 -0
  125. {tweek-0.2.0 → tweek-0.2.1}/tweek/proxy/interceptor.py +0 -0
  126. {tweek-0.2.0 → tweek-0.2.1}/tweek/proxy/server.py +0 -0
  127. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/__init__.py +0 -0
  128. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/docker_bridge.py +0 -0
  129. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/executor.py +0 -0
  130. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/layers.py +0 -0
  131. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/linux.py +0 -0
  132. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/profile_generator.py +0 -0
  133. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/project.py +0 -0
  134. {tweek-0.2.0 → tweek-0.2.1}/tweek/sandbox/registry.py +0 -0
  135. {tweek-0.2.0 → tweek-0.2.1}/tweek/screening/__init__.py +0 -0
  136. {tweek-0.2.0 → tweek-0.2.1}/tweek/screening/context.py +0 -0
  137. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/__init__.py +0 -0
  138. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/language.py +0 -0
  139. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/llm_reviewer.py +0 -0
  140. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/local_model.py +0 -0
  141. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/local_reviewer.py +0 -0
  142. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/model_registry.py +0 -0
  143. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/rate_limiter.py +0 -0
  144. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/secret_scanner.py +0 -0
  145. {tweek-0.2.0 → tweek-0.2.1}/tweek/security/session_analyzer.py +0 -0
  146. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/SKILL.md +0 -0
  147. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/__init__.py +0 -0
  148. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/cli-reference.md +0 -0
  149. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/overrides-reference.md +0 -0
  150. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/scripts/__init__.py +0 -0
  151. {tweek-0.2.0 → tweek-0.2.1}/tweek/skill_template/scripts/check_installed.py +0 -0
  152. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/__init__.py +0 -0
  153. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/config.py +0 -0
  154. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/fingerprints.py +0 -0
  155. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/guard.py +0 -0
  156. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/isolation.py +0 -0
  157. {tweek-0.2.0 → tweek-0.2.1}/tweek/skills/scanner.py +0 -0
  158. {tweek-0.2.0 → tweek-0.2.1}/tweek/vault/__init__.py +0 -0
  159. {tweek-0.2.0 → tweek-0.2.1}/tweek/vault/cross_platform.py +0 -0
  160. {tweek-0.2.0 → tweek-0.2.1}/tweek/vault/keychain.py +0 -0
  161. {tweek-0.2.0 → tweek-0.2.1}/tweek.egg-info/dependency_links.txt +0 -0
  162. {tweek-0.2.0 → tweek-0.2.1}/tweek.egg-info/entry_points.txt +0 -0
  163. {tweek-0.2.0 → tweek-0.2.1}/tweek.egg-info/requires.txt +0 -0
  164. {tweek-0.2.0 → tweek-0.2.1}/tweek.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tweek
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Defense-in-depth security for AI coding assistants - protect credentials, code, and system from prompt injection attacks
5
5
  Author: Tommy Mancino
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tweek"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Defense-in-depth security for AI coding assistants - protect credentials, code, and system from prompt injection attacks"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -184,41 +184,32 @@ class TestScopeSelection:
184
184
  assert "Installation Scope" not in result.output
185
185
  assert "global" in result.output.lower()
186
186
 
187
- def test_scope_defaults_to_project_in_git_repo(self, runner, tmp_path):
188
- """In a git repo, scope should default to project."""
189
- # Create a .git directory to simulate a git repo
190
- git_dir = tmp_path / "project" / ".git"
191
- git_dir.mkdir(parents=True)
192
- project_dir = tmp_path / "project"
193
-
187
+ def test_scope_defaults_to_global(self, runner, tmp_path):
188
+ """Scope should default to global (recommended)."""
194
189
  with patch.object(Path, 'home', return_value=tmp_path):
195
190
  with patch('tweek.cli.Path.home', return_value=tmp_path):
196
- with patch('tweek.cli.Path.cwd', return_value=project_dir):
197
- result = runner.invoke(
198
- main,
199
- ['install', '--skip-env-scan', '--skip-proxy-check'],
200
- input='1\n1\n', # Accept default scope (project), llm=auto
201
- catch_exceptions=False,
202
- )
203
-
204
- assert "Git repo detected" in result.output
191
+ result = runner.invoke(
192
+ main,
193
+ ['install', '--skip-env-scan', '--skip-proxy-check'],
194
+ input='1\n1\n', # Accept default scope (global), llm=auto
195
+ catch_exceptions=False,
196
+ )
205
197
 
206
- def test_scope_defaults_to_global_without_git(self, runner, tmp_path):
207
- """Without a git repo, scope should default to global."""
208
- no_git_dir = tmp_path / "no-git-project"
209
- no_git_dir.mkdir(parents=True)
198
+ assert "recommended" in result.output.lower()
199
+ assert "global" in result.output.lower()
210
200
 
201
+ def test_scope_option_2_is_project(self, runner, tmp_path):
202
+ """Selecting option 2 should install to project only."""
211
203
  with patch.object(Path, 'home', return_value=tmp_path):
212
204
  with patch('tweek.cli.Path.home', return_value=tmp_path):
213
- with patch('tweek.cli.Path.cwd', return_value=no_git_dir):
214
- result = runner.invoke(
215
- main,
216
- ['install', '--skip-env-scan', '--skip-proxy-check'],
217
- input='2\n1\n', # Accept default scope (global), llm=auto
218
- catch_exceptions=False,
219
- )
220
-
221
- assert "No git repo" in result.output
205
+ result = runner.invoke(
206
+ main,
207
+ ['install', '--skip-env-scan', '--skip-proxy-check'],
208
+ input='2\n1\n', # scope=project, llm=auto
209
+ catch_exceptions=False,
210
+ )
211
+
212
+ assert "project" in result.output.lower()
222
213
 
223
214
 
224
215
  # ═══════════════════════════════════════════════════════════════
@@ -10,7 +10,7 @@ Tweek provides:
10
10
  - Per-skill/per-tool security policies
11
11
  """
12
12
 
13
- __version__ = "0.2.0"
13
+ __version__ = "0.2.1"
14
14
  __author__ = "Tommy Mancino"
15
15
 
16
16
  # "TOO MUCH PRESSURE!" - Tweek Tweak
@@ -236,13 +236,13 @@ def install(install_global: bool, dev_test: bool, backup: bool, skip_env_scan: b
236
236
  else:
237
237
  console.print()
238
238
  console.print("[yellow]⚠ Claude Code not detected on this system[/yellow]")
239
- console.print(" [dim]Tweek hooks require Claude Code to function.[/dim]")
240
- console.print(" [dim]https://docs.anthropic.com/en/docs/claude-code[/dim]")
239
+ console.print(" [white]Tweek hooks require Claude Code to function.[/white]")
240
+ console.print(" [white]https://docs.anthropic.com/en/docs/claude-code[/white]")
241
241
  console.print()
242
242
  if quick or not click.confirm("Continue installing hooks anyway?", default=False):
243
243
  if not quick:
244
244
  console.print()
245
- console.print("[dim]Run 'tweek install' later after installing Claude Code.[/dim]")
245
+ console.print("[white]Run 'tweek install' later after installing Claude Code.[/white]")
246
246
  return
247
247
  console.print()
248
248
 
@@ -250,25 +250,16 @@ def install(install_global: bool, dev_test: bool, backup: bool, skip_env_scan: b
250
250
  # Step 2: Scope selection (always shown unless --global or --quick)
251
251
  # ─────────────────────────────────────────────────────────────
252
252
  if not install_global and not dev_test and not quick:
253
- # Smart default: if in a git repo, default to project; otherwise global
254
- in_git_repo = (Path.cwd() / ".git").exists()
255
- default_scope = 1 if in_git_repo else 2
256
-
257
253
  console.print()
258
254
  console.print("[bold]Installation Scope[/bold]")
259
255
  console.print()
260
- console.print(" [cyan]1.[/cyan] This project only (./.claude/)")
261
- console.print(" [dim]Protects only the current project[/dim]")
262
- console.print(" [cyan]2.[/cyan] All projects globally (~/.claude/)")
263
- console.print(" [dim]Protects every project on this machine[/dim]")
264
- console.print()
265
- if in_git_repo:
266
- console.print(f" [dim]Git repo detected — defaulting to project scope[/dim]")
267
- else:
268
- console.print(f" [dim]No git repo — defaulting to global scope[/dim]")
256
+ console.print(" [cyan]1.[/cyan] All projects globally (~/.claude/) [green](recommended)[/green]")
257
+ console.print(" [white]Protects every project on this machine[/white]")
258
+ console.print(" [cyan]2.[/cyan] This directory only (./.claude/)")
259
+ console.print(" [white]Protects only the current directory[/white]")
269
260
  console.print()
270
- scope_choice = click.prompt("Select", type=click.IntRange(1, 2), default=default_scope)
271
- if scope_choice == 2:
261
+ scope_choice = click.prompt("Select", type=click.IntRange(1, 2), default=1)
262
+ if scope_choice == 1:
272
263
  install_global = True
273
264
  console.print()
274
265
 
@@ -298,8 +289,8 @@ def install(install_global: bool, dev_test: bool, backup: bool, skip_env_scan: b
298
289
  with open(project_settings) as f:
299
290
  project_config = json.load(f)
300
291
  if _has_tweek_hooks(project_config):
301
- console.print("[dim]Note: Tweek is also installed in this project.[/dim]")
302
- console.print("[dim]Project-level settings take precedence over global.[/dim]")
292
+ console.print("[white]Note: Tweek is also installed in this project.[/white]")
293
+ console.print("[white]Project-level settings take precedence over global.[/white]")
303
294
  console.print()
304
295
  else:
305
296
  # Installing per-project — check if global hooks exist
@@ -308,8 +299,8 @@ def install(install_global: bool, dev_test: bool, backup: bool, skip_env_scan: b
308
299
  with open(global_settings) as f:
309
300
  global_config = json.load(f)
310
301
  if _has_tweek_hooks(global_config):
311
- console.print("[dim]Note: Tweek is also installed globally.[/dim]")
312
- console.print("[dim]Project-level settings will take precedence in this directory.[/dim]")
302
+ console.print("[white]Note: Tweek is also installed globally.[/white]")
303
+ console.print("[white]Project-level settings will take precedence in this directory.[/white]")
313
304
  console.print()
314
305
  except (json.JSONDecodeError, IOError):
315
306
  pass
@@ -804,14 +795,14 @@ def _check_python_version(console: Console, quick: bool) -> None:
804
795
  resolved_system = Path(system_python3).resolve()
805
796
 
806
797
  if resolved_install != resolved_system:
807
- console.print(f"[dim] Note: system python3 is {resolved_system}[/dim]")
808
- console.print(f"[dim] Hooks will use {resolved_install} (the Python running this install)[/dim]")
798
+ console.print(f"[white] Note: system python3 is {resolved_system}[/white]")
799
+ console.print(f"[white] Hooks will use {resolved_install} (the Python running this install)[/white]")
809
800
  except (OSError, ValueError):
810
801
  pass
811
802
  else:
812
803
  if not quick:
813
804
  console.print("[yellow] Note: python3 not found on PATH[/yellow]")
814
- console.print(f"[dim] Hooks will use {sys.executable} directly[/dim]")
805
+ console.print(f"[white] Hooks will use {sys.executable} directly[/white]")
815
806
 
816
807
 
817
808
  def _configure_llm_provider(tweek_dir: Path, interactive: bool, quick: bool) -> dict:
@@ -2185,6 +2176,114 @@ def status(verbose: bool, json_out: bool):
2185
2176
  print_doctor_results(checks)
2186
2177
 
2187
2178
 
2179
+ @main.command("upgrade")
2180
+ def upgrade():
2181
+ """Upgrade Tweek to the latest version from PyPI.
2182
+
2183
+ Detects how Tweek was installed (uv, pipx, or pip) and runs
2184
+ the appropriate upgrade command.
2185
+ """
2186
+ import subprocess
2187
+
2188
+ console.print("[cyan]Checking for updates...[/cyan]")
2189
+ console.print()
2190
+
2191
+ current_version = None
2192
+ try:
2193
+ from tweek import __version__
2194
+ current_version = __version__
2195
+ console.print(f" Current version: [bold]{current_version}[/bold]")
2196
+ except ImportError:
2197
+ pass
2198
+
2199
+ # Detect install method and upgrade
2200
+ upgraded = False
2201
+
2202
+ # Try uv first
2203
+ try:
2204
+ result = subprocess.run(
2205
+ ["uv", "tool", "list"], capture_output=True, text=True, timeout=10
2206
+ )
2207
+ if result.returncode == 0 and "tweek" in result.stdout:
2208
+ console.print(" Install method: [cyan]uv[/cyan]")
2209
+ console.print()
2210
+ console.print("[white]Upgrading via uv...[/white]")
2211
+ proc = subprocess.run(
2212
+ ["uv", "tool", "upgrade", "tweek"],
2213
+ capture_output=False, timeout=120
2214
+ )
2215
+ if proc.returncode == 0:
2216
+ upgraded = True
2217
+ else:
2218
+ console.print("[yellow]uv upgrade failed, trying reinstall...[/yellow]")
2219
+ subprocess.run(
2220
+ ["uv", "tool", "install", "--force", "tweek"],
2221
+ capture_output=False, timeout=120
2222
+ )
2223
+ upgraded = True
2224
+ except (FileNotFoundError, subprocess.TimeoutExpired):
2225
+ pass
2226
+
2227
+ # Try pipx
2228
+ if not upgraded:
2229
+ try:
2230
+ result = subprocess.run(
2231
+ ["pipx", "list"], capture_output=True, text=True, timeout=10
2232
+ )
2233
+ if result.returncode == 0 and "tweek" in result.stdout:
2234
+ console.print(" Install method: [cyan]pipx[/cyan]")
2235
+ console.print()
2236
+ console.print("[white]Upgrading via pipx...[/white]")
2237
+ proc = subprocess.run(
2238
+ ["pipx", "upgrade", "tweek"],
2239
+ capture_output=False, timeout=120
2240
+ )
2241
+ upgraded = proc.returncode == 0
2242
+ except (FileNotFoundError, subprocess.TimeoutExpired):
2243
+ pass
2244
+
2245
+ # Try pip
2246
+ if not upgraded:
2247
+ try:
2248
+ result = subprocess.run(
2249
+ [sys.executable, "-m", "pip", "show", "tweek"],
2250
+ capture_output=True, text=True, timeout=10
2251
+ )
2252
+ if result.returncode == 0:
2253
+ console.print(" Install method: [cyan]pip[/cyan]")
2254
+ console.print()
2255
+ console.print("[white]Upgrading via pip...[/white]")
2256
+ proc = subprocess.run(
2257
+ [sys.executable, "-m", "pip", "install", "--upgrade", "tweek"],
2258
+ capture_output=False, timeout=120
2259
+ )
2260
+ upgraded = proc.returncode == 0
2261
+ except (FileNotFoundError, subprocess.TimeoutExpired):
2262
+ pass
2263
+
2264
+ if not upgraded:
2265
+ console.print("[red]Could not determine install method.[/red]")
2266
+ console.print("[white]Try manually:[/white]")
2267
+ console.print(" uv tool upgrade tweek")
2268
+ console.print(" pipx upgrade tweek")
2269
+ console.print(" pip install --upgrade tweek")
2270
+ return
2271
+
2272
+ # Show new version
2273
+ console.print()
2274
+ try:
2275
+ result = subprocess.run(
2276
+ ["tweek", "--version"], capture_output=True, text=True, timeout=10
2277
+ )
2278
+ if result.returncode == 0:
2279
+ new_version = result.stdout.strip()
2280
+ console.print(f"[green]✓[/green] Updated to {new_version}")
2281
+ else:
2282
+ console.print("[green]✓[/green] Update complete")
2283
+ except (FileNotFoundError, subprocess.TimeoutExpired):
2284
+ console.print("[green]✓[/green] Update complete")
2285
+
2286
+
2188
2287
  @main.command(
2189
2288
  epilog="""\b
2190
2289
  Examples:
@@ -0,0 +1,149 @@
1
+ # ISC License
2
+ #
3
+ # Copyright (c) 2018-2025, Andrea Giammarchi, @WebReflection
4
+ #
5
+ # Permission to use, copy, modify, and/or distribute this software for any
6
+ # purpose with or without fee is hereby granted, provided that the above
7
+ # copyright notice and this permission notice appear in all copies.
8
+ #
9
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14
+ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ # PERFORMANCE OF THIS SOFTWARE.
16
+
17
+ import json as _json
18
+
19
+ class _Known:
20
+ def __init__(self):
21
+ self.key = []
22
+ self.value = []
23
+
24
+ class _String:
25
+ def __init__(self, value):
26
+ self.value = value
27
+
28
+
29
+ def _array_keys(value):
30
+ keys = []
31
+ i = 0
32
+ for _ in value:
33
+ keys.append(i)
34
+ i += 1
35
+ return keys
36
+
37
+ def _object_keys(value):
38
+ keys = []
39
+ for key in value:
40
+ keys.append(key)
41
+ return keys
42
+
43
+ def _is_array(value):
44
+ return isinstance(value, (list, tuple))
45
+
46
+ def _is_object(value):
47
+ return isinstance(value, dict)
48
+
49
+ def _is_string(value):
50
+ return isinstance(value, str)
51
+
52
+ def _index(known, input, value):
53
+ input.append(value)
54
+ index = str(len(input) - 1)
55
+ known.key.append(value)
56
+ known.value.append(index)
57
+ return index
58
+
59
+ def _loop(keys, input, known, output):
60
+ for key in keys:
61
+ value = output[key]
62
+ if isinstance(value, _String):
63
+ _ref(key, input[int(value.value)], input, known, output)
64
+
65
+ return output
66
+
67
+ def _ref(key, value, input, known, output):
68
+ if _is_array(value) and value not in known:
69
+ known.append(value)
70
+ value = _loop(_array_keys(value), input, known, value)
71
+ elif _is_object(value) and value not in known:
72
+ known.append(value)
73
+ value = _loop(_object_keys(value), input, known, value)
74
+
75
+ output[key] = value
76
+
77
+ def _relate(known, input, value):
78
+ if _is_string(value) or _is_array(value) or _is_object(value):
79
+ try:
80
+ return known.value[known.key.index(value)]
81
+ except:
82
+ return _index(known, input, value)
83
+
84
+ return value
85
+
86
+ def _transform(known, input, value):
87
+ if _is_array(value):
88
+ output = []
89
+ for val in value:
90
+ output.append(_relate(known, input, val))
91
+ return output
92
+
93
+ if _is_object(value):
94
+ obj = {}
95
+ for key in value:
96
+ obj[key] = _relate(known, input, value[key])
97
+ return obj
98
+
99
+ return value
100
+
101
+ def _wrap(value):
102
+ if _is_string(value):
103
+ return _String(value)
104
+
105
+ if _is_array(value):
106
+ i = 0
107
+ for val in value:
108
+ value[i] = _wrap(val)
109
+ i += 1
110
+
111
+ elif _is_object(value):
112
+ for key in value:
113
+ value[key] = _wrap(value[key])
114
+
115
+ return value
116
+
117
+ def parse(value, *args, **kwargs):
118
+ json = _json.loads(value, *args, **kwargs)
119
+ wrapped = []
120
+ for value in json:
121
+ wrapped.append(_wrap(value))
122
+
123
+ input = []
124
+ for value in wrapped:
125
+ if isinstance(value, _String):
126
+ input.append(value.value)
127
+ else:
128
+ input.append(value)
129
+
130
+ value = input[0]
131
+
132
+ if _is_array(value):
133
+ return _loop(_array_keys(value), input, [value], value)
134
+
135
+ if _is_object(value):
136
+ return _loop(_object_keys(value), input, [value], value)
137
+
138
+ return value
139
+
140
+
141
+ def stringify(value, *args, **kwargs):
142
+ known = _Known()
143
+ input = []
144
+ output = []
145
+ i = int(_index(known, input, value))
146
+ while i < len(input):
147
+ output.append(_transform(known, input, input[i]))
148
+ i += 1
149
+ return _json.dumps(output, *args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tweek
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Defense-in-depth security for AI coding assistants - protect credentials, code, and system from prompt injection attacks
5
5
  Author: Tommy Mancino
6
6
  License-Expression: Apache-2.0
@@ -46,6 +46,7 @@ tweek/cli_helpers.py
46
46
  tweek/cli_model.py
47
47
  tweek/diagnostics.py
48
48
  tweek/licensing.py
49
+ tweek-openclaw-plugin/node_modules/flatted/python/flatted.py
49
50
  tweek.egg-info/PKG-INFO
50
51
  tweek.egg-info/SOURCES.txt
51
52
  tweek.egg-info/dependency_links.txt
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
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