[2026.04 Week 1] Five Trending Repos of the Week
TL;DR
Claude Code tooling dominated. Roughly half of the 100 repos trending on GitHub this week were agent harness plugins, skill registries, or Claude Code analysis projects.
A leaked source map triggered a wave of repos dissecting and reconstructing the CLI internals.
Outside the agent ecosystem, the strongest signals were in multimedia tooling and UI performance, with projects replacing paid products with open-source alternatives.
Agent skills expanded beyond coding. Trending skill repos now cover marketing playbooks, design languages, compliance workflows, and executive advisory.
This week’s picks:
AutoAgent (⭐ 8.8k). Zero-code framework that lets users build and orchestrate LLM agent systems through natural language.
OpenScreen (⭐ 22.4k). Open-source screen recorder that produces polished demo videos with zooms, annotations, and smooth camera pans.
VibeVoice (⭐ 36.4k). Microsoft’s open-source voice AI framework for speech-to-text and text-to-speech that handles up to 60 minutes of audio in a single pass.
rtk (⭐ 18.2k). Rust CLI proxy that compresses shell output before it reaches your LLM saving 60-90% of tokens.
boneyard (⭐ 2.8k). Generates loading placeholder screens from real React components by capturing their DOM layout at build time.
HKUDS/AutoAgent
⭐ 8.8k · Python
AutoAgent lets users describe an agent in natural language and turns that description into a running Python module. The framework generates the code, writes it to disk, executes it, and registers the new agent in a singleton registry, all in a single tool call. No restart required.
The core mechanism is create_agent, a tool that LLM agents invoke to build new agents at runtime.
@register_tool("create_agent")
def create_agent(agent_name: str, agent_description: str,
agent_tools: list[str], agent_instructions: str,
context_variables):
"""Use this tool to create a new agent or modify an existing agent."""
tools_str = ""
code_env = context_variables.get("code_env", LocalEnv())
path = get_metachain_path(code_env)
agents_dir = path + "/autoagent/agents"
for tool in agent_tools:
tools_str += f"from autoagent.tools import {tool}\n"
agent_func = f"get_{agent_name.lower().replace(' ', '_')}"
if has_format_keys(agent_instructions):
format_keys = extract_format_keys(agent_instructions)
format_keys_values = []
for fk in format_keys:
format_keys_values.append(f"{fk}=context_variables.get('{fk}', '')")
# ... builds instructions as a function that reads context at call time
else:
instructions_str = f"""instructions = {repr(agent_instructions)}"""
tool_list = "[{}]".format(', '.join(f'{tool}' for tool in agent_tools))
create_codes = f"""\
from autoagent.types import Agent
{tools_str}
from autoagent.registry import register_plugin_agent
@register_plugin_agent(name="{agent_name}", func_name="{agent_func}")
def {agent_func}(model: str):
'''{agent_description}'''
{instructions_str}
return Agent(name="{agent_name}", model=model,
instructions=instructions, functions={tool_list})
"""
msg = create_file(agents_dir + "/" + agent_name.lower().replace(' ', '_') + ".py",
create_codes, context_variables)
result = code_env.run_command(
'cd {} && python autoagent/agents/{}.py'.format(
path, agent_name.lower().replace(' ', '_')))The generated Python file includes a @register_plugin_agent decorator that self-registers in the framework’s singleton registry on import. So the moment code_env.run_command executes the file, the new agent is discoverable by every other agent in the system.
The template variable handling is worth noting. If the instruction text contains {user_name} or similar placeholders, the generator wraps the instructions in a function that reads from context_variables at call time. Static instructions become a simple string assignment instead. This means agent behavior can adapt to runtime context without regenerating code.
The trade-off here is trust. The framework lets an LLM write and execute arbitrary Python. AutoAgent mitigates this by constraining tool imports to registered tools only and running inside either a local or Docker environment. But the design is fundamentally optimistic. It bets that the productivity gain of self-modifying agents outweighs the safety cost.
siddharthvaddem/openscreen
⭐ 22.4k · TypeScript
OpenScreen is an open-source screen recording tool that competes with Screen Studio. You record your screen, add zooms and annotations, and export a polished demo video. No watermarks, no subscription.
The motion blur during camera pans is not CSS. OpenScreen renders video playback through PixiJS, a GPU-accelerated 2D engine that runs on WebGL. The video sits inside a PixiJS Container with two GPU filters applied as a processing chain.
const blurFilter = new BlurFilter();
blurFilter.quality = 3;
blurFilter.resolution = app.renderer.resolution;
blurFilter.blur = 0;
const motionBlurFilter = new MotionBlurFilter([0, 0], 5, 0);
videoContainer.filters = [blurFilter, motionBlurFilter];MotionBlurFilter comes from pixi-filters, a library of WebGL shaders for PixiJS. The constructor takes a velocity vector [0, 0], a kernel size of 5 (how many samples the shader takes along the blur direction), and an offset of 0. Every frame, the zoom transform recalculates these GPU parameters from camera velocity.
const velocityX = dx / dtSeconds;
const velocityY = dy / dtSeconds;
const scaleVelocity =
Math.abs(dScale / dtSeconds) * Math.max(stageSize.width, stageSize.height) * 0.5;
const speed = Math.sqrt(velocityX * velocityX + velocityY * velocityY) + scaleVelocity;
const normalised = Math.min(1, speed / PEAK_VELOCITY_PPS);
const targetBlur =
speed < VELOCITY_THRESHOLD_PPS ? 0 : normalised * normalised * MAX_BLUR_PX * amountResponse;
const dirMag = Math.sqrt(velocityX * velocityX + velocityY * velocityY) || 1;
const velocityScale = targetBlur * 2.4;
motionBlurFilter.velocity =
targetBlur > 0
? { x: (velocityX / dirMag) * velocityScale, y: (velocityY / dirMag) * velocityScale }
: { x: 0, y: 0 };
motionBlurFilter.kernelSize = targetBlur > 8 ? 15 : targetBlur > 4 ? 11 : 7;
motionBlurFilter.offset = targetBlur > 0.5 ? -0.2 : 0;Three GPU parameters update each frame. The velocity vector tells the shader which direction to blur and how far. The kernelSize steps up from 7 to 11 to 15 as blur intensity increases, because more samples mean smoother blur but more GPU work. The offset shifts the blur center slightly at high speeds for a more natural trailing effect.
The normalised * normalised curve means slow pans produce almost no blur while fast movements ramp sharply. This matches real camera sensor behavior, where motion blur scales with the square of velocity.
The velocity calculation fuses two kinds of movement. scaleVelocity converts zoom rate into equivalent pixel velocity by multiplying by half the viewport size. A fast zoom-in produces directional blur even when the camera position stays fixed. The same filter setup is duplicated in frameRenderer.ts for video export, so the export pipeline renders frame-by-frame through the same PixiJS stage used for preview. What you see during editing is what you get in the final video.
microsoft/VibeVoice
⭐ 36.4k · Python
VibeVoice is Microsoft’s open-source voice AI framework for both speech-to-text and text-to-speech. It runs at an unusually low 7.5 Hz token rate instead of the typical 50-100 Hz, which lets it process up to 60 minutes of audio in a single pass without chunking.
The architecture connects an LLM backbone to a diffusion head for audio generation. The conditioning mechanism between them is worth a closer look.
modular_vibevoice_diffusion_head.py:43-46,126-161
def modulate(x, shift, scale):
"""Apply modulation to input tensor."""
return x * (1 + scale) + shift
class HeadLayer(nn.Module):
def __init__(self, embed_dim, ffn_dim, cond_dim, norm_eps=1e-5):
super().__init__()
# ... SwiGLU FFN and RMSNorm
self.adaLN_modulation = nn.Sequential(
ACT2FN['silu'],
nn.Linear(cond_dim, 3 * self.embed_dim, bias=False)
)
def forward(self, x, c):
shift_ffn, scale_ffn, gate_ffn = self.adaLN_modulation(c).chunk(3, dim=-1)
x = x + gate_ffn * self.ffn(modulate(self.norm(x), shift_ffn, scale_ffn))
return x
class FinalLayer(nn.Module):
def __init__(self, hidden_size, output_size, cond_size, norm_eps=1e-5):
super().__init__()
self.norm_final = RMSNorm(hidden_size, eps=norm_eps, elementwise_affine=False)
self.linear = nn.Linear(hidden_size, output_size, bias=False)
self.adaLN_modulation = nn.Sequential(
ACT2FN['silu'],
nn.Linear(cond_size, 2 * hidden_size, bias=False)
)
def forward(self, x, c):
shift, scale = self.adaLN_modulation(c).chunk(2, dim=-1)
x = modulate(self.norm_final(x), shift, scale)
x = self.linear(x)
return xThis is adaptive layer normalization (adaLN), a technique borrowed from Diffusion Transformers (DiT). Instead of concatenating conditions onto the input, the conditioning vector c projects into shift, scale, and gate parameters through a single linear layer. These parameters then modulate the normalized features.
The HeadLayer produces three parameters while the FinalLayer produces two. The difference matters. HeadLayer includes a gate_ffn that acts as a learned residual weight, controlling how much the FFN transformation contributes. When training starts with zero-initialized weights in the adaLN projection, the gate stays near zero, which means each layer acts as an identity function early on. Training stability improves because the diffusion head starts as a passthrough and gradually learns to transform.
The FinalLayer drops the gate and uses affine-free normalization (elementwise_affine=False). It only shifts and scales, then projects to output dimensions. This is intentional. The final layer needs to produce raw diffusion predictions, not gated residuals.
rtk-ai/rtk
⭐ 18.2k · Rust
rtk is a CLI proxy that compresses shell output before it reaches your LLM. You type rtk git log instead of git log, and the output shrinks by 60-90%. The filtering is straightforward text processing. What matters more is how rtk makes the interception invisible.
When you run rtk init, it installs a shell hook into Claude Code’s settings that intercepts every Bash command before execution. The hook reads the command from Claude Code’s JSON input, delegates to rtk rewrite, and substitutes the result back. The LLM never knows rtk exists.
The rtk rewrite command does the actual matching. It takes a raw shell command, matches it against a registry of 100+ patterns, and returns the rtk-prefixed equivalent. The result is communicated through exit codes.
pub fn run(cmd: &str) -> anyhow::Result<()> {
let excluded = crate::core::config::Config::load()
.map(|c| c.hooks.exclude_commands)
.unwrap_or_default();
// SECURITY: check deny/ask BEFORE rewrite so non-RTK commands are also covered.
let verdict = check_command(cmd);
if verdict == PermissionVerdict::Deny {
std::process::exit(2);
}
match registry::rewrite_command(cmd, &excluded) {
Some(rewritten) => match verdict {
PermissionVerdict::Allow => {
print!("{}", rewritten);
let _ = std::io::stdout().flush();
Ok(())
}
PermissionVerdict::Ask => {
print!("{}", rewritten);
let _ = std::io::stdout().flush();
std::process::exit(3);
}
PermissionVerdict::Deny => unreachable!(),
},
None => {
// No RTK equivalent. Exit 1 = passthrough.
std::process::exit(1);
}
}
}Four exit codes drive the entire hook protocol. Exit 0 means the hook can auto-allow the rewritten command without interrupting the LLM. Exit 1 means no rtk equivalent exists, so the original passes through untouched. Exit 2 means a deny rule matched, and Claude Code should block the command. Exit 3 rewrites the command but still lets Claude Code prompt for permission. This is how git push becomes rtk git push while the user still sees the confirmation dialog.
The security check runs before the rewrite, not after. This means deny rules apply to the original command even if rtk has no equivalent for it. The ordering matters because the hook intercepts all Bash commands, not just ones rtk knows about.
registry::rewrite_command also handles compound shell commands by tokenizing on &&, ||, and ;, then rewriting each segment independently. git add . && cargo test becomes rtk git add . && rtk cargo test. Pipe chains only rewrite the first command, since piped output doesn’t reach the LLM anyway. The registry matches commands against a precompiled RegexSet for O(1) classification regardless of rule count.
0xGF/boneyard
⭐ 2.8k · TypeScript
Boneyard generates skeleton loading screens from your actual React components. You wrap a component with <Skeleton>, run npx boneyard-js build, and get JSON descriptors that replay as gray placeholder rectangles at runtime. Most of the complexity is in the build step.
Skeleton layouts depend on getBoundingClientRect(), which requires a real browser to compute CSS layouts, font metrics, and media query breakpoints. There is no way to derive these from source code alone. So boneyard uses Playwright, a browser automation library, to launch headless Chromium, navigate to your running dev server, and extract geometry from the rendered DOM.
The bridge between React and Playwright is a build-time flag. The CLI injects window.__BONEYARD_BUILD = true before any page loads. The React <Skeleton> component checks this flag at module load time and exposes its extraction function on window, while stamping data-boneyard attributes on its DOM element so the CLI can find it.
// Module-level: expose snapshotBones for CLI build mode
if (typeof window !== 'undefined' && (window as any).__BONEYARD_BUILD) {
(window as any).__BONEYARD_SNAPSHOT = snapshotBones
}
// Inside the Skeleton component render:
const dataAttrs: Record<string, string> = {}
if (name) {
dataAttrs['data-boneyard'] = name
if (snapshotConfig) {
dataAttrs['data-boneyard-config'] = JSON.stringify(snapshotConfig)
}
}The CLI then crawls every internal link on your dev server. For each page at each breakpoint width (375px, 768px, 1280px by default), it resizes the viewport and calls page.evaluate() to run the extraction inside the browser context.
const bones = await page.evaluate(() => {
const fn = window.__BONEYARD_SNAPSHOT
if (!fn) return { results: {} }
const elements = document.querySelectorAll('[data-boneyard]')
const results = {}
for (const el of elements) {
const name = el.getAttribute('data-boneyard')
if (!name || results[name]) continue
const configStr = el.getAttribute('data-boneyard-config')
const config = configStr ? JSON.parse(configStr) : undefined
const target = el.firstElementChild
if (!target) continue
try {
results[name] = fn(target, name, config)
} catch { /* skip on error */ }
}
return { results }
})page.evaluate() runs JavaScript inside Playwright’s Chromium instance, not in Node.js. The snapshotBones function executes in the same browser context where React rendered the component. It walks the real DOM, calls getBoundingClientRect() on every visible element, and returns bone positions as percentages of the container width.
The result is a clean separation of concerns. React components mark themselves for capture with data attributes. Playwright provides the real browser environment. The library’s own extraction function does the geometry work. No screenshots, no computer vision. Just the DOM reporting where everything is.

