From c1ffac36f320cb6d03ae92b65a98704292b4f68a Mon Sep 17 00:00:00 2001 From: baxyz Date: Wed, 24 Jun 2026 14:02:27 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat(copilot-dev):=20=E2=9C=A8=20add=20READ?= =?UTF-8?q?ME=20and=20devcontainer=20feature=20configuration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 8 +-- src/copilot-dev/README.md | 68 +++++++++++++++++++++++ src/copilot-dev/devcontainer-feature.json | 64 +++++++++++++++++++++ src/copilot-dev/install.sh | 40 +++++++++++++ 4 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 src/copilot-dev/README.md create mode 100644 src/copilot-dev/devcontainer-feature.json create mode 100755 src/copilot-dev/install.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 9fdb413..1f94122 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,12 +2,12 @@ "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", diff --git a/src/copilot-dev/README.md b/src/copilot-dev/README.md new file mode 100644 index 0000000..d808280 --- /dev/null +++ b/src/copilot-dev/README.md @@ -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 diff --git a/src/copilot-dev/devcontainer-feature.json b/src/copilot-dev/devcontainer-feature.json new file mode 100644 index 0000000..e0b7a76 --- /dev/null +++ b/src/copilot-dev/devcontainer-feature.json @@ -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: `(): `. 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 `` with a value from `conventionalCommits.scopes`): `feat(): ✨ add `, `fix(): 🐛 handle `, `docs(): 📝 update `, `refactor(): ♻️ simplify `, `chore(): ⬆️ bump `." + } + ], + // 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: `(): `. 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" + ] +} diff --git a/src/copilot-dev/install.sh b/src/copilot-dev/install.sh new file mode 100755 index 0000000..805db70 --- /dev/null +++ b/src/copilot-dev/install.sh @@ -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!" From 965bf5905abc54b8d0bf604d6fc3847fda594031 Mon Sep 17 00:00:00 2001 From: baxyz Date: Wed, 24 Jun 2026 14:03:02 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(github-dev):=20=F0=9F=90=9B=20update=20?= =?UTF-8?q?README=20and=20feature=20configuration=20for=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/github-dev/README.md | 79 ++++++++++-------------- src/github-dev/devcontainer-feature.json | 41 ++---------- 2 files changed, 36 insertions(+), 84 deletions(-) diff --git a/src/github-dev/README.md b/src/github-dev/README.md index 3d6dab6..b608e1b 100644 --- a/src/github-dev/README.md +++ b/src/github-dev/README.md @@ -1,33 +1,35 @@ # GitHub Development Environment (github-dev) -Installs the **GitHub CLI (`gh`)** and adds the essential GitHub VS Code extensions (Copilot Chat, Pull Requests & Issues, GitHub Actions, RemoteHub). Automatically authenticates `gh` if a token is available in the environment. +Installs the **GitHub CLI (`gh`)** and the essential GitHub VS Code **platform** extensions (Pull Requests & Issues, GitHub Actions, RemoteHub). Automatically authenticates `gh` if a token is available in the environment. + +> **AI assistant:** For GitHub Copilot Chat and the shared commit-message / PR-description generation instructions, use [`copilot-dev`](../copilot-dev) alongside this feature. ## Usage -```json +```jsonc { - "features": { - "ghcr.io/helpers4/devcontainer/github-dev:1": {} - } + "features": { + "ghcr.io/helpers4/devcontainer/github-dev:1": {} + } } ``` -Combine with `essential-dev` for a complete development environment: +With Copilot: -```json +```jsonc { - "features": { - "ghcr.io/helpers4/devcontainer/essential-dev:1": {}, - "ghcr.io/helpers4/devcontainer/github-dev:1": {} - } + "features": { + "ghcr.io/helpers4/devcontainer/github-dev:1": {}, + "ghcr.io/helpers4/devcontainer/copilot-dev:1": {} + } } ``` ## Options -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `ghVersion` | string | `latest` | GitHub CLI version to install (e.g. `2.50.0` or `latest`) | +| Option | Type | Default | Description | +|--------------|--------|----------|-----------------------------------------------------------| +| `ghVersion` | string | `latest` | GitHub CLI version to install (e.g. `2.50.0` or `latest`) | ## What Gets Installed @@ -51,32 +53,12 @@ gh repo clone org/repo # Clone a repository ### VS Code Extensions -| Extension | Purpose | -|-----------|---------| -| `github.copilot-chat` | AI chat assistant and code completions (replaces the deprecated `github.copilot` extension) | -| `github.vscode-pull-request-github` | PR and issue management inside VS Code | -| `github.vscode-github-actions` | GitHub Actions workflow editor with validation | -| `github.remotehub` | Browse remote GitHub repositories without cloning | -| `ms-vscode.remote-repositories` | Open and work on remote repositories without cloning (companion to RemoteHub) | - -## Copilot Chat commit-message guidance - -This feature ships a shared **Copilot Chat commit-message instruction** for the helpers4 organization (Conventional Commits + gitmoji). It is injected via `github.copilot.chat.commitMessageGeneration.instructions`. - -The instruction does **not** hardcode the list of allowed scopes — instead, it tells Copilot Chat to look up the workspace setting `conventionalCommits.scopes` defined in each repo's `.vscode/settings.json`. This keeps a single source of truth per repo (used by both the *Conventional Commits* extension UI and the AI commit-message generator). - -## Copilot Chat pull-request title & description guidance - -This feature also ships a shared instruction for **PR titles and descriptions** via `github.copilot.chat.pullRequestDescriptionGeneration.instructions`: - -- The PR **title** follows the same Conventional Commits + gitmoji format as commit messages, and reads its scope list from the same `conventionalCommits.scopes` workspace setting. -- The PR **body** is **repo-agnostic**: Copilot looks for the active repository's own template (`.github/PULL_REQUEST_TEMPLATE.md`, `pull_request_template.md`, files under `.github/PULL_REQUEST_TEMPLATE/`, etc.) and reproduces its sections, headings, and checkbox labels verbatim — ticking checkboxes based on the diff. If the workspace also bundles an org-level `.github` repo as a sibling folder, that template is used as a fallback. If no template is found anywhere, a minimal default structure (Description / Changes / How Has This Been Tested? / Related Issues) is used. - -This means external users adopting this feature get the format guarantees without having to adopt helpers4's specific template. - -### Override or disable - -Both `commitMessageGeneration.instructions` and `pullRequestDescriptionGeneration.instructions` are array settings — VS Code merges entries from User, Workspace, and feature-injected sources. To override, add your own entry in your User or Workspace settings; to disable a helpers4 default, set the corresponding array explicitly to `[]` in your settings (this clears feature-injected items). +| Extension | Purpose | +|--------------------------------------|---------------------------------------------------------------------------------| +| `github.vscode-pull-request-github` | PR and issue management inside VS Code | +| `github.vscode-github-actions` | GitHub Actions workflow editor with validation | +| `github.remotehub` | Browse remote GitHub repositories without cloning | +| `ms-vscode.remote-repositories` | Open and work on remote repositories without cloning (companion to RemoteHub) | ## Authentication @@ -86,11 +68,11 @@ Set `GH_TOKEN` (or `GITHUB_TOKEN`) in your environment and `gh` will authenticat **Local / DevPod:** add to your `devcontainer.json`: -```json +```jsonc { - "remoteEnv": { - "GH_TOKEN": "${localEnv:GH_TOKEN}" - } + "remoteEnv": { + "GH_TOKEN": "${localEnv:GH_TOKEN}" + } } ``` @@ -112,7 +94,8 @@ gh auth login ## Version History -- **v1.0.3**: Add shared Copilot Chat pull-request title & description instruction. PR titles follow the same Conventional Commits + gitmoji format as commits; PR bodies follow the **active repository's own** PR template when available (with sibling org `.github` repo as fallback), and gracefully fall back to a minimal default otherwise — repo-agnostic, no helpers4-specific assumptions. -- **v1.0.2**: Add shared Copilot Chat commit-message instruction (Conventional Commits + gitmoji) for the helpers4 org. The instruction references each repo's `conventionalCommits.scopes` workspace setting instead of hardcoding scopes, removing duplication across the 6 helpers4 repos. -- **v1.0.1**: Remove deprecated `github.copilot` extension (superseded by `github.copilot-chat`). Add `ms-vscode.remote-repositories` (companion to RemoteHub). -- **v1.0.0**: Initial release. gh CLI, Copilot, Copilot Chat, Pull Requests & Issues, GitHub Actions, RemoteHub extensions. Auto-auth via `GH_TOKEN`/`GITHUB_TOKEN`. Extracted from `essential-dev`. +- **v1.0.5**: Extract Copilot Chat extension and generation instructions into the standalone `copilot-dev` feature (breaking change — add `copilot-dev` to your `devcontainer.json` if you relied on Copilot being bundled here). +- **v1.0.4**: Add shared Copilot Chat pull-request title & description instruction. +- **v1.0.3**: Add shared Copilot Chat commit-message instruction (Conventional Commits + gitmoji). +- **v1.0.2**: Remove deprecated `github.copilot` extension. Add `ms-vscode.remote-repositories`. +- **v1.0.0**: Initial release. diff --git a/src/github-dev/devcontainer-feature.json b/src/github-dev/devcontainer-feature.json index 90f01df..c6f054c 100644 --- a/src/github-dev/devcontainer-feature.json +++ b/src/github-dev/devcontainer-feature.json @@ -1,11 +1,11 @@ { "id": "github-dev", - "version": "1.0.4", + "version": "1.0.5", "name": "GitHub Development Environment", - "description": "GitHub CLI (gh) and GitHub VS Code extensions (Copilot Chat, Pull Requests & Issues, GitHub Actions, RemoteHub). Centralizes all GitHub tooling in one feature, including shared Copilot Chat instructions for commit messages and pull-request titles/descriptions (Conventional Commits + gitmoji + the active repo's PR template).", + "description": "GitHub CLI (gh) and GitHub VS Code platform extensions (Pull Requests & Issues, GitHub Actions, RemoteHub). For Copilot Chat and AI generation instructions use the copilot-dev feature.", "documentationURL": "https://github.com/helpers4/devcontainer/tree/main/src/github-dev", "licenseURL": "https://github.com/helpers4/devcontainer/blob/main/LICENSE", - "keywords": ["helpers4", "github", "gh", "copilot", "cli"], + "keywords": ["helpers4", "github", "gh", "cli"], "options": { "ghVersion": { "type": "string", @@ -20,39 +20,8 @@ "github.vscode-pull-request-github", "github.vscode-github-actions", "github.remotehub", - "ms-vscode.remote-repositories", - // AI Assistant - "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: `(): `. 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 `` with a value from `conventionalCommits.scopes`): `feat(): ✨ add `, `fix(): 🐛 handle `, `docs(): 📝 update `, `refactor(): ♻️ simplify `, `chore(): ⬆️ bump `." - } - ], - // 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: `(): `. 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." - } - ] - } + "ms-vscode.remote-repositories" + ] } }, "installsAfter": [ From 478983f12fc725a7223fdf258c04691e24a7c3e0 Mon Sep 17 00:00:00 2001 From: baxyz Date: Wed, 24 Jun 2026 14:03:14 +0000 Subject: [PATCH 3/3] =?UTF-8?q?feat(mistral-dev):=20=E2=9C=A8=20add=20mist?= =?UTF-8?q?ral-dev=20feature=20with=20installation=20script=20and=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 2 + src/mistral-dev/README.md | 70 ++++++++++ src/mistral-dev/devcontainer-feature.json | 48 +++++++ src/mistral-dev/install.sh | 160 ++++++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 src/mistral-dev/README.md create mode 100644 src/mistral-dev/devcontainer-feature.json create mode 100755 src/mistral-dev/install.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f94122..621e5e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,8 @@ "essential-dev", "git-absorb", "github-dev", + "helpers4-common", + "mistral-dev", "package-auto-install", "peon-ping", "pnpm-store", diff --git a/src/mistral-dev/README.md b/src/mistral-dev/README.md new file mode 100644 index 0000000..d4f710d --- /dev/null +++ b/src/mistral-dev/README.md @@ -0,0 +1,70 @@ +# Mistral Vibe Development Environment (mistral-dev) + +Installs the [Mistral Vibe](https://docs.mistral.ai/vibe/code/overview) IDE extension +across supported editors so every devcontainer gets AI-assisted coding powered +by Mistral out of the box. Credentials and configuration stored in `~/.vibe/` +on the host are linked into the container — they persist across all rebuilds, +including `--no-cache`. + +## Example Usage + +```jsonc +{ + "features": { + "ghcr.io/helpers4/devcontainer/mistral-dev:1": {} + } +} +``` + +With the optional CLI: + +```jsonc +{ + "features": { + "ghcr.io/helpers4/devcontainer/mistral-dev:1": { + "installCli": true + } + } +} +``` + +## Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `username` | string | `node` | Container user that receives the `~/.vibe` symlink. | +| `installCli` | boolean | `false` | Install the `vibe` CLI. Requires Python 3.12+ or `uv` in the container. | + +## IDE support + +| Editor | Status | ID | +|--------|--------|----| +| VS Code | ✅ | `mistralai.mistral-vibe-code` | +| Cursor | ✅ | `mistralai.mistral-vibe-code` (same registry as VS Code) | + +## How it works + +### Extension +The feature declares the `mistralai.mistral-vibe-code` extension via the +`customizations` field in `devcontainer-feature.json`. The devcontainer runtime +installs it automatically. + +### Credential persistence +At build time, `install.sh` generates `/usr/local/share/mistral-dev/setup-credentials.sh` +with the target user's home directory baked in. At every container start +(`postStartCommand`), that script replaces `~/.vibe` with a symlink pointing to +the host bind-mounted path `/mnt/h4vibe` (sourced from `~/.vibe` on the host). + +This means: +- Credentials survive container rebuilds (including `--no-cache`). +- First-time auth inside the container writes back to the host automatically. +- `VIBE_HOME` is not required — the symlink is transparent to Mistral Vibe. + +### CLI (optional) +When `installCli: true`, the `vibe` command is installed at build time via `uv` +(preferred) or `pip`. See [Mistral Vibe CLI docs](https://docs.mistral.ai/vibe/code/cli/install-setup). + +## OS and Architecture Support + +- **OS:** Linux (Debian/Ubuntu-based images) +- **Architectures:** amd64, arm64 diff --git a/src/mistral-dev/devcontainer-feature.json b/src/mistral-dev/devcontainer-feature.json new file mode 100644 index 0000000..753a66c --- /dev/null +++ b/src/mistral-dev/devcontainer-feature.json @@ -0,0 +1,48 @@ +{ + "id": "mistral-dev", + "version": "1.0.0", + "name": "Mistral Vibe Development Environment", + "description": "Installs the Mistral Vibe IDE extension (mistralai.mistral-vibe-code) for VS Code and Cursor. Mounts ~/.vibe from the host via a directory symlink — credentials and config persist across all rebuilds.", + "documentationURL": "https://github.com/helpers4/devcontainer/tree/main/src/mistral-dev", + "licenseURL": "https://github.com/helpers4/devcontainer/blob/main/LICENSE", + "keywords": [ + "helpers4", + "mistral", + "vibe", + "ai", + "vscode" + ], + "options": { + "username": { + "type": "string", + "default": "node", + "description": "Username in the container that should receive the Mistral Vibe credentials symlink." + }, + "installCli": { + "type": "boolean", + "default": false, + "description": "Also install the Mistral Vibe CLI (vibe command). Requires Python 3.12+ and pip in the container." + } + }, + "mounts": [ + { + // Neutral staging path — install.sh resolves the correct target home at build + // time and bakes it into the generated setup-credentials.sh via printf %q. + "source": "${localEnv:HOME}/.vibe", + "target": "/mnt/h4vibe", + "type": "bind" + } + ], + "postStartCommand": "/usr/local/share/mistral-dev/setup-credentials.sh", + "customizations": { + "vscode": { + // Works for both VS Code and Cursor (same extension registry). + "extensions": [ + "mistralai.mistral-vibe-code" + ] + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/src/mistral-dev/install.sh b/src/mistral-dev/install.sh new file mode 100755 index 0000000..76de870 --- /dev/null +++ b/src/mistral-dev/install.sh @@ -0,0 +1,160 @@ +#!/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 — bind mounts are NOT available yet. +# Resolves the target user's home directory and generates the runtime +# credentials script with TARGET_HOME baked in. + +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:-false}}" + +# Bootstrap helpers4 shared library. helpers4-common installs it; if running +# standalone (e.g. devcontainer features test), create it inline so the feature +# is self-contained without a GHCR pull. +if [ ! -f /usr/local/share/helpers4/common.sh ]; then + mkdir -p /usr/local/share/helpers4 + cat > /usr/local/share/helpers4/common.sh << 'H4_COMMON' +# shellcheck shell=bash +h4_detect_user() { + USERNAME="${USERNAME:-${_REMOTE_USER:-automatic}}" + if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + local _uid1000 + _uid1000="$(awk -v val=1000 -F: '$3==val{print $1; exit}' /etc/passwd 2>/dev/null || true)" + local candidate + for candidate in vscode node codespace "${_uid1000}"; do + if [ -n "${candidate}" ] && id -u "${candidate}" >/dev/null 2>&1; then + USERNAME="${candidate}"; break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root + elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root + fi + export USERNAME +} +h4_resolve_home() { + if [ "${USERNAME}" = "root" ]; then + USER_HOME=/root + else + USER_HOME="$(getent passwd "${USERNAME}" 2>/dev/null | cut -d: -f6)" + [ -n "${USER_HOME}" ] || USER_HOME="/home/${USERNAME}" + fi + export USER_HOME +} +h4_apt_update() { + if [ "$(find /var/lib/apt/lists -maxdepth 1 \( -name '*.lz4' -o -name '*.gz' \) 2>/dev/null | wc -l)" = "0" ]; then + apt-get update -y -q + fi +} +h4_ensure_packages() { + local missing=() pkg + for pkg in "$@"; do dpkg -s "${pkg}" >/dev/null 2>&1 || missing+=("${pkg}"); done + if [ "${#missing[@]}" -gt 0 ]; then + h4_apt_update + apt-get install -y -q --no-install-recommends "${missing[@]}" + fi +} +H4_COMMON +fi +# shellcheck source=/dev/null +. /usr/local/share/helpers4/common.sh + +# USERNAME is injected by the devcontainer CLI from the 'username' feature option. +# h4_detect_user falls back to UID-1000 candidate or root when not explicitly set. +h4_detect_user +h4_resolve_home + +echo "🔧 Configuring mistral-dev feature..." +echo " Username: ${USERNAME}" +echo " Home: ${USER_HOME}" +echo " Install CLI: ${INSTALL_CLI}" + +# ============================================================================ +# 1. Generate the runtime credentials script with TARGET_HOME baked in. +# ============================================================================ +# Generating rather than copying means postStartCommand always targets the +# correct user's home regardless of which user the container runtime invokes +# the script as. + +SCRIPT="/usr/local/share/mistral-dev/setup-credentials.sh" +mkdir -p "$(dirname "${SCRIPT}")" + +{ + cat << 'HEADER' +#!/usr/bin/env bash +# This file is part of helpers4. +# Copyright (C) 2025 baxyz +# SPDX-License-Identifier: LGPL-3.0-or-later +# +# Runs at container START (postStartCommand) — bind mounts are available. +# Replaces TARGET_HOME/.vibe with a symlink to the host-mounted directory +# so credentials and all Mistral Vibe config persist across rebuilds. +set -euo pipefail +HEADER + printf 'TARGET_HOME=%q\n' "${USER_HOME}" +} > "${SCRIPT}" + +cat >> "${SCRIPT}" << 'EOF' + +STAGED="/mnt/h4vibe" +TARGET="${TARGET_HOME}/.vibe" + +if [ ! -d "${STAGED}" ]; then + echo "[mistral-dev] ERROR: ${STAGED} is not mounted — cannot link ~/.vibe" >&2 + exit 1 +fi + +rm -rf "${TARGET}" +ln -sf "${STAGED}" "${TARGET}" +echo "[mistral-dev] ~/.vibe linked to host — credentials persist across rebuilds." +EOF + +chmod +x "${SCRIPT}" +echo " ✅ Installed ${SCRIPT}" + +# ============================================================================ +# 2. Optionally install the Mistral Vibe CLI. +# ============================================================================ + +if [ "${INSTALL_CLI}" = "true" ]; then + echo "" + echo "Installing Mistral Vibe CLI..." + + # Prefer uv (fast, self-contained); fall back to pip if uv is absent. + if command -v uv >/dev/null 2>&1; then + UV_TOOL_BIN_DIR=/usr/local/bin uv tool install mistral-vibe + echo " ✅ vibe installed via uv" + elif command -v pip3 >/dev/null 2>&1 || command -v pip >/dev/null 2>&1; then + PIP_CMD="$(command -v pip3 2>/dev/null || command -v pip)" + # Ensure Python 3.12+ — Vibe requires it. + PYTHON_CMD="$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true)" + if [ -z "${PYTHON_CMD}" ]; then + echo " ⚠️ Python not found — skipping Vibe CLI install. Install Python 3.12+ and re-run pip install mistral-vibe." >&2 + else + PY_VER="$("${PYTHON_CMD}" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" + PY_MAJOR="${PY_VER%%.*}" + PY_MINOR="${PY_VER##*.}" + if [ "${PY_MAJOR}" -lt 3 ] || { [ "${PY_MAJOR}" -eq 3 ] && [ "${PY_MINOR}" -lt 12 ]; }; then + echo " ⚠️ Python ${PY_VER} found but Vibe CLI requires 3.12+ — skipping CLI install." >&2 + else + "${PIP_CMD}" install --quiet mistral-vibe + echo " ✅ vibe installed via pip (Python ${PY_VER})" + fi + fi + else + echo " ⚠️ Neither uv nor pip found — skipping Vibe CLI install." >&2 + echo " To install manually: curl -LsSf https://mistral.ai/vibe/install.sh | bash" >&2 + fi +fi + +echo "" +echo "🎉 mistral-dev configuration complete!"