@@ -8,8 +8,152 @@ The plugin system follows the same pattern as Git's external subcommands:
88
99- Any executable named ` repos-<plugin> ` in your ` PATH ` becomes a plugin
1010- When you run ` repos <plugin> <args> ` , the tool automatically finds and executes ` repos-<plugin> ` with the provided arguments
11+ - ** NEW** : The core ` repos ` CLI automatically handles common options (` --config ` , ` --tag ` , ` --exclude-tag ` , ` --debug ` ) and passes filtered context to plugins via environment variables
1112- This provides complete isolation, crash safety, and the ability to write plugins in any language
1213
14+ ## Context Injection (Simplified Plugin Development)
15+
16+ As of version 0.0.10, plugins can opt into receiving pre-processed context from the core ` repos ` CLI. This means plugins don't need to:
17+
18+ - Parse ` --config ` , ` --tag ` , ` --exclude-tag ` , ` --debug ` options themselves
19+ - Load and parse the YAML configuration file
20+ - Apply tag filtering logic
21+
22+ ### How Context Injection Works
23+
24+ When you run:
25+
26+ ``` bash
27+ repos health --config custom.yaml --tag flow --exclude-tag deprecated prs
28+ ```
29+
30+ The core CLI:
31+
32+ 1 . Parses ` --config ` , ` --tag ` , ` --exclude-tag ` options
33+ 2 . Loads the config file
34+ 3 . Applies tag filtering (28 repos → 5 repos matching criteria)
35+ 4 . Serializes filtered repositories to a temp JSON file
36+ 5 . Sets environment variables:
37+ - ` REPOS_PLUGIN_PROTOCOL=1 ` (indicates context injection is available)
38+ - ` REPOS_FILTERED_REPOS_FILE=/tmp/repos-xxx.json ` (path to filtered repos)
39+ - ` REPOS_DEBUG=1 ` (if --debug flag was passed)
40+ - ` REPOS_TOTAL_REPOS=28 ` (total repos in config)
41+ - ` REPOS_FILTERED_COUNT=5 ` (repos after filtering)
42+ 6 . Executes ` repos-health prs ` with only plugin-specific args
43+
44+ ### Using Context Injection in Your Plugin
45+
46+ ** Rust Example:**
47+
48+ ``` rust
49+ use anyhow :: Result ;
50+ use repos :: {Repository , load_plugin_context, is_debug_mode};
51+
52+ #[tokio:: main]
53+ async fn main () -> Result <()> {
54+ // Try to load injected context
55+ let repos = if let Some (repos ) = load_plugin_context ()? {
56+ // New protocol: use pre-filtered repos from core CLI
57+ let debug = is_debug_mode ();
58+ if debug {
59+ eprintln! (" Using injected context with {} repos" , repos . len ());
60+ }
61+ repos
62+ } else {
63+ // Legacy fallback: parse args and load config manually
64+ // (for backwards compatibility when run directly)
65+ load_config_manually ()?
66+ };
67+
68+ // Now just implement your plugin logic
69+ for repo in repos {
70+ println! (" Processing: {}" , repo . name);
71+ // Your plugin functionality here
72+ }
73+
74+ Ok (())
75+ }
76+ ```
77+
78+ ** Python Example:**
79+
80+ ``` python
81+ # !/usr/bin/env python3
82+ import os
83+ import json
84+ import sys
85+
86+ def main ():
87+ # Check if context injection is available
88+ if os.environ.get(' REPOS_PLUGIN_PROTOCOL' ) == ' 1' :
89+ # Load pre-filtered repositories
90+ repos_file = os.environ.get(' REPOS_FILTERED_REPOS_FILE' )
91+ with open (repos_file, ' r' ) as f:
92+ repos = json.load(f)
93+
94+ debug = os.environ.get(' REPOS_DEBUG' ) == ' 1'
95+ if debug:
96+ total = os.environ.get(' REPOS_TOTAL_REPOS' , ' ?' )
97+ print (f " Using injected context: { len (repos)} / { total} repos " , file = sys.stderr)
98+ else :
99+ # Legacy fallback: parse args and load config manually
100+ repos = load_config_manually()
101+
102+ # Implement plugin logic with filtered repos
103+ for repo in repos:
104+ print (f " Processing: { repo[' name' ]} " )
105+ # Your plugin functionality here
106+
107+ if __name__ == ' __main__' :
108+ main()
109+ ```
110+
111+ ** Bash Example:**
112+
113+ ``` bash
114+ #! /bin/bash
115+
116+ # Check if context injection is available
117+ if [ " $REPOS_PLUGIN_PROTOCOL " = " 1" ]; then
118+ # Load pre-filtered repositories
119+ REPOS=$( cat " $REPOS_FILTERED_REPOS_FILE " )
120+
121+ if [ " $REPOS_DEBUG " = " 1" ]; then
122+ echo " Using injected context: $REPOS_FILTERED_COUNT /$REPOS_TOTAL_REPOS repos" >&2
123+ fi
124+
125+ # Process filtered repos using jq
126+ echo " $REPOS " | jq -r ' .[] | .name' | while read -r repo_name; do
127+ echo " Processing: $repo_name "
128+ # Your plugin functionality here
129+ done
130+ else
131+ # Legacy fallback: parse args and load config manually
132+ # (for backwards compatibility when run directly)
133+ echo " Loading config manually..." >&2
134+ # ... manual config loading logic ...
135+ fi
136+ ```
137+
138+ ### Benefits of Context Injection
139+
140+ 1 . ** Less boilerplate** : No need to parse common CLI options
141+ 2 . ** Consistent behavior** : Filtering works the same across all plugins
142+ 3 . ** Better performance** : Config loaded once, not per plugin
143+ 4 . ** Backwards compatible** : Plugins still work when run directly
144+ 5 . ** Language agnostic** : Available via environment variables
145+
146+ ### Supported Common Options
147+
148+ When invoking plugins through ` repos <plugin> ` , these options are automatically handled:
149+
150+ - ` --config <path> ` or ` -c <path> ` : Custom config file
151+ - ` --tag <tag> ` or ` -t <tag> ` : Filter repos by tag (can be repeated)
152+ - ` --exclude-tag <tag> ` or ` -e <tag> ` : Exclude repos by tag (can be repeated)
153+ - ` --debug ` or ` -d ` : Enable debug output
154+
155+ All other arguments are passed to the plugin as-is.
156+
13157## Creating a Plugin
14158
15159To create a plugin:
0 commit comments