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.
- package/package.json +2 -4
- package/.github/workflows/ci.yml +0 -72
- package/.smithery/shttp/module.js +0 -3
- package/.smithery/shttp/module.js.map +0 -7
- package/.smithery/stdio/index.cjs +0 -74
- package/.smithery/stdio/index.cjs.map +0 -7
- package/CHANGELOG.md +0 -65
- package/CODE_OF_CONDUCT.md +0 -34
- package/COMPARISON.md +0 -20
- package/CONTRIBUTING.md +0 -137
- package/DEVELOPMENT.md +0 -109
- package/LICENSE +0 -21
- package/README.md +0 -319
- package/ROADMAP.md +0 -24
- package/SECURITY.md +0 -88
- package/docs/COMMUNITY_FAQ.md +0 -48
- package/docs/COMPETITIVE_ANALYSIS.md +0 -90
- package/docs/DEMO_SCRIPT.md +0 -86
- package/docs/ENTERPRISE.md +0 -169
- package/docs/FAQ.md +0 -228
- package/docs/QUICK_START.md +0 -155
- package/docs/README.md +0 -67
- package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
- package/docs/social-preview-hq.png +0 -0
- package/docs/social-preview.png +0 -0
- package/examples/basic_usage.py +0 -84
- package/package.json.real +0 -24
- package/pyproject.toml +0 -50
- package/registry-server.json +0 -20
- package/scripts/demo_governance.py +0 -109
- package/scripts/sse_bridge.py +0 -87
- package/scripts/verify_windows_paths.py +0 -48
- package/server.json +0 -20
- package/src/mcp_server_nucleus/__init__.py +0 -8583
- package/src/mcp_server_nucleus/__main__.py +0 -9
- package/src/mcp_server_nucleus/cli.py +0 -1225
- package/src/mcp_server_nucleus/commitment_core.py +0 -146
- package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
- package/src/mcp_server_nucleus/core/__init__.py +0 -6
- package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
- package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
- package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
- package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
- package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
- package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
- package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
- package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
- package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
- package/src/mcp_server_nucleus/runtime/agent.py +0 -517
- package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
- package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
- package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
- package/src/mcp_server_nucleus/runtime/auth.py +0 -98
- package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
- package/src/mcp_server_nucleus/runtime/broker.py +0 -126
- package/src/mcp_server_nucleus/runtime/budget.py +0 -153
- package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
- package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
- package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
- package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
- package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
- package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
- package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
- package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
- package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
- package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
- package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
- package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
- package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
- package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
- package/src/mcp_server_nucleus/runtime/common.py +0 -134
- package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
- package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
- package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
- package/src/mcp_server_nucleus/runtime/critic.py +0 -150
- package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
- package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
- package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
- package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
- package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
- package/src/mcp_server_nucleus/runtime/export.py +0 -64
- package/src/mcp_server_nucleus/runtime/factory.py +0 -594
- package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
- package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
- package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
- package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
- package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
- package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
- package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
- package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
- package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
- package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
- package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
- package/src/mcp_server_nucleus/runtime/installer.py +0 -81
- package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
- package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
- package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
- package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
- package/src/mcp_server_nucleus/runtime/locking.py +0 -228
- package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
- package/src/mcp_server_nucleus/runtime/memory.py +0 -158
- package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
- package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
- package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
- package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
- package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
- package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
- package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
- package/src/mcp_server_nucleus/runtime/policy.py +0 -117
- package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
- package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
- package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
- package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
- package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
- package/src/mcp_server_nucleus/runtime/registry.py +0 -82
- package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
- package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
- package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
- package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
- package/src/mcp_server_nucleus/runtime/storage.py +0 -83
- package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
- package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
- package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
- package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
- package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
- package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
- package/src/mcp_server_nucleus/runtime/team.py +0 -59
- package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
- package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
- package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
- package/src/mcp_server_nucleus/tool_tiers.py +0 -243
- package/tests/__init__.py +0 -1
- package/tests/conftest.py +0 -42
- package/tests/test_core.py +0 -235
- 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."
|