opencode-mad 0.3.6 → 0.3.8
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/agents/mad-fixer.md +3 -2
- package/agents/mad-merger.md +37 -3
- package/agents/orchestrator.md +64 -0
- package/install.js +1 -1
- package/package.json +1 -1
- package/plugins/mad-plugin.ts +265 -2
- package/skills/mad-workflow/SKILL.md +11 -0
package/agents/mad-fixer.md
CHANGED
|
@@ -203,8 +203,9 @@ mad_blocked(
|
|
|
203
203
|
|
|
204
204
|
1. **NEVER work on main directly** - Always work in your assigned worktree
|
|
205
205
|
2. **Commit your changes** - Make atomic commits with clear messages
|
|
206
|
-
3. **
|
|
207
|
-
4. **
|
|
206
|
+
3. **If you need to merge manually, ALWAYS use `--no-ff`** - Preserves history and enables easy reverts
|
|
207
|
+
4. **Call mad_done when finished** - The orchestrator handles merging
|
|
208
|
+
5. **Use mad_blocked if stuck** - Don't guess, ask for clarification
|
|
208
209
|
|
|
209
210
|
## Remember
|
|
210
211
|
|
package/agents/mad-merger.md
CHANGED
|
@@ -29,6 +29,39 @@ You are a **MAD Merger subagent**. Your role is to intelligently resolve git mer
|
|
|
29
29
|
|
|
30
30
|
**ALL conflict resolution MUST be done in a worktree.** You NEVER modify code on main directly.
|
|
31
31
|
|
|
32
|
+
## Git Merge Policy
|
|
33
|
+
|
|
34
|
+
**ALWAYS use `--no-ff` (no fast-forward) for merges.**
|
|
35
|
+
|
|
36
|
+
### Why `--no-ff` is Required
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# ✅ CORRECT - Always use --no-ff
|
|
40
|
+
git merge --no-ff feat/feature-branch -m "merge: feature description"
|
|
41
|
+
|
|
42
|
+
# ❌ WRONG - Never use fast-forward merges
|
|
43
|
+
git merge feat/feature-branch
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Benefits of `--no-ff`:
|
|
47
|
+
|
|
48
|
+
1. **Preserves history** - Creates a merge commit even when fast-forward is possible, making it clear when features were integrated
|
|
49
|
+
2. **Facilitates reverts** - Easy to revert an entire feature with a single `git revert <merge-commit>`
|
|
50
|
+
3. **Shows feature boundaries** - The git log clearly shows which commits belong to which feature branch
|
|
51
|
+
4. **Audit trail** - Provides a clear record of when and what was merged
|
|
52
|
+
|
|
53
|
+
### Example:
|
|
54
|
+
```
|
|
55
|
+
* abc1234 (HEAD -> main) merge: add user authentication
|
|
56
|
+
|\
|
|
57
|
+
| * def5678 feat: add password hashing
|
|
58
|
+
| * ghi9012 feat: add login endpoint
|
|
59
|
+
|/
|
|
60
|
+
* previous commit on main
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Without `--no-ff`, these commits would be linear and you'd lose the visual grouping of the feature.
|
|
64
|
+
|
|
32
65
|
## When You're Called
|
|
33
66
|
|
|
34
67
|
The orchestrator spawns you when `mad_merge` encounters conflicts. You receive:
|
|
@@ -229,9 +262,10 @@ import { login, signup } from './auth';
|
|
|
229
262
|
## Important Rules
|
|
230
263
|
|
|
231
264
|
1. **NEVER work on main directly** - Always work in your assigned worktree
|
|
232
|
-
2. **
|
|
233
|
-
3. **
|
|
234
|
-
4. **
|
|
265
|
+
2. **ALWAYS use `--no-ff` for merges** - Preserves history and enables easy reverts
|
|
266
|
+
3. **Commit your resolution** - Make a clear commit with what you resolved
|
|
267
|
+
4. **Call mad_done when finished** - The orchestrator handles the final merge
|
|
268
|
+
5. **Use mad_blocked if stuck** - Don't guess on fundamental conflicts
|
|
235
269
|
|
|
236
270
|
## Remember
|
|
237
271
|
|
package/agents/orchestrator.md
CHANGED
|
@@ -516,6 +516,69 @@ Wait for all testers to complete. Only proceed to merge if ALL are marked done.
|
|
|
516
516
|
|
|
517
517
|
---
|
|
518
518
|
|
|
519
|
+
## Phase 5.5: Final Global Check
|
|
520
|
+
|
|
521
|
+
**IMPORTANT: Run this after all merges are complete!**
|
|
522
|
+
|
|
523
|
+
Use `mad_final_check` to verify the entire project's build and lint status:
|
|
524
|
+
|
|
525
|
+
```
|
|
526
|
+
mad_final_check()
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
This will:
|
|
530
|
+
1. Run all configured build/lint commands (npm run build, npm run lint, etc.)
|
|
531
|
+
2. Compare any errors against files modified during the session
|
|
532
|
+
3. Categorize errors as "session errors" or "pre-existing errors"
|
|
533
|
+
|
|
534
|
+
### Handling Results
|
|
535
|
+
|
|
536
|
+
#### If session errors are found:
|
|
537
|
+
These are bugs introduced by the MAD session. Create a fix worktree:
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
mad_worktree_create(
|
|
541
|
+
branch: "fix-session-errors",
|
|
542
|
+
task: "Fix build/lint errors introduced during session:
|
|
543
|
+
[list of errors]
|
|
544
|
+
|
|
545
|
+
YOU OWN ALL FILES in this worktree."
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
Task(
|
|
549
|
+
subagent_type: "mad-fixer",
|
|
550
|
+
description: "Fix session errors",
|
|
551
|
+
prompt: "Work in worktree 'fix-session-errors'. Fix the build/lint errors, commit, and call mad_done."
|
|
552
|
+
)
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
#### If only pre-existing errors are found:
|
|
556
|
+
These are NOT caused by the session. Inform the user:
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
"Your session completed successfully! No new errors were introduced.
|
|
560
|
+
|
|
561
|
+
However, I found [N] pre-existing build/lint errors that were already in the codebase.
|
|
562
|
+
Would you like me to fix them? (Note: these are not caused by our changes today)"
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
If the user says yes, create a worktree to fix them:
|
|
566
|
+
|
|
567
|
+
```
|
|
568
|
+
mad_worktree_create(
|
|
569
|
+
branch: "fix-preexisting-errors",
|
|
570
|
+
task: "Fix pre-existing build/lint errors (NOT from this session):
|
|
571
|
+
[list of errors]
|
|
572
|
+
|
|
573
|
+
These errors existed before the MAD session started."
|
|
574
|
+
)
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
#### If no errors:
|
|
578
|
+
Celebrate! The project is clean.
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
519
582
|
## Available Tools
|
|
520
583
|
|
|
521
584
|
| Tool | Description |
|
|
@@ -530,6 +593,7 @@ Wait for all testers to complete. Only proceed to merge if ALL are marked done.
|
|
|
530
593
|
| `mad_blocked` | Mark task blocked |
|
|
531
594
|
| `mad_read_task` | Read task description |
|
|
532
595
|
| `mad_log` | Log events for debugging |
|
|
596
|
+
| `mad_final_check` | Run global build/lint and categorize errors |
|
|
533
597
|
|
|
534
598
|
## Subagents
|
|
535
599
|
|
package/install.js
CHANGED
package/package.json
CHANGED
package/plugins/mad-plugin.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { execSync } from "child_process"
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Current version of opencode-mad
|
|
16
|
-
const CURRENT_VERSION = "0.3.
|
|
16
|
+
const CURRENT_VERSION = "0.3.8"
|
|
17
17
|
|
|
18
18
|
// Update notification state (shown only once per session)
|
|
19
19
|
let updateNotificationShown = false
|
|
@@ -424,7 +424,7 @@ Handles merge conflicts by reporting them.`,
|
|
|
424
424
|
const gitRoot = getGitRoot()
|
|
425
425
|
const worktreePath = join(gitRoot, "worktrees", args.worktree)
|
|
426
426
|
const doneFile = join(worktreePath, ".agent-done")
|
|
427
|
-
const branch = args.worktree
|
|
427
|
+
const branch = args.worktree
|
|
428
428
|
|
|
429
429
|
if (!existsSync(worktreePath)) {
|
|
430
430
|
return getUpdateNotification() + `Worktree not found: ${worktreePath}`
|
|
@@ -728,6 +728,269 @@ Latest version: ${updateInfo.latest}`
|
|
|
728
728
|
}
|
|
729
729
|
},
|
|
730
730
|
}),
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Final check - run global build/lint and categorize errors
|
|
734
|
+
*/
|
|
735
|
+
mad_final_check: tool({
|
|
736
|
+
description: `Run global build/lint checks on the main project after all merges.
|
|
737
|
+
Compares errors against files modified during the MAD session to distinguish:
|
|
738
|
+
- Session errors: caused by changes made during this session
|
|
739
|
+
- Pre-existing errors: already present before the session started
|
|
740
|
+
|
|
741
|
+
Use this at the end of the MAD workflow to ensure code quality.`,
|
|
742
|
+
args: {
|
|
743
|
+
baseCommit: tool.schema.string().optional().describe("The commit SHA from before the MAD session started. If not provided, will try to detect from reflog."),
|
|
744
|
+
},
|
|
745
|
+
async execute(args, context) {
|
|
746
|
+
try {
|
|
747
|
+
const gitRoot = getGitRoot()
|
|
748
|
+
|
|
749
|
+
// 1. Determine base commit for comparison
|
|
750
|
+
let baseCommit = args.baseCommit
|
|
751
|
+
if (!baseCommit) {
|
|
752
|
+
// Try to find the commit before MAD session started (look for last commit before worktrees were created)
|
|
753
|
+
const reflogResult = runCommand('git reflog --format="%H %gs" -n 50', gitRoot)
|
|
754
|
+
if (reflogResult.success) {
|
|
755
|
+
// Find first commit that's not a merge from a MAD branch
|
|
756
|
+
const lines = reflogResult.output.split('\n')
|
|
757
|
+
for (const line of lines) {
|
|
758
|
+
if (!line.includes('merge') || (!line.includes('feat-') && !line.includes('fix-'))) {
|
|
759
|
+
baseCommit = line.split(' ')[0]
|
|
760
|
+
break
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (!baseCommit) {
|
|
765
|
+
baseCommit = 'HEAD~10' // Fallback
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// 2. Get list of files modified during session
|
|
770
|
+
const diffResult = runCommand(`git diff ${baseCommit}..HEAD --name-only`, gitRoot)
|
|
771
|
+
const modifiedFiles = diffResult.success
|
|
772
|
+
? diffResult.output.split('\n').filter(f => f.trim()).map(f => f.trim())
|
|
773
|
+
: []
|
|
774
|
+
|
|
775
|
+
let report = getUpdateNotification() + `# Final Project Check\n\n`
|
|
776
|
+
report += `📊 **Session Summary:**\n`
|
|
777
|
+
report += `- Base commit: \`${baseCommit.substring(0, 8)}\`\n`
|
|
778
|
+
report += `- Files modified: ${modifiedFiles.length}\n\n`
|
|
779
|
+
|
|
780
|
+
// 3. Detect project type and run checks
|
|
781
|
+
const packageJson = join(gitRoot, "package.json")
|
|
782
|
+
const goMod = join(gitRoot, "go.mod")
|
|
783
|
+
const cargoToml = join(gitRoot, "Cargo.toml")
|
|
784
|
+
const pyProject = join(gitRoot, "pyproject.toml")
|
|
785
|
+
|
|
786
|
+
interface CheckError {
|
|
787
|
+
file: string
|
|
788
|
+
line?: number
|
|
789
|
+
message: string
|
|
790
|
+
isSessionError: boolean
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const allErrors: CheckError[] = []
|
|
794
|
+
let checksRun = 0
|
|
795
|
+
|
|
796
|
+
// Helper to parse errors and categorize them
|
|
797
|
+
const parseAndCategorize = (output: string, checkName: string) => {
|
|
798
|
+
// Common patterns for file:line:message
|
|
799
|
+
const patterns = [
|
|
800
|
+
/^(.+?):(\d+):\d*:?\s*(.+)$/gm, // file:line:col: message
|
|
801
|
+
/^(.+?)\((\d+),\d+\):\s*(.+)$/gm, // file(line,col): message (TypeScript)
|
|
802
|
+
/^\s*(.+?):(\d+)\s+(.+)$/gm, // file:line message
|
|
803
|
+
]
|
|
804
|
+
|
|
805
|
+
for (const pattern of patterns) {
|
|
806
|
+
let match
|
|
807
|
+
while ((match = pattern.exec(output)) !== null) {
|
|
808
|
+
const file = match[1].trim().replace(/\\/g, '/')
|
|
809
|
+
const line = parseInt(match[2])
|
|
810
|
+
const message = match[3].trim()
|
|
811
|
+
|
|
812
|
+
// Check if this file was modified during session
|
|
813
|
+
const isSessionError = modifiedFiles.some(mf =>
|
|
814
|
+
file.endsWith(mf) || mf.endsWith(file) || file.includes(mf) || mf.includes(file)
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
allErrors.push({ file, line, message, isSessionError })
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Run checks based on project type
|
|
823
|
+
if (existsSync(packageJson)) {
|
|
824
|
+
const pkg = JSON.parse(readFileSync(packageJson, "utf-8"))
|
|
825
|
+
|
|
826
|
+
if (pkg.scripts?.lint) {
|
|
827
|
+
checksRun++
|
|
828
|
+
report += `## 🔍 Lint Check\n`
|
|
829
|
+
const lintResult = runCommand("npm run lint 2>&1", gitRoot)
|
|
830
|
+
if (lintResult.success) {
|
|
831
|
+
report += `✅ Lint passed\n\n`
|
|
832
|
+
} else {
|
|
833
|
+
report += `❌ Lint failed\n`
|
|
834
|
+
parseAndCategorize(lintResult.error || lintResult.output, "lint")
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (pkg.scripts?.build) {
|
|
839
|
+
checksRun++
|
|
840
|
+
report += `## 🔨 Build Check\n`
|
|
841
|
+
const buildResult = runCommand("npm run build 2>&1", gitRoot)
|
|
842
|
+
if (buildResult.success) {
|
|
843
|
+
report += `✅ Build passed\n\n`
|
|
844
|
+
} else {
|
|
845
|
+
report += `❌ Build failed\n`
|
|
846
|
+
parseAndCategorize(buildResult.error || buildResult.output, "build")
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (pkg.scripts?.typecheck || pkg.scripts?.["type-check"]) {
|
|
851
|
+
checksRun++
|
|
852
|
+
const cmd = pkg.scripts?.typecheck ? "npm run typecheck" : "npm run type-check"
|
|
853
|
+
report += `## 📝 TypeCheck\n`
|
|
854
|
+
const tcResult = runCommand(`${cmd} 2>&1`, gitRoot)
|
|
855
|
+
if (tcResult.success) {
|
|
856
|
+
report += `✅ TypeCheck passed\n\n`
|
|
857
|
+
} else {
|
|
858
|
+
report += `❌ TypeCheck failed\n`
|
|
859
|
+
parseAndCategorize(tcResult.error || tcResult.output, "typecheck")
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (existsSync(goMod)) {
|
|
865
|
+
checksRun++
|
|
866
|
+
report += `## 🔨 Go Build\n`
|
|
867
|
+
const goBuild = runCommand("go build ./... 2>&1", gitRoot)
|
|
868
|
+
if (goBuild.success) {
|
|
869
|
+
report += `✅ Go build passed\n\n`
|
|
870
|
+
} else {
|
|
871
|
+
parseAndCategorize(goBuild.error || goBuild.output, "go build")
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
checksRun++
|
|
875
|
+
report += `## 🔍 Go Vet\n`
|
|
876
|
+
const goVet = runCommand("go vet ./... 2>&1", gitRoot)
|
|
877
|
+
if (goVet.success) {
|
|
878
|
+
report += `✅ Go vet passed\n\n`
|
|
879
|
+
} else {
|
|
880
|
+
parseAndCategorize(goVet.error || goVet.output, "go vet")
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (existsSync(cargoToml)) {
|
|
885
|
+
checksRun++
|
|
886
|
+
report += `## 🔨 Cargo Check\n`
|
|
887
|
+
const cargoCheck = runCommand("cargo check 2>&1", gitRoot)
|
|
888
|
+
if (cargoCheck.success) {
|
|
889
|
+
report += `✅ Cargo check passed\n\n`
|
|
890
|
+
} else {
|
|
891
|
+
parseAndCategorize(cargoCheck.error || cargoCheck.output, "cargo")
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
checksRun++
|
|
895
|
+
report += `## 🔍 Cargo Clippy\n`
|
|
896
|
+
const clippy = runCommand("cargo clippy 2>&1", gitRoot)
|
|
897
|
+
if (clippy.success) {
|
|
898
|
+
report += `✅ Clippy passed\n\n`
|
|
899
|
+
} else {
|
|
900
|
+
parseAndCategorize(clippy.error || clippy.output, "clippy")
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (existsSync(pyProject)) {
|
|
905
|
+
checksRun++
|
|
906
|
+
report += `## 🔍 Python Lint (ruff/flake8)\n`
|
|
907
|
+
let pyLint = runCommand("ruff check . 2>&1", gitRoot)
|
|
908
|
+
if (!pyLint.success && pyLint.error?.includes("not found")) {
|
|
909
|
+
pyLint = runCommand("flake8 . 2>&1", gitRoot)
|
|
910
|
+
}
|
|
911
|
+
if (pyLint.success) {
|
|
912
|
+
report += `✅ Python lint passed\n\n`
|
|
913
|
+
} else {
|
|
914
|
+
parseAndCategorize(pyLint.error || pyLint.output, "python lint")
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
checksRun++
|
|
918
|
+
report += `## 📝 Python Type Check (mypy)\n`
|
|
919
|
+
const mypy = runCommand("mypy . 2>&1", gitRoot)
|
|
920
|
+
if (mypy.success) {
|
|
921
|
+
report += `✅ Mypy passed\n\n`
|
|
922
|
+
} else {
|
|
923
|
+
parseAndCategorize(mypy.error || mypy.output, "mypy")
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
if (checksRun === 0) {
|
|
928
|
+
report += `⚠️ No build/lint scripts detected in this project.\n`
|
|
929
|
+
report += `Supported: package.json (npm), go.mod, Cargo.toml, pyproject.toml\n`
|
|
930
|
+
logEvent("warn", "mad_final_check: no checks detected", { gitRoot })
|
|
931
|
+
return report
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// 4. Categorize and report errors
|
|
935
|
+
const sessionErrors = allErrors.filter(e => e.isSessionError)
|
|
936
|
+
const preExistingErrors = allErrors.filter(e => !e.isSessionError)
|
|
937
|
+
|
|
938
|
+
report += `---\n\n## 📋 Error Summary\n\n`
|
|
939
|
+
|
|
940
|
+
if (allErrors.length === 0) {
|
|
941
|
+
report += `🎉 **All checks passed!** No errors detected.\n`
|
|
942
|
+
logEvent("info", "mad_final_check: all checks passed", { checksRun })
|
|
943
|
+
return report
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (sessionErrors.length > 0) {
|
|
947
|
+
report += `### ❌ Session Errors (${sessionErrors.length})\n`
|
|
948
|
+
report += `*These errors are in files modified during this session:*\n\n`
|
|
949
|
+
for (const err of sessionErrors.slice(0, 10)) {
|
|
950
|
+
report += `- \`${err.file}${err.line ? `:${err.line}` : ''}\`: ${err.message.substring(0, 100)}\n`
|
|
951
|
+
}
|
|
952
|
+
if (sessionErrors.length > 10) {
|
|
953
|
+
report += `- ... and ${sessionErrors.length - 10} more\n`
|
|
954
|
+
}
|
|
955
|
+
report += `\n`
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
if (preExistingErrors.length > 0) {
|
|
959
|
+
report += `### ⚠️ Pre-existing Errors (${preExistingErrors.length})\n`
|
|
960
|
+
report += `*These errors are NOT caused by this session - they existed before:*\n\n`
|
|
961
|
+
for (const err of preExistingErrors.slice(0, 10)) {
|
|
962
|
+
report += `- \`${err.file}${err.line ? `:${err.line}` : ''}\`: ${err.message.substring(0, 100)}\n`
|
|
963
|
+
}
|
|
964
|
+
if (preExistingErrors.length > 10) {
|
|
965
|
+
report += `- ... and ${preExistingErrors.length - 10} more\n`
|
|
966
|
+
}
|
|
967
|
+
report += `\n`
|
|
968
|
+
report += `💡 **These pre-existing errors are not your fault!**\n`
|
|
969
|
+
report += `Would you like me to create a worktree to fix them? Just say "fix pre-existing errors".\n`
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// 5. Final verdict
|
|
973
|
+
report += `\n---\n\n`
|
|
974
|
+
if (sessionErrors.length > 0) {
|
|
975
|
+
report += `⚠️ **Action required:** Fix the ${sessionErrors.length} session error(s) before considering this session complete.\n`
|
|
976
|
+
} else if (preExistingErrors.length > 0) {
|
|
977
|
+
report += `✅ **Session successful!** Your changes introduced no new errors.\n`
|
|
978
|
+
report += `The ${preExistingErrors.length} pre-existing error(s) can be fixed separately if desired.\n`
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
logEvent("info", "mad_final_check completed", {
|
|
982
|
+
checksRun,
|
|
983
|
+
sessionErrors: sessionErrors.length,
|
|
984
|
+
preExistingErrors: preExistingErrors.length
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
return report
|
|
988
|
+
} catch (e: any) {
|
|
989
|
+
logEvent("error", "mad_final_check exception", { error: e.message, stack: e.stack })
|
|
990
|
+
return getUpdateNotification() + `❌ Error running final check: ${e.message}`
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
}),
|
|
731
994
|
},
|
|
732
995
|
|
|
733
996
|
// Event hooks
|
|
@@ -97,12 +97,21 @@ Merge completed work:
|
|
|
97
97
|
mad_merge(worktree: "feat-feature-name")
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
> **Note:** `mad_merge` automatically uses `--no-ff` to preserve history. If you ever need to merge manually, always use `git merge --no-ff`.
|
|
101
|
+
|
|
100
102
|
### 7. Cleanup
|
|
101
103
|
Remove finished worktrees:
|
|
102
104
|
```
|
|
103
105
|
mad_cleanup(worktree: "feat-feature-name")
|
|
104
106
|
```
|
|
105
107
|
|
|
108
|
+
### 8. Final Check
|
|
109
|
+
Verify global project health:
|
|
110
|
+
```
|
|
111
|
+
mad_final_check()
|
|
112
|
+
```
|
|
113
|
+
This distinguishes session errors from pre-existing issues.
|
|
114
|
+
|
|
106
115
|
## Best Practices
|
|
107
116
|
|
|
108
117
|
1. **Keep subtasks focused** - Each should be completable in one session
|
|
@@ -110,6 +119,7 @@ mad_cleanup(worktree: "feat-feature-name")
|
|
|
110
119
|
3. **Test before merge** - Always run mad_test first
|
|
111
120
|
4. **Handle blocks promptly** - Don't let blocked tasks linger
|
|
112
121
|
5. **Merge sequentially** - Avoid merge conflict cascades
|
|
122
|
+
6. **Always use `--no-ff` for merges** - Preserves feature history and enables easy reverts
|
|
113
123
|
|
|
114
124
|
## Available Tools
|
|
115
125
|
|
|
@@ -123,6 +133,7 @@ mad_cleanup(worktree: "feat-feature-name")
|
|
|
123
133
|
| `mad_done` | Mark task complete |
|
|
124
134
|
| `mad_blocked` | Mark task blocked |
|
|
125
135
|
| `mad_read_task` | Read task description |
|
|
136
|
+
| `mad_final_check` | Run global build/lint and categorize errors |
|
|
126
137
|
|
|
127
138
|
## Example
|
|
128
139
|
|