nucleus-mcp 1.0.7 → 1.2.0

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 (135) hide show
  1. package/package.json +2 -4
  2. package/.github/workflows/ci.yml +0 -72
  3. package/.smithery/shttp/module.js +0 -3
  4. package/.smithery/shttp/module.js.map +0 -7
  5. package/.smithery/stdio/index.cjs +0 -74
  6. package/.smithery/stdio/index.cjs.map +0 -7
  7. package/CHANGELOG.md +0 -65
  8. package/CODE_OF_CONDUCT.md +0 -34
  9. package/COMPARISON.md +0 -20
  10. package/CONTRIBUTING.md +0 -137
  11. package/DEVELOPMENT.md +0 -109
  12. package/LICENSE +0 -21
  13. package/README.md +0 -319
  14. package/ROADMAP.md +0 -24
  15. package/SECURITY.md +0 -88
  16. package/docs/COMMUNITY_FAQ.md +0 -48
  17. package/docs/COMPETITIVE_ANALYSIS.md +0 -90
  18. package/docs/DEMO_SCRIPT.md +0 -86
  19. package/docs/ENTERPRISE.md +0 -169
  20. package/docs/FAQ.md +0 -228
  21. package/docs/QUICK_START.md +0 -155
  22. package/docs/README.md +0 -67
  23. package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
  24. package/docs/social-preview-hq.png +0 -0
  25. package/docs/social-preview.png +0 -0
  26. package/examples/basic_usage.py +0 -84
  27. package/package.json.real +0 -24
  28. package/pyproject.toml +0 -50
  29. package/registry-server.json +0 -20
  30. package/scripts/demo_governance.py +0 -109
  31. package/scripts/sse_bridge.py +0 -87
  32. package/scripts/verify_windows_paths.py +0 -48
  33. package/server.json +0 -20
  34. package/src/mcp_server_nucleus/__init__.py +0 -8583
  35. package/src/mcp_server_nucleus/__main__.py +0 -9
  36. package/src/mcp_server_nucleus/cli.py +0 -1225
  37. package/src/mcp_server_nucleus/commitment_core.py +0 -146
  38. package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
  39. package/src/mcp_server_nucleus/core/__init__.py +0 -6
  40. package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
  41. package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
  42. package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
  43. package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
  44. package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
  45. package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
  46. package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
  47. package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
  48. package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
  49. package/src/mcp_server_nucleus/runtime/agent.py +0 -517
  50. package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
  51. package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
  52. package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
  53. package/src/mcp_server_nucleus/runtime/auth.py +0 -98
  54. package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
  55. package/src/mcp_server_nucleus/runtime/broker.py +0 -126
  56. package/src/mcp_server_nucleus/runtime/budget.py +0 -153
  57. package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
  58. package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
  59. package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
  60. package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
  61. package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
  62. package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
  63. package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
  64. package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
  65. package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
  66. package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
  67. package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
  68. package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
  69. package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
  70. package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
  71. package/src/mcp_server_nucleus/runtime/common.py +0 -134
  72. package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
  73. package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
  74. package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
  75. package/src/mcp_server_nucleus/runtime/critic.py +0 -150
  76. package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
  77. package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
  78. package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
  79. package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
  80. package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
  81. package/src/mcp_server_nucleus/runtime/export.py +0 -64
  82. package/src/mcp_server_nucleus/runtime/factory.py +0 -594
  83. package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
  84. package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
  85. package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
  86. package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
  87. package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
  88. package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
  89. package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
  90. package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
  91. package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
  92. package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
  93. package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
  94. package/src/mcp_server_nucleus/runtime/installer.py +0 -81
  95. package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
  96. package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
  97. package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
  98. package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
  99. package/src/mcp_server_nucleus/runtime/locking.py +0 -228
  100. package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
  101. package/src/mcp_server_nucleus/runtime/memory.py +0 -158
  102. package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
  103. package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
  104. package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
  105. package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
  106. package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
  107. package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
  108. package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
  109. package/src/mcp_server_nucleus/runtime/policy.py +0 -117
  110. package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
  111. package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
  112. package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
  113. package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
  114. package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
  115. package/src/mcp_server_nucleus/runtime/registry.py +0 -82
  116. package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
  117. package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
  118. package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
  119. package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
  120. package/src/mcp_server_nucleus/runtime/storage.py +0 -83
  121. package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
  122. package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
  123. package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
  124. package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
  125. package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
  126. package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
  127. package/src/mcp_server_nucleus/runtime/team.py +0 -59
  128. package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
  129. package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
  130. package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
  131. package/src/mcp_server_nucleus/tool_tiers.py +0 -243
  132. package/tests/__init__.py +0 -1
  133. package/tests/conftest.py +0 -42
  134. package/tests/test_core.py +0 -235
  135. package/tests/test_search.py +0 -69
@@ -1,205 +0,0 @@
1
- """
2
- Nucleus Marketing Engine
3
- ========================
4
- The Content Brain for GentleQuest.
5
- Auto-generates strategy and content from raw logs.
6
-
7
- Capabilities:
8
- - brain_synthesize_strategy: Reads marketing_log.md -> Updates strategy.md
9
- """
10
-
11
- import os
12
- from pathlib import Path
13
- from typing import Dict, Optional
14
- import logging
15
- import time
16
-
17
- try:
18
- from google import genai
19
- HAS_GENAI = True
20
- except ImportError:
21
- HAS_GENAI = False
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
- # Constants
26
- MARKETING_LOG_PATH = "docs/marketing/marketing_log.md"
27
- STRATEGY_PATH = "docs/marketing/strategy.md"
28
-
29
- def _get_api_key() -> Optional[str]:
30
- return os.environ.get("GEMINI_API_KEY")
31
-
32
- def brain_synthesize_strategy(
33
- project_root: str,
34
- focus_topic: Optional[str] = None
35
- ) -> Dict[str, str]:
36
- """
37
- Analyze marketing logs and update the strategy document.
38
-
39
- Args:
40
- project_root: Absolute path to project root
41
- focus_topic: Optional specific topic to focus on
42
-
43
- Returns:
44
- Dict with status and path to updated strategy
45
- """
46
- if not HAS_GENAI:
47
- return {"status": "error", "message": "google-genai library not installed"}
48
-
49
- api_key = _get_api_key()
50
- if not api_key:
51
- return {"status": "error", "message": "GEMINI_API_KEY not found in environment"}
52
-
53
- root_path = Path(project_root)
54
- log_path = root_path / MARKETING_LOG_PATH
55
- strategy_path = root_path / STRATEGY_PATH
56
-
57
- if not log_path.exists():
58
- return {"status": "error", "message": f"Log file not found at {log_path}"}
59
-
60
- # Read the log
61
- with open(log_path, 'r') as f:
62
- log_content = f.read()
63
-
64
- # Read existing strategy if valid
65
- existing_strategy = ""
66
- if strategy_path.exists():
67
- with open(strategy_path, 'r') as f:
68
- existing_strategy = f.read()
69
-
70
- # Construct Prompt
71
- prompt = f"""
72
- You are the Chief Strategy Officer for GentleQuest (an AI mental health app for developers).
73
-
74
- TASK:
75
- Analyze the daily marketing log and update the High-Level Strategy.
76
-
77
- INPUT DATA (Marketing Log):
78
- {log_content[-2000:]} # Last 2000 chars for context
79
-
80
- EXISTING STRATEGY:
81
- {existing_strategy}
82
-
83
- INSTRUCTIONS:
84
- 1. Identify 'Rising Trends' and 'High Engagement' items from the log.
85
- 2. Synthesize them into concrete strategic 'Angles' (e.g., "Pivot to Anti-Streak messaging").
86
- 3. Output the FULL content for the new 'strategy.md' file.
87
- 4. Keep it concise, actionable, and formatted in Markdown.
88
- 5. Include a 'Recent Insights' section based on the logs.
89
-
90
- OUTPUT FORMAT:
91
- (Markdown only)
92
- """
93
-
94
- try:
95
- client = genai.Client(api_key=api_key)
96
- response = client.models.generate_content(
97
- model="gemini-2.0-flash-exp",
98
- contents=prompt
99
- )
100
-
101
- new_strategy = response.text
102
-
103
- # Write to file
104
- strategy_path.parent.mkdir(parents=True, exist_ok=True)
105
- with open(strategy_path, 'w') as f:
106
- f.write(new_strategy)
107
-
108
- except Exception as e:
109
- logger.error(f"GenAI Strategy Synthesis failed: {e}")
110
- return {"status": "error", "message": str(e)}
111
-
112
- def brain_optimize_workflow(
113
- project_root: str
114
- ) -> Dict[str, str]:
115
- """
116
- Scan marketing logs for 'META-FEEDBACK' and suggest workflow improvements.
117
-
118
- Args:
119
- project_root: Absolute path to project root
120
-
121
- Returns:
122
- Dict with status and suggestions
123
- """
124
- if not HAS_GENAI:
125
- return {"status": "error", "message": "google-genai library not installed"}
126
-
127
- api_key = _get_api_key()
128
- if not api_key:
129
- return {"status": "error", "message": "GEMINI_API_KEY not found"}
130
-
131
- root_path = Path(project_root)
132
- log_path = root_path / MARKETING_LOG_PATH
133
- cheatsheet_path = root_path / ".agent/workflows/marketing_autopilot_cheatsheet.md"
134
-
135
- if not log_path.exists():
136
- return {"status": "error", "message": "Log file not found"}
137
-
138
- with open(log_path, 'r') as f:
139
- log_content = f.read()
140
-
141
- # Check if there is any meta-feedback
142
- if "META-FEEDBACK" not in log_content:
143
- return {"status": "skipped", "message": "No META-FEEDBACK found in logs."}
144
-
145
- # Read existing cheatsheet for context
146
- current_cheatsheet = ""
147
- if cheatsheet_path.exists():
148
- with open(cheatsheet_path, 'r') as f:
149
- current_cheatsheet = f.read()
150
-
151
- prompt = f"""
152
- You are the 'Process Optimizer' for the Marketing Autopilot.
153
-
154
- INPUT DATA:
155
- 1. Marketing Log (Looking for 'META-FEEDBACK' tags):
156
- {log_content[-5000:]}
157
-
158
- 2. Current Cheatsheet (The Rules):
159
- {current_cheatsheet}
160
-
161
- TASK:
162
- 1. Extract all 'META-FEEDBACK' suggestions from the log.
163
- 2. Analyze if the Cheatsheet needs updating.
164
- 3. Generate a list of actionable tasks for 'task.md'.
165
-
166
- OUTPUT FORMAT:
167
- (Markdown Checklist Items Only)
168
- - [ ] Update Cheatsheet: [Specific Prompt Change]
169
- - [ ] Fix Dashboard UI: [Specific Bug]
170
- """
171
-
172
- try:
173
- client = genai.Client(api_key=api_key)
174
- response = client.models.generate_content(
175
- model="gemini-2.0-flash-exp",
176
- contents=prompt
177
- )
178
-
179
- new_tasks = response.text
180
-
181
- # Determine where to write tasks
182
- # Plan A: Try to find the specific conversation task.md (This is hard from here without context)
183
- # Plan B: Write to the Central Ledger (V2) or a dedicated improvements file
184
-
185
- # We'll use a dedicated file for Workflow Improvements that the Human can triage
186
- # This avoids messing up specific task.lhs if IDs change
187
- improvement_path = root_path / ".brain/ledger/WORKFLOW_IMPROVEMENTS.md"
188
- improvement_path.parent.mkdir(parents=True, exist_ok=True)
189
-
190
- # If the file exists, append. If not, create.
191
- mode = 'a' if improvement_path.exists() else 'w'
192
- with open(improvement_path, mode) as f:
193
- timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
194
- f.write(f"\n\n## 🧬 Workflow Upgrades (Auto-Generated {timestamp})\n{new_tasks}")
195
-
196
- return {
197
- "status": "success",
198
- "message": "Optimization tasks generated in WORKFLOW_IMPROVEMENTS.md",
199
- "path": str(improvement_path)
200
- }
201
-
202
- except Exception as e:
203
- logger.error(f"Workflow Optimization failed: {e}")
204
- return {"status": "error", "message": str(e)}
205
-
@@ -1,79 +0,0 @@
1
-
2
- from typing import List, Dict, Any
3
- from .base import Capability
4
- from ..vector_store import VectorStore
5
-
6
- class MemoryOps(Capability):
7
- """Capability for Long-term Memory Access (RAG)"""
8
-
9
- def __init__(self):
10
- self.vector_store = VectorStore()
11
-
12
- @property
13
- def name(self) -> str:
14
- return "memory_ops"
15
-
16
- @property
17
- def description(self) -> str:
18
- return "Tools for accessing and searching long-term memory (RAG)."
19
-
20
- def get_tools(self) -> List[Dict]:
21
- return [
22
- {
23
- "name": "brain_store_memory",
24
- "description": "Store a text chunk in long-term memory with metadata.",
25
- "parameters": {
26
- "type": "object",
27
- "properties": {
28
- "content": {"type": "string", "description": "The text content to remember"},
29
- "category": {"type": "string", "description": "Category (e.g., learning, preference, fact)"},
30
- "source": {"type": "string", "description": "Source of info (e.g., session_id, user)"},
31
- "tags": {"type": "array", "items": {"type": "string"}, "description": "Optional tags"}
32
- },
33
- "required": ["content"]
34
- }
35
- },
36
- {
37
- "name": "brain_search_memory",
38
- "description": "Semantic search for relevant memories.",
39
- "parameters": {
40
- "type": "object",
41
- "properties": {
42
- "query": {"type": "string", "description": "Search query"},
43
- "limit": {"type": "integer", "description": "Max results (default 5)"}
44
- },
45
- "required": ["query"]
46
- }
47
- }
48
- ]
49
-
50
- def execute_tool(self, tool_name: str, args: Dict) -> Any:
51
- try:
52
- if tool_name == "brain_store_memory":
53
- metadata = {
54
- "category": args.get("category", "general"),
55
- "source": args.get("source", "agent"),
56
- "tags": args.get("tags", [])
57
- }
58
- doc_id = self.vector_store.store_memory(args["content"], metadata)
59
- return f"Stored memory: {doc_id}"
60
-
61
- elif tool_name == "brain_search_memory":
62
- results = self.vector_store.search_memory(
63
- query=args["query"],
64
- limit=args.get("limit", 5)
65
- )
66
- if not results:
67
- return "No matching memories found."
68
-
69
- # Format for LLM consumption
70
- formatted = []
71
- for r in results:
72
- meta = r.get("metadata", {})
73
- formatted.append(f"- [{meta.get('category', 'gen')}] {r['content']}")
74
-
75
- return "\n".join(formatted)
76
-
77
- return f"Tool {tool_name} not found"
78
- except Exception as e:
79
- return f"Error executing {tool_name}: {str(e)}"
@@ -1,127 +0,0 @@
1
- from typing import List, Dict, Any
2
- import os
3
- import time
4
- from pathlib import Path
5
- from .base import Capability
6
-
7
- class ProofSystem(Capability):
8
- def __init__(self):
9
- # Determine brain path
10
- brain_path_str = os.environ.get("NUCLEAR_BRAIN_PATH")
11
- if not brain_path_str:
12
- brain_path_str = "/Users/lokeshgarg/.gemini/antigravity/brain/7c654df4-b83e-43f9-8620-f15868ec39d1"
13
-
14
- self.brain_path = Path(brain_path_str)
15
- self.proofs_dir = self.brain_path / "features" / "proofs"
16
- self.proofs_dir.mkdir(parents=True, exist_ok=True)
17
-
18
- @property
19
- def name(self) -> str:
20
- return "proof_system"
21
-
22
- @property
23
- def description(self) -> str:
24
- return "Generates tangible evidence of work (proofs) to build trust and transparency."
25
-
26
- def get_tools(self) -> List[Dict[str, Any]]:
27
- return [
28
- {
29
- "name": "brain_generate_proof",
30
- "description": "Generate a markdown proof document for a feature.",
31
- "parameters": {
32
- "type": "object",
33
- "properties": {
34
- "feature_id": {"type": "string", "description": "Feature to generate proof for"},
35
- "thinking": {"type": "string", "description": "AI's decision-making process (markdown)"},
36
- "deployed_url": {"type": "string", "description": "Production URL"},
37
- "files_changed": {"type": "array", "items": {"type": "string"}, "description": "List of files modified"},
38
- "risk_level": {"type": "string", "description": "low/medium/high"},
39
- "rollback_time": {"type": "string", "description": "Estimated time to rollback"}
40
- },
41
- "required": ["feature_id"]
42
- }
43
- },
44
- {
45
- "name": "brain_get_proof",
46
- "description": "Get the proof document for a feature.",
47
- "parameters": {
48
- "type": "object",
49
- "properties": {
50
- "feature_id": {"type": "string", "description": "Feature ID to get proof for"}
51
- },
52
- "required": ["feature_id"]
53
- }
54
- },
55
- {
56
- "name": "brain_list_proofs",
57
- "description": "List all proof documents.",
58
- "parameters": {
59
- "type": "object",
60
- "properties": {}
61
- }
62
- }
63
- ]
64
-
65
- def execute_tool(self, tool_name: str, args: Dict) -> Any:
66
- try:
67
- if tool_name == "brain_generate_proof":
68
- return self._generate_proof(args)
69
- elif tool_name == "brain_get_proof":
70
- return self._get_proof(args.get("feature_id"))
71
- elif tool_name == "brain_list_proofs":
72
- return self._list_proofs()
73
- return f"Tool {tool_name} not found"
74
- except Exception as e:
75
- return f"Error executing {tool_name}: {str(e)}"
76
-
77
- def _generate_proof(self, args: Dict) -> Dict:
78
- feature_id = args.get("feature_id")
79
- thinking = args.get("thinking", "No thinking provided.")
80
- deployed_url = args.get("deployed_url", "N/A")
81
- files_changed = args.get("files_changed", [])
82
- risk_level = args.get("risk_level", "low")
83
- rollback_time = args.get("rollback_time", "Unknown")
84
-
85
- # Format the proof markdown (Tier 1 Style)
86
- timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
87
-
88
- files_block = "\n".join([f"- {f}" for f in files_changed]) if files_changed else "None"
89
-
90
- content = f"""# Proof: {feature_id}
91
-
92
- > Generated: {timestamp}
93
-
94
- ## Thinking
95
- {thinking}
96
-
97
- ## Deployed URL
98
- {deployed_url}
99
-
100
- ## Files Changed
101
- {files_block}
102
-
103
- ## Rollback Plan
104
- - **Risk Level:** {risk_level.upper()}
105
- - **Estimated Time:** {rollback_time}
106
- - **Strategy:** `git revert` or restore from backup.
107
- """
108
-
109
- file_path = self.proofs_dir / f"{feature_id}.md"
110
- file_path.write_text(content)
111
-
112
- return {
113
- "success": True,
114
- "message": f"Proof generated for {feature_id}",
115
- "path": str(file_path)
116
- }
117
-
118
- def _get_proof(self, feature_id: str) -> str:
119
- file_path = self.proofs_dir / f"{feature_id}.md"
120
- if not file_path.exists():
121
- return f"Proof for {feature_id} not found."
122
- return file_path.read_text()
123
-
124
- def _list_proofs(self) -> List[str]:
125
- if not self.proofs_dir.exists():
126
- return []
127
- return [p.name for p in self.proofs_dir.glob("*.md")]
@@ -1,80 +0,0 @@
1
- from typing import List, Dict, Any
2
- import os
3
- import json
4
- import urllib.request
5
- import urllib.error
6
- from .base import Capability
7
-
8
- class RenderOps(Capability):
9
- def __init__(self):
10
- self.api_key = os.environ.get("RENDER_API_KEY")
11
- self.base_url = "https://api.render.com/v1"
12
-
13
- @property
14
- def name(self) -> str:
15
- return "render_ops"
16
-
17
- @property
18
- def description(self) -> str:
19
- return "Tools for interacting with the Render.com API (Deployments, Services)."
20
-
21
- def get_tools(self) -> List[Dict[str, Any]]:
22
- return [
23
- {
24
- "name": "render_list_services",
25
- "description": "List all services in Render account.",
26
- "parameters": {"type": "object", "properties": {}}
27
- },
28
- {
29
- "name": "render_deploy_service",
30
- "description": "Trigger a deployment for a specific service.",
31
- "parameters": {
32
- "type": "object",
33
- "properties": {
34
- "service_id": {"type": "string"}
35
- },
36
- "required": ["service_id"]
37
- }
38
- }
39
- ]
40
-
41
- def execute_tool(self, tool_name: str, args: Dict) -> str:
42
- if not self.api_key:
43
- return "Error: RENDER_API_KEY not found in environment."
44
-
45
- headers = {
46
- "Authorization": f"Bearer {self.api_key}",
47
- "Content-Type": "application/json",
48
- "Accept": "application/json"
49
- }
50
-
51
- try:
52
- if tool_name == "render_list_services":
53
- url = f"{self.base_url}/services?limit=20"
54
- req = urllib.request.Request(url, headers=headers)
55
-
56
- with urllib.request.urlopen(req) as response:
57
- if response.status == 200:
58
- data = json.load(response)
59
- services = [f"{s['service']['name']} ({s['service']['id']})" for s in data]
60
- return "Services:\n" + "\n".join(services)
61
- return f"Error listing services: HTTP {response.status}"
62
-
63
- elif tool_name == "render_deploy_service":
64
- sid = args.get('service_id')
65
- url = f"{self.base_url}/services/{sid}/deploys"
66
- data = json.dumps({"clearCache": "clear"}).encode('utf-8')
67
- req = urllib.request.Request(url, data=data, headers=headers, method="POST")
68
-
69
- with urllib.request.urlopen(req) as response:
70
- if response.status == 201:
71
- deploy = json.load(response)
72
- return f"Deployment triggered: {deploy['id']}"
73
- return f"Error triggering deploy: HTTP {response.status}"
74
-
75
- except urllib.error.HTTPError as e:
76
- return f"Render API Error ({e.code}): {e.read().decode('utf-8')}"
77
- except Exception as e:
78
- return f"Exception calling Render API: {e}"
79
-
80
- return f"Tool {tool_name} not found in RenderOps."