⚠️ 网站开发中,包含大量测试数据,请勿当真
发现 Vibe Coding 新世界

发现 Vibe Coding 的 最优工具

聚合 Vibe Coding 领域的最新资讯、工具和资源,帮助开发者快速找到优质的 AI 开发工具。

热门工具

最受欢迎的 Vibe Coding 工具

查看全部 →
Cursor
Freemium
IDE

AI 驱动的代码编辑器,基于 VS Code 构建

访问官网
Windsurf
Free
IDE

Codeium 推出的 AI 编程环境

访问官网
Lovable
Freemium
AI 平台

AI 驱动的全栈应用构建平台

访问官网
v0
Freemium
UI 生成

Vercel 推出的 AI UI 生成工具

访问官网
Bolt.new
Free
AI 平台

AI 全栈应用开发平台

访问官网

最新资讯

Vibe Coding 领域的最新动态

查看全部 →

Önceki yazımızda Kubernetes üzerinde iki ayrı kurulum (vanilla PostgreSQL ve TimescaleDB) yapıp az veriyle tablo + index boyutlarını kıyaslamıştık. Bu yazıda aynı environment üzerinde TimescaleDB'nin data retention (eski veriyi silme) yaklaşımını pratikte test ediyoruz. Namespace: database TimescaleDB pod: timescaledb-single chart (master pod) StorageClass: local-path (tek node) Komutlarda iki değişken kullanacağız: NS ve TSPOD. NS=database TSPOD="$(kubectl get pod -n $NS -l release=timescaledb,role=master -o jsonpath='{.items[0].metadata.name}')" echo "TSPOD=$TSPOD" Bu blok sonraki komutlarda kullanacağımız değişkenleri hazırlar. Bu deneyde 48 saatlik veri bastık (5 dakikada bir), sonra 24 saatten eski chunk'ları manuel olarak dropladık. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' CREATE EXTENSION IF NOT EXISTS timescaledb; CREATE SCHEMA IF NOT EXISTS lab; DROP TABLE IF EXISTS lab.sensor_ret_manual; CREATE TABLE lab.sensor_ret_manual ( time TIMESTAMPTZ NOT NULL, device_id INT, temperature DOUBLE PRECISION ); SELECT create_hypertable( 'lab.sensor_ret_manual', 'time', chunk_time_interval => INTERVAL '1 hour', create_default_indexes => FALSE ); CREATE INDEX sensor_ret_manual_time_idx ON lab.sensor_ret_manual(time); INSERT INTO lab.sensor_ret_manual SELECT generate_series(NOW() - INTERVAL '48 hours', NOW(), INTERVAL '5 minutes'), (random() * 10)::int, random() * 100; SQL Burada lab schema'sını ve 1 saatlik chunk'larla hypertable'ı oluşturup sonra 48 saatlik örnek veri basıyoruz. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT count(*) AS rows_before FROM lab.sensor_ret_manual; SELECT show_chunks('lab.sensor_ret_manual') AS chunk_before; SELECT pg_size_pretty(sum(pg_total_relation_size(chunk::regclass))) AS total_before FROM show_chunks('lab.sensor_ret_manual') AS chunk; SQL Burada ise silmeden önce satır sayısı, hangi chunk'ların oluştuğu ve chunk'ların toplam disk boyutunu görüyoruz. Benim makinemdeki çıktılar: rows_before: 577 chunk_before: 49 chunk total_before: 1176 kB kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT drop_chunks('lab.sensor_ret_manual', older_than => INTERVAL '24 hours'); SQL İşte şimdi 'now() - 24 hours' çizgisinin tamamen solunda kalan chunk tablolarını topluca DROP ettik. Satır satır silme yok. Chunk bazında hızlıca droplar. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT count(*) AS rows_after FROM lab.sensor_ret_manual; SELECT show_chunks('lab.sensor_ret_manual') AS chunk_after; SELECT pg_size_pretty(sum(pg_total_relation_size(chunk::regclass))) AS total_after FROM show_chunks('lab.sensor_ret_manual') AS chunk; SQL Burada drop_chunks sonrasında satır/chunk/boyutun gerçekten azaldığını doğruladık. Benim makinemdeki çıktılar: rows_after: 299 chunk_after: 25 chunk total_after: 600 kB Önemli not: 'older_than => 24 hours' her zaman 'tam 24 saatin dışındaki tüm satırlar gider' demek değildir. Cutoff çizgisini kesen bir chunk içinde hem eski hem yeni veri varsa, o chunk komple droplanmaz. Bu nedenle az miktarda daha eski satır kalabilir. Bu deneyde ise TimescaleDB'nin dahili job mekanizması ile otomatik retention kurduk ve job'ı manuel tetikledik. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' DROP TABLE IF EXISTS lab.sensor_ret_policy; CREATE TABLE lab.sensor_ret_policy ( time TIMESTAMPTZ NOT NULL, device_id INT, temperature DOUBLE PRECISION ); SELECT create_hypertable( 'lab.sensor_ret_policy', 'time', chunk_time_interval => INTERVAL '1 hour', create_default_indexes => FALSE ); CREATE INDEX sensor_ret_policy_time_idx ON lab.sensor_ret_policy(time); INSERT INTO lab.sensor_ret_policy SELECT generate_series(NOW() - INTERVAL '48 hours', NOW(), INTERVAL '5 minutes'), (random() * 10)::int, random() * 100; SQL Bu blok ile otomatik retention deneyi için ikinci bir hypertable oluşturduk ve aynı şekilde 48 saatlik örnek veriyi doldurduk. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT add_retention_policy('lab.sensor_ret_policy', INTERVAL '24 hours'); SQL Burada ise TimescaleDB içinde arka planda çalışacak bir retention job'u oluşturduk. Dönüş değeri job_id olur. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT job_id, proc_name, schedule_interval, config FROM timescaledb_information.jobs WHERE proc_name = 'policy_retention' ORDER BY job_id DESC; CALL run_job(<yukaridaki_job_id>); SQL Burası retention job'unun konfigürasyonunu gösterir ve beklemeden hemen çalıştırır. Normalde job schedule_interval'a göre otomatik çalışır. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT count(*) AS rows_after FROM lab.sensor_ret_policy; SELECT pg_size_pretty(sum(pg_total_relation_size(chunk::regclass))) AS total_after FROM show_chunks('lab.sensor_ret_policy') AS chunk; SQL Bu blok policy çalıştıktan sonra kalan satır sayısını ve chunk toplam boyutunu ölçer. Yani job'un gerçekten eski chunk'ları dropladığını kanıtlar. Benim makinemdeki çıktılar: rows_after: 299 total_after: 600 kB Bu, retention policy'nin temelde drop_chunks ile aynı chunk droplama mekanizmasını otomatik olarak çalıştırdığını gösteriyor. kubectl exec -n $NS -i $TSPOD -- psql -U postgres -v ON_ERROR_STOP=1 <<'SQL' SELECT remove_retention_policy('lab.sensor_ret_policy'); DROP SCHEMA IF EXISTS lab CASCADE; SQL Burası retention policy'yi kaldırır ve lab schema'sını CASCADE ile silerek tüm test tablolarını ve internal chunk tablolarını temizler. TimescaleDB'nin sunduğu yerleşik, chunk tabanlı silme yöntemlerini kullanmak çoğu zaman en verimli yoldur. Yine de hangi yöntemin doğru olduğuna senaryo karar verir. Artıları: İnce ayar: Sadece belirli cihazın verisi, belirli aralık, GDPR gibi seçici silmeler için uygundur. Chunk sınırına bağlı kalmadan hedeflediğin satırları silersin. Eksileri: PostgreSQL MVCC nedeniyle satırlar diskten hemen silinmez; dead tuple olarak kalır. Büyük silmeler table bloat üretir; disk alanını gerçekten geri almak için VACUUM (bazen agresif vacuum) ve kimi zaman REINDEX gerekebilir. Çok miktarda veride satır satır silmek yavaş ve maliyetlidir (IO/CPU artışı). Artıları: Chunk'lar komple droplandığı için genellikle çok hızlıdır (DROP TABLE benzeri). Relation size çoğu senaryoda hemen düşer (örneğin benim makinemde 1176 kB -> 600 kB). add_retention_policy ile cron/script yazmadan, DB içindeki job mekanizmasıyla otomatik yönetim sağlanır. Zaman serisi kullanımında en doğal model: 'ham veriyi X süre tut, eskisini at'. Eksileri / dikkat edilmesi gerekenler: Granularity: Retention satır bazlı değil chunk bazlıdır. Cutoff'u kesen chunk kalabilir; bu yüzden az miktarda daha eski satır tutulabilir. Seçici silme için uygun değildir (ör. sadece device_id=3 verisini silmek). Bu tip işlerde DELETE gerekir. Lock ihtiyacı: Chunk drop, ilgili chunk'lar üzerinde lock alır; uzun transaction varsa drop gecikebilir veya timeout görebiliriz. Hedefiniz 'X süreden eski zaman serisi ham verisini otomatik ve verimli şekilde kaldırmak' ise TimescaleDB'nin retention policy (add_retention_policy) veya manuel drop_chunks yaklaşımı genellikle en doğru ve yönetimi en kolay çözümdür. Buna rağmen seçici ve ince ayarlı silme ihtiyaçlarında SQL DELETE hala gerekli bir araçtır.

I needed a demo video for an RFP automation platform I'm building. The typical approach: record your screen, stumble through clicks, re-record when something breaks, then spend an hour in a video editor syncing voiceover. I've done it before. It's painful. So I tried a different approach: let AI do the whole thing. Claude Code wrote the entire recording pipeline — Playwright scripts, ffmpeg assembly, speed control, subtitle generation edge-tts generated the narration with Microsoft's neural voices Gemini 3.1 Pro reviewed the final video for audio/video sync issues The result: a 3-minute narrated demo with 14 scenes, variable speed segments, and subtitles. No video editor. No screen recording app. No manual voiceover. 🎥 Watch the video demo on Loom Note: Interactive video player available on the original article Here's how the whole process worked — including the parts that broke. Playwright (record) → edge-tts (voice) → ffmpeg (assemble) → Gemini (QA) I described what I wanted: a demo video using Playwright to record the browser, with text-to-speech for coordinated voiceover, covering the full application workflow. Claude Code decided the structure — 14 scenes from dashboard to API docs — wrote the scene scripts, and generated the modules. Each one is a TypeScript function that drives the browser through a specific feature: export const scene: SceneFn = async (page) => { await page.getByText("Generate Answer").click(); // Wait for AI to finish const indicator = page.getByText("Generating AI answer..."); await indicator.waitFor({ state: "hidden", timeout: 90_000 }); // Scroll through the answer smoothly const scrollPanel = page.locator("[data-demo-scroll=true]"); await scrollPanel.evaluate((el) => { el.scrollTo({ top: el.scrollHeight / 3, behavior: "smooth" }); }); await longPause(page); }; A test wrapper runs all 14 scenes in sequence, recording timestamps: for (const id of sceneIds) { const start = (Date.now() - videoStartTime) / 1000; await SCENES[id](page); timestamps.push({ id, start, end: (Date.now() - videoStartTime) / 1000 }); } Output: one .webm video + a scene-timestamps.json file. This separation is key — it lets us manipulate each scene independently during assembly. Each scene has a narration line. edge-tts turns them into MP3 files using Microsoft's neural TTS — free, no API key, surprisingly natural: edge-tts --text "Let's generate an AI answer..." \ --voice en-US-GuyNeural \ --write-media voice/08-generate-answer.mp3 14 scenes, 30 seconds to generate all voices. Claude Code wrote the narration script too — I reviewed and tweaked the phrasing, but the drafting was AI. Claude Code also wrote the assembly script. In theory, it's simple: split the video by timestamps, overlay voice, concatenate. In practice, this is where I spent most of the iteration time with Claude. Nobody wants to watch an AI loading spinner for 30 seconds. The solution: per-scene speed segments. { id: "08-generate-answer", speed: [ { from: 0, to: 1, speed: 1 }, // Click button at normal speed { from: 1, to: 6, speed: 15 }, // AI generation: 30s → 2s { from: 6, to: 10, speed: 1 }, // Read the answer at normal speed ], } The from/to values are proportional (0-10 scale). ffmpeg applies this via a split/trim/setpts/concat filtergraph. The result: boring waits are compressed 15x while meaningful interactions play at real speed. This is the lesson that took the most iterations to learn. When merging voice with video per-scene, then concatenating: Problem 1: Using ffmpeg's -shortest flag silently truncates the longer stream. Voice gets cut mid-sentence. Problem 2 (the nasty one): ffmpeg starts each concatenated clip's audio where the previous clip's audio ended, not where the video starts. If clip A has 30s video but only 13s audio, clip B's audio starts at t=13 instead of t=30. This causes progressive drift — by scene 10, the voice is over a minute behind the visuals. The fix: Every clip's audio track must exactly match its video duration: ffmpeg -i video.mp4 -i voice.mp3 \ -filter_complex "[1:a]adelay=500|500,apad=whole_dur=VIDEO_DURATION[audio]" \ -map 0:v -map "[audio]" -c:v copy -c:a aac output.mp4 apad=whole_dur pads the audio with silence to exactly match the video length. No drift possible. Here's where it got really interesting. After fixing the pipeline, I needed to verify audio/video sync across 14 scenes. Watching the whole video manually each time is tedious and my ears aren't reliable after the 10th iteration. I uploaded the video to Gemini 3.1 Pro and asked it to analyze the synchronization — which actions happen visually vs. when the narration describes them. Gemini caught every single sync issue with precise timestamps: Action Visual Audio Drift Create RFP 01:51 02:09 18s late Assign Style Guide 02:20 03:05 45s late Generate Answer 02:22 03:31 1m 9s late Chat refinement 03:22 03:58 36s late Assign team 03:55 04:28 33s late Classic progressive drift. Each scene's audio shifts further behind because the previous scene's audio track was shorter than its video. After applying the apad fix, Gemini's analysis: 13 scenes perfect sync, 1 flagged as ~2 seconds late. The flagged scene was actually fine — I'd intentionally added a 1.5-second voice delay to let a visual transition settle before narration began. Gemini was being slightly over-strict. Score: 0 missed issues, 1 false positive out of 14 scenes. That's better QA than I'd get from watching the video myself. The process wasn't "ask Claude once, get perfect video." It was iterative: Me: "I want a demo video using Playwright with TTS, covering the full workflow" Claude: Decides on 14 scenes, generates the pipeline. First recording works. Me: "The voice is desynced from scene 5 onwards" Claude: Debugs, discovers -shortest issue. Fixes with apad. Me: "The answer doesn't scroll — you can't see the bullet points" Claude: Investigates DOM, finds wrong scroll container. Fixes with programmatic parent discovery. Me: "Still no bullet points in the generated answer" Claude: Tests via API, finds the AI returns plain text despite HTML instructions. Adds normalizeAnswerHtml post-processor. Me: "The KB upload scene has too much dead time, and the style guide voice starts too early" Claude: Increases speed compression from 4x to 8x, adds 2s voice delay to style guide scene. Each round: I watch the video, describe what's wrong in plain language, Claude debugs and fixes. The feedback loop is fast because re-recording takes 4 minutes and assembly takes 1 minute. Task Who Write 14 Playwright scene scripts Claude Code Write assembly pipeline (800 lines) Claude Code Write speed segment logic Claude Code Debug audio sync issues Claude Code Fix scroll container detection Claude Code Add HTML normalization post-processor Claude Code Generate voiceover audio edge-tts QA audio/video synchronization Gemini 3.1 Pro Write narration text Claude Code (I reviewed and adjusted) Review video and give feedback Me Choose what to show and in what order Me The creative direction was mine. Everything else was AI. # Record 14 scenes npx playwright test e2e/tests/demo-record.spec.ts --headed # ~4 min # Generate voices npx tsx scripts/demo-record.ts voice # ~30 sec # Assemble with speed control + subtitles npx tsx scripts/demo-record.ts assemble # ~1 min Output: 3:47 narrated video, 14 scenes, variable speed, soft subtitles Pipeline code: ~800 lines TypeScript (assembly) + ~200 lines (scenes) Re-record time: Under 6 minutes end-to-end Video editors used: Zero If the UI changes tomorrow, I update one scene file and re-run. The entire pipeline is version-controlled and reproducible. For the first iteration, manually recording your screen while narrating would be faster. A screen recording tool gives you a video in real time — no pipeline to build. But manual recording has its own costs: you need a quiet environment and a decent microphone, any stumble means re-recording, editing voiceover timing in a video editor is tedious, and audio quality depends entirely on your hardware. The automated approach pays off from the second iteration onward. When the UI changed, I updated one scene file and re-ran. When the narration needed tweaking, I edited a text string — no re-recording my voice. After five rounds of feedback-and-fix, I'd have spent hours in a video editor doing the same thing manually. And if a client asks for a demo next month after a redesign, it's a 6-minute re-run, not a full re-shoot. This pipeline was built for a product demo, but the pattern — browser automation producing narrated video — has broader implications. Think about QA. Today, test evidence is usually a CI log that says PASS or FAIL. When a client asks "show me that the payment flow works," you re-run the test and hope they trust a green checkmark. Imagine instead handing them a narrated video: the test runs, the voiceover explains each step, and the video is generated automatically on every release. Regression testing becomes not just a technical checkpoint but a reviewable artifact. The same applies to compliance and auditing. Regulated industries need proof that systems work as specified. A version-controlled pipeline that produces timestamped video evidence on demand is fundamentally different from manual screen recordings buried in a shared drive. And onboarding — new team members could watch auto-generated walkthroughs that stay current with the actual UI, not documentation screenshots from six months ago. The underlying shift is that video is becoming a programmatic output, not a creative production. When the cost of producing a video drops from hours to minutes, and re-producing it is a single command, you start using video in places where it was never practical before. Playwright's recordVideo is production-quality for demos — 720p/25fps, no overhead Never use -shortest in ffmpeg when merging audio streams for concatenation. Use apad=whole_dur to match audio duration to video duration exactly. Variable speed segments are the difference between a boring demo and a watchable one. 15x compression for loading spinners, 1x for actual interactions. Gemini 3.1 Pro is a legitimate video QA tool. Upload a video, ask "is the audio synced with the visuals?" — it'll give you a timestamped report with near-perfect accuracy. The human-AI feedback loop matters more than getting it right first try. I described problems in plain language ("the scroll doesn't work"), Claude debugged and fixed. Five iterations to a polished result. AI is great at automation, humans are great at judgment. I wrote the narration script and decided what to show. AI did everything else. When video becomes a command, you use it everywhere. The same pipeline that records a demo can generate QA evidence, onboarding walkthroughs, or compliance artifacts — all version-controlled and reproducible on every release. Tools used: Claude Code, Playwright, ffmpeg, edge-tts, Gemini 3.1 Pro Originally published on javieraguilar.ai Want to see more AI agent projects? Check out my portfolio where I showcase multi-agent systems, MCP development, and compliance automation.

HN gets 500+ stories a day. The front page is ranked by votes - which surfaces popular content, not necessarily the best discussions. A post about a Google outage will outrank a thread where infrastructure engineers are quietly sharing how they handle failover. sift tries to find the second kind. The scoring algorithm looks at five signals in the comment tree: Depth breadth (30% weight) - Not max depth. The fraction of comments at 3+ levels of conversation. A thread where 20% of comments are three replies deep means real back-and-forth happened. Practitioner markers (25%) - Comments containing experience phrases ("I built," "we used," "in production"), code blocks, specific tool names, or hedging language like "FWIW" and "YMMV" that correlates with practitioners. Score velocity (15%) - Points per hour. Sustained interest over time, not a spike. Author diversity (15%) - Unique authors relative to comment count, weighted by thread size. High diversity at depth 3+ means different people are engaging with each other's thinking. Reference density (15%) - Comments with external links. Citation culture. Before these signals run, threads pass through quality filters: flame detection (hostile short comments at depth), comment length IQR (catches pile-ons), temporal spread (catches flash-in-the-pan), and a discussion density band-pass. 10-12 discussions per day. 6 above the fold, rest behind a tap. Each with a context paragraph explaining why it was picked. Updated 4x/day. Yesterday's picks are gone. Astro static site. Zero client JS. Self-hosted fonts (Newsreader + JetBrains Mono). Cloudflare Pages. GitHub Actions for the pipeline (fetch from Algolia, score, build, deploy). Cloudflare KV for cross-run state so repeated threads get decayed. Total cost: $0/month on free tiers. Total page weight: ~18KB HTML, ~7KB CSS, ~320KB fonts. https://sift.1mb.dev

Improving Layout Consistency in devlog-ist/landing The devlog-ist/landing project aims to provide a platform for developers to share their experiences and insights. Recent work focuses on refining the visual presentation and responsiveness of the landing page. The initial layout of the stats page sections within devlog-ist/landing exhibited inconsistencies in height, particularly when displaying education, experience, technologies, and notable projects side-by-side in a grid. This resulted in an uneven appearance, detracting from the overall user experience. Additionally, the profile card text in the retro theme did not fully utilize the available width on desktop screens. To address these issues, the layout was modified to stack the education, experience, technologies, and notable projects vertically instead of using a side-by-side grid. This ensures consistent height across sections, providing a cleaner and more organized presentation. Furthermore, the profile card text within the retro theme was adjusted to properly fill the available width on desktop, enhancing readability and visual appeal. // Example: Simplified representation of the layout change // (Illustrative, not actual code from the commit) // Previous layout (grid-based) <div class="grid"> <div class="education">...</div> <div class="experience">...</div> <div class="technologies">...</div> <div class="projects">...</div> </div> // New layout (vertical stacking) <div> <div class="education">...</div> <div class="experience">...</div> <div class="technologies">...</div> <div class="projects">...</div> </div> Vertical Stacking: Opting for a vertical layout ensured uniform height across sections, resolving the uneven appearance caused by the grid. Width Adjustment: Modifying the profile card text to occupy the full width improved readability and visual consistency on desktop screens. The implemented changes resulted in a more visually consistent and responsive landing page. The vertical stacking of sections eliminated the uneven height issue, while the width adjustment of the profile card text enhanced the user experience on desktop devices. This experience highlighted the importance of considering content-dependent height variations when designing grid-based layouts. Vertical stacking provides a more predictable and consistent presentation when content height is variable. Additionally, thorough testing across different screen sizes is crucial for identifying and addressing responsiveness issues.

All I wanted was to point a swarm of Claude Code agents at a set of GitHub issues and let them work in parallel. iloom does exactly that: spin up isolated git worktrees, assign an agent to each issue, let them run concurrently, come back to a set of PRs. I ran il start on my Linux dev server and hit three errors in a row. First, it couldn't find the code CLI command (I use Code Server, which doesn't ship one). Passed --no-code. Second, it detected a non-interactive environment (I was in a Claude Code terminal session). Passed --epic explicitly. Third: Terminal window launching not yet supported on linux. No flag. No fallback. No workaround. The core workflow, the entire reason the tool exists, was hardcoded for macOS. Every invocation mode failed: single issue, epic, one-shot. The worktree creation succeeded each time, then the process died trying to open a Terminal.app window on a headless server, leaving orphaned worktrees behind. I filed issue #795 and started reading the codebase. By the end of the morning I'd filed four items across two repos and two organizations, rewritten the terminal launching system, and discovered a Linux kernel limit that most developers don't know exists. I never got to test the agent swarm. iloom-cli is a TypeScript CLI (and VS Code extension) by iloom-ai that orchestrates structured AI-assisted development. You point it at GitHub issues, and it spins up isolated git worktrees with dedicated database branches and port assignments (3000 + issue_number). Each worktree gets its own Claude Code agent working independently. The concept is genuinely clever: parallel AI development without context contamination between tasks. About 79 stars at the time I found it. For a tool solving a real workflow problem with 4,500+ tests, that's low. Project iloom-cli Stars ~79 at time of writing Maintainer Small team (iloom-ai), active collaborator acreeger Code health Heavy test coverage, clean separation of concerns, mature patterns Docs Detailed CLAUDE.md, command docs, but Linux/WSL support undocumented Contributor UX Excellent. Fast reviews, structured feedback, maintainer gives credit Worth using Yes on macOS. On Linux, now yes (with our merged PR) The codebase is substantial TypeScript with clear architectural intent. Business logic lives in lib/ (WorkspaceManager, GitWorktreeManager, DatabaseManager), CLI commands are separate classes in commands/, and utilities sit in utils/. Strategy pattern shows up in the database provider abstraction (Neon, Supabase, PlanetScale all implement the same interface) and, after our PR, in the terminal backends. Dependency injection runs through the core classes. The test suite is the standout: 4,500+ tests across 135 files, with a behavior-focused testing philosophy documented in their CLAUDE.md. Vitest handles the runner with global mock cleanup configured project-wide. Pre-commit hooks run lint, compile, test, and build. This is a codebase that takes quality seriously. What they got right: the worktree isolation model is the real product insight. Each GitHub issue gets its own worktree, its own database branch, its own port. Agents can't step on each other. Port conflicts can't happen. The orchestration layer (WorkspaceManager) ties these pieces together cleanly. What was rough: the terminal launching code. Before our PR, terminal.ts was a ~400-line monolith with macOS hardcoded throughout. Terminal.app support, iTerm2 support via AppleScript, nothing else. On Linux it threw an error and quit. On WSL it threw an error and quit. In a headless SSH session it threw an error and quit. This is a CLI tool designed for autonomous agent workflows, and it required a GUI window on a Mac. The other surprise was the agent loading system. iloom passes agent definitions to Claude Code as CLI arguments. All 8-9 agent templates, totaling 215KB of JSON, get crammed into a single --agents argument regardless of which agents the workflow actually needs. This works on macOS, where the effective per-argument limit is stack_size / 4 (around 16MB). On Linux, it crashes immediately. Most developers know about ARG_MAX, the total argument space limit (typically 2MB on Linux). What they don't know is that the kernel also enforces MAX_ARG_STRLEN, a separate per-argument cap of PAGE_SIZE * 32 (128KB on most systems). The 215KB agents blob isn't even close to hitting the total limit. It's dead on arrival because a single argument can't exceed 128KB, full stop. On macOS nobody noticed because the effective limit is two orders of magnitude higher. The E2BIG error shows up after the terminal backends work perfectly, which makes it especially confusing to debug. I refactored the terminal launching system from the ~400-line monolith into pluggable backends using a strategy pattern: Platform Backend Terminal(s) macOS DarwinBackend Terminal.app, iTerm2 Linux (GUI) LinuxBackend gnome-terminal, konsole, xterm Linux (headless) TmuxBackend tmux sessions WSL WSLBackend Windows Terminal via wt.exe A factory in index.ts detects the environment and picks the right backend. On Linux, it tries GUI terminals first (checking DISPLAY / WAYLAND_DISPLAY to avoid SIGABRT when a terminal emulator is installed but no display server is running), then falls back to tmux automatically. Headless Linux (SSH, Docker, CI, Code Server) is arguably the most common deployment target for a tool that orchestrates autonomous agents. That's the environment where tmux becomes essential, and it was completely unsupported. The public API stayed unchanged: openTerminalWindow and openMultipleTerminalWindows work identically regardless of platform. The PR went through two thorough review rounds from acreeger. The first caught three deduplication issues (duplicated command-building logic across backends, duplicate iTerm2 detection, overlapping platform detection functions). All fair. The second round caught two real bugs: tmux sessions created by openSingle() lacked the iloom- prefix, making them invisible to findIloomSession(), and the WSL backend didn't append ; exec bash, so terminal tabs closed the instant the command finished. Good catches both times. After I addressed everything, the maintainer approved and merged PR #796 the same day. Final terminal.ts: ~105 lines, down from ~400. Four platform backends, 36 new tests, zero regressions against the existing 4,500. I also filed issue #797 for the E2BIG problem (the 215KB agents JSON exceeding Linux's per-argument kernel limit) and a feature request against Claude Code for an --agents-file flag. The maintainer is exploring rendering agents to worktree subfolders as a fix. iloom is for developers who are already using Claude Code and want to parallelize it across multiple issues without agents interfering with each other. The worktree isolation model is sound, the codebase is well-tested, and the maintainer runs one of the best review processes I've seen on a project this size: structured feedback graded by severity, fast turnaround, credit given openly. The project's trajectory is upward. It's young (September 2025), actively developed, and now works on Linux. The platform gap was a growing pain, not a design flaw. The underlying architecture handled the expansion cleanly, which says something about the foundations. There's a broader lesson here. I was running Code Server over the network to a remote Linux box, through a Claude Code terminal session. That's three layers of edge case that nobody on a macOS laptop will encounter. But every one of those layers represents a real deployment scenario: SSH sessions, Docker containers, Chromebooks with Linux, CI pipelines. My setup wasn't the edge case. The MacBook is. The most valuable QA you can do for any project is to use it on a platform the developers don't use. What would push iloom further: lazy agent loading (don't pass 215KB when the workflow needs 40KB), documentation for non-macOS platforms, and broader awareness. If you're running Claude Code agents on anything other than a MacBook, this tool now works where you actually need it. Star iloom-cli and try it on your next multi-issue sprint. If you're on Linux or WSL, that works now. Here's the PR that made it happen, and here's the issue that started it. This is Review Bomb #4, a series where I find under-the-radar projects on GitHub, read the code, contribute something, and write it up. If you know a project that deserves more eyeballs, drop it in the comments.

Who did what, when, and with what outcome? If your API needs to answer that for compliance, security, or debugging, you need a structured audit trail. This post shows how to get one quickly using the HazelJS Audit Starter: a small Orders API that logs every HTTP request and every business event to console, file, and optionally Kafka — with actors, resources, and redaction built in. Compliance — Regulators and policies often require a record of who accessed or changed what. Security — After an incident, you need a timeline of actions and actors. Debugging — “Why did this order change?” is easier when every create/update/delete is logged. Doing this from scratch means instrumenting every endpoint, normalizing event shape, and piping events to logs or a SIEM. The HazelJS Audit Starter and @hazeljs/audit give you a ready-made pattern: one interceptor for HTTP, one service for custom events, and pluggable transports (console, file, Kafka). The hazeljs-audit-starter is a minimal HazelJS app that demonstrates: AuditModule with console (stdout) and file (logs/audit.jsonl) transports. AuditInterceptor on the Orders controller so every GET / POST / PUT / DELETE is logged (success or failure). AuditService in the orders service for explicit events: order.create, order.update, order.delete with resource id and metadata. @Audit decorator on controller methods for clear action/resource semantics. Demo user context via headers (X-User-Id, X-User-Name, X-User-Role) so each event has an actor (swap for JWT in production). Optional Kafka — set KAFKA_BROKERS and events go to a topic as JSON or Avro. So: every request is audited automatically, and every important business action is audited explicitly, in one place. Clone or place the starter next to the HazelJS monorepo, then: cd hazeljs-audit-starter npm install cp .env.example .env npm run dev Server runs at http://localhost:3000. Try: # Health (no auth) curl http://localhost:3000/health # Create an order (send user headers so the event has an actor) curl -X POST http://localhost:3000/orders \ -H "Content-Type: application/json" \ -H "X-User-Id: u1" \ -H "X-User-Name: alice" \ -H "X-User-Role: admin" \ -d '{"customerId":"c1","amount":99.99}' Each request produces an audit event. You’ll see one JSON line on stdout and one line appended to logs/audit.jsonl. Every event is a single JSON object with a consistent shape. Example from the file after a few POST /orders calls: { "action": "order.create", "actor": { "id": "u1", "username": "alice", "role": "admin" }, "resource": "Order", "resourceId": "ord-1", "result": "success", "metadata": { "amount": 99.99, "customerId": "c1" }, "timestamp": "2026-03-02T12:25:30.806Z", "_type": "audit" } action — e.g. order.create, http.get, order.delete. actor — who did it (from request context or headers in the starter). resource / resourceId — what was affected. result — success, failure, or denied. timestamp — ISO string (set automatically if you omit it). metadata — extra data; sensitive keys (password, token, etc.) are redacted by default. So you get a clear, queryable trail: who did what, when, and with what outcome. 1. Module and transports In app.module.ts, the app registers the audit module with console and file transports (and optional Kafka when env is set): AuditModule.forRoot({ transports: [ new ConsoleAuditTransport(), new FileAuditTransport({ filePath: path.join(process.cwd(), process.env.AUDIT_LOG_FILE || 'logs/audit.jsonl'), ensureDir: true, // maxSizeBytes, rollDaily for rotation }), ], redactKeys: ['password', 'token', 'authorization', 'secret'], }); 2. Controller: interceptor + @Audit The Orders controller uses the global AuditInterceptor and the @Audit decorator so every HTTP call is logged and each method has a clear action/resource: @Controller('/orders') @UseGuards(DemoUserGuard) @UseInterceptors(AuditInterceptor) export class OrdersController { @Post() @Audit({ action: 'order.create', resource: 'Order' }) create(@Body() body: CreateOrderDto, @Req() req: RequestWithUser) { // ... return this.ordersService.create(body, ctx); } } 3. Service: custom events For business-level events, the service injects AuditService and calls log() with action, actor, resource, and metadata: this.audit.log({ action: 'order.create', actor: this.audit.actorFromContext(context), resource: 'Order', resourceId: order.id, result: 'success', metadata: { amount: order.total }, }); So you get both: automatic HTTP audit and explicit domain events, in one pipeline. Console — One JSON line per event on stdout (handy for local dev and container logs). File — Same JSON lines in logs/audit.jsonl (or AUDIT_LOG_FILE). You can enable rotation by size (AUDIT_LOG_MAX_SIZE_MB) or by day (AUDIT_LOG_ROLL_DAILY=true). Kafka (optional) — Set KAFKA_BROKERS and optionally KAFKA_AUDIT_TOPIC and KAFKA_AUDIT_AVRO=true; the starter adds KafkaAuditTransport at startup and events are published as JSON or Avro. See src/audit-kafka.ts for the Avro schema and createKafkaAuditTransport. One event is sent to all configured transports, so you can have dev (console + file) and prod (file + Kafka) without changing your business code. Replace DemoUserGuard with @hazeljs/auth (JWT) so context.user comes from a verified token. Keep redactKeys in forRoot so sensitive fields in metadata are never logged in full. For scale or central logging, add KafkaAuditTransport (or your own AuditTransport) and keep file/console as needed. Use file rotation (AUDIT_LOG_MAX_SIZE_MB or AUDIT_LOG_ROLL_DAILY) so logs/audit.jsonl doesn’t grow unbounded. The HazelJS Audit Starter shows how to add a full audit trail to a small API with minimal code: register AuditModule with the transports you want, put AuditInterceptor and @Audit on your controller, and call AuditService.log() for domain events. Events are structured, include actor and resource, and can go to console, file, and Kafka (including Avro). Clone the starter, run it, and then adapt it to your stack and policies. Starter: hazeljs-audit-starter Package: @hazeljs/audit Docs: HazelJS documentation

准备好开始 Vibe Coding 了吗?

探索最适合你的 AI 编程工具,提升开发效率

立即探索