@bobbyg603/mog 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -13
- package/package.json +1 -1
- package/src/index.ts +16 -7
- package/src/worktree.ts +3 -3
package/README.md
CHANGED
|
@@ -11,10 +11,13 @@ mog workingdevshero/automate-it 123
|
|
|
11
11
|
|
|
12
12
|
That's it. `mog` will:
|
|
13
13
|
|
|
14
|
-
1. Fetch the issue title, description, and
|
|
14
|
+
1. Fetch the issue title, description, labels, and comments via `gh` CLI
|
|
15
15
|
2. Create a git worktree on a clean branch (`123-fix-broken-login`)
|
|
16
16
|
3. Run Claude Code inside a persistent Docker sandbox (microVM) with `--dangerously-skip-permissions`
|
|
17
|
-
4.
|
|
17
|
+
4. **Plan** — analyze the codebase and create an implementation plan
|
|
18
|
+
5. **Build** — execute each task in the plan, one at a time
|
|
19
|
+
6. **Review** — self-review all changes for missed patterns, duplication, and quality
|
|
20
|
+
7. Squash commits, push the branch, and open a PR that `Closes #123`
|
|
18
21
|
|
|
19
22
|
## Prerequisites
|
|
20
23
|
|
|
@@ -57,12 +60,37 @@ If your session ever expires, just run `mog init` again to re-authenticate.
|
|
|
57
60
|
# One-time setup
|
|
58
61
|
mog init
|
|
59
62
|
|
|
60
|
-
#
|
|
61
|
-
mog
|
|
63
|
+
# Auto-detect repo from git remote (run from inside a git repo)
|
|
64
|
+
mog 123
|
|
62
65
|
|
|
63
|
-
#
|
|
64
|
-
mog
|
|
65
|
-
|
|
66
|
+
# Explicit repo
|
|
67
|
+
mog owner/repo 123
|
|
68
|
+
|
|
69
|
+
# Include files the project needs at runtime (e.g. .env, credentials)
|
|
70
|
+
# Files are copied into the worktree and removed before pushing
|
|
71
|
+
mog 123 --include .env --include serviceAccountKey.json
|
|
72
|
+
|
|
73
|
+
# List open issues
|
|
74
|
+
mog list
|
|
75
|
+
mog list --verbose
|
|
76
|
+
mog owner/repo list --verbose
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Re-mogging
|
|
80
|
+
|
|
81
|
+
Running `mog` again on an issue that already has an open PR will:
|
|
82
|
+
|
|
83
|
+
1. Fetch review comments and feedback from the existing PR
|
|
84
|
+
2. Include that feedback in the prompt so Claude addresses it
|
|
85
|
+
3. Start fresh from the default branch
|
|
86
|
+
4. Force-push to update the existing PR
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Re-mog after getting PR feedback — Claude sees reviewer comments
|
|
90
|
+
mog 123
|
|
91
|
+
|
|
92
|
+
# Start completely over, ignoring the existing PR
|
|
93
|
+
mog 123 --fresh
|
|
66
94
|
```
|
|
67
95
|
|
|
68
96
|
## How it works
|
|
@@ -71,7 +99,8 @@ mog sparx-tech/hub-firmware 45
|
|
|
71
99
|
┌──────────────────────────────────────────────────────────┐
|
|
72
100
|
│ Host machine │
|
|
73
101
|
│ │
|
|
74
|
-
│ 1. gh issue view #123 → fetch title, body, labels
|
|
102
|
+
│ 1. gh issue view #123 → fetch title, body, labels, │
|
|
103
|
+
│ comments, and PR review feedback (if re-mogging) │
|
|
75
104
|
│ 2. git worktree add → clean branch from default branch │
|
|
76
105
|
│ │
|
|
77
106
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
@@ -80,12 +109,14 @@ mog sparx-tech/hub-firmware 45
|
|
|
80
109
|
│ │ • ~/mog-repos mounted as workspace │ │
|
|
81
110
|
│ │ • Auth persists across runs (login once) │ │
|
|
82
111
|
│ │ • Isolated from host (own Docker daemon) │ │
|
|
83
|
-
│ │ •
|
|
84
|
-
│ │ •
|
|
112
|
+
│ │ • Phase 1: Plan — analyze codebase, create plan │ │
|
|
113
|
+
│ │ • Phase 2: Build — execute tasks one at a time │ │
|
|
114
|
+
│ │ • Phase 3: Review — self-review for quality │ │
|
|
85
115
|
│ └────────────────────────────────────────────────────┘ │
|
|
86
116
|
│ │
|
|
87
|
-
│ 3.
|
|
88
|
-
│ 4.
|
|
117
|
+
│ 3. Squash commits into one │
|
|
118
|
+
│ 4. git push origin branch (force-push if updating PR) │
|
|
119
|
+
│ 5. gh pr create --body "Closes #123" (or update PR) │
|
|
89
120
|
└──────────────────────────────────────────────────────────┘
|
|
90
121
|
```
|
|
91
122
|
|
|
@@ -94,7 +125,7 @@ mog sparx-tech/hub-firmware 45
|
|
|
94
125
|
| Environment Variable | Default | Description |
|
|
95
126
|
|---|---|---|
|
|
96
127
|
| `MOG_REPOS_DIR` | `~/mog-repos` | Where repos are cloned and worktrees created (also the sandbox workspace) |
|
|
97
|
-
| `MOG_MAX_ITERATIONS` | `
|
|
128
|
+
| `MOG_MAX_ITERATIONS` | `30` | Max build loop iterations per issue |
|
|
98
129
|
| `MOG_MAX_CONTINUATIONS` | — | Legacy alias for `MOG_MAX_ITERATIONS` |
|
|
99
130
|
|
|
100
131
|
## Worktree management
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -216,16 +216,18 @@ async function main() {
|
|
|
216
216
|
const { defaultBranch } = ensureRepo(repo, owner, repoName, reposDir);
|
|
217
217
|
log.info(`Default branch: ${defaultBranch}`);
|
|
218
218
|
|
|
219
|
-
const { worktreeDir, branchName } = createWorktree(
|
|
219
|
+
const { worktreeDir, branchName, reused } = createWorktree(
|
|
220
220
|
reposDir, owner, repoName, defaultBranch, issueNum, issue.title
|
|
221
221
|
);
|
|
222
222
|
|
|
223
223
|
// Check for existing PR (unless --fresh)
|
|
224
224
|
let existingPR: PRFeedback | undefined;
|
|
225
|
+
let isRetry = reused;
|
|
225
226
|
if (!fresh) {
|
|
226
227
|
const pr = fetchPRFeedback(repo, branchName);
|
|
227
228
|
if (pr) {
|
|
228
229
|
existingPR = pr;
|
|
230
|
+
isRetry = true;
|
|
229
231
|
log.ok(`Found existing PR #${pr.prNumber} — will include review feedback and update it.`);
|
|
230
232
|
}
|
|
231
233
|
}
|
|
@@ -242,7 +244,7 @@ async function main() {
|
|
|
242
244
|
|
|
243
245
|
// Build prompts
|
|
244
246
|
const prFeedback = existingPR?.reviews || "";
|
|
245
|
-
const planningPrompt = buildPlanningPrompt(repo, issueNum, issue, prFeedback);
|
|
247
|
+
const planningPrompt = buildPlanningPrompt(repo, issueNum, issue, prFeedback, isRetry);
|
|
246
248
|
const buildingPromptFn = (remaining: string[], plan: string) =>
|
|
247
249
|
buildBuildingPrompt(repo, issueNum, issue, remaining, plan);
|
|
248
250
|
const reviewPrompt = buildReviewPrompt(repo, issueNum, issue);
|
|
@@ -319,7 +321,7 @@ function tryRecoverSandbox(reposDir: string): boolean {
|
|
|
319
321
|
return true;
|
|
320
322
|
}
|
|
321
323
|
|
|
322
|
-
function formatIssueContext(issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string): string {
|
|
324
|
+
function formatIssueContext(issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string, isRetry?: boolean): string {
|
|
323
325
|
let context = `## Issue: ${issue.title}
|
|
324
326
|
|
|
325
327
|
### Description
|
|
@@ -335,22 +337,29 @@ ${issue.labels}`;
|
|
|
335
337
|
${issue.comments}`;
|
|
336
338
|
}
|
|
337
339
|
|
|
338
|
-
if (
|
|
340
|
+
if (isRetry) {
|
|
339
341
|
context += `
|
|
340
342
|
|
|
343
|
+
### Re-attempt Notice
|
|
344
|
+
**This is a re-attempt of a previous run.** The issue description, comments, or requirements may have been updated since the last attempt. Carefully review ALL context above to catch anything that may have changed.`;
|
|
345
|
+
|
|
346
|
+
if (prFeedback) {
|
|
347
|
+
context += `
|
|
348
|
+
|
|
341
349
|
### Previous PR Review Feedback
|
|
342
|
-
|
|
350
|
+
Address the following reviewer feedback in your implementation:
|
|
343
351
|
|
|
344
352
|
${prFeedback}`;
|
|
353
|
+
}
|
|
345
354
|
}
|
|
346
355
|
|
|
347
356
|
return context;
|
|
348
357
|
}
|
|
349
358
|
|
|
350
|
-
function buildPlanningPrompt(repo: string, issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string): string {
|
|
359
|
+
function buildPlanningPrompt(repo: string, issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string, isRetry?: boolean): string {
|
|
351
360
|
return `You are working on GitHub issue #${issueNum} for the repository ${repo}.
|
|
352
361
|
|
|
353
|
-
${formatIssueContext(issueNum, issue, prFeedback)}
|
|
362
|
+
${formatIssueContext(issueNum, issue, prFeedback, isRetry)}
|
|
354
363
|
|
|
355
364
|
## Instructions
|
|
356
365
|
|
package/src/worktree.ts
CHANGED
|
@@ -73,7 +73,7 @@ export function createWorktree(
|
|
|
73
73
|
defaultBranch: string,
|
|
74
74
|
issueNum: string,
|
|
75
75
|
issueTitle: string
|
|
76
|
-
): { worktreeDir: string; branchName: string } {
|
|
76
|
+
): { worktreeDir: string; branchName: string; reused: boolean } {
|
|
77
77
|
const safeTitle = issueTitle
|
|
78
78
|
.toLowerCase()
|
|
79
79
|
.replace(/[^a-z0-9]/g, "-")
|
|
@@ -87,7 +87,7 @@ export function createWorktree(
|
|
|
87
87
|
|
|
88
88
|
if (fs.existsSync(worktreeDir)) {
|
|
89
89
|
log.warn(`Worktree already exists at ${worktreeDir}, reusing.`);
|
|
90
|
-
return { worktreeDir, branchName };
|
|
90
|
+
return { worktreeDir, branchName, reused: true };
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
log.info(`Creating worktree for branch '${branchName}'...`);
|
|
@@ -120,5 +120,5 @@ export function createWorktree(
|
|
|
120
120
|
Bun.spawnSync(["git", "submodule", "update", "--init", "--recursive"], { cwd: worktreeDir });
|
|
121
121
|
|
|
122
122
|
log.ok(`Worktree created at ${worktreeDir}`);
|
|
123
|
-
return { worktreeDir, branchName };
|
|
123
|
+
return { worktreeDir, branchName, reused: false };
|
|
124
124
|
}
|