-
Notifications
You must be signed in to change notification settings - Fork 1
Support bootstrap scripts in rule frontmatter (preferred over file-based) #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7ad7b2a
27def2c
64d4d8b
0c706e9
058f599
b23a569
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -824,6 +824,69 @@ coding-context -p version=1.2.3 my-task | |
|
|
||
| This is useful when rules contain template syntax that should be preserved for the AI agent to process. | ||
|
|
||
| #### `bootstrap` (optional) | ||
|
|
||
| **Type:** String (multiline) | ||
| **Purpose:** Shell script to execute before the rule is included. This is the **preferred method** for defining bootstrap scripts. | ||
|
|
||
| The bootstrap script runs via `sh -c`, with output sent to stderr (not included in the AI context). This allows rules to fetch dynamic data, set up environment, or prepare context before the rule content is processed. | ||
|
|
||
| **Example:** | ||
| ```yaml | ||
| --- | ||
| languages: | ||
| - go | ||
| bootstrap: | | ||
| #! /bin/sh | ||
| set -eux | ||
| echo "Fetching project dependencies..." >&2 | ||
| go list -m all > /tmp/go-deps.txt | ||
| echo "Dependencies cached" >&2 | ||
| --- | ||
|
|
||
| # Go Dependency Context | ||
|
|
||
| Dependencies are listed in /tmp/go-deps.txt | ||
| ``` | ||
|
|
||
| **Multiple commands:** | ||
| ```yaml | ||
| --- | ||
| bootstrap: | | ||
| #! /bin/sh | ||
| set -eux | ||
|
|
||
| echo "Setting up environment..." >&2 | ||
|
|
||
| if [ -z "$API_KEY" ]; then | ||
| echo "Warning: API_KEY not set" >&2 | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Fetching data..." >&2 | ||
| curl -s -H "Authorization: Bearer $API_KEY" \ | ||
| "https://api.example.com/data" > /tmp/data.json | ||
|
|
||
| echo "Setup complete" >&2 | ||
| --- | ||
| ``` | ||
|
|
||
| **Usage:** | ||
| ```bash | ||
| export API_KEY="your-api-key" | ||
| coding-context implement-feature | ||
| ``` | ||
|
|
||
| **Notes:** | ||
| - The bootstrap script is saved to a temporary executable file and run directly | ||
| - Use `#!/bin/sh` (POSIX shell) or `#!/bin/bash` (bash) as the first line to select the interpreter | ||
| - Output goes to stderr, not the assembled context | ||
| - If both frontmatter `bootstrap:` and file-based bootstrap exist, frontmatter takes precedence | ||
| - Environment variables from the parent process are available | ||
| - Exit code 0 is required for successful execution | ||
|
|
||
| **See also:** [Bootstrap Scripts](#bootstrap-scripts) for file-based alternative | ||
|
|
||
| **Other common fields:** | ||
| ```yaml | ||
| --- | ||
|
|
@@ -833,6 +896,10 @@ stage: implementation | |
| priority: high | ||
| team: backend | ||
| agent: cursor | ||
| bootstrap: | | ||
| #! /bin/sh | ||
| set -eux | ||
| echo "Running setup..." >&2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot as above.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in b23a569. Added |
||
| --- | ||
| ``` | ||
|
|
||
|
|
@@ -849,23 +916,54 @@ Rules are discovered in many locations. See [Search Paths Reference](./search-pa | |
|
|
||
| ## Bootstrap Scripts | ||
|
|
||
| Bootstrap scripts are executable files that run before their associated rule file is processed. | ||
| Bootstrap scripts run before their associated rule file is processed. There are two ways to define bootstrap scripts: | ||
|
|
||
| ### Frontmatter Bootstrap (Preferred) | ||
|
|
||
| Define the bootstrap script directly in the rule's frontmatter using the `bootstrap:` field. This is the **preferred method** because: | ||
| - The script is co-located with the rule content | ||
| - No separate file to manage or chmod | ||
| - Easier to version control and review | ||
|
|
||
| **Example:** | ||
| ```markdown | ||
| --- | ||
| bootstrap: | | ||
| #! /bin/sh | ||
| set -eux | ||
| echo "Fetching JIRA data..." >&2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot add shebang.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in b23a569. Added |
||
| curl -s -H "Authorization: Bearer $JIRA_API_TOKEN" \ | ||
| "https://api.example.com/issue/$JIRA_ISSUE_KEY" \ | ||
| | jq -r '.fields' > /tmp/jira-data.json | ||
| echo "Data fetched" >&2 | ||
| --- | ||
|
|
||
| # JIRA Context | ||
|
|
||
| Issue data is available in /tmp/jira-data.json | ||
| ``` | ||
|
|
||
| **See:** [Rule Frontmatter - bootstrap field](#bootstrap-optional) for more details. | ||
|
|
||
| ### File-Based Bootstrap (Legacy) | ||
|
|
||
| Alternatively, create a separate executable file for backward compatibility. | ||
|
|
||
| ### Naming Convention | ||
| #### Naming Convention | ||
|
|
||
| For a rule file named `my-rule.md`, the bootstrap script must be named `my-rule-bootstrap` (no extension). | ||
|
|
||
| **Example:** | ||
| - Rule: `.agents/rules/jira-context.md` | ||
| - Bootstrap: `.agents/rules/jira-context-bootstrap` | ||
|
|
||
| ### Requirements | ||
| #### Requirements | ||
|
|
||
| 1. **Executable permission:** `chmod +x script-name` | ||
| 2. **Same directory:** Must be in same directory as the rule file | ||
| 3. **Naming:** Must match rule filename plus `-bootstrap` suffix | ||
|
|
||
| ### Output Handling | ||
| #### Output Handling | ||
|
|
||
| - Bootstrap script output goes to **stderr**, not the main context | ||
| - The script's stdout is not captured | ||
|
|
@@ -881,7 +979,7 @@ curl -s "https://api.example.com/data" > /tmp/data.json | |
| echo "Data fetched successfully" >&2 | ||
| ``` | ||
|
|
||
| ### Environment Access | ||
| #### Environment Access | ||
|
|
||
| Bootstrap scripts can access all environment variables from the parent process. | ||
|
|
||
|
|
@@ -904,6 +1002,10 @@ curl -s -H "Authorization: Bearer $API_KEY" \ | |
| | jq -r '.fields' > /tmp/issue-data.json | ||
| ``` | ||
|
|
||
| ### Priority | ||
|
|
||
| If a rule has **both** frontmatter `bootstrap:` and a file-based bootstrap script, the **frontmatter bootstrap is used** (file is ignored). | ||
|
|
||
| ## YAML Frontmatter Specification | ||
|
|
||
| ### Valid Frontmatter | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,6 +155,93 @@ General information about the project. | |
| } | ||
| } | ||
|
|
||
| func TestBootstrapFromFrontmatter(t *testing.T) { | ||
| dirs := setupTestDirs(t) | ||
|
|
||
| // Create a rule file with bootstrap in frontmatter | ||
| ruleFile := filepath.Join(dirs.rulesDir, "setup.md") | ||
| ruleContent := `--- | ||
| bootstrap: | | ||
| #!/bin/sh | ||
| echo "Bootstrap from frontmatter" | ||
| --- | ||
| # Setup | ||
|
|
||
| This is a setup guide with frontmatter bootstrap. | ||
| ` | ||
| if err := os.WriteFile(ruleFile, []byte(ruleContent), 0o644); err != nil { | ||
| t.Fatalf("failed to write rule file: %v", err) | ||
| } | ||
|
|
||
| createStandardTask(t, dirs.tasksDir, "test-task") | ||
|
|
||
| // Run the program | ||
| output := runTool(t, "-C", dirs.tmpDir, "test-task") | ||
|
|
||
| // Check that bootstrap output appears | ||
| if !strings.Contains(output, "Bootstrap from frontmatter") { | ||
| t.Errorf("bootstrap output from frontmatter not found in output") | ||
| } | ||
|
|
||
| // Check that rule content is present | ||
| if !strings.Contains(output, "# Setup") { | ||
| t.Errorf("rule content not found in output") | ||
| } | ||
|
|
||
| // Check that task content is present | ||
| if !strings.Contains(output, "# Test Task") { | ||
| t.Errorf("task content not found in output") | ||
| } | ||
| } | ||
|
|
||
| func TestBootstrapFrontmatterPreferredOverFile(t *testing.T) { | ||
| dirs := setupTestDirs(t) | ||
|
|
||
| // Create a rule file with bootstrap in frontmatter | ||
| ruleFile := filepath.Join(dirs.rulesDir, "setup.md") | ||
| ruleContent := `--- | ||
| bootstrap: | | ||
| #!/bin/sh | ||
| echo "Using frontmatter bootstrap" | ||
| --- | ||
| # Priority Test | ||
|
|
||
| Testing that frontmatter bootstrap is preferred. | ||
| ` | ||
| if err := os.WriteFile(ruleFile, []byte(ruleContent), 0o644); err != nil { | ||
| t.Fatalf("failed to write rule file: %v", err) | ||
| } | ||
|
|
||
| // Also create a file-based bootstrap (should be ignored) | ||
| bootstrapFile := filepath.Join(dirs.rulesDir, "setup-bootstrap") | ||
| bootstrapContent := `#!/bin/bash | ||
| echo "Using file bootstrap" | ||
| ` | ||
| if err := os.WriteFile(bootstrapFile, []byte(bootstrapContent), 0o755); err != nil { | ||
| t.Fatalf("failed to write bootstrap file: %v", err) | ||
| } | ||
|
|
||
| createStandardTask(t, dirs.tasksDir, "test-task") | ||
|
|
||
| // Run the program | ||
| output := runTool(t, "-C", dirs.tmpDir, "test-task") | ||
|
|
||
| // Check that frontmatter bootstrap is used | ||
| if !strings.Contains(output, "Using frontmatter bootstrap") { | ||
| t.Errorf("frontmatter bootstrap output not found in output") | ||
| } | ||
|
|
||
| // Check that file bootstrap is NOT used | ||
| if strings.Contains(output, "Using file bootstrap") { | ||
| t.Errorf("file bootstrap should not be used when frontmatter bootstrap is present") | ||
| } | ||
|
Comment on lines
+229
to
+237
|
||
|
|
||
| // Check that rule content is present | ||
| if !strings.Contains(output, "# Priority Test") { | ||
| t.Errorf("rule content not found in output") | ||
| } | ||
| } | ||
|
|
||
| func TestMultipleBootstrapFiles(t *testing.T) { | ||
| dirs := setupTestDirs(t) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 058f599. Updated all frontmatter bootstrap examples in documentation to use
#! /bin/sh(with space) andset -euxfor better error handling and debugging.