Skip to content

Automation

The Idea

Configure skills to auto-load for specific agents via oh-my-opencode.json.

Context

Wanted content-seed harvesting to be always active, not requiring manual /content-seed invocation each session.

Solution: Add skills array to agent config.

Implementation

// ~/.config/opencode/oh-my-opencode.json
{
  "agents": {
    "sisyphus": {
      "model": "...",
      "skills": ["content-seed"]  // ← auto-loads
    }
  }
}

Why It Matters

  • Skills become persistent behaviors, not one-off invocations
  • Enables “always-on” passive agents (harvesters, monitors, etc.)
  • Configuration as behavior definition

Content Angle

  • Tutorial: “Making OpenCode skills always-active”
  • Pattern: “Passive AI behaviors through skill injection”

The Idea

Build a “Teresa Torres-style /today command” for personal productivity - a single command that aggregates calendar, emails, and newsletters into a daily briefing one-pager in Obsidian, then git pushes for multi-machine sync.

Context

While testing moltbot’s morning-briefing skill, user referenced a maily.so article about Teresa Torres (product coach, non-developer) who built her own /today command with Claude Code. The article inspired reimagining the morning briefing as a complete personal AI pipeline.

The Vision

πŸ“… apple-calendar ──┐
πŸ“§ himalaya email ──┼──▢ πŸŒ… morning-briefing ──▢ πŸ“ Obsidian ──▢ git push
πŸ“° newsletter-parserβ”˜       (Korean one-pager)      (vault)       (sync)

Key insight from Teresa Torres article:

“When I have a task, do I need to be involved? Can Claude just do it? Or does it need my input?”

Raw Exchange

User: “yes but output is not slack. it’s one pager in obsidian vault. once you post morning brief you push to git so I can sync in any other machine.”

Read more

The Idea

Invert the documentation burden: user thinks freely, AI silently captures.

Context

While building a content seed capture system, user realized that manual capture commands (seed-idea, seed-prompt) still require context-switching. The insight: AI should be a passive harvester, not a tool to invoke.

Raw Quote

“seed idea capture and naming should be automated. I am just doing ideation and feedback on agent work and you harvest all”

Expansion Potential

This pattern applies beyond content seeds:

  • Meeting notes (human talks, AI summarizes)
  • Code documentation (human codes, AI documents)
  • Decision logs (human decides, AI records rationale)
  • Learning journals (human works, AI extracts lessons)

The shift: from “user documents with AI help” to “AI documents while user works”

Content Angle

  • Philosophy piece: “The Passive AI Collaborator”
  • Tutorial: “Building auto-capture systems with LLMs”
  • Case study: “How I stopped taking notes and let AI do it”

What Happened

Playwright automation for NotebookLM upload failed with “Add source button not clickable”. Screenshots showed the button was visible but clicks didn’t register.

Investigation

Screenshot analysis revealed: Google’s UI had an invisible modal overlay (search dialog, promo, or consent dialog) capturing all clicks before they reached the target button.

Key symptom: Element is visible, selector finds it, but click() fails silently or times out.

Resolution

Created dismissOverlays() function to clear modals before interacting:

async function dismissOverlays(page: Page) {
  // Press Escape multiple times to dismiss any open dialogs
  for (let i = 0; i < 3; i++) {
    await page.keyboard.press("Escape");
    await page.waitForTimeout(200);
  }
  
  // Click outside any modal areas
  try {
    await page.click("body", { position: { x: 0, y: 0 }, force: true });
  } catch {
    // Ignore if click fails
  }
}

// Usage before any critical interaction
await dismissOverlays(page);
await page.click('button[aria-label="Add source"]');

Lesson

Google products love modals. When automating Google UIs (Docs, Drive, NotebookLM, etc.), always:

  1. Take debug screenshots before failing
  2. Assume invisible overlays exist
  3. Add dismissOverlays() as standard practice
  4. Use force: true as last resort (bypasses actionability checks)

Prevention Pattern

// Before any critical Google UI interaction:
await dismissOverlays(page);
await page.waitForTimeout(500); // Let animations settle
await page.click(selector, { timeout: 10000 });