Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Project structure: `cmd/` (entry points), `internal/` (business logic), `magefil

Documentation structure: `docs/user/reference/cli/` (auto-generated CLI docs, regenerated by `mage docs`), `docs/user/reference/config/` (hand-written TOML config reference), `docs/user/how-to/` (workflow guides), `docs/user/explanation/` (conceptual docs).

**Agent skills track tool behavior.** `internal/app/azldev/agentskill/` emits the AI-agent skills and instruction files that describe azldev's CLI, config, and workflows. After a behavioral change (command syntax, flags, config schema, overlay types, workflows), check whether these need updating — see [instructions/agent-skills.instructions.md](instructions/agent-skills.instructions.md).

The TOML config files in `defaultconfigs/` are loaded via `internal/projectconfig/`.

**IMPORTANT**: Code generation runs automatically with build/test commands. `mage generate` (runs `go generate` for each package in parallel) is a prerequisite for building and runs automatically with `mage build` and `mage unit`. `mage docs` rebuilds the binary and updates the JSON schema (`schemas/azldev.schema.json`) and CLI docs (`docs/user/reference/cli/`). Run `mage docs` explicitly after changing config structs or Cobra command descriptions so that checked-in generated files stay current (checked by PR gates).
Expand Down
82 changes: 82 additions & 0 deletions .github/instructions/agent-skills.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
applyTo: "internal/app/azldev/agentskill/**"
description: "How to maintain azldev's emitted AI-agent skills and instruction files. Read before adding or editing a skill, an instruction wrapper, or the emit mechanism in the agentskill package."
---

# Maintaining the emitted agent skills and instruction files

The `agentskill` package is the **single source of truth** for the AI-agent skill and
instruction files that `azldev docs agent install` writes into distro repositories, and
that `azldev docs agent show` (a read-only MCP tool) serves. Everything is embedded in
the binary so the on-disk files, the CLI output, and the MCP response stay version-matched.

For the full architecture — the registries, the redirect-wrapper-vs-full-body rendering,
and how dynamic values are substituted — read the package doc in
[doc.go](../../internal/app/azldev/agentskill/doc.go).

## Model

- **Skills hold the content.** A `Skill` in the `skills` registry
([agentskill.go](../../internal/app/azldev/agentskill/agentskill.go)) pairs a name with a
body template under [content/](../../internal/app/azldev/agentskill/content/). Skills are the
cross-agent medium — served via `docs agent show` / the `docs-agent-show` MCP tool, or inlined
on disk with `--full`.
- **Instruction files are lightweight wrappers.** An `Instruction` in the `instructions` registry
is selected by its `applyTo` glob and only *points at* skills — `SkillPointer{Skill, Purpose}`
renders as "You MUST read the `<skill>` skill `<purpose>`". Put substantive guidance in a
**skill**, never in a wrapper. Do not make a wrapper reference the CLI or MCP tool — those are
unavailable in `--full` installs; naming the skill works in every mode.

## Adding or editing a skill

1. Add a `Skill{Name, Description, bodyTemplate}` to the `skills` registry.
2. Add a `content/<topic>.md.tmpl` body, named for the topic (e.g. `mock.md.tmpl`,
`azldev.md.tmpl`). The front-matter `name` must equal the skill's base name (and its on-disk
directory), lowercase-hyphen, ≤ 64 chars. The `description` drives discovery, ≤ 1024 chars.
3. Write the description inline in the registry entry. The description is the **load gate**
(an agent decides whether to open the skill from it), so **lead with a directive** —
"Read this before <action>; do not <do it> from memory." — then what the skill covers,
then a `Triggers include ...` keyword list. **Avoid a colon-space (`: `) in
descriptions** — it is ambiguous in YAML front matter.
4. Add a content test in
[agentskill_test.go](../../internal/app/azldev/agentskill/agentskill_test.go) (assert the
front-matter `name` and a distinctive, validated phrase). `TestSkillFrontmatterInvariants`
auto-covers the spec limits.

## Adding or editing an instruction wrapper

1. Add an `Instruction{Name, ApplyTo, Description, Title, Intro, Skills}` to the `instructions`
registry. `ApplyTo` may reference bindings (e.g. `{{ .RenderedSpecsDir }}/**/*`) — it is
rendered against `Params` at emit time.
2. `Skills` is a list of `SkillPointer` — name each skill with a short purpose ("to add or change
overlays").
3. A little hand-written prose here is fine, if it helps direct agents to the right skill. Keep it short, and avoid
repeating the skill content.
3. Keep the count-based tests happy: `Files()` emits one file per skill plus one per instruction.

## Content accuracy is the hard part

Distilled content **drifts**. The azurelinux copies these are distilled from are frequently stale.
Before shipping any skill/instruction:

- **Validate every CLI, flag, and config claim against the current code** (command trees under
`internal/app/azldev/cmds/`, config under `internal/projectconfig/`). Do not trust the source you
distilled from. Confirm with `./out/bin/azldev <cmd> --help` and `azldev config generate-schema`.
- Prefer a **drift-guard test** whenever a code enum can back the content. Example:
`TestOverlaysSkillCoversAllOverlayTypes` extracts the overlay-type enum from the jsonschema tag
on `projectconfig.ComponentOverlay.Type` and fails if the skill omits a type.

## Config-resolved bindings

Repo-specific values (lock dir, rendered-specs dir, work dir) are resolved from the target `azldev.toml` in
[cmds/docs/agent.go](../../internal/app/azldev/cmds/docs/agent.go) and degrade to azldev's defaults
when no config is present. To add a binding, extend `Bindings`, resolve it in `resolveBindings`, and
reference it in a template as `{{ .FieldName }}`.

## Before you commit

- `mage unit` and `mage check all` pass.
- `mage docs` produces **no drift** (adding a registry skill/instruction should not change CLI docs,
the JSON schema, or the MCP snapshot).
- Sanity-check emitted output: `./out/bin/azldev docs agent install -o "$(mktemp -d)"` and
`./out/bin/azldev docs agent show --skill <name>`.
165 changes: 165 additions & 0 deletions docs/developer/prds/agent-scaffolding.prd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# PRD: Agent Scaffolding via `azldev docs agent`

Title: PRD: Agent Scaffolding via `azldev docs agent`
Author: Daniel McIlvaney
Created: 2026-07-02
Status: Draft

## Overview

`azldev docs agent` should be able to **initialize a new distro repository with the AI-agent
capabilities that the Azure Linux repo has today** — the skills, instruction files, schema, and MCP
wiring that let coding agents drive `azldev` effectively. `microsoft/azurelinux` is the reference
implementation we distill the generic, non-distro-specific content from.

A v1 exists: `azldev docs agent install` emits a single light-wrapper skill
([.agents/skills/azldev/SKILL.md](internal/app/azldev/agentskill/content/skill-wrapper.md.tmpl)) plus
a path-specific instructions file, and `azldev docs agent show` serves the full skill via a read-only
MCP tool. This PRD generalizes that into a **scaffolder for a distro repo's whole agent system**.

## Problem Statement

Azure Linux has a mature, PRD-governed agent system (`docs/prds/agent-instructions.prd.md` in
`microsoft/azurelinux`): repo-wide `copilot-instructions.md`, nested `AGENTS.md` guardrails,
`.github/instructions/*.instructions.md`, ~13 `.github/skills/skill-*`, prompts, and agents. The
azldev-facing subset of that content — the `build` / `add` / `fix-overlay` / `mock` / `update` skills,
the `comp-toml` / `distro-toml` instruction files, the CLI reference in `copilot-instructions.md`, and
the mirrored `external/schemas/azldev.schema.json` — is **almost entirely azldev-operation knowledge**:
command syntax, flags, semantics, overlay types, and the tool's recommended inner loop. That content:

- **Drifts** every time azldev changes (the azurelinux PRD flags this in "Areas to Revisit Regularly"
and its Schema Reference note).
- Must be **hand-recreated** in every new distro repo that uses azldev.

There is no way to bootstrap a new distro repo with this capability, and no single source of truth for
the azldev-generic portion. The azurelinux PRD explicitly anticipates this feature:

> Open Question #4: "Should azldev have a command to install the resources into other repos?"
> Out of Scope (v1): "Automated regeneration of agent instructions via azldev."

## Goals

1. `azldev docs agent init` scaffolds a new distro repo with the azldev-generic agent substrate.
2. `azldev docs agent update` refreshes that substrate in place, without clobbering repo-authored
editorial content.
3. The generic content is authored/embedded **in azldev** (single source of truth = the binary), so
azurelinux and every new distro consume the same, version-matched material.
4. Content is bound to the **target repo's resolved config** (output dirs, upstream distro, dist tag)
at generation time.

## Non-Goals

- Generating distro **editorial/policy** (e.g. "never install RPMs on the host", hygiene rules,
canonical example components). Those stay human-authored in the target repo.
- Generating non-azldev skills (azurelinux's Koji/AKS ops skills, `azl-diagnose` agent, KQL/metrics).
- Owning the target repo's `azldev.toml` (project config). A repo must already have one — see Decisions.

## Locked Decisions

| # | Decision | Choice |
| --- | --- | --- |
| D1 | Source of truth for generic content | **Distill azurelinux's generic skills/instructions into azldev** (embedded); regenerate azurelinux + new repos from the binary. One source of truth = the tool. |
| D2 | Init scope | **azldev-mechanics substrate only** (skill set + config instructions + CLI reference + schema + MCP configs + version pin). Editorial is left as empty/stub slots for the repo to author. |
| D3 | Config binding | **Resolve bindings from the invoking project's `azldev.toml` when present; otherwise degrade gracefully to azldev's built-in defaults** (no hard requirement). Supersedes the earlier "require config" stance so that `show` / the MCP tool and greenfield repos still emit sensible, default-accurate content. |

Implied: default emit layout flips from `.agents/skills/` to **`.github/skills/`** (matches azurelinux
and Copilot CLI / coding-agent discovery), configurable either way. Skill naming/prefix configurable to
match host convention (azurelinux uses `skill-*`).

## Design

### The ownership boundary

| Emitted by azldev (generated, overwritten on `update`) | Human-owned (never touched) |
| --- | --- |
| Skill set: build / add / fix-overlay / mock / update / render / remove | Distro policy & hygiene rules |
| `comp-toml` / `distro-toml` / spec / rendered-specs `.instructions.md` | Canonical example components |
| `copilot-instructions` azldev section + CLI reference | Non-azldev skills (Koji/AKS), infra agents |
| `azldev.schema.json` (via `config generate-schema`) | Repo-specific prose, links |
| `.vscode/mcp.json` and `.mcp.json` (azldev MCP server registration), `.azldev-version` | — |

### Content sources (why generation beats hand-maintenance)

- **Command syntax / flags / semantics** → the Cobra command tree (already emitted by
[docs markdown](internal/app/azldev/cmds/docs/markdown.go)).
- **Overlay types + required fields** → the JSON schema (`config generate-schema`).
- **Resolved bindings** (output-dir / log-dir / work-dir paths, upstream distro, dist tag) → azldev
resolves these from the target repo's `azldev.toml`, so the "repo-specific" build-output table
azurelinux hand-wrote becomes generated per repo.
- **Recommended inner loop / workflow narrative** → authored once in azldev's embedded templates
(distilled from azurelinux's skills), rendered with the above.

### Composition / no-clobber

- Every generated file carries a "generated by azldev docs agent — do not hand-edit" marker (in-body
for `SKILL.md`, since YAML frontmatter must be first).
- `update` overwrites only generated files. Editorial lives in **separate** files the tool never
writes (e.g. a repo-authored `AGENTS.md` that links to the generated skills; a policy
`.instructions.md`).
- Open question: whether to also support marker-delimited regions inside a shared file (e.g. an
azldev section inside a repo-owned `copilot-instructions.md`) vs. keeping generated content in
discrete files. Prefer discrete files first (simplest, safest).

### Command surface

- `azldev docs agent init [-o dir]` — scaffold the substrate into a repo (requires `azldev.toml`).
- `azldev docs agent update [-o dir]` — refresh generated files in place.
- `azldev docs agent show --skill <name>` — print a skill (read-only MCP tool). Parameterized by skill name
once the set is multiple (per the earlier multi-skill decision).
- `azldev docs agent check [-o dir]` — (proposed) drift gate for CI: verify committed generated files
match what the pinned azldev version would emit. Closes the azurelinux PRD's sync concern.
- Layout/config flags: `--skills-dir` / `--layout` (`.github` | `.agents`), `--skill-prefix`.

### MCP

- The read-only `docs-agent-show` tool (already implemented) is how agents load skill bodies on
demand; the emitted wrappers reference it. `init` also writes `.vscode/mcp.json` for VS Code and
`.mcp.json` for Copilot CLI, registering the `azldev advanced mcp` server so the tool is available.
Read-only annotation enables auto-approval, which directly addresses azurelinux's "tool approvals
are difficult" note in `DEVELOPING.md`.

## Relationship to v1 (already shipped)

v1 = single `azldev` skill wrapper + one instructions file + read-only `docs-agent-show` tool, emitted
to `.agents/skills/`. This PRD generalizes it: a skill **set**, `init`/`update`/`check`, config-resolved
bindings, `.github/` default layout, schema + `mcp.json` emission, and a composition model. The v1
shared package [internal/app/azldev/agentskill](internal/app/azldev/agentskill/agentskill.go) is the
natural home for the skill registry (the "small lift" refactor already scoped: singletons → `[]Skill`).

## Companion Workstream: rework azurelinux

Once azldev emits the substrate, azurelinux is reworked as a **two-way port**:

1. Lift azurelinux's generic skill/instruction content up into azldev's embedded templates (they are the
distilled source).
2. Have azurelinux re-consume it via `azldev docs agent update`, keeping only editorial deltas in
files azldev never touches.
3. Regenerate `external/schemas/azldev.schema.json`; add Phase 6 `.vscode/mcp.json` and `.mcp.json`.

## Implementation Plan (proposed)

- **Phase 1 — Skill registry**: refactor `agentskill` singletons → `[]Skill`; parameterize `show`;
configurable layout + naming. (Foundational; independent of content.)
- **Phase 2 — Config-resolved bindings**: inject resolved paths (lock dir, rendered-specs
dir) from the invoking project's `azldev.toml` into templates, degrading to azldev's built-in
defaults when no configuration is available. (Upstream-distro / dist-tag bindings follow in
Phase 3 alongside the skills that consume them.)
- **Phase 3 — Content distillation**: port azurelinux's build / add / fix-overlay / mock / update
skill content + `comp-toml` / `distro-toml` instructions into embedded templates (real content
replaces the pie placeholder).
- **Phase 4 — Substrate emission**: schema (`config generate-schema`), `.vscode/mcp.json`, `.mcp.json`,
`.azldev-version`, `copilot-instructions`/`AGENTS` scaffold; `init` vs `update`.
- **Phase 5 — Drift gate**: `docs agent check` for CI.
- **Phase 6 — azurelinux rework**: the companion port above.

## Open Questions

1. **Composition mechanism**: discrete files only, or also marker-delimited regions inside repo-owned
files? (Lean: discrete first.)
2. **Skill naming**: adopt azurelinux's `skill-*`, or an `azldev-*` namespace, as the emitted default?
3. **Prompts / agents**: azurelinux's prompts (`.prompt.md`, VS Code only) and agents (`.agent.md`) —
in scope for generation, or left as repo-authored orchestration over the generated skills?
4. **Editorial stubs**: D2 says "empty/stub slots" — how much stub scaffolding (empty policy
`AGENTS.md`? a TODO checklist?) vs. nothing at all?
5. **`check` in CI**: exact contract for "matches the pinned azldev version" given the version stamp is
embedded in output.
2 changes: 1 addition & 1 deletion docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Add a Component](./how-to/add-component.md) — import or create a new package
- [Build a Component](./how-to/build-component.md) — build RPMs from component definitions
- [Build an Image](./how-to/build-image.md) — build and boot Azure Linux images
- [Set Up AI Coding Agents](./how-to/set-up-ai-agents.md) — emit agent skill and instruction files

## Explanation

Expand All @@ -29,4 +30,3 @@
### CLI Commands

Auto-generated from `azldev --help`. See [reference/cli/](./reference/cli/azldev.md) for per-command documentation.

80 changes: 80 additions & 0 deletions docs/user/how-to/set-up-ai-agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# How To: Set Up AI Coding Agents

This guide shows how to use `azldev docs agent` to teach AI coding agents (such as
GitHub Copilot) how to work with azldev in your repository.

## Background

azldev can emit two kinds of agent-facing files:

- **Agent Skills** under `.agents/skills/` — on-demand capabilities,
in the tool-neutral [Agent Skills](https://agentskills.io/) format, that agents
load when a task involves azldev.
- **Path-specific instructions** under `.github/instructions/` that apply automatically
to azldev configuration and generated output.

Every emitted file identifies itself as generated. Keep repository-specific policy and
editorial guidance in separate files rather than editing emitted files.

The authoritative skill content ships inside the azldev binary. By default the
emitted `SKILL.md` is a light wrapper that points agents at a read-only MCP tool
(`docs-agent-show`) so they always load the guidance that matches the installed
azldev version.

## Emit the Agent Files

Run the command from the root of the target repository:

```bash
azldev docs agent install
```

Or point it at another repository:

```bash
azldev docs agent install -o ../other-repo
```

Use `--dry-run` / `-n` to preview which files would be written without changing
anything:

```bash
azldev -n docs agent install
```

## Wire Up the MCP Server (Recommended)

The emitted wrapper files reference the read-only `docs-agent-show` MCP tool. To
make that tool available to agents, register the azldev MCP server with your agent
tooling. The server is started with:

```bash
azldev advanced mcp
```

With the server configured, an agent can call `docs-agent-show` (a read-only tool
that most agent frameworks can auto-approve) to load the full azldev skill on
demand.

## Emit Without the MCP Server

If the target environment will not run the azldev MCP server, inline the full skill
into `SKILL.md` instead of the light wrapper:

```bash
azldev docs agent install --full
```

## Preview the Skill

Run without `--skill` to list the available skills:

```bash
azldev docs agent show
```

To print a full skill document to stdout — the same content the MCP tool serves — run:

```bash
azldev docs agent show --skill azldev
```
1 change: 1 addition & 0 deletions docs/user/reference/cli/azldev_docs.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading