Skip to content

Commit 67f0fbf

Browse files
authored
Merge branch 'main' into release-please--branches--main
2 parents 426b2d7 + 346ac72 commit 67f0fbf

11 files changed

Lines changed: 125 additions & 977 deletions

‎.github/copilot-instructions.md‎

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -165,23 +165,8 @@ Per-script quirks (auth style, pagination, sentinels, reports) live in [`docs/sc
165165

166166
### Testing Approach
167167

168-
The project has a bats unit-test suite in `tests/`. Run the full suite with:
169-
170-
```bash
171-
bats tests/
172-
```
173-
174-
| File | What it covers |
175-
|------|----------------|
176-
| `tests/test_common.bats` | `lib/github-common.sh` pure-logic functions and API helpers |
177-
| `tests/test_script_validation.bats` | Every script — missing env vars, invalid args, `--help`, script-specific guards |
178-
| `tests/mock_curl.sh` | Universal curl mock used by both test files |
179-
180-
Every new script **must** include a test section in `tests/test_script_validation.bats` before it is merged. At minimum test: each required env var missing exits 1, unknown CLI args exit 1, and any enum or allowlist validation specific to the script.
181-
182-
Additional testing approaches:
183-
- **Always test on a test organization first** before running against production
184-
- **Dry-run flags** — several scripts support `--dry-run` to preview changes without applying them
168+
Run `bats tests/`. Test files, the mocking pattern and the required cases for
169+
every new script are in the "Testing" section of [`AGENTS.md`](../AGENTS.md).
185170

186171
### Commit Messages — Conventional Commits (required)
187172

@@ -222,7 +207,7 @@ When you change one of these files, you must also update the files in the "Also
222207

223208
| When you change… | Also update |
224209
|------------------|-------------|
225-
| `lib/github-common.sh` — any public function signature or behaviour | All 18 scripts that source it; verify each caller still passes the right arguments. Check with: `grep -r "github-common" . --include="*.sh"` |
210+
| `lib/github-common.sh` — any public function signature or behaviour | Every script that sources it; verify each caller still passes the right arguments. List them with: `grep -rl "github-common" . --include="*.sh"` |
226211
| `lib/github-common.sh` — add a new helper function | `AGENTS.md` shared library table; `README.md` if the function affects usage |
227212
| `gh_api_paginate` signature or pagination behaviour | `github-repo-permissions-report.sh` (3 call sites), `github-copilot-report.sh` (`fetch_seats`) — both pipe output through `jq -s '.'` and depend on the `jq_filter`/`api_version` parameter order |
228213
| Any script's required env vars | That script's `# ===` header comment; the corresponding README.md section's env var table; that script's `action.yml` inputs (add/remove required inputs to match) |

‎.github/hooks/scripts/block-file-writes-via-bash.sh‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,34 @@
33
# block-file-writes-via-bash.sh
44
#
55
# preToolUse hook for the bash tool. Denies commands that write file contents
6-
# through heredocs or tee, steering the agent to the create/edit tools.
6+
# through heredocs, tee, or interpreter heredoc scripts, steering the agent to
7+
# the create/edit tools. Allows the command if the payload cannot be parsed.
78
# =============================================================================
89

910
set -euo pipefail
1011

12+
deny() {
13+
jq -nc '{permissionDecision: "deny",
14+
permissionDecisionReason: "Write files with the create or edit tools; bash heredoc writes are blocked."}'
15+
exit 0
16+
}
17+
18+
# Fail open: an unparseable payload must not block every bash call
1119
payload="$(cat)"
12-
tool_name="$(printf '%s' "${payload}" | jq -r '.toolName // ""')"
20+
tool_name="$(printf '%s' "${payload}" | jq -r '.toolName // ""' 2>/dev/null)" || exit 0
1321
[[ "${tool_name}" == "bash" ]] || exit 0
1422

1523
# toolArgs arrives as a JSON string on some hosts and as an object on others
16-
command="$(printf '%s' "${payload}" | jq -r '.toolArgs // {} | if type == "string" then fromjson else . end | .command // ""')"
24+
command="$(printf '%s' "${payload}" | jq -r '.toolArgs // {} | if type == "string" then fromjson else . end | .command // ""' 2>/dev/null)" || exit 0
1725

1826
# Heredoc redirected into a file or tee: cat > f <<EOF, tee f <<'EOF', cat >> f <<-EOF
1927
if printf '%s' "${command}" | grep -Eq '(>|tee)[^<|;&]*<<-?[[:space:]]*['\''"\\]?[A-Za-z_]'; then
20-
jq -nc '{permissionDecision: "deny",
21-
permissionDecisionReason: "Write files with the create or edit tools; bash heredoc writes are blocked."}'
28+
deny
29+
fi
30+
31+
# Heredoc script for an interpreter that opens a file for writing
32+
if printf '%s' "${command}" | grep -Eq '(python3?|node|ruby|perl)[^<|;&]*<<' \
33+
&& printf '%s' "${command}" | grep -Eq 'open\([^)]*['\''"][wa]\+?['\''"]|writeFile|write_text|File\.write'; then
34+
deny
2235
fi
2336
exit 0
Lines changed: 12 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,20 @@
11
---
2-
description: "Write only the resulting content into files. Never echo prompt instructions, rationale, or meta-commentary into documentation, comments, or code being produced from a prompt."
2+
description: "Write only the resulting content into files, with no prompt framing or meta-commentary"
33
applyTo: '**'
44
---
55

66
# Exclude Prompt Data
77

8-
When a prompt contains instructional or contextual data used to guide a change,
9-
that data must not appear in the file being updated. The output must reflect
10-
only the *result* of the instruction — not the instruction itself, the
11-
reasoning behind it, or any acknowledgment that it was applied.
8+
Write each file as if its content always belonged there. A reader with no
9+
knowledge of the prompt should find it natural and self-contained.
1210

13-
## Core Rule
11+
- Describe the subject, not the change: "Validates email format", not
12+
"Added email validation as requested".
13+
- Example data uses generic placeholders (`Jane Doe`, `jane.doe@example.com`,
14+
`Acme Corp`, `my-org`, `example.com`), never real names, emails, domains,
15+
enterprises or organizations from the prompt or local configuration.
16+
- Output is polished and production-ready regardless of how the prompt was
17+
phrased.
1418

15-
> **Never echo prompt content into the file being changed.**
16-
>
17-
> Only write the outcome. Strip any meta-commentary, rationale, or framing that
18-
> originated in the prompt.
19-
20-
## What Counts as Prompt Data
21-
22-
Prompt data is any content the user provides as instruction or context rather
23-
than as intended file content:
24-
25-
- Descriptions of what to add or change (`"add a --verbose flag that..."`)
26-
- Inline rationale or motivation (`"because the old behavior caused..."`)
27-
- References to the prompt itself (`"as requested"`, `"per the prompt"`,
28-
`"the new feature has been added as"`)
29-
- Meta-commentary about the update
30-
(`"This section has been updated to reflect..."`)
31-
- Code comments that narrate a change rather than describe the code
32-
(`"// Added email validation as requested"`,
33-
`"// Now validates the input per the new requirement"`)
34-
- Structural scaffold labels used as section markers or template slots
35-
(the word `this` in `## this Title` is scaffolding, not heading text)
36-
37-
## What Belongs in the Output
38-
39-
The output file should contain only:
40-
41-
- The feature, fix, or content the prompt requested — written as if it always
42-
belonged there
43-
- Documentation or code that a reader would find useful independent of how the
44-
change was requested
45-
- Generic, cliche placeholder data in examples (e.g., `Jane Doe`,
46-
`jane.doe@example.com`, `Acme Corp`, `example.com`) — never real names,
47-
emails, domains, or organization identifiers pulled from the prompt or local
48-
configuration
49-
- Language formatting applied to terms in the prompt carries through to the
50-
output — if the prompt wraps a term in backticks or uses a specific syntax
51-
convention, follow that same convention in the output
52-
53-
## Output Quality
54-
55-
The prompt's writing quality does not set the bar for the output. Regardless
56-
of how a prompt is phrased, the result must be polished and production-ready:
57-
58-
- Correct grammar, capitalization, and punctuation throughout
59-
- No draft-quality prose or casually written sections
60-
- Informal or sloppy phrasing in the prompt must not carry into the output
61-
62-
## Use Cases
63-
64-
### Adding a Feature Flag to Documentation
65-
66-
**Prompt**
67-
68-
```text
69-
Update file.ext with new feature --new-opt <argument>, documenting the new
70-
feature in features.md
71-
```
72-
73-
**Acceptable result — `features.md`**
74-
75-
```text
76-
### --new-opt
77-
78-
Enables extended output. Requires a value argument. Example:
79-
80-
```bash
81-
file --new-opt foo
82-
```
83-
```
84-
85-
**Unacceptable result — `features.md`**
86-
87-
```text
88-
### --new-opt
89-
90-
The new feature `--new-opt` requiring an argument has now been added as
91-
requested. The feature is documented as such.
92-
93-
Enables extended output. Requires a value argument. Example:
94-
95-
```bash
96-
file --new-opt foo
97-
```
98-
```
99-
100-
The unacceptable version echoes the prompt's framing
101-
(`"has now been added as requested"`, `"The feature is documented as such"`).
102-
That language belongs in the prompt, not the file.
103-
104-
---
105-
106-
### Updating a Code File
107-
108-
**Prompt**
109-
110-
```text
111-
Add input validation to the createUser function — email must be a valid format.
112-
```
113-
114-
**Acceptable result**
115-
116-
```js
117-
function createUser(name, email) {
118-
// Rejects addresses missing a local part, @ sign, or domain
119-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
120-
throw new Error('Invalid email address.');
121-
}
122-
// ...
123-
}
124-
```
125-
126-
**Unacceptable result**
127-
128-
```js
129-
// Added email validation as requested in the prompt
130-
function createUser(name, email) {
131-
// Per the instruction, we now validate that email must be a valid format
132-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
133-
throw new Error('Invalid email address.');
134-
}
135-
// ...
136-
}
137-
```
138-
139-
The unacceptable version leaks prompt phrasing into code comments. Code
140-
comments and documentation updates are appropriate and encouraged — they should
141-
describe what the code does, its constraints, or its intent. What they must
142-
never do is narrate the change, reference the prompt, or report back as if
143-
responding to the user who requested it.
144-
145-
## Exceptions
146-
147-
A small number of cases legitimately require prompt content to appear in the
148-
file. Treat these as exceptions, not loopholes:
149-
150-
- **Verbatim transcription requested.** The user explicitly asks for prompt
151-
text to be inserted as-is (e.g., "paste this block into the README under
152-
`## Notice`"). Insert exactly what was requested and nothing more.
153-
- **The file *is* a prompt or instruction artifact.** When editing prompt
154-
files, skill definitions, or instruction files, instructional content is the
155-
intended payload. The rule still applies one level up: do not add
156-
meta-commentary about *this* edit into those files.
157-
- **Changelog or release-note entries.** A short, factual line describing the
158-
change is appropriate. Keep it about the change, not about the request
159-
(`Added --verbose flag` ✓ / `Added --verbose flag as requested by user` ✗).
160-
161-
## Self-Check Before Saving
162-
163-
Before committing an edit produced from a prompt, scan the diff for any of the
164-
following and remove what you find:
165-
166-
- [ ] Phrases like "as requested", "per the prompt", "per your instruction",
167-
"as you asked"
168-
- [ ] Sentences that announce a change rather than describe the subject
169-
("This section now covers...", "Updated to include...")
170-
- [ ] Comments that explain why code was written instead of what it does
171-
- [ ] Verbatim restatement of the user's request inside the file
172-
- [ ] Acknowledgments of the prompt's existence at all
173-
174-
If any of these appear, rewrite the affected section so a fresh reader — with
175-
no knowledge of the prompt — would find the content natural and self-contained.
176-
177-
## Troubleshooting
178-
179-
| Symptom | Fix |
180-
|---|---|
181-
| Output contains "as requested" or "per the prompt" | Remove it |
182-
| Docs announce a change instead of documenting it | Rewrite directly |
183-
| Code comments narrate the change | Describe the code's behavior |
184-
| Prompt scaffold labels appear in output headings | Replace with original |
185-
186-
## Summary
187-
188-
Write the result, not the story of how you got there. A reader of the
189-
output file should see clean, useful content — with no trace of the prompt
190-
that produced it.
19+
Exceptions: verbatim text the user asks to insert, and prompt or instruction
20+
files whose payload is instructions.
Lines changed: 6 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,12 @@
11
---
22
name: 'No Heredoc File Operations'
3-
description: 'Prevents terminal heredoc file corruption in VS Code Copilot by enforcing use of file editing tools instead of shell redirections'
3+
description: 'Write file contents with the create and edit tools'
44
applyTo: '**'
55
---
66

7-
# MANDATORY: File Operation Override
7+
# File Writes
88

9-
This instruction applies to ALL agents and ALL file operations. It takes precedence over any other learned behavior.
10-
11-
## The Problem
12-
13-
Terminal heredoc operations are BROKEN in VS Code's Copilot integration. They cause:
14-
15-
- File corruption from tab characters triggering shell completion
16-
- Mangled content from quote/backtick escaping failures
17-
- Truncated files from exit code 130 interruptions
18-
- Garbage output from special character interpretation
19-
20-
## The Rule
21-
22-
**BEFORE writing ANY terminal command that creates or modifies a file, STOP.**
23-
24-
Ask yourself: "Am I about to use `cat`, `echo`, `printf`, `tee`, or `>>`/`>` to write content to a file?"
25-
26-
If YES → **DO NOT EXECUTE.** Use file editing tools instead.
27-
28-
## Forbidden Patterns
29-
30-
```bash
31-
# ALL OF THESE CORRUPT FILES - NEVER USE THEM
32-
cat > file << EOF
33-
cat > file << 'EOF'
34-
cat > file <<EOF
35-
cat > file <<'EOF'
36-
cat > file <<-EOF
37-
cat >> file << EOF
38-
echo "multi
39-
line" > file
40-
printf '%s\n' "line1" "line2" > file
41-
tee file << EOF
42-
tee file << 'EOF'
43-
```
44-
45-
## Required Approach
46-
47-
Instead of terminal commands for file content:
48-
49-
- **New files** → Use the file creation/editing tool provided by your environment
50-
- **Modify files** → Use the file editing tool provided by your environment
51-
- **Delete files** → Use the file deletion tool or `rm` command
52-
53-
## Terminal IS Allowed For
54-
55-
- `npm install`, `pip install`, `cargo add` (package management)
56-
- `npm run build`, `make`, `cargo build` (builds)
57-
- `npm test`, `pytest`, `go test` (testing)
58-
- `git add`, `git commit`, `git push` (version control)
59-
- `node script.js`, `python app.py` (running existing code)
60-
- `ls`, `cd`, `mkdir`, `pwd`, `rm` (filesystem navigation)
61-
- `curl`, `wget` (downloading, but not piping to files with content manipulation)
62-
63-
## Terminal is FORBIDDEN For
64-
65-
- ANY file creation with content
66-
- ANY file modification with content
67-
- ANY heredoc syntax (`<<`)
68-
- ANY multi-line string redirection
69-
70-
## Enforcement
71-
72-
This is not a suggestion. This is a hard technical requirement due to VS Code terminal integration bugs. Ignoring this instruction will result in corrupted files that the user must manually fix.
73-
74-
When you need to create or edit a file:
75-
76-
1. Stop before typing any terminal command
77-
2. Use the appropriate file editing tool
78-
3. The tool will handle the content correctly without corruption
9+
Write and modify file contents with the create and edit tools. Terminal
10+
heredocs, `tee`, and interpreter heredoc scripts that open files for writing
11+
corrupt content in the VS Code terminal; the preToolUse hook
12+
`.github/hooks/scripts/block-file-writes-via-bash.sh` denies them.

0 commit comments

Comments
 (0)