Skip to content

Commit 9fb5da7

Browse files
feat: convert copilot instructions and new-feature prompt to APS v1.0 (#82)
- Rewrite .github/copilot-instructions.md using APS <instructions> and <constants> sections - Replace .github/prompts/new-feature.prompt.md with APS-compliant .github/agents/new-feature.agent.md - Agent includes structured processes, constants, formats, and proper MUST/SHOULD/MAY vocabulary Co-authored-by: Copilot <[email protected]>
1 parent f736315 commit 9fb5da7

3 files changed

Lines changed: 334 additions & 169 deletions

File tree

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
---
2+
name: New Feature
3+
description: "Implement a new Dev Container Feature in this repository."
4+
tools:
5+
- search/codebase
6+
- search/fileSearch
7+
- search/textSearch
8+
- read/readFile
9+
- edit/createDirectory
10+
- edit/createFile
11+
- edit/editFiles
12+
- execute/runInTerminal
13+
- execute/getTerminalOutput
14+
- web/fetch
15+
- web/githubRepo
16+
- todo
17+
user-invocable: true
18+
disable-model-invocation: false
19+
target: vscode
20+
---
21+
22+
<instructions>
23+
You MUST parse the user invocation to extract FEATURE_ID, SOURCE_REPO, and optional parameters.
24+
You MUST inspect SOURCE_REPO's latest GitHub release to determine the release asset naming pattern.
25+
You MUST install tools from GitHub releases, not from package managers like apt.
26+
You MUST support a version option defaulting to "latest" that resolves via the GitHub API.
27+
You MUST implement multi-architecture support mapping uname -m to release asset names per ARCH_MAP.
28+
You MUST create all files listed in DELIVERABLES_CREATE.
29+
You MUST update all files listed in DELIVERABLES_UPDATE with minimal targeted changes.
30+
You MUST follow the install.sh conventions from existing features in this repository.
31+
You MUST use set -e or set -euo pipefail in all generated shell scripts.
32+
You MUST include the root check pattern in generated install.sh files.
33+
You MUST include the check_packages helper for apt dependencies in generated install.sh files.
34+
You MUST verify installation with the binary's version command in install.sh.
35+
You MUST use the dev-container-features-test-lib pattern in generated test files.
36+
You MUST use conventional commit format for any commits.
37+
You MUST NOT modify unrelated files or make drive-by refactors.
38+
You MUST NOT remove or reorder existing README content beyond adding the new entry.
39+
You MUST NOT run devcontainer features test automatically.
40+
You SHOULD read an existing feature's install.sh as a reference before generating a new one.
41+
You SHOULD provide graceful fallback logic when GitHub API calls fail during version resolution.
42+
You SHOULD suggest test commands to the user after scaffolding using format:SCAFFOLD_SUMMARY.
43+
You MAY implement checksum verification if the user requests it and upstream provides checksums.
44+
You MAY add extra options to devcontainer-feature.json if the user specifies them.
45+
</instructions>
46+
47+
<constants>
48+
INVOCATION_EXAMPLES: TEXT<<
49+
- new-feature ripgrep in @BurntSushi/ripgrep
50+
- new-feature mise in @jdx/mise version=2024.7.1
51+
- new-feature protolint in @yoheimuta/protolint checksum=true
52+
>>
53+
54+
DELIVERABLES_CREATE: YAML<<
55+
- path: "src/{FEATURE_ID}/devcontainer-feature.json"
56+
purpose: "Feature metadata with id, name, version, description, and options"
57+
- path: "src/{FEATURE_ID}/install.sh"
58+
purpose: "Installer script that downloads from GitHub releases"
59+
- path: "test/{FEATURE_ID}/test.sh"
60+
purpose: "Basic smoke test using dev-container-features-test-lib"
61+
- path: "test/_global/{FEATURE_ID}-specific-version.sh"
62+
purpose: "Version-pinned test that greps for expected version string"
63+
>>
64+
65+
DELIVERABLES_UPDATE: YAML<<
66+
- path: "test/_global/all-tools.sh"
67+
action: "Add a check line for the new binary before reportResults"
68+
- path: "test/_global/scenarios.json"
69+
action: "Add feature to all-tools.features and add a version-pinned scenario"
70+
- path: ".github/workflows/test.yaml"
71+
action: "Add feature id to matrix.features array"
72+
- path: "README.md"
73+
action: "Add row to features table and add usage section"
74+
>>
75+
76+
ARCH_MAP: YAML<<
77+
x86_64: "x86_64"
78+
amd64: "x86_64"
79+
aarch64: "aarch64"
80+
arm64: "aarch64"
81+
armv7l: "armv7"
82+
i386: "i686"
83+
i686: "i686"
84+
>>
85+
86+
FEATURE_JSON_TEMPLATE: JSON<<
87+
{
88+
"name": "{FEATURE_DISPLAY_NAME}",
89+
"id": "{FEATURE_ID}",
90+
"version": "1.0.0",
91+
"description": "{DESCRIPTION}",
92+
"options": {
93+
"version": {
94+
"type": "string",
95+
"default": "latest",
96+
"description": "Version to install from GitHub releases"
97+
}
98+
}
99+
}
100+
>>
101+
102+
INSTALL_SH_STRUCTURE: TEXT<<
103+
1. Shebang: #!/usr/bin/env bash with set -e
104+
2. Variables: REPO_OWNER, REPO_NAME, BINARY_NAME, VERSION
105+
3. Root check: exit 1 if $(id -u) is not 0
106+
4. Clean up: rm -rf /var/lib/apt/lists/*
107+
5. check_packages helper: install apt deps if missing
108+
6. Install deps: curl jq ca-certificates tar
109+
7. get_latest_version: query GitHub API /repos/OWNER/REPO/releases/latest
110+
8. Version resolution: if empty or "latest" call get_latest_version
111+
9. Architecture: uname -m with case for x86_64, i686, aarch64, armv7l
112+
10. OS: uname -s mapped to linux/darwin identifiers
113+
11. Download URL: construct from owner/repo/version/arch/os
114+
12. Temp dir: mktemp -d, cd, curl -sSL, extract
115+
13. Move: mv binary /usr/local/bin/, chmod if needed
116+
14. Cleanup: rm -rf temp dir, rm -rf /var/lib/apt/lists/*
117+
15. Verify: run binary --version
118+
>>
119+
120+
TEST_PATTERN: TEXT<<
121+
#!/bin/bash
122+
set -e
123+
source dev-container-features-test-lib
124+
check "{FEATURE_ID}" {BINARY_NAME} --version
125+
reportResults
126+
>>
127+
128+
VERSION_TEST_PATTERN: TEXT<<
129+
#!/bin/bash
130+
set -e
131+
source dev-container-features-test-lib
132+
check "{FEATURE_ID} with specific version" /bin/bash -c "{BINARY_NAME} --version | grep '{PIN_VERSION}'"
133+
reportResults
134+
>>
135+
</constants>
136+
137+
<formats>
138+
<format id="SCAFFOLD_SUMMARY" name="Scaffold Summary" purpose="Report files created and modified and suggest test commands after scaffolding.">
139+
# Feature: <FEATURE_DISPLAY_NAME> (<FEATURE_ID>)
140+
141+
Source: <SOURCE_REPO>
142+
143+
## Files Created
144+
<CREATED_FILES>
145+
146+
## Files Updated
147+
<UPDATED_FILES>
148+
149+
## Suggested Test Commands
150+
<TEST_COMMANDS>
151+
152+
## Notes
153+
<NOTES>
154+
WHERE:
155+
- <CREATED_FILES> is Markdown; bulleted list of created file paths.
156+
- <FEATURE_DISPLAY_NAME> is String; human-friendly feature name.
157+
- <FEATURE_ID> is String; lowercase identifier used in file paths.
158+
- <NOTES> is String; warnings, assumptions, or follow-up actions.
159+
- <SOURCE_REPO> is String; GitHub owner/repo of the upstream tool.
160+
- <TEST_COMMANDS> is Markdown; shell commands to test the new feature.
161+
- <UPDATED_FILES> is Markdown; bulleted list of updated file paths with change summary.
162+
</format>
163+
</formats>
164+
165+
<runtime>
166+
FEATURE_ID: ""
167+
FEATURE_DISPLAY_NAME: ""
168+
SOURCE_REPO: ""
169+
BINARY_NAME: ""
170+
REQUESTED_VERSION: "latest"
171+
SUPPORT_CHECKSUM: false
172+
RELEASE_ASSET_PATTERN: ""
173+
</runtime>
174+
175+
<triggers>
176+
<trigger event="user_message" target="main" />
177+
</triggers>
178+
179+
<processes>
180+
<process id="main" name="Main Workflow">
181+
RUN `parse-input`
182+
RUN `inspect-upstream`
183+
RUN `scaffold`
184+
RUN `integrate`
185+
RETURN: format="SCAFFOLD_SUMMARY"
186+
</process>
187+
188+
<process id="parse-input" name="Parse User Input">
189+
SET FEATURE_ID := <ID> (from "Agent Inference" using INP)
190+
SET SOURCE_REPO := <REPO> (from "Agent Inference" using INP)
191+
SET FEATURE_DISPLAY_NAME := <NAME> (from "Agent Inference" using FEATURE_ID)
192+
SET BINARY_NAME := <BIN> (from "Agent Inference" using FEATURE_ID)
193+
IF INP contains "version=":
194+
SET REQUESTED_VERSION := <VER> (from "Agent Inference" using INP)
195+
IF INP contains "checksum=true":
196+
SET SUPPORT_CHECKSUM := true (from "Agent Inference")
197+
</process>
198+
199+
<process id="inspect-upstream" name="Inspect Upstream Releases">
200+
USE `web/githubRepo` where: query="latest release assets", repo=SOURCE_REPO
201+
CAPTURE RELEASE_ASSET_PATTERN from result
202+
</process>
203+
204+
<process id="scaffold" name="Create Feature Files">
205+
USE `read/readFile` where: filePath="src/bat/install.sh"
206+
CAPTURE REFERENCE_INSTALL from result
207+
USE `edit/createDirectory` where: dirPath="src/{FEATURE_ID}"
208+
SET FEATURE_JSON := <JSON> (from "Agent Inference" using FEATURE_DISPLAY_NAME, FEATURE_ID, FEATURE_JSON_TEMPLATE)
209+
USE `edit/createFile` where: content=FEATURE_JSON, filePath="src/{FEATURE_ID}/devcontainer-feature.json"
210+
SET INSTALL_SCRIPT := <SCRIPT> (from "Agent Inference" using INSTALL_SH_STRUCTURE, REFERENCE_INSTALL, RELEASE_ASSET_PATTERN, SOURCE_REPO, SUPPORT_CHECKSUM)
211+
USE `edit/createFile` where: content=INSTALL_SCRIPT, filePath="src/{FEATURE_ID}/install.sh"
212+
USE `edit/createDirectory` where: dirPath="test/{FEATURE_ID}"
213+
SET TEST_SCRIPT := <SCRIPT> (from "Agent Inference" using BINARY_NAME, FEATURE_ID, TEST_PATTERN)
214+
USE `edit/createFile` where: content=TEST_SCRIPT, filePath="test/{FEATURE_ID}/test.sh"
215+
SET VERSION_TEST := <SCRIPT> (from "Agent Inference" using BINARY_NAME, FEATURE_ID, REQUESTED_VERSION, VERSION_TEST_PATTERN)
216+
USE `edit/createFile` where: content=VERSION_TEST, filePath="test/_global/{FEATURE_ID}-specific-version.sh"
217+
</process>
218+
219+
<process id="integrate" name="Update Existing Files">
220+
USE `read/readFile` where: filePath="test/_global/all-tools.sh"
221+
USE `edit/editFiles` where: filePath="test/_global/all-tools.sh"
222+
USE `read/readFile` where: filePath="test/_global/scenarios.json"
223+
USE `edit/editFiles` where: filePath="test/_global/scenarios.json"
224+
USE `read/readFile` where: filePath=".github/workflows/test.yaml"
225+
USE `edit/editFiles` where: filePath=".github/workflows/test.yaml"
226+
USE `read/readFile` where: filePath="README.md"
227+
USE `edit/editFiles` where: filePath="README.md"
228+
</process>
229+
</processes>
230+
231+
<input>
232+
Feature request in the form: <feature-id> in @<owner>/<repo> [version=<ver>] [checksum=true]
233+
Examples: "ripgrep in @BurntSushi/ripgrep", "mise in @jdx/mise version=2024.7.1"
234+
</input>

.github/copilot-instructions.md

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,100 @@
1-
# General Instructions
2-
3-
- Ensure any code you produce is correct.
4-
- for commit messages use conventional commit format, e.g. `feat: add new feature`, `fix: correct a bug`, `docs: update documentation`, etc. https://www.conventionalcommits.org/en/v1.0.0/
5-
6-
## New Feature Instructions
7-
8-
When creating a new feature, follow these steps:
9-
- Update the README.md to include the new feature in the list of features.
10-
- Each feature should be implemented under src/feature-name. Include a devcontainer-feature.json and install.sh file.
11-
- The install.sh file should install the package from the source repository. Not using a package manager like apt.
12-
- Each feature should have a test under test/feature-name/test.sh
13-
- Each feature should include a global test under test/_global/feature-name-specific-version.sh if a specific version can be installed.
14-
- Update the ../../test/_global/all-tools.sh to validate the new feature is installed.
15-
- Update the ../../test/_global/scenarios.json to include the new feature.
16-
- include the feature in the workflows
17-
- run `devcontainer features test -f $(feature) -i ubuntu:latest`
1+
<instructions>
2+
You MUST use conventional commit format for all commits.
3+
You MUST use the @new-feature agent when scaffolding a new feature.
4+
You MUST install tools from GitHub releases, not package managers like apt.
5+
You MUST treat skopeo as the sole exception to the GitHub releases rule.
6+
You MUST follow the install.sh pattern in INSTALL_SH_STEPS for new features.
7+
You MUST use dev-container-features-test-lib in test scripts per TEST_PATTERN.
8+
You MUST create all files in DELIVERABLES_CREATE when adding a new feature.
9+
You MUST update all files in DELIVERABLES_UPDATE when adding a new feature.
10+
You MUST NOT modify unrelated files when making changes.
11+
You SHOULD read an existing feature's install.sh as reference before writing a new one.
12+
You SHOULD verify changes pass devcontainer features test before submitting.
13+
</instructions>
14+
15+
<constants>
16+
REPO_DESCRIPTION: "Dev Container Features published to ghcr.io/jsburckhardt/devcontainer-features. Each feature installs a CLI tool into a dev container."
17+
18+
FEATURE_STRUCTURE: TEXT<<
19+
src/<feature-id>/
20+
devcontainer-feature.json metadata: id, name, version, description, options
21+
install.sh installer runs as root inside the container
22+
test/<feature-id>/
23+
test.sh basic install validation
24+
test/_global/
25+
scenarios.json global test scenarios (all-tools + per-feature version pins)
26+
all-tools.sh smoke test verifying every feature binary works
27+
<feature-id>-specific-version.sh version-pinned test
28+
>>
29+
30+
CI_WORKFLOWS: YAML<<
31+
- file: ".github/workflows/test.yaml"
32+
purpose: "Matrix tests each feature against debian:latest, ubuntu:latest, mcr.microsoft.com/devcontainers/base:ubuntu; also runs global scenarios"
33+
- file: ".github/workflows/release.yaml"
34+
purpose: "Manually triggered; publishes features to GHCR and opens a docs PR"
35+
>>
36+
37+
TEST_PREREQUISITE: "npm install -g @devcontainers/cli"
38+
39+
TEST_COMMANDS: TEXT<<
40+
devcontainer features test -f <feature-id> -i ubuntu:latest
41+
make test-matrix feature=<feature-id>
42+
make test
43+
devcontainer features test --global-scenarios-only --filter <scenario-name> .
44+
>>
45+
46+
INSTALL_SH_STEPS: TEXT<<
47+
1. Shebang: #!/usr/bin/env bash with set -e or set -euo pipefail
48+
2. Root check: exit 1 if $(id -u) is not 0
49+
3. Clean up: rm -rf /var/lib/apt/lists/*
50+
4. check_packages helper: install apt deps if missing
51+
5. Install deps: curl jq ca-certificates tar
52+
6. get_latest_version: query GitHub API /repos/OWNER/REPO/releases/latest
53+
7. Version resolution: if empty or "latest" call get_latest_version
54+
8. Architecture: uname -m with case for x86_64, i686, aarch64, armv7l
55+
9. OS: uname -s mapped to unknown-linux-gnu or apple-darwin
56+
10. Download URL: construct from owner/repo/version/arch/os
57+
11. Temp dir: mktemp -d, cd, curl -sSL, extract
58+
12. Move: mv binary to /usr/local/bin/
59+
13. Cleanup: rm -rf temp dir and apt lists
60+
14. Verify: run binary --version
61+
>>
62+
63+
TEST_PATTERN: TEXT<<
64+
#!/bin/bash
65+
set -e
66+
source dev-container-features-test-lib
67+
check "<label>" <command>
68+
reportResults
69+
>>
70+
71+
VERSION_TEST_PATTERN: TEXT<<
72+
#!/bin/bash
73+
set -e
74+
source dev-container-features-test-lib
75+
check "<feature> with specific version" /bin/bash -c "<binary> --version | grep '<version>'"
76+
reportResults
77+
>>
78+
79+
DELIVERABLES_CREATE: YAML<<
80+
- path: "src/<feature-id>/devcontainer-feature.json"
81+
purpose: "Feature metadata with id, name, version 1.0.0, description, options.version default latest"
82+
- path: "src/<feature-id>/install.sh"
83+
purpose: "Installer script following INSTALL_SH_STEPS"
84+
- path: "test/<feature-id>/test.sh"
85+
purpose: "Basic smoke test"
86+
- path: "test/_global/<feature-id>-specific-version.sh"
87+
purpose: "Version-pinned test"
88+
>>
89+
90+
DELIVERABLES_UPDATE: YAML<<
91+
- path: "test/_global/all-tools.sh"
92+
action: "Add a check line for the new binary before reportResults"
93+
- path: "test/_global/scenarios.json"
94+
action: "Add feature to all-tools.features and add a version-pinned scenario"
95+
- path: ".github/workflows/test.yaml"
96+
action: "Add feature id to matrix.features array"
97+
- path: "README.md"
98+
action: "Add row to features table and add usage section"
99+
>>
100+
</constants>

0 commit comments

Comments
 (0)