@jaggerxtrm/specialists 3.0.2 → 3.2.1
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 +66 -257
- package/bin/install.js +156 -266
- package/dist/index.js +2049 -262
- package/hooks/specialists-session-start.mjs +105 -0
- package/package.json +1 -1
- package/specialists/bug-hunt.specialist.yaml +53 -20
- package/specialists/codebase-explorer.specialist.yaml +43 -24
- package/specialists/feature-design.specialist.yaml +48 -29
- package/specialists/planner.specialist.yaml +87 -0
- package/specialists/specialist-author.specialist.yaml +56 -0
- package/specialists/sync-docs.specialist.yaml +53 -0
- package/specialists/xt-merge.specialist.yaml +78 -0
- package/hooks/beads-close-memory-prompt.mjs +0 -47
- package/hooks/beads-commit-gate.mjs +0 -58
- package/hooks/beads-edit-gate.mjs +0 -53
- package/hooks/beads-stop-gate.mjs +0 -52
- package/hooks/specialists-main-guard.mjs +0 -90
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
specialist:
|
|
2
|
+
metadata:
|
|
3
|
+
name: xt-merge
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
description: "Drains the xt worktree PR queue in FIFO order: lists open xt/ PRs sorted by creation time, checks CI status on the oldest, merges it with --rebase --delete-branch, then rebases all remaining branches onto the new default branch and force-pushes them. Handles rebase conflicts, CI re-triggering, and reports final queue state."
|
|
6
|
+
category: workflow
|
|
7
|
+
tags: [git, pr, merge, worktree, xt, rebase, ci]
|
|
8
|
+
updated: "2026-03-22"
|
|
9
|
+
|
|
10
|
+
execution:
|
|
11
|
+
mode: tool
|
|
12
|
+
model: anthropic/claude-sonnet-4-6
|
|
13
|
+
fallback_model: google-gemini-cli/gemini-3-flash-preview
|
|
14
|
+
timeout_ms: 300000
|
|
15
|
+
response_format: markdown
|
|
16
|
+
permission_required: MEDIUM
|
|
17
|
+
|
|
18
|
+
prompt:
|
|
19
|
+
system: |
|
|
20
|
+
You are a PR merge specialist for xt worktree workflows.
|
|
21
|
+
|
|
22
|
+
Your job is to drain the queue of open PRs from xt worktree sessions. These PRs
|
|
23
|
+
were created by `xt end` — each branch was rebased onto origin/main at the time
|
|
24
|
+
it was pushed, so they form an ordered queue that must be merged FIFO.
|
|
25
|
+
|
|
26
|
+
## FIFO ordering
|
|
27
|
+
|
|
28
|
+
Merge the oldest-created PR first. After each merge, main advances and all
|
|
29
|
+
remaining branches must be rebased onto the new main before their CI results
|
|
30
|
+
are meaningful. Merging out of order increases conflict surface unnecessarily.
|
|
31
|
+
|
|
32
|
+
## Your workflow
|
|
33
|
+
|
|
34
|
+
1. List open PRs: `gh pr list --state open --json number,title,headRefName,createdAt,isDraft`
|
|
35
|
+
Filter for branches starting with "xt/", sort by createdAt ascending.
|
|
36
|
+
Skip draft PRs.
|
|
37
|
+
|
|
38
|
+
2. Check CI on the head PR: `gh pr checks <number>`
|
|
39
|
+
Do NOT merge if checks are pending or failing. Report status and stop.
|
|
40
|
+
|
|
41
|
+
3. Merge the head PR:
|
|
42
|
+
`gh pr merge <number> --rebase --delete-branch`
|
|
43
|
+
Always use --rebase for linear history. Always --delete-branch to clean up remote.
|
|
44
|
+
|
|
45
|
+
4. Rebase all remaining xt/ branches onto the new main:
|
|
46
|
+
```
|
|
47
|
+
git fetch origin main
|
|
48
|
+
git checkout xt/<branch>
|
|
49
|
+
git rebase origin/main
|
|
50
|
+
git push origin xt/<branch> --force-with-lease
|
|
51
|
+
```
|
|
52
|
+
Repeat in queue order. If a rebase produces conflicts, stop and report the
|
|
53
|
+
conflicted files with enough context for the user to resolve them.
|
|
54
|
+
|
|
55
|
+
5. Repeat from step 2 until the queue is empty.
|
|
56
|
+
|
|
57
|
+
## Constraints
|
|
58
|
+
|
|
59
|
+
- Never merge a PR with failing or pending CI.
|
|
60
|
+
- Never use --squash or --merge; always --rebase.
|
|
61
|
+
- Never force-push without --force-with-lease.
|
|
62
|
+
- If you hit a rebase conflict you cannot safely resolve, stop and show the
|
|
63
|
+
conflicted files. Do not guess at conflict resolution.
|
|
64
|
+
- Report the queue state (PR number, branch, CI status) before each merge action.
|
|
65
|
+
|
|
66
|
+
task_template: |
|
|
67
|
+
Drain the xt worktree PR merge queue.
|
|
68
|
+
|
|
69
|
+
$prompt
|
|
70
|
+
|
|
71
|
+
Working directory: $cwd
|
|
72
|
+
|
|
73
|
+
List all open PRs from xt/ branches, sort oldest-first, check CI on the oldest,
|
|
74
|
+
merge it if green, rebase the remaining branches onto the new main, and repeat
|
|
75
|
+
until the queue is empty. Report final state when done.
|
|
76
|
+
|
|
77
|
+
communication:
|
|
78
|
+
output_to: .specialists/merge-prs-result.md
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// beads-close-memory-prompt — Claude Code PostToolUse hook
|
|
3
|
-
// After `bd close`: injects a short reminder into Claude's context to capture
|
|
4
|
-
// knowledge and consider underused beads features.
|
|
5
|
-
// Output to stdout is shown to Claude as additional context.
|
|
6
|
-
//
|
|
7
|
-
// Installed by: specialists install
|
|
8
|
-
|
|
9
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
|
|
12
|
-
let input;
|
|
13
|
-
try {
|
|
14
|
-
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
15
|
-
} catch {
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Only fire on Bash tool
|
|
20
|
-
if (input.tool_name !== 'Bash') process.exit(0);
|
|
21
|
-
|
|
22
|
-
const cmd = (input.tool_input?.command ?? '').trim();
|
|
23
|
-
|
|
24
|
-
// Only fire when the command is `bd close ...`
|
|
25
|
-
if (!/\bbd\s+close\b/.test(cmd)) process.exit(0);
|
|
26
|
-
|
|
27
|
-
// Only fire in projects that use beads
|
|
28
|
-
const cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
29
|
-
if (!existsSync(join(cwd, '.beads'))) process.exit(0);
|
|
30
|
-
|
|
31
|
-
// Inject reminder into Claude's context
|
|
32
|
-
process.stdout.write(
|
|
33
|
-
'\n[beads] Issue(s) closed. Before moving on:\n\n' +
|
|
34
|
-
' Knowledge worth keeping?\n' +
|
|
35
|
-
' bd remember "key insight from this work"\n' +
|
|
36
|
-
' bd memories <keyword> -- search what is already stored\n\n' +
|
|
37
|
-
' Discovered related work while implementing?\n' +
|
|
38
|
-
' bd create --title="..." --deps=discovered-from:<id>\n\n' +
|
|
39
|
-
' Underused features to consider:\n' +
|
|
40
|
-
' bd dep add <a> <b> -- link blocking relationships between issues\n' +
|
|
41
|
-
' bd graph -- visualize issue dependency graph\n' +
|
|
42
|
-
' bd orphans -- issues referenced in commits but still open\n' +
|
|
43
|
-
' bd preflight -- PR readiness checklist before gh pr create\n' +
|
|
44
|
-
' bd stale -- issues not touched recently\n'
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
process.exit(0);
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// beads-commit-gate — Claude Code PreToolUse hook
|
|
3
|
-
// Blocks `git commit` when in_progress beads issues still exist.
|
|
4
|
-
// Forces: close issues first, THEN commit.
|
|
5
|
-
// Exit 0: allow | Exit 2: block (stderr shown to Claude)
|
|
6
|
-
//
|
|
7
|
-
// Installed by: specialists install
|
|
8
|
-
|
|
9
|
-
import { execSync } from 'node:child_process';
|
|
10
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
|
|
13
|
-
let input;
|
|
14
|
-
try {
|
|
15
|
-
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
16
|
-
} catch {
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const tool = input.tool_name ?? '';
|
|
21
|
-
if (tool !== 'Bash') process.exit(0);
|
|
22
|
-
|
|
23
|
-
const cmd = input.tool_input?.command ?? '';
|
|
24
|
-
if (!/\bgit\s+commit\b/.test(cmd)) process.exit(0);
|
|
25
|
-
|
|
26
|
-
const cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
27
|
-
if (!existsSync(join(cwd, '.beads'))) process.exit(0);
|
|
28
|
-
|
|
29
|
-
let inProgress = 0;
|
|
30
|
-
let summary = '';
|
|
31
|
-
try {
|
|
32
|
-
const output = execSync('bd list --status=in_progress', {
|
|
33
|
-
encoding: 'utf8',
|
|
34
|
-
cwd,
|
|
35
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
-
timeout: 8000,
|
|
37
|
-
});
|
|
38
|
-
inProgress = (output.match(/in_progress/g) ?? []).length;
|
|
39
|
-
summary = output.trim();
|
|
40
|
-
} catch {
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (inProgress > 0) {
|
|
45
|
-
process.stderr.write(
|
|
46
|
-
'🚫 BEADS GATE: Close open issues before committing.\n\n' +
|
|
47
|
-
`Open issues:\n${summary}\n\n` +
|
|
48
|
-
'Next steps:\n' +
|
|
49
|
-
' 3. bd close <id1> <id2> ... ← you are here\n' +
|
|
50
|
-
' 4. git add <files> && git commit -m "..."\n' +
|
|
51
|
-
' 5. git push -u origin <feature-branch>\n' +
|
|
52
|
-
' 6. gh pr create --fill && gh pr merge --squash\n' +
|
|
53
|
-
' 7. git checkout master && git reset --hard origin/master\n'
|
|
54
|
-
);
|
|
55
|
-
process.exit(2);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
process.exit(0);
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// beads-edit-gate — Claude Code PreToolUse hook
|
|
3
|
-
// Blocks file edits when no beads issue is in_progress.
|
|
4
|
-
// Only active in projects with a .beads/ directory.
|
|
5
|
-
// Exit 0: allow | Exit 2: block (stderr shown to Claude)
|
|
6
|
-
//
|
|
7
|
-
// Installed by: specialists install
|
|
8
|
-
|
|
9
|
-
import { execSync } from 'node:child_process';
|
|
10
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
|
|
13
|
-
let input;
|
|
14
|
-
try {
|
|
15
|
-
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
16
|
-
} catch {
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
21
|
-
if (!existsSync(join(cwd, '.beads'))) process.exit(0);
|
|
22
|
-
|
|
23
|
-
let inProgress = 0;
|
|
24
|
-
try {
|
|
25
|
-
const output = execSync('bd list --status=in_progress', {
|
|
26
|
-
encoding: 'utf8',
|
|
27
|
-
cwd,
|
|
28
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
-
timeout: 8000,
|
|
30
|
-
});
|
|
31
|
-
inProgress = (output.match(/in_progress/g) ?? []).length;
|
|
32
|
-
} catch {
|
|
33
|
-
process.exit(0);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (inProgress === 0) {
|
|
37
|
-
process.stderr.write(
|
|
38
|
-
'🚫 BEADS GATE: No active issue — create one before editing files.\n\n' +
|
|
39
|
-
' bd create --title="<what you\'re doing>" --type=task --priority=2\n' +
|
|
40
|
-
' bd update <id> --status=in_progress\n\n' +
|
|
41
|
-
'Full workflow (do this every session):\n' +
|
|
42
|
-
' 1. bd create + bd update in_progress ← you are here\n' +
|
|
43
|
-
' 2. Edit files / write code\n' +
|
|
44
|
-
' 3. bd close <id> close when done\n' +
|
|
45
|
-
' 4. git add <files> && git commit\n' +
|
|
46
|
-
' 5. git push -u origin <feature-branch>\n' +
|
|
47
|
-
' 6. gh pr create --fill && gh pr merge --squash\n' +
|
|
48
|
-
' 7. git checkout master && git reset --hard origin/master\n'
|
|
49
|
-
);
|
|
50
|
-
process.exit(2);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
process.exit(0);
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// beads-stop-gate — Claude Code Stop hook
|
|
3
|
-
// Blocks the agent from stopping when in_progress beads issues remain.
|
|
4
|
-
// Exit 0: allow stop | Exit 2: block stop (stderr shown to Claude)
|
|
5
|
-
//
|
|
6
|
-
// Installed by: specialists install
|
|
7
|
-
|
|
8
|
-
import { execSync } from 'node:child_process';
|
|
9
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
|
|
12
|
-
let input;
|
|
13
|
-
try {
|
|
14
|
-
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
15
|
-
} catch {
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
20
|
-
if (!existsSync(join(cwd, '.beads'))) process.exit(0);
|
|
21
|
-
|
|
22
|
-
let inProgress = 0;
|
|
23
|
-
let summary = '';
|
|
24
|
-
try {
|
|
25
|
-
const output = execSync('bd list --status=in_progress', {
|
|
26
|
-
encoding: 'utf8',
|
|
27
|
-
cwd,
|
|
28
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
-
timeout: 8000,
|
|
30
|
-
});
|
|
31
|
-
inProgress = (output.match(/in_progress/g) ?? []).length;
|
|
32
|
-
summary = output.trim();
|
|
33
|
-
} catch {
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (inProgress > 0) {
|
|
38
|
-
process.stderr.write(
|
|
39
|
-
'🚫 BEADS STOP GATE: Unresolved issues — complete the session close protocol.\n\n' +
|
|
40
|
-
`Open issues:\n${summary}\n\n` +
|
|
41
|
-
'Session close protocol:\n' +
|
|
42
|
-
' 3. bd close <id1> <id2> ... close all in_progress issues\n' +
|
|
43
|
-
' 4. git add <files> && git commit -m "..." commit your changes\n' +
|
|
44
|
-
' 5. git push -u origin <feature-branch> push feature branch\n' +
|
|
45
|
-
' 6. gh pr create --fill create PR\n' +
|
|
46
|
-
' 7. gh pr merge --squash merge PR\n' +
|
|
47
|
-
' 8. git checkout master && git reset --hard origin/master\n'
|
|
48
|
-
);
|
|
49
|
-
process.exit(2);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
process.exit(0);
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Claude Code PreToolUse hook — block writes and direct master pushes
|
|
3
|
-
// Exit 0: allow | Exit 2: block (message shown to user)
|
|
4
|
-
//
|
|
5
|
-
// Installed by: specialists install
|
|
6
|
-
|
|
7
|
-
import { execSync } from 'node:child_process';
|
|
8
|
-
import { readFileSync } from 'node:fs';
|
|
9
|
-
|
|
10
|
-
let branch = '';
|
|
11
|
-
try {
|
|
12
|
-
branch = execSync('git branch --show-current', {
|
|
13
|
-
encoding: 'utf8',
|
|
14
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
15
|
-
}).trim();
|
|
16
|
-
} catch {}
|
|
17
|
-
|
|
18
|
-
// Not in a git repo or not on a protected branch — allow
|
|
19
|
-
if (!branch || (branch !== 'main' && branch !== 'master')) {
|
|
20
|
-
process.exit(0);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let input;
|
|
24
|
-
try {
|
|
25
|
-
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
26
|
-
} catch {
|
|
27
|
-
process.exit(0);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const tool = input.tool_name ?? '';
|
|
31
|
-
|
|
32
|
-
const WRITE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
33
|
-
|
|
34
|
-
if (WRITE_TOOLS.has(tool)) {
|
|
35
|
-
process.stderr.write(
|
|
36
|
-
`⛔ You are on '${branch}' — never edit files directly on master.\n\n` +
|
|
37
|
-
'Full workflow:\n' +
|
|
38
|
-
' 1. git checkout -b feature/<name> ← start here\n' +
|
|
39
|
-
' 2. bd create + bd update in_progress track your work\n' +
|
|
40
|
-
' 3. Edit files / write code\n' +
|
|
41
|
-
' 4. bd close <id> && git add && git commit\n' +
|
|
42
|
-
' 5. git push -u origin feature/<name>\n' +
|
|
43
|
-
' 6. gh pr create --fill && gh pr merge --squash\n' +
|
|
44
|
-
' 7. git checkout master && git reset --hard origin/master\n'
|
|
45
|
-
);
|
|
46
|
-
process.exit(2);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Block direct commits and pushes to master — use feature branches + gh pr create/merge
|
|
50
|
-
if (tool === 'Bash') {
|
|
51
|
-
const cmd = (input.tool_input?.command ?? '').trim().replace(/\s+/g, ' ');
|
|
52
|
-
|
|
53
|
-
if (/^git commit/.test(cmd)) {
|
|
54
|
-
process.stderr.write(
|
|
55
|
-
`⛔ Don't commit directly to '${branch}' — use a feature branch.\n\n` +
|
|
56
|
-
'Full workflow:\n' +
|
|
57
|
-
' 1. git checkout -b feature/<name> ← start here\n' +
|
|
58
|
-
' 2. bd create + bd update in_progress track your work\n' +
|
|
59
|
-
' 3. Edit files / write code\n' +
|
|
60
|
-
' 4. bd close <id> && git add && git commit\n' +
|
|
61
|
-
' 5. git push -u origin feature/<name>\n' +
|
|
62
|
-
' 6. gh pr create --fill && gh pr merge --squash\n' +
|
|
63
|
-
' 7. git checkout master && git reset --hard origin/master\n'
|
|
64
|
-
);
|
|
65
|
-
process.exit(2);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (/^git push/.test(cmd)) {
|
|
69
|
-
const tokens = cmd.split(' ');
|
|
70
|
-
const lastToken = tokens[tokens.length - 1];
|
|
71
|
-
const explicitMaster = /^(master|main)$/.test(lastToken) || /:(master|main)$/.test(lastToken);
|
|
72
|
-
const impliedMaster = tokens.length <= 3 && (branch === 'main' || branch === 'master');
|
|
73
|
-
if (explicitMaster || impliedMaster) {
|
|
74
|
-
process.stderr.write(
|
|
75
|
-
`⛔ Don't push directly to '${branch}' — use the PR workflow.\n\n` +
|
|
76
|
-
'Next steps:\n' +
|
|
77
|
-
' 5. git push -u origin <feature-branch> ← push your branch\n' +
|
|
78
|
-
' 6. gh pr create --fill create PR\n' +
|
|
79
|
-
' gh pr merge --squash merge it\n' +
|
|
80
|
-
' 7. git checkout master sync master\n' +
|
|
81
|
-
' git reset --hard origin/master\n\n' +
|
|
82
|
-
'If you\'re not on a feature branch yet:\n' +
|
|
83
|
-
' git checkout -b feature/<name> (then re-commit and push)\n'
|
|
84
|
-
);
|
|
85
|
-
process.exit(2);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
process.exit(0);
|