beads-orchestration 2.0.0
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/LICENSE +21 -0
- package/README.md +214 -0
- package/SKILL.md +263 -0
- package/bootstrap.py +928 -0
- package/package.json +37 -0
- package/scripts/cli.js +64 -0
- package/scripts/postinstall.js +71 -0
- package/skills/create-beads-orchestration/SKILL.md +263 -0
- package/skills/subagents-discipline/SKILL.md +158 -0
- package/templates/CLAUDE.md +326 -0
- package/templates/agents/architect.md +121 -0
- package/templates/agents/code-reviewer.md +248 -0
- package/templates/agents/detective.md +101 -0
- package/templates/agents/discovery.md +492 -0
- package/templates/agents/merge-supervisor.md +119 -0
- package/templates/agents/scout.md +100 -0
- package/templates/agents/scribe.md +96 -0
- package/templates/beads-workflow-injection-api.md +116 -0
- package/templates/beads-workflow-injection-git.md +108 -0
- package/templates/beads-workflow-injection.md +111 -0
- package/templates/frontend-reviews-requirement.md +61 -0
- package/templates/hooks/block-orchestrator-tools.sh +98 -0
- package/templates/hooks/clarify-vague-request.sh +39 -0
- package/templates/hooks/enforce-bead-for-supervisor.sh +32 -0
- package/templates/hooks/enforce-branch-before-edit.sh +47 -0
- package/templates/hooks/enforce-concise-response.sh +41 -0
- package/templates/hooks/enforce-sequential-dispatch.sh +63 -0
- package/templates/hooks/inject-discipline-reminder.sh +28 -0
- package/templates/hooks/log-dispatch-prompt.sh +39 -0
- package/templates/hooks/memory-capture.sh +104 -0
- package/templates/hooks/remind-inprogress.sh +14 -0
- package/templates/hooks/session-start.sh +121 -0
- package/templates/hooks/validate-completion.sh +131 -0
- package/templates/hooks/validate-epic-close.sh +84 -0
- package/templates/mcp.json.template +12 -0
- package/templates/memory/recall.sh +121 -0
- package/templates/settings.json +74 -0
- package/templates/skills/react-best-practices/SKILL.md +487 -0
- package/templates/skills/subagents-discipline/SKILL.md +127 -0
- package/templates/ui-constraints.md +76 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Hook: Validate bead close — PR must be merged, epic children must be complete
|
|
3
|
+
# Prevents closing a bead whose branch has no merged PR
|
|
4
|
+
# Prevents closing an epic when children are still open
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
TOOL_INPUT="${CLAUDE_TOOL_INPUT:-}"
|
|
9
|
+
|
|
10
|
+
# Only check Bash commands containing "bd close"
|
|
11
|
+
if ! echo "$TOOL_INPUT" | jq -e '.command' >/dev/null 2>&1; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
|
|
16
|
+
|
|
17
|
+
# Check if this is a bd close command
|
|
18
|
+
if ! echo "$COMMAND" | grep -qE 'bd\s+close'; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Allow --force override
|
|
23
|
+
if echo "$COMMAND" | grep -qE '\-\-force'; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Extract the ID being closed (handles: bd close ID, bd close ID && ..., etc.)
|
|
28
|
+
CLOSE_ID=$(echo "$COMMAND" | sed -E 's/.*bd\s+close\s+([A-Za-z0-9._-]+).*/\1/')
|
|
29
|
+
|
|
30
|
+
if [ -z "$CLOSE_ID" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# === CHECK 1: PR merge validation ===
|
|
35
|
+
# Only applies if repo has a remote and branch exists
|
|
36
|
+
BRANCH="bd-${CLOSE_ID}"
|
|
37
|
+
|
|
38
|
+
HAS_REMOTE=$(git remote get-url origin 2>/dev/null || echo "")
|
|
39
|
+
if [ -n "$HAS_REMOTE" ]; then
|
|
40
|
+
REMOTE_BRANCH=$(git ls-remote --heads origin "$BRANCH" 2>/dev/null || echo "")
|
|
41
|
+
|
|
42
|
+
if [ -n "$REMOTE_BRANCH" ]; then
|
|
43
|
+
# Branch exists on remote — check for merged PR
|
|
44
|
+
if command -v gh >/dev/null 2>&1; then
|
|
45
|
+
MERGED_PR=$(gh pr list --head "$BRANCH" --state merged --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
46
|
+
|
|
47
|
+
if [ -z "$MERGED_PR" ]; then
|
|
48
|
+
cat << EOF
|
|
49
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Cannot close bead '$CLOSE_ID' — branch '$BRANCH' has no merged PR. Create and merge a PR first, or use 'bd close $CLOSE_ID --force' to override."}}
|
|
50
|
+
EOF
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# === CHECK 2: Epic children validation ===
|
|
58
|
+
# Check if this is an epic by looking for children
|
|
59
|
+
CHILDREN=$(bd show "$CLOSE_ID" --json 2>/dev/null | jq -r '.[0].children // empty' 2>/dev/null || echo "")
|
|
60
|
+
|
|
61
|
+
if [ -z "$CHILDREN" ] || [ "$CHILDREN" = "null" ]; then
|
|
62
|
+
# Not an epic or no children, allow close
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# This is an epic - check if all children are complete
|
|
67
|
+
INCOMPLETE=$(bd list --json 2>/dev/null | jq -r --arg epic "$CLOSE_ID" '
|
|
68
|
+
[.[] | select(.parent == $epic and .status != "done" and .status != "closed")] | length
|
|
69
|
+
' 2>/dev/null || echo "0")
|
|
70
|
+
|
|
71
|
+
if [ "$INCOMPLETE" != "0" ] && [ "$INCOMPLETE" != "" ]; then
|
|
72
|
+
# Get list of incomplete children for the error message
|
|
73
|
+
INCOMPLETE_LIST=$(bd list --json 2>/dev/null | jq -r --arg epic "$CLOSE_ID" '
|
|
74
|
+
[.[] | select(.parent == $epic and .status != "done" and .status != "closed")] | .[] | "\(.id) (\(.status))"
|
|
75
|
+
' 2>/dev/null | tr '\n' ', ' | sed 's/,$//')
|
|
76
|
+
|
|
77
|
+
cat << EOF
|
|
78
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Cannot close epic '$CLOSE_ID' - has $INCOMPLETE incomplete children: $INCOMPLETE_LIST. Mark all children as done first."}}
|
|
79
|
+
EOF
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# All checks passed, allow close
|
|
84
|
+
exit 0
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# recall.sh - Search the project knowledge base
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# .beads/memory/recall.sh "keyword" # Search by keyword
|
|
7
|
+
# .beads/memory/recall.sh "keyword" --type learned # Filter by type
|
|
8
|
+
# .beads/memory/recall.sh --recent 10 # Show N most recent
|
|
9
|
+
# .beads/memory/recall.sh --stats # Knowledge base stats
|
|
10
|
+
# .beads/memory/recall.sh "keyword" --all # Include archive
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
KNOWLEDGE_FILE="$SCRIPT_DIR/knowledge.jsonl"
|
|
17
|
+
ARCHIVE_FILE="$SCRIPT_DIR/knowledge.archive.jsonl"
|
|
18
|
+
|
|
19
|
+
if [[ ! -f "$KNOWLEDGE_FILE" ]] || [[ ! -s "$KNOWLEDGE_FILE" ]]; then
|
|
20
|
+
echo "No knowledge entries yet."
|
|
21
|
+
echo "Entries are created automatically from bd comment commands with INVESTIGATION: or LEARNED: prefixes."
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Parse arguments
|
|
26
|
+
QUERY=""
|
|
27
|
+
TYPE_FILTER=""
|
|
28
|
+
INCLUDE_ARCHIVE=false
|
|
29
|
+
SHOW_RECENT=0
|
|
30
|
+
SHOW_STATS=false
|
|
31
|
+
|
|
32
|
+
while [[ $# -gt 0 ]]; do
|
|
33
|
+
case "$1" in
|
|
34
|
+
--type)
|
|
35
|
+
TYPE_FILTER="${2:-}"
|
|
36
|
+
shift 2
|
|
37
|
+
;;
|
|
38
|
+
--all)
|
|
39
|
+
INCLUDE_ARCHIVE=true
|
|
40
|
+
shift
|
|
41
|
+
;;
|
|
42
|
+
--recent)
|
|
43
|
+
SHOW_RECENT="${2:-10}"
|
|
44
|
+
shift 2
|
|
45
|
+
;;
|
|
46
|
+
--stats)
|
|
47
|
+
SHOW_STATS=true
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
--help|-h)
|
|
51
|
+
echo "Usage: recall.sh [query] [--type learned|investigation] [--all] [--recent N] [--stats]"
|
|
52
|
+
exit 0
|
|
53
|
+
;;
|
|
54
|
+
*)
|
|
55
|
+
QUERY="$1"
|
|
56
|
+
shift
|
|
57
|
+
;;
|
|
58
|
+
esac
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
# Stats mode
|
|
62
|
+
if [[ "$SHOW_STATS" == "true" ]]; then
|
|
63
|
+
TOTAL=$(wc -l < "$KNOWLEDGE_FILE" | tr -d ' ')
|
|
64
|
+
LEARNED=$(grep -c '"type":"learned"' "$KNOWLEDGE_FILE" 2>/dev/null) || LEARNED=0
|
|
65
|
+
INVESTIGATION=$(grep -c '"type":"investigation"' "$KNOWLEDGE_FILE" 2>/dev/null) || INVESTIGATION=0
|
|
66
|
+
UNIQUE_KEYS=$(jq -r '.key' "$KNOWLEDGE_FILE" 2>/dev/null | sort -u | wc -l | tr -d ' ')
|
|
67
|
+
ARCHIVE_COUNT=0
|
|
68
|
+
[[ -f "$ARCHIVE_FILE" ]] && ARCHIVE_COUNT=$(wc -l < "$ARCHIVE_FILE" | tr -d ' ')
|
|
69
|
+
|
|
70
|
+
echo "## Knowledge Base Stats"
|
|
71
|
+
echo " Active entries: $TOTAL"
|
|
72
|
+
echo " Unique keys: $UNIQUE_KEYS"
|
|
73
|
+
echo " Learned: $LEARNED"
|
|
74
|
+
echo " Investigation: $INVESTIGATION"
|
|
75
|
+
echo " Archived: $ARCHIVE_COUNT"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Recent mode
|
|
80
|
+
if [[ "$SHOW_RECENT" -gt 0 ]]; then
|
|
81
|
+
echo "## Recent Knowledge ($SHOW_RECENT entries)"
|
|
82
|
+
echo ""
|
|
83
|
+
tail -"$SHOW_RECENT" "$KNOWLEDGE_FILE" | jq -r '
|
|
84
|
+
"[\(.type | ascii_upcase | .[0:5])] \(.key)\n \(.content | .[0:120])\n source=\(.source) bead=\(.bead)\n"
|
|
85
|
+
' 2>/dev/null
|
|
86
|
+
exit 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Search mode (default)
|
|
90
|
+
if [[ -z "$QUERY" ]]; then
|
|
91
|
+
echo "Usage: recall.sh <keyword> [--type learned|investigation] [--all]"
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Build file list
|
|
96
|
+
FILES="$KNOWLEDGE_FILE"
|
|
97
|
+
if [[ "$INCLUDE_ARCHIVE" == "true" && -f "$ARCHIVE_FILE" ]]; then
|
|
98
|
+
FILES="$ARCHIVE_FILE $KNOWLEDGE_FILE"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Search and deduplicate (latest entry for each key wins)
|
|
102
|
+
RESULTS=$(cat $FILES | grep -i "$QUERY" 2>/dev/null || true)
|
|
103
|
+
|
|
104
|
+
# Apply type filter
|
|
105
|
+
if [[ -n "$TYPE_FILTER" ]]; then
|
|
106
|
+
RESULTS=$(echo "$RESULTS" | grep "\"type\":\"$TYPE_FILTER\"" 2>/dev/null || true)
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
if [[ -z "$RESULTS" ]]; then
|
|
110
|
+
echo "No knowledge entries matching '$QUERY'"
|
|
111
|
+
[[ -n "$TYPE_FILTER" ]] && echo " (filtered by type: $TYPE_FILTER)"
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Deduplicate by key (latest wins) and format output
|
|
116
|
+
echo "$RESULTS" | jq -s '
|
|
117
|
+
group_by(.key) | map(max_by(.ts)) | sort_by(-.ts) | .[] |
|
|
118
|
+
"[\(.type | ascii_upcase | .[0:5])] \(.key)\n \(.content | .[0:200])\n source=\(.source) bead=\(.bead) tags=\(.tags | join(","))\n"
|
|
119
|
+
' -r 2>/dev/null
|
|
120
|
+
|
|
121
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{"type": "command", "command": ".claude/hooks/block-orchestrator-tools.sh"}
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"matcher": "Task",
|
|
11
|
+
"hooks": [
|
|
12
|
+
{"type": "command", "command": ".claude/hooks/enforce-bead-for-supervisor.sh"},
|
|
13
|
+
{"type": "command", "command": ".claude/hooks/enforce-sequential-dispatch.sh"},
|
|
14
|
+
{"type": "command", "command": ".claude/hooks/remind-inprogress.sh"},
|
|
15
|
+
{"type": "command", "command": ".claude/hooks/inject-discipline-reminder.sh"}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"matcher": "Edit",
|
|
20
|
+
"hooks": [
|
|
21
|
+
{"type": "command", "command": ".claude/hooks/enforce-branch-before-edit.sh"}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Write",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{"type": "command", "command": ".claude/hooks/enforce-branch-before-edit.sh"}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"matcher": "Bash",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{"type": "command", "command": ".claude/hooks/validate-epic-close.sh"}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"PostToolUse": [
|
|
38
|
+
{
|
|
39
|
+
"matcher": "Task",
|
|
40
|
+
"hooks": [
|
|
41
|
+
{"type": "command", "command": ".claude/hooks/enforce-concise-response.sh"},
|
|
42
|
+
{"type": "command", "command": ".claude/hooks/log-dispatch-prompt.sh", "timeout": 10}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"matcher": "Bash",
|
|
47
|
+
"hooks": [
|
|
48
|
+
{"type": "command", "command": ".claude/hooks/memory-capture.sh", "timeout": 10}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"SubagentStop": [
|
|
53
|
+
{
|
|
54
|
+
"hooks": [
|
|
55
|
+
{"type": "command", "command": ".claude/hooks/validate-completion.sh"}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"SessionStart": [
|
|
60
|
+
{
|
|
61
|
+
"hooks": [
|
|
62
|
+
{"type": "command", "command": ".claude/hooks/session-start.sh"}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"UserPromptSubmit": [
|
|
67
|
+
{
|
|
68
|
+
"hooks": [
|
|
69
|
+
{"type": "command", "command": ".claude/hooks/clarify-vague-request.sh"}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|