@brickhouse-tech/sync-agents 0.1.4 → 0.1.6
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 +147 -0
- package/package.json +1 -1
- package/src/sh/sync-agents.sh +226 -0
package/README.md
CHANGED
|
@@ -63,6 +63,9 @@ AGENTS.md is also symlinked to CLAUDE.md so that Claude reads the index natively
|
|
|
63
63
|
| `watch` | Watch `.agents/` for changes and auto-regenerate `AGENTS.md` |
|
|
64
64
|
| `import <url>` | Import a rule/skill/workflow from a URL |
|
|
65
65
|
| `hook` | Install a pre-commit git hook for auto-sync |
|
|
66
|
+
| `inherit <label> <path>` | Add an inheritance link to AGENTS.md |
|
|
67
|
+
| `inherit --list` | List current inheritance links |
|
|
68
|
+
| `inherit --remove <label>` | Remove an inheritance link by label |
|
|
66
69
|
| `status` | Show the current sync status of all targets and symlinks |
|
|
67
70
|
| `add <type> <name>` | Add a new rule, skill, or workflow from a template (type is `rule`, `skill`, or `workflow`) |
|
|
68
71
|
| `index` | Regenerate `AGENTS.md` by scanning the contents of `.agents/` |
|
|
@@ -79,6 +82,150 @@ AGENTS.md is also symlinked to CLAUDE.md so that Claude reads the index natively
|
|
|
79
82
|
| `--dry-run` | Show what would be done without making changes |
|
|
80
83
|
| `--force` | Overwrite existing files and symlinks |
|
|
81
84
|
|
|
85
|
+
## Inheritance
|
|
86
|
+
|
|
87
|
+
Projects can inherit agent rules from parent directories (org, team, global) using a convention-based approach. This enables hierarchical rule sharing without duplicating files.
|
|
88
|
+
|
|
89
|
+
### How It Works
|
|
90
|
+
|
|
91
|
+
Add an `## Inherits` section to your project's `AGENTS.md` that links to parent-level agent configs:
|
|
92
|
+
|
|
93
|
+
```markdown
|
|
94
|
+
## Inherits
|
|
95
|
+
- [global](../../AGENTS.md)
|
|
96
|
+
- [team](../AGENTS.md)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
AI agents (Claude, Codex, etc.) follow markdown links natively — when they read your project's `AGENTS.md`, they'll traverse the inheritance chain and apply rules from all levels.
|
|
100
|
+
|
|
101
|
+
### Hierarchy Example
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
~/code/ # Global: security norms, universal rules
|
|
105
|
+
├── .agents/
|
|
106
|
+
├── AGENTS.md
|
|
107
|
+
└── org/ # Org-level: coding standards, shared workflows
|
|
108
|
+
├── .agents/
|
|
109
|
+
├── AGENTS.md
|
|
110
|
+
└── team/ # Team-level: language-specific rules
|
|
111
|
+
├── .agents/
|
|
112
|
+
├── AGENTS.md
|
|
113
|
+
└── project/ # Project: project-specific rules + inherits
|
|
114
|
+
├── .agents/
|
|
115
|
+
└── AGENTS.md → ## Inherits links to team, org, global
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Inheritance is upward-only.** A project declares what it inherits from. Parent directories don't need to know about their children — when an agent works at the org level, it already has access to org-level rules.
|
|
119
|
+
|
|
120
|
+
### Managing Inheritance
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Add an inheritance link
|
|
124
|
+
sync-agents inherit global ../../AGENTS.md
|
|
125
|
+
sync-agents inherit team ../AGENTS.md
|
|
126
|
+
|
|
127
|
+
# List current inheritance links
|
|
128
|
+
sync-agents inherit --list
|
|
129
|
+
|
|
130
|
+
# Remove an inheritance link
|
|
131
|
+
sync-agents inherit --remove global
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The `## Inherits` section is preserved across `sync-agents index` regenerations.
|
|
135
|
+
|
|
136
|
+
### Full Example
|
|
137
|
+
|
|
138
|
+
Set up a three-level hierarchy: global rules → org standards → project config.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# 1. Create global rules (e.g. ~/code/.agents/)
|
|
142
|
+
cd ~/code
|
|
143
|
+
sync-agents init
|
|
144
|
+
sync-agents add rule security
|
|
145
|
+
cat > .agents/rules/security.md << 'EOF'
|
|
146
|
+
---
|
|
147
|
+
trigger: always_on
|
|
148
|
+
---
|
|
149
|
+
# Security
|
|
150
|
+
- Never commit secrets or API keys
|
|
151
|
+
- Validate all external input
|
|
152
|
+
- Use parameterized queries for database access
|
|
153
|
+
EOF
|
|
154
|
+
|
|
155
|
+
# 2. Create org-level rules (e.g. ~/code/myorg/.agents/)
|
|
156
|
+
cd ~/code/myorg
|
|
157
|
+
sync-agents init
|
|
158
|
+
sync-agents add rule go-standards
|
|
159
|
+
cat > .agents/rules/go-standards.md << 'EOF'
|
|
160
|
+
---
|
|
161
|
+
trigger: always_on
|
|
162
|
+
---
|
|
163
|
+
# Go Standards
|
|
164
|
+
- Use `gofmt` and `golangci-lint` on all Go files
|
|
165
|
+
- Prefer table-driven tests
|
|
166
|
+
- Export only what consumers need
|
|
167
|
+
EOF
|
|
168
|
+
|
|
169
|
+
# 3. Create project with inheritance
|
|
170
|
+
cd ~/code/myorg/api-service
|
|
171
|
+
sync-agents init
|
|
172
|
+
sync-agents add rule api-conventions
|
|
173
|
+
|
|
174
|
+
# Link to parent levels
|
|
175
|
+
sync-agents inherit org ../AGENTS.md
|
|
176
|
+
sync-agents inherit global ../../AGENTS.md
|
|
177
|
+
|
|
178
|
+
# Sync to agent directories
|
|
179
|
+
sync-agents sync
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The project's `AGENTS.md` now looks like:
|
|
183
|
+
|
|
184
|
+
```markdown
|
|
185
|
+
## Inherits
|
|
186
|
+
- [org](../AGENTS.md)
|
|
187
|
+
- [global](../../AGENTS.md)
|
|
188
|
+
|
|
189
|
+
## Rules
|
|
190
|
+
- [api-conventions](.agents/rules/api-conventions.md)
|
|
191
|
+
|
|
192
|
+
## Skills
|
|
193
|
+
_No skills defined yet._
|
|
194
|
+
|
|
195
|
+
## Workflows
|
|
196
|
+
_No workflows defined yet._
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
When an AI agent reads this file, it follows the `## Inherits` links and applies rules from all three levels — project-specific API conventions, org-wide Go standards, and global security rules.
|
|
200
|
+
|
|
201
|
+
### Verifying Inheritance
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Check what's inherited
|
|
205
|
+
sync-agents inherit --list
|
|
206
|
+
# Output:
|
|
207
|
+
# - [org](../AGENTS.md)
|
|
208
|
+
# - [global](../../AGENTS.md)
|
|
209
|
+
|
|
210
|
+
# Remove a link if no longer needed
|
|
211
|
+
sync-agents inherit --remove global
|
|
212
|
+
|
|
213
|
+
# Re-add with a different path
|
|
214
|
+
sync-agents inherit global ../../AGENTS.md
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
The [`examples/`](examples/) directory contains ready-to-use rules, skills, and workflows. Import them directly:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
sync-agents import https://raw.githubusercontent.com/brickhouse-tech/sync-agents/main/examples/rules/no-secrets.md
|
|
223
|
+
sync-agents import https://raw.githubusercontent.com/brickhouse-tech/sync-agents/main/examples/skills/code-review.md
|
|
224
|
+
sync-agents import https://raw.githubusercontent.com/brickhouse-tech/sync-agents/main/examples/workflows/pr-checklist.md
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
See [examples/README.md](examples/README.md) for the full list.
|
|
228
|
+
|
|
82
229
|
## Usage
|
|
83
230
|
|
|
84
231
|
```bash
|
package/package.json
CHANGED
package/src/sh/sync-agents.sh
CHANGED
|
@@ -75,6 +75,9 @@ ${BOLD}COMMANDS${RESET}
|
|
|
75
75
|
watch Watch .agents/ for changes and auto-regenerate index
|
|
76
76
|
import <url> Import a rule/skill/workflow from a URL
|
|
77
77
|
hook Install a pre-commit git hook for auto-sync
|
|
78
|
+
inherit <label> <path> Add an inheritance link to AGENTS.md (convention-based)
|
|
79
|
+
inherit --list List current inheritance links
|
|
80
|
+
inherit --remove <label> Remove an inheritance link by label
|
|
78
81
|
|
|
79
82
|
${BOLD}OPTIONS${RESET}
|
|
80
83
|
-h, --help Show this help message
|
|
@@ -526,6 +529,198 @@ cmd_clean() {
|
|
|
526
529
|
info "Clean complete."
|
|
527
530
|
}
|
|
528
531
|
|
|
532
|
+
# --------------------------------------------------------------------------
|
|
533
|
+
# Inherit
|
|
534
|
+
# --------------------------------------------------------------------------
|
|
535
|
+
|
|
536
|
+
cmd_inherit() {
|
|
537
|
+
local action="${1:-}"
|
|
538
|
+
|
|
539
|
+
# --list: show current inherits
|
|
540
|
+
if [[ "$action" == "--list" ]]; then
|
|
541
|
+
if [[ ! -f "$PROJECT_ROOT/$AGENTS_MD" ]]; then
|
|
542
|
+
info "No AGENTS.md found."
|
|
543
|
+
return 0
|
|
544
|
+
fi
|
|
545
|
+
local in_section="false"
|
|
546
|
+
while IFS= read -r line; do
|
|
547
|
+
if [[ "$line" =~ ^##[[:space:]]+Inherits ]]; then
|
|
548
|
+
in_section="true"
|
|
549
|
+
continue
|
|
550
|
+
fi
|
|
551
|
+
if [[ "$in_section" == "true" ]] && [[ "$line" =~ ^## ]]; then
|
|
552
|
+
break
|
|
553
|
+
fi
|
|
554
|
+
if [[ "$in_section" == "true" ]] && [[ "$line" =~ ^-[[:space:]]+\[ ]]; then
|
|
555
|
+
echo "$line"
|
|
556
|
+
fi
|
|
557
|
+
done < "$PROJECT_ROOT/$AGENTS_MD"
|
|
558
|
+
return 0
|
|
559
|
+
fi
|
|
560
|
+
|
|
561
|
+
# --remove <label>: remove an inherit entry
|
|
562
|
+
if [[ "$action" == "--remove" ]]; then
|
|
563
|
+
local label="${2:-}"
|
|
564
|
+
if [[ -z "$label" ]]; then
|
|
565
|
+
error "Usage: sync-agents inherit --remove <label>"
|
|
566
|
+
exit 1
|
|
567
|
+
fi
|
|
568
|
+
if [[ ! -f "$PROJECT_ROOT/$AGENTS_MD" ]]; then
|
|
569
|
+
error "No AGENTS.md found."
|
|
570
|
+
exit 1
|
|
571
|
+
fi
|
|
572
|
+
# Remove the line matching [label](...) from the Inherits section
|
|
573
|
+
local tmp
|
|
574
|
+
tmp="$(mktemp)"
|
|
575
|
+
local in_section="false"
|
|
576
|
+
local removed="false"
|
|
577
|
+
while IFS= read -r line; do
|
|
578
|
+
if [[ "$line" =~ ^##[[:space:]]+Inherits ]]; then
|
|
579
|
+
in_section="true"
|
|
580
|
+
echo "$line" >> "$tmp"
|
|
581
|
+
continue
|
|
582
|
+
fi
|
|
583
|
+
if [[ "$in_section" == "true" ]] && [[ "$line" =~ ^## ]]; then
|
|
584
|
+
in_section="false"
|
|
585
|
+
fi
|
|
586
|
+
if [[ "$in_section" == "true" ]] && [[ "$line" == *"[$label]("* ]]; then
|
|
587
|
+
removed="true"
|
|
588
|
+
continue
|
|
589
|
+
fi
|
|
590
|
+
echo "$line" >> "$tmp"
|
|
591
|
+
done < "$PROJECT_ROOT/$AGENTS_MD"
|
|
592
|
+
mv "$tmp" "$PROJECT_ROOT/$AGENTS_MD"
|
|
593
|
+
if [[ "$removed" == "true" ]]; then
|
|
594
|
+
info "Removed inherit: $label"
|
|
595
|
+
else
|
|
596
|
+
warn "No inherit found with label: $label"
|
|
597
|
+
fi
|
|
598
|
+
return 0
|
|
599
|
+
fi
|
|
600
|
+
|
|
601
|
+
# Default: add <label> <path>
|
|
602
|
+
local label="$action"
|
|
603
|
+
local path="${2:-}"
|
|
604
|
+
|
|
605
|
+
if [[ -z "$label" ]] || [[ -z "$path" ]]; then
|
|
606
|
+
error "Usage: sync-agents inherit <label> <path>"
|
|
607
|
+
error " sync-agents inherit --list"
|
|
608
|
+
error " sync-agents inherit --remove <label>"
|
|
609
|
+
exit 1
|
|
610
|
+
fi
|
|
611
|
+
|
|
612
|
+
# Validate the path exists (resolve relative to PROJECT_ROOT)
|
|
613
|
+
local resolved_path
|
|
614
|
+
if [[ "$path" == /* ]] || [[ "$path" == ~* ]]; then
|
|
615
|
+
resolved_path="${path/#\~/$HOME}"
|
|
616
|
+
else
|
|
617
|
+
resolved_path="$PROJECT_ROOT/$path"
|
|
618
|
+
fi
|
|
619
|
+
|
|
620
|
+
if [[ ! -f "$resolved_path" ]] && [[ ! -d "$resolved_path" ]]; then
|
|
621
|
+
warn "Path does not exist: $path (link will be added anyway)"
|
|
622
|
+
fi
|
|
623
|
+
|
|
624
|
+
# Check if AGENTS.md exists
|
|
625
|
+
if [[ ! -f "$PROJECT_ROOT/$AGENTS_MD" ]]; then
|
|
626
|
+
error "No AGENTS.md found. Run 'sync-agents init' first."
|
|
627
|
+
exit 1
|
|
628
|
+
fi
|
|
629
|
+
|
|
630
|
+
# Check if Inherits section exists; if not, add it after the header
|
|
631
|
+
if ! grep -q "^## Inherits" "$PROJECT_ROOT/$AGENTS_MD"; then
|
|
632
|
+
# Insert Inherits section right after the header block (after first blank line following description)
|
|
633
|
+
local tmp
|
|
634
|
+
tmp="$(mktemp)"
|
|
635
|
+
local header_done="false"
|
|
636
|
+
local inherits_written="false"
|
|
637
|
+
while IFS= read -r line; do
|
|
638
|
+
echo "$line" >> "$tmp"
|
|
639
|
+
# Write inherits section after the description paragraph (first line starting with "This file")
|
|
640
|
+
if [[ "$header_done" == "false" ]] && [[ "$line" == "This file indexes"* ]]; then
|
|
641
|
+
header_done="true"
|
|
642
|
+
{
|
|
643
|
+
echo ""
|
|
644
|
+
echo "## Inherits"
|
|
645
|
+
echo ""
|
|
646
|
+
echo "- [$label]($path)"
|
|
647
|
+
} >> "$tmp"
|
|
648
|
+
inherits_written="true"
|
|
649
|
+
fi
|
|
650
|
+
done < "$PROJECT_ROOT/$AGENTS_MD"
|
|
651
|
+
# Fallback: if header pattern wasn't found, append at the end before ## Rules
|
|
652
|
+
if [[ "$inherits_written" == "false" ]]; then
|
|
653
|
+
rm "$tmp"
|
|
654
|
+
tmp="$(mktemp)"
|
|
655
|
+
while IFS= read -r line; do
|
|
656
|
+
if [[ "$line" == "## Rules" ]] && [[ "$inherits_written" == "false" ]]; then
|
|
657
|
+
{
|
|
658
|
+
echo "## Inherits"
|
|
659
|
+
echo ""
|
|
660
|
+
echo "- [$label]($path)"
|
|
661
|
+
echo ""
|
|
662
|
+
} >> "$tmp"
|
|
663
|
+
inherits_written="true"
|
|
664
|
+
fi
|
|
665
|
+
echo "$line" >> "$tmp"
|
|
666
|
+
done < "$PROJECT_ROOT/$AGENTS_MD"
|
|
667
|
+
fi
|
|
668
|
+
mv "$tmp" "$PROJECT_ROOT/$AGENTS_MD"
|
|
669
|
+
else
|
|
670
|
+
# Inherits section exists — check for duplicate label
|
|
671
|
+
if grep -q "\[$label\](" "$PROJECT_ROOT/$AGENTS_MD"; then
|
|
672
|
+
warn "Inherit with label '$label' already exists. Use --remove first to update."
|
|
673
|
+
return 1
|
|
674
|
+
fi
|
|
675
|
+
# Append to existing Inherits section (after last inherit entry or section header)
|
|
676
|
+
local tmp
|
|
677
|
+
tmp="$(mktemp)"
|
|
678
|
+
local in_section="false"
|
|
679
|
+
local added="false"
|
|
680
|
+
while IFS= read -r line; do
|
|
681
|
+
if [[ "$line" =~ ^##[[:space:]]+Inherits ]]; then
|
|
682
|
+
in_section="true"
|
|
683
|
+
echo "$line" >> "$tmp"
|
|
684
|
+
continue
|
|
685
|
+
fi
|
|
686
|
+
# When we hit the next section or blank line after entries, insert
|
|
687
|
+
if [[ "$in_section" == "true" ]] && [[ "$added" == "false" ]]; then
|
|
688
|
+
if [[ "$line" =~ ^## ]] || [[ -z "$line" ]]; then
|
|
689
|
+
# Check if previous content had entries; add after them
|
|
690
|
+
if [[ "$line" =~ ^## ]]; then
|
|
691
|
+
{
|
|
692
|
+
echo "- [$label]($path)"
|
|
693
|
+
echo ""
|
|
694
|
+
} >> "$tmp"
|
|
695
|
+
added="true"
|
|
696
|
+
in_section="false"
|
|
697
|
+
fi
|
|
698
|
+
fi
|
|
699
|
+
if [[ "$line" =~ ^-[[:space:]]+\[ ]]; then
|
|
700
|
+
echo "$line" >> "$tmp"
|
|
701
|
+
continue
|
|
702
|
+
fi
|
|
703
|
+
if [[ -z "$line" ]] && [[ "$added" == "false" ]]; then
|
|
704
|
+
echo "- [$label]($path)" >> "$tmp"
|
|
705
|
+
added="true"
|
|
706
|
+
in_section="false"
|
|
707
|
+
echo "$line" >> "$tmp"
|
|
708
|
+
continue
|
|
709
|
+
fi
|
|
710
|
+
fi
|
|
711
|
+
echo "$line" >> "$tmp"
|
|
712
|
+
done < "$PROJECT_ROOT/$AGENTS_MD"
|
|
713
|
+
# If we never added (section was at end of file)
|
|
714
|
+
if [[ "$added" == "false" ]]; then
|
|
715
|
+
echo "- [$label]($path)" >> "$tmp"
|
|
716
|
+
echo "" >> "$tmp"
|
|
717
|
+
fi
|
|
718
|
+
mv "$tmp" "$PROJECT_ROOT/$AGENTS_MD"
|
|
719
|
+
fi
|
|
720
|
+
|
|
721
|
+
info "Added inherit: [$label]($path)"
|
|
722
|
+
}
|
|
723
|
+
|
|
529
724
|
# --------------------------------------------------------------------------
|
|
530
725
|
# Index generator
|
|
531
726
|
# --------------------------------------------------------------------------
|
|
@@ -534,6 +729,25 @@ generate_agents_md() {
|
|
|
534
729
|
local outfile="$PROJECT_ROOT/$AGENTS_MD"
|
|
535
730
|
local agents_dir="$PROJECT_ROOT/$AGENTS_DIR"
|
|
536
731
|
|
|
732
|
+
# Preserve existing Inherits section before regenerating
|
|
733
|
+
local inherits_block=""
|
|
734
|
+
if [[ -f "$outfile" ]]; then
|
|
735
|
+
local in_section="false"
|
|
736
|
+
while IFS= read -r line; do
|
|
737
|
+
if [[ "$line" =~ ^##[[:space:]]+Inherits ]]; then
|
|
738
|
+
in_section="true"
|
|
739
|
+
inherits_block+="$line"$'\n'
|
|
740
|
+
continue
|
|
741
|
+
fi
|
|
742
|
+
if [[ "$in_section" == "true" ]] && [[ "$line" =~ ^## ]]; then
|
|
743
|
+
break
|
|
744
|
+
fi
|
|
745
|
+
if [[ "$in_section" == "true" ]]; then
|
|
746
|
+
inherits_block+="$line"$'\n'
|
|
747
|
+
fi
|
|
748
|
+
done < "$outfile"
|
|
749
|
+
fi
|
|
750
|
+
|
|
537
751
|
cat > "$outfile" <<'HEADER'
|
|
538
752
|
|
|
539
753
|
---
|
|
@@ -550,6 +764,11 @@ This file indexes all rules, skills, and workflows defined in `.agents/`.
|
|
|
550
764
|
HEADER
|
|
551
765
|
|
|
552
766
|
{
|
|
767
|
+
# Inherits (preserved from previous AGENTS.md)
|
|
768
|
+
if [[ -n "$inherits_block" ]]; then
|
|
769
|
+
printf '%s\n' "$inherits_block"
|
|
770
|
+
fi
|
|
771
|
+
|
|
553
772
|
# Rules
|
|
554
773
|
echo "## Rules"
|
|
555
774
|
echo ""
|
|
@@ -681,6 +900,10 @@ main() {
|
|
|
681
900
|
shift
|
|
682
901
|
;;
|
|
683
902
|
-*)
|
|
903
|
+
if [[ -n "$command" ]]; then
|
|
904
|
+
# Unknown flag after command — pass to subcommand
|
|
905
|
+
break
|
|
906
|
+
fi
|
|
684
907
|
error "Unknown option: $1"
|
|
685
908
|
usage
|
|
686
909
|
exit 1
|
|
@@ -751,6 +974,9 @@ main() {
|
|
|
751
974
|
hook)
|
|
752
975
|
cmd_hook
|
|
753
976
|
;;
|
|
977
|
+
inherit)
|
|
978
|
+
cmd_inherit "$@"
|
|
979
|
+
;;
|
|
754
980
|
"")
|
|
755
981
|
usage
|
|
756
982
|
exit 0
|