Jake Shore d5e86e050b MCP Pipeline + workspace sync — 2026-02-06
=== WHAT'S BEEN DONE (Recent) ===

MCP Pipeline Factory:
- 38 MCP servers tracked across 7 pipeline stages
- 31 servers at Stage 16 (Website Built) — ready to deploy
- All 30 production servers patched to 100/100 protocol compliance
- Built complete testing infra: mcp-jest, mcp-validator, mcp-add, MCP Inspector
- 702 auto-generated test cases ready for live API testing
- Autonomous pipeline operator system w/ 7 Discord channels + cron jobs
- Dashboard live at 192.168.0.25:8888 (drag-drop kanban)

CloseBot MCP:
- 119 tools, 4,656 lines TypeScript, compiles clean
- 14 modules (8 tool groups + 6 UI apps)

GHL MCP:
- Stage 11 (Edge Case Testing) — 42 failing tests identified

Sub-agent _meta Labels:
- All 643 tools across 5 MCPs tagged (GHL, Google Ads, Meta Ads, Google Console, Twilio)

OpenClaw Upwork Launch:
- 15 graphics, 6 mockups, 2 PDFs, 90-sec Remotion video
- 3-tier pricing: $2,499 / $7,499 / $24,999
- First $20k deal closed + $2k/mo retainer (hospice)

Other:
- Surya Blender animation scripts (7 tracks)
- Clawdbot architecture deep dive doc
- Pipeline state.json updates

=== TO-DO (Open Items) ===

BLOCKERS:
- [ ] GHL MCP: Fix 42 failing edge case tests (Stage 11)
- [ ] Expired Anthropic API key in localbosses-app .env.local
- [ ] Testing strategy decision: structural vs live API vs hybrid

NEEDS API KEYS (can't progress without):
- [ ] Meta Ads MCP — needs META_ADS_API_KEY for Stage 8→9
- [ ] Twilio MCP — needs TWILIO_API_KEY for Stage 8→9
- [ ] CloseBot MCP — needs CLOSEBOT_API_KEY for live testing
- [ ] 702 test cases across all servers need live API credentials

PIPELINE ADVANCEMENT:
- [ ] Stage 7→8: CloseBot + Google Console need design approval
- [ ] Stage 6→7: 22 servers need UI apps built
- [ ] Stage 5→6: 5 servers need core tools built (FreshBooks, Gusto, Jobber, Keap, Lightspeed)
- [ ] Stage 1→5: 3 new MCPs need scaffolding (Compliance GRC, HR People Ops, Product Analytics)

PENDING REVIEW:
- [ ] Jake review OpenClaw video + gallery → finalize Upwork listing
- [ ] LocalBosses UI redesign (Steve Jobs critique delivered, recs available)

QUEUED PROJECTS:
- [ ] SongSense AI music analysis product (architecture done, build not started)
- [ ] 8-Week Agent Study Plan execution (curriculum posted, Week 1 not started)
2026-02-06 06:22:26 -05:00

239 lines
7.2 KiB
Python

"""
SURYA Blender Utilities
Shared functions for all track animations
"""
import bpy
import math
from mathutils import Vector, Matrix, Euler
import random
# Color palette (RGB normalized 0-1)
COLORS = {
"skin": (0.957, 0.447, 0.714, 1.0), # #f472b6 Pink
"u_saved_me": (0.133, 0.827, 0.933, 1.0), # #22d3ee Cyan
"nothing": (0.4, 0.4, 0.4, 1.0), # #666666 Grey
"natures_call": (0.369, 0.918, 0.831, 1.0), # #5eead4 Teal
"idk": (0.957, 0.447, 0.714, 1.0), # #f472b6 Pink
"with_u": (0.984, 0.749, 0.141, 1.0), # #fbbf24 Gold
"hollow": (0.984, 0.749, 0.141, 1.0), # #fbbf24 Gold
"intro": (0.102, 0.039, 0.180, 1.0), # #1a0a2e Deep purple
"white": (1.0, 1.0, 1.0, 1.0),
"black": (0.0, 0.0, 0.0, 1.0),
}
# Animation settings
FPS = 30
DURATION_SECONDS = 25
TOTAL_FRAMES = FPS * DURATION_SECONDS # 750 frames
def clear_scene():
"""Remove all objects from the current scene."""
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
# Clear orphan data
for block in bpy.data.meshes:
if block.users == 0:
bpy.data.meshes.remove(block)
for block in bpy.data.materials:
if block.users == 0:
bpy.data.materials.remove(block)
for block in bpy.data.curves:
if block.users == 0:
bpy.data.curves.remove(block)
def create_scene(name):
"""Create a new scene with the given name."""
scene = bpy.data.scenes.new(name)
bpy.context.window.scene = scene
scene.frame_start = 1
scene.frame_end = TOTAL_FRAMES
scene.render.fps = FPS
return scene
def setup_scene(background_color=(0.1, 0.04, 0.18, 1.0)):
"""Setup scene with camera and world settings."""
# Set world background
world = bpy.context.scene.world
if world is None:
world = bpy.data.worlds.new("World")
bpy.context.scene.world = world
world.use_nodes = True
bg_node = world.node_tree.nodes.get("Background")
if bg_node:
bg_node.inputs[0].default_value = background_color
bg_node.inputs[1].default_value = 1.0 # Strength
# Set render settings
bpy.context.scene.render.resolution_x = 1920
bpy.context.scene.render.resolution_y = 1080
def create_camera(location=(0, -10, 5), rotation=(1.1, 0, 0)):
"""Create and setup camera."""
bpy.ops.object.camera_add(location=location)
camera = bpy.context.active_object
camera.rotation_euler = Euler(rotation, 'XYZ')
bpy.context.scene.camera = camera
return camera
def create_emission_material(name, color, strength=2.0):
"""Create an emission material for glowing objects."""
mat = bpy.data.materials.new(name=name)
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
# Clear default nodes
nodes.clear()
# Create emission shader
emission = nodes.new('ShaderNodeEmission')
emission.inputs[0].default_value = color
emission.inputs[1].default_value = strength
# Output
output = nodes.new('ShaderNodeOutputMaterial')
links.new(emission.outputs[0], output.inputs[0])
return mat
def create_basic_material(name, color, metallic=0.0, roughness=0.5):
"""Create a basic PBR material."""
mat = bpy.data.materials.new(name=name)
mat.use_nodes = True
bsdf = mat.node_tree.nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs["Base Color"].default_value = color
bsdf.inputs["Metallic"].default_value = metallic
bsdf.inputs["Roughness"].default_value = roughness
return mat
def create_sphere(location=(0, 0, 0), radius=1.0, segments=32, rings=16, name="Sphere"):
"""Create a UV sphere."""
bpy.ops.mesh.primitive_uv_sphere_add(
radius=radius,
segments=segments,
ring_count=rings,
location=location
)
obj = bpy.context.active_object
obj.name = name
return obj
def create_icosahedron(location=(0, 0, 0), radius=1.0, name="Icosahedron"):
"""Create an icosahedron."""
bpy.ops.mesh.primitive_ico_sphere_add(
radius=radius,
subdivisions=1,
location=location
)
obj = bpy.context.active_object
obj.name = name
return obj
def create_curve_from_points(points, name="Curve", bevel_depth=0.02):
"""Create a curve from a list of 3D points."""
curve_data = bpy.data.curves.new(name=name, type='CURVE')
curve_data.dimensions = '3D'
curve_data.bevel_depth = bevel_depth
curve_data.bevel_resolution = 4
spline = curve_data.splines.new('NURBS')
spline.points.add(len(points) - 1)
for i, point in enumerate(points):
spline.points[i].co = (point[0], point[1], point[2], 1)
spline.use_endpoint_u = True
curve_obj = bpy.data.objects.new(name, curve_data)
bpy.context.collection.objects.link(curve_obj)
return curve_obj
def keyframe_location(obj, frame, location):
"""Set a location keyframe."""
obj.location = location
obj.keyframe_insert(data_path="location", frame=frame)
def keyframe_scale(obj, frame, scale):
"""Set a scale keyframe."""
if isinstance(scale, (int, float)):
scale = (scale, scale, scale)
obj.scale = scale
obj.keyframe_insert(data_path="scale", frame=frame)
def keyframe_rotation(obj, frame, rotation):
"""Set a rotation keyframe (Euler)."""
obj.rotation_euler = rotation
obj.keyframe_insert(data_path="rotation_euler", frame=frame)
def animate_camera_orbit(camera, center=(0, 0, 0), radius=10, height=5,
start_frame=1, end_frame=750, revolutions=1):
"""Animate camera orbiting around a center point."""
for frame in range(start_frame, end_frame + 1):
t = (frame - start_frame) / (end_frame - start_frame)
angle = t * 2 * math.pi * revolutions
x = center[0] + radius * math.cos(angle)
y = center[1] + radius * math.sin(angle)
z = center[2] + height
camera.location = (x, y, z)
camera.keyframe_insert(data_path="location", frame=frame)
# Point at center
direction = Vector(center) - Vector((x, y, z))
rot_quat = direction.to_track_quat('-Z', 'Y')
camera.rotation_euler = rot_quat.to_euler()
camera.keyframe_insert(data_path="rotation_euler", frame=frame)
def create_star_field(count=200, radius=20, min_size=0.02, max_size=0.08):
"""Create a field of star particles."""
stars = []
for i in range(count):
x = random.uniform(-radius, radius)
y = random.uniform(-radius, radius)
z = random.uniform(-radius/2, radius)
size = random.uniform(min_size, max_size)
bpy.ops.mesh.primitive_ico_sphere_add(radius=size, subdivisions=1, location=(x, y, z))
star = bpy.context.active_object
star.name = f"Star_{i:03d}"
# Add emission material
mat = create_emission_material(f"StarMat_{i:03d}", COLORS["white"], strength=5.0)
star.data.materials.append(mat)
stars.append(star)
return stars
def smooth_interpolation(t):
"""Smooth step interpolation (ease in-out)."""
return t * t * (3 - 2 * t)
def ease_in_out(t):
"""Ease in-out cubic."""
if t < 0.5:
return 4 * t * t * t
else:
return 1 - pow(-2 * t + 2, 3) / 2