Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
"conventionalCommits.scopes": [
"angular-dev",
"auto-header",
"claude-dev",
"helpers4-common",
"deps",
"deps-dev",
"CI-CD",
"claude-dev",
"copilot-dev",
"deprecated",
"deps-dev",
"deps",
"dotfiles-sync",
"essential-dev",
"git-absorb",
"github-dev",
"helpers4-common",
"mistral-dev",
"package-auto-install",
"peon-ping",
"pnpm-store",
Expand Down
68 changes: 68 additions & 0 deletions src/copilot-dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# GitHub Copilot Development Environment (copilot-dev)

Installs the [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat)
VS Code extension, the `gh copilot` CLI extension, and shared instructions for
commit messages, pull-request descriptions, and code reviews
(Conventional Commits + gitmoji + active repo PR template).

> **Note:** This feature covers the AI assistant only. For `gh` CLI and
> GitHub platform extensions (PR reviews, Actions, RemoteHub), use
> [`github-dev`](../github-dev).

## Example Usage

```jsonc
{
"features": {
"ghcr.io/helpers4/devcontainer/copilot-dev:1": {}
}
}
```

Combined with the platform tooling (recommended — `gh` must be present for the
`gh copilot` extension to install):

```jsonc
{
"features": {
"ghcr.io/helpers4/devcontainer/github-dev:1": {},
"ghcr.io/helpers4/devcontainer/copilot-dev:1": {}
}
}
```

## What's included

### Extension

- `github.copilot-chat` — GitHub Copilot Chat AI assistant in VS Code / Cursor

### CLI

- `gh copilot` — GitHub Copilot in the terminal (`gh copilot explain`, `gh copilot suggest`)
Installed via `gh extension install github/gh-copilot`. Requires `gh` CLI to be present
(install `github-dev` first, or any other feature that provides `gh`). Skipped with a
warning if `gh` is not found at build time.

### Shared instructions

Injected via VS Code settings — applied automatically to every Copilot Chat request of the
corresponding type:

| Setting | Trigger | What it enforces |
| --- | --- | --- |
| `commitMessageGeneration.instructions` | Generate Commit Message | Conventional Commits + gitmoji, scopes from `conventionalCommits.scopes` workspace setting |
| `pullRequestDescriptionGeneration.instructions` | Create Pull Request | Same title convention + repo PR template auto-detection with fallback |
| `reviewSelection.instructions` | Review and Comment | Correctness bugs, security issues, simplification - no style feedback |

#### Override or disable

All three are array settings — VS Code merges entries from User, Workspace, and
feature-injected sources. To add your own rules, append an entry in your User or
Workspace settings. To disable a helpers4 default entirely, set the array
explicitly to `[]` in your settings.

## OS and Architecture Support

- **OS:** Linux (Debian/Ubuntu-based images) for the CLI install; any for IDE-only use
- **Architectures:** amd64, arm64
64 changes: 64 additions & 0 deletions src/copilot-dev/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"id": "copilot-dev",
"version": "1.0.1",
"name": "GitHub Copilot Development Environment",
"description": "GitHub Copilot Chat VS Code extension, gh copilot CLI extension, and shared instructions for commit messages, pull-request descriptions, and code reviews (Conventional Commits + gitmoji + active repo PR template).",
"documentationURL": "https://github.com/helpers4/devcontainer/tree/main/src/copilot-dev",
"licenseURL": "https://github.com/helpers4/devcontainer/blob/main/LICENSE",
"keywords": ["helpers4", "github", "copilot", "ai", "vscode", "cli"],
"options": {
"installCli": {
"type": "boolean",
"default": true,
"description": "Install the gh copilot CLI extension (gh copilot explain / suggest). Requires gh CLI to be present (e.g. via github-dev). Skipped with a warning if gh is not found."
}
},
"customizations": {
"vscode": {
"extensions": [
"github.copilot-chat"
],
"settings": {
// GitHub Copilot
"github.copilot.enable": {
"*": true
},
// Shared Copilot Chat commit-message guidance for the helpers4 organization.
// The list of allowed scopes is intentionally NOT hardcoded here — each repo
// declares its own scopes via `conventionalCommits.scopes` in its workspace
// settings, and Copilot Chat reads that list from the workspace at generation
// time. End users can override or extend this array in their own settings.
"github.copilot.chat.commitMessageGeneration.instructions": [
{
"text": "Generate commit messages in English only. Use Conventional Commits format with a gitmoji between the scope and the description: `<type>(<scope>): <emoji> <description>`. The scope is optional; when used, it MUST be one of the values listed in the workspace setting `conventionalCommits.scopes` (defined in `.vscode/settings.json` for the active repository) — never invent a scope. Pick the most specific gitmoji from https://gitmoji.dev that matches the change. Common mapping (non-exhaustive): feat → ✨ (new feature), 🚸 (UX), ♿️ (a11y), 🌐 (i18n), 💬 (text/literals); fix → 🐛 (bug), 🚑️ (hotfix), 🔒️ (security), 🩹 (simple), 🥅 (errors), 🚨 (warnings), ✏️ (typo); docs → 📝 (general), 💡 (source comments), 📄 (license); refactor → ♻️ (refactor), 🎨 (structure), 🔥 (remove code), ⚰️ (dead code), 🚚 (move/rename); test → ✅ (add/update), 🧪 (failing test), 💚 (fix CI); chore → 🔧 (config), 🙈 (gitignore), 🔖 (tag/release), 📌 (pin deps), 🩺 (healthcheck); perf → ⚡️; style → 💄 (UI), 🎨 (code style); ci → 👷 (CI build), 💚 (fix CI); build → 📦️ (artefacts), ➕ (add dep), ➖ (remove dep), ⬆️ (upgrade dep), ⬇️ (downgrade dep); revert → ⏪️. Always include exactly ONE emoji. Keep the description short (≤72 chars), lowercase, imperative mood, no period at the end. If there are multiple logical changes, use a bullet list in the body. Generic shape examples (replace `<scope>` with a value from `conventionalCommits.scopes`): `feat(<scope>): ✨ add <thing>`, `fix(<scope>): 🐛 handle <case>`, `docs(<scope>): 📝 update <topic>`, `refactor(<scope>): ♻️ simplify <thing>`, `chore(<scope>): ⬆️ bump <dep>`."
}
],
// Shared Copilot Chat pull-request title & description guidance.
// Repo-agnostic: the instruction tells Copilot to follow the active
// repository's own PR template if one exists (e.g. `.github/PULL_REQUEST_TEMPLATE.md`,
// `.github/pull_request_template.md`, or any file under
// `.github/PULL_REQUEST_TEMPLATE/`), and to fall back to a sensible
// default otherwise. Title format follows the same Conventional Commits
// + gitmoji convention as commit messages, also driven by the
// `conventionalCommits.scopes` workspace setting.
"github.copilot.chat.pullRequestDescriptionGeneration.instructions": [
{
"text": "Generate pull-request titles and descriptions in English only.\n\n=== TITLE ===\nThe PR title MUST follow the same Conventional Commits + gitmoji format used for commit messages: `<type>(<scope>): <emoji> <description>`. Reuse the same rules: scope optional; when used it MUST come from the workspace setting `conventionalCommits.scopes` defined in the active repo's `.vscode/settings.json` (never invent a scope; if that setting is empty or absent, omit the scope); exactly ONE gitmoji from https://gitmoji.dev (type→emoji mapping: feat→✨, fix→🐛, docs→📝, refactor→♻️, test→✅, chore→🔧, perf→⚡️, style→💄, ci→👷, build→📦️, revert→⏪️, with more specific variants when applicable); description ≤72 chars, lowercase, imperative mood, no trailing period. If the PR groups several conventional types, pick the dominant one for the title and list the others in the body.\n\n=== DESCRIPTION ===\nThe PR body MUST follow the active repository's own pull-request template, if one is available. Look for it in this order and use the first match found in the workspace:\n 1. `.github/PULL_REQUEST_TEMPLATE.md` (or `pull_request_template.md`)\n 2. `docs/PULL_REQUEST_TEMPLATE.md` or `PULL_REQUEST_TEMPLATE.md` at the repo root\n 3. Any file under `.github/PULL_REQUEST_TEMPLATE/` (multiple templates — pick the one whose name best matches the change, e.g. `bug.md` for fixes, `feature.md` for feats; otherwise pick the first one alphabetically)\n 4. If the workspace also bundles an organization-level `.github` repo as a sibling folder (e.g. `helpers4/.github`), use its `PULL_REQUEST_TEMPLATE.md` as a fallback.\n\nWhen a template is found, reproduce its sections in the exact order with the exact heading wording, preserve checkbox label text verbatim, and:\n - Fill text sections (e.g. *Description*, *How tested*, *Related issues*) with content derived from the diff and commits.\n - Tick (`[x]`) every checkbox item that applies based on the diff and commits; leave the others unchecked (`[ ]`). Never invent checklist items beyond what the template lists.\n - Use `Closes #N`, `Fixes #N`, or `Refs #N` for issue references; if no issue is linked, write `None.`\n - For optional/contextual sections (Screenshots, Additional Context, Migration notes…), write `N/A` when not applicable.\n - Never omit a section present in the template; never add sections beyond it.\n\nIf NO template is found anywhere, fall back to this minimal, repo-agnostic structure:\n\n## Description\n## Changes\n## How Has This Been Tested?\n## Related Issues\n\nWhere *Description* is 2–6 sentences, *Changes* is a concise bullet list of the most important diffs, *How Has This Been Tested?* lists the actual commands run (or states `Not tested.`), and *Related Issues* uses `Closes #N` lines or `None.`\n\nGeneral rules: English only; preserve heading wording and ordering verbatim when a template exists; preserve checkbox label text verbatim; no marketing tone."
}
],
// Shared code review guidance (reviewSelection).
// Focuses on correctness bugs, security issues, and simplification
// opportunities. Style, formatting, and naming are intentionally
// excluded — those are enforced by linters and per-repo conventions.
"github.copilot.chat.reviewSelection.instructions": [
{
"text": "Review the selected code in English only. Focus exclusively on three categories — correctness bugs, security issues, and simplification — and ignore style, formatting, and naming.\n\n=== CORRECTNESS ===\nHunt for logic errors a careful reviewer would catch in one sitting:\n- Inverted or wrong conditions (off-by-one, boundary exclusion, wrong comparator)\n- Null / undefined / empty-string dereference on a reachable path (error handler, optional field, cold cache)\n- Missing `await` on an async call; unhandled promise rejection\n- Wrong variable used in copy-pasted code\n- Error silently swallowed in a catch block\n- Bash: unquoted variable expansion that word-splits or glob-expands; missing `set -euo pipefail`; `rm -rf` without a non-empty guard; command substitution that hides exit codes (`$(cmd)` vs `cmd || ...`)\n- TypeScript: non-null assertion (`!`) hiding a real null path; `as` cast that silences a type mismatch; implicit `any` widening; `==` vs `===`\n\n=== SECURITY ===\nFlag only exploitable or credential-leaking issues:\n- Command injection: user-controlled input passed to `exec`, `eval`, template literals in shell, or similar without sanitization\n- Path traversal: user-controlled path segments without normalization/containment\n- Credential / secret written to a log, environment variable dump, or client-side bundle\n- XSS: unsanitized content injected into innerHTML, dangerouslySetInnerHTML, or document.write\n- Insecure default: world-writable file permission, bind to 0.0.0.0 without intent, disabled TLS verification\n- Dependency confusion / supply-chain: package install from an untrusted or unpinned source\n\n=== SIMPLIFICATION ===\nFlag concrete complexity that can be removed without changing behavior:\n- Code that re-implements a utility already present in the codebase or standard library — name the existing alternative\n- Dead code left behind (unreachable branch, unused variable, commented-out block)\n- Derivable state: a variable that is always computable from other state, kept in sync manually\n- Two or more near-identical blocks that could be unified — show the unified form\n\n=== OUTPUT FORMAT ===\nFor each finding: one sentence stating the issue, then one sentence with the concrete fix or the simpler alternative. No bullet-point padding, no summaries, no praise. If there are no findings in a category, omit that category entirely. If there are no findings at all, reply with a single line: 'No issues found.'"
}
]
}
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}
40 changes: 40 additions & 0 deletions src/copilot-dev/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# This file is part of helpers4.
# Copyright (C) 2025 baxyz
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# Runs at BUILD TIME.
# Installs the gh-copilot CLI extension if gh is available.

set -euo pipefail

if [ "$(id -u)" -ne 0 ]; then
echo 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi

INSTALL_CLI="${_BUILD_ARG_INSTALLCLI:-${INSTALLCLI:-true}}"

echo "🔧 Configuring copilot-dev feature..."
echo " Install CLI: ${INSTALL_CLI}"

# ============================================================================
# Install gh copilot CLI extension
# ============================================================================

if [ "${INSTALL_CLI}" = "true" ]; then
if ! command -v gh >/dev/null 2>&1; then
echo " ⚠️ gh CLI not found — skipping gh copilot extension install."
echo " Add github-dev before copilot-dev, or install gh manually."
else
echo " Installing gh copilot extension..."
# --force re-installs if already present (idempotent).
gh extension install github/gh-copilot --force
echo " ✅ gh copilot extension installed: $(gh copilot --version 2>/dev/null || echo 'ok')"
fi
else
echo " Skipping gh copilot CLI install (installCli=false)."
fi

echo ""
echo "🎉 copilot-dev configuration complete!"
Loading