Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Sync with Upstream

on:
schedule:
# Run daily at 6:00 UTC
- cron: '0 6 * * *'
workflow_dispatch: # Manual trigger

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout fork
uses: actions/checkout@v4
with:
ref: dev
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Git
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"

- name: Add upstream remote
run: git remote add upstream https://github.com/code-yeongyu/oh-my-opencode.git

- name: Fetch upstream
run: git fetch upstream dev

- name: Check for updates
id: check
run: |
UPSTREAM_COMMIT=$(git rev-parse upstream/dev)
FORK_BASE=$(git merge-base HEAD upstream/dev)
if [ "$UPSTREAM_COMMIT" != "$FORK_BASE" ]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "New commits found in upstream"
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "Fork is up to date"
fi

- name: Rebase on upstream
if: steps.check.outputs.has_updates == 'true'
run: |
git rebase upstream/dev || {
echo "Rebase failed - conflicts detected"
git rebase --abort
exit 1
}

- name: Setup Bun
if: steps.check.outputs.has_updates == 'true'
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
if: steps.check.outputs.has_updates == 'true'
run: bun install

- name: Build
if: steps.check.outputs.has_updates == 'true'
run: bun run build

- name: Push changes
if: steps.check.outputs.has_updates == 'true'
run: git push --force-with-lease origin dev

- name: Get version
if: steps.check.outputs.has_updates == 'true'
id: version
run: |
VERSION=$(jq -r .version package.json)
BOULDER_VERSION="${VERSION}-boulder-$(date +%Y%m%d-%H%M%S)"
echo "version=$BOULDER_VERSION" >> $GITHUB_OUTPUT

- name: Rename artifact
if: steps.check.outputs.has_updates == 'true'
run: cp dist/index.js dist/oh-my-opencode-boulder.js

- name: Create Release
if: steps.check.outputs.has_updates == 'true'
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.version.outputs.version }}
name: Boulder Loop v${{ steps.version.outputs.version }}
body: |
Synced with upstream oh-my-opencode.

## Installation
Download `oh-my-opencode-boulder.js` and place in `~/.config/opencode/plugins/`
files: |
dist/oh-my-opencode-boulder.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
SisyphusAgentConfigSchema,
ExperimentalConfigSchema,
RalphLoopConfigSchema,
BoulderLoopConfigSchema,
TmuxConfigSchema,
TmuxLayoutSchema,
} from "./schema"
Expand All @@ -25,6 +26,7 @@ export type {
ExperimentalConfig,
DynamicContextPruningConfig,
RalphLoopConfig,
BoulderLoopConfig,
TmuxConfig,
TmuxLayout,
SisyphusConfig,
Expand Down
12 changes: 12 additions & 0 deletions src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const HookNameSchema = z.enum([

"thinking-block-validator",
"ralph-loop",
"boulder-loop",
"category-skill-reminder",

"compaction-context-injector",
Expand Down Expand Up @@ -304,6 +305,15 @@ export const RalphLoopConfigSchema = z.object({
state_dir: z.string().optional(),
})

export const BoulderLoopConfigSchema = z.object({
/** Enable boulder loop functionality (default: true) */
enabled: z.boolean().default(true),
/** Default hours if not specified in command (default: 4) */
default_hours: z.number().min(1).max(24).default(4),
/** Custom state file directory relative to project root */
state_dir: z.string().optional(),
})

export const BackgroundTaskConfigSchema = z.object({
defaultConcurrency: z.number().min(1).optional(),
providerConcurrency: z.record(z.string(), z.number().min(0)).optional(),
Expand Down Expand Up @@ -401,6 +411,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
auto_update: z.boolean().optional(),
skills: SkillsConfigSchema.optional(),
ralph_loop: RalphLoopConfigSchema.optional(),
boulder_loop: BoulderLoopConfigSchema.optional(),
background_task: BackgroundTaskConfigSchema.optional(),
notification: NotificationConfigSchema.optional(),
babysitting: BabysittingConfigSchema.optional(),
Expand All @@ -426,6 +437,7 @@ export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningCo
export type SkillsConfig = z.infer<typeof SkillsConfigSchema>
export type SkillDefinition = z.infer<typeof SkillDefinitionSchema>
export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>
export type BoulderLoopConfig = z.infer<typeof BoulderLoopConfigSchema>
export type NotificationConfig = z.infer<typeof NotificationConfigSchema>
export type BabysittingConfig = z.infer<typeof BabysittingConfigSchema>
export type CategoryConfig = z.infer<typeof CategoryConfigSchema>
Expand Down
18 changes: 18 additions & 0 deletions src/features/builtin-commands/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { CommandDefinition } from "../claude-code-command-loader"
import type { BuiltinCommandName, BuiltinCommands } from "./types"
import { INIT_DEEP_TEMPLATE } from "./templates/init-deep"
import { RALPH_LOOP_TEMPLATE, CANCEL_RALPH_TEMPLATE } from "./templates/ralph-loop"
import { BOULDER_LOOP_TEMPLATE, CANCEL_BOULDER_TEMPLATE } from "./templates/boulder-loop"
import { STOP_CONTINUATION_TEMPLATE } from "./templates/stop-continuation"
import { REFACTOR_TEMPLATE } from "./templates/refactor"
import { START_WORK_TEMPLATE } from "./templates/start-work"
Expand Down Expand Up @@ -75,6 +76,23 @@ $ARGUMENTS
description: "(builtin) Stop all continuation mechanisms (ralph loop, todo continuation, boulder) for this session",
template: `<command-instruction>
${STOP_CONTINUATION_TEMPLATE}
</command-instruction>`,
},
"boulder": {
description: "(builtin) Start time-based development loop until deadline",
template: `<command-instruction>
${BOULDER_LOOP_TEMPLATE}
</command-instruction>

<user-task>
$ARGUMENTS
</user-task>`,
argumentHint: '"task description" --until=HH:MM or --hours=N',
},
"cancel-boulder": {
description: "(builtin) Cancel active Boulder Loop",
template: `<command-instruction>
${CANCEL_BOULDER_TEMPLATE}
</command-instruction>`,
},
}
Expand Down
40 changes: 40 additions & 0 deletions src/features/builtin-commands/templates/boulder-loop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const BOULDER_LOOP_TEMPLATE = `You are starting a Boulder Loop - a TIME-BASED development loop that runs until a specific deadline.

## How Boulder Loop Works

1. You will work continuously until the deadline
2. You CANNOT stop early - the system will keep injecting prompts until time runs out
3. When you finish your current task, find NEW improvements to make
4. The loop only ends when the deadline is reached

## Rules

- You cannot stop working until the deadline
- If all tasks are done, find NEW work: clippy warnings, test coverage, docs, refactoring
- Check for TODOs in code, outdated dependencies, missing error handling
- Each iteration should make meaningful progress
- Use todos to track your work

## Exit Conditions

1. **Deadline Reached**: Loop stops automatically when time expires
2. **Cancel**: User runs \`/cancel-boulder\` command (emergency only)

## Your Task

Parse the arguments below and begin working. Format:
\`"task description" --until=HH:MM\` or \`"task description" --hours=N\`

Examples:
- \`"Refactor auth module" --until=06:00\` (work until 6 AM)
- \`"Improve test coverage" --hours=4\` (work for 4 hours)
- \`"Fix all warnings" --until=tomorrow\` (work until midnight)`

export const CANCEL_BOULDER_TEMPLATE = `Cancel the currently active Boulder Loop.

This will:
1. Stop the time-based loop immediately
2. Clear the loop state file
3. Allow the session to end normally

Check if a boulder loop is active and cancel it. Inform the user of the result.`
2 changes: 1 addition & 1 deletion src/features/builtin-commands/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CommandDefinition } from "../claude-code-command-loader"

export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation"
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation" | "boulder" | "cancel-boulder"

export interface BuiltinCommandConfig {
disabled_commands?: BuiltinCommandName[]
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/boulder-loop/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const HOOK_NAME = "boulder-loop"
export const DEFAULT_STATE_FILE = ".sisyphus/boulder-loop.local.md"
export const DEFAULT_HOURS = 4
Loading