Skip to content
Open
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
118 changes: 118 additions & 0 deletions .github/skills/app-graph/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
name: app-graph
description: >
Generate an interactive application graph visualization from a Radius
app.bicep file. Use when asked to visualize a Radius application,
generate an app graph, build an app graph, render the graph for an app,
show the application graph, or create an interactive graph. Produces
a self-contained HTML viewer (Cytoscape + dagre) that mirrors the
radius-project/github-extension graph renderer, opens it in the default
browser, and emits an inline mermaid preview in chat.
---

# Radius Application Graph

Use this skill to render an interactive application graph from a Radius
`app.bicep` file. The skill compiles the Bicep, invokes `rad graph build`,
and produces a single self-contained `app-graph.html` whose renderer is
ported verbatim from `radius-project/github-extension`
(`src/content/graph-renderer.ts` + `src/content/graph-navigation.ts`).
Comment on lines +8 to +19

## Output Format

Your entire visible response must follow this exact sequence. No extra
headings, no analysis preamble.

1. Say: I will generate an application graph for `<app-name>`.
2. Show exactly these lines as a single blockquote:
> Compile app.bicep with the bicep CLI.
> Build the static graph artifact with `rad graph build`.
> Render the interactive HTML viewer.
> Generate an inline mermaid preview.
3. Say: Here is an inline preview of the graph:
4. Output a single ```mermaid fenced code block matching the rules in
[mermaid.md](references/mermaid.md).
5. Say: A full interactive viewer has been written to `./app-graph.html`.
6. Say: Opening it in your default browser now.
7. Open the file (see Internal Workflow step 6).
8. Say: Click any node in the viewer to see source-code and
app-definition links.

That is the COMPLETE chat response.

## Internal Workflow (do NOT show these steps to the user)

1. Locate the Bicep file. Default search order: `./app.bicep`,
`./.radius/app.bicep`. If neither exists, ask the user for the path.
2. Verify the `rad` CLI is available and supports `rad graph build`.
Run `rad graph build --help`. If the subcommand is missing, instruct
the user to install or build `rad` from
`radius-project/radius` (`features/radius-graph` branch until the
change merges to `main`). Do NOT auto-build.
3. Verify the `bicep` CLI is on PATH. Try `bicep --version`, then
`az bicep version` as fallback. If neither works, instruct the user
to install Bicep and abort.
4. Run `rad graph build --bicep <file> --output ./.radius/static/app.json`.
The CLI compiles Bicep, parses resources/connections, computes
diff hashes, and writes the `StaticGraphArtifact` JSON.
Read [artifact-path.md](references/artifact-path.md) for details.
5. Read the JSON, then render the HTML viewer:
a. Read [`template/app-graph.html.tmpl`](template/app-graph.html.tmpl).
b. Replace the literal token `__GRAPH_DATA__` with the file
contents of `app.json` (substitute as a JSON literal — do NOT
wrap in quotes).
Comment on lines +61 to +63
c. Write the result to `./app-graph.html`.
6. Open the HTML file in the user's default browser:
- Windows: `Start-Process .\app-graph.html`
- macOS: `open ./app-graph.html`
- Linux: `xdg-open ./app-graph.html`
7. Emit the mermaid preview per [mermaid.md](references/mermaid.md). One
node per resource, edges from each resource's `Outbound` connections
only when the target resource is also present, node text =
`<name><br/><shortType>`, no diff coloring (single-graph mode).

## Renderer Conventions

Read [rendering.md](references/rendering.md) for the exact Cytoscape +
dagre options, the Primer color tables, and the edge construction rule.
Read [visual-style.md](references/visual-style.md) for the resource-type
icon + color palette used to theme nodes and the legend in single-graph
mode.

## Schema

Read [schema.md](references/schema.md) for the `StaticGraphArtifact` and
`ApplicationGraphResource` shape consumed by the HTML viewer.

## Validation Checklist

Before saying "Opening it in your default browser now", verify ALL:

- [ ] `./app-graph.html` exists and is non-empty.
- [ ] The JSON substituted into the template is valid (parses).
- [ ] Every resource in `application.resources` has `id`, `name`, `type`.
- [ ] The mermaid block only references resource IDs that also appear
as mermaid node declarations (no dangling edges).
- [ ] The opener command used matches the host OS.

## Guardrails

- The renderer in `template/app-graph.html.tmpl` is a port of
`graph-renderer.ts` + `graph-navigation.ts`. The layout, popup
behavior, edge rule, and Primer color tables MUST stay verbatim. The
resource-type icon/color palette ([visual-style.md](references/visual-style.md))
is an additive extension — extend it for new resource types instead
of rewriting the renderer.
- Do NOT inline a screenshot in chat in place of the mermaid block — the
user wants a structural preview, not a rasterized one.
- Do NOT push to or create any orphan branch from this skill. The
`--orphan-branch` flag is reserved for the CI workflow.
- Do NOT prompt the user before opening the HTML — the Output Format
already announces the open step.
- If `rad graph build` fails, surface the stderr verbatim and stop.
Do NOT attempt to construct the JSON manually.
- The HTML viewer is single-graph only. Diff coloring is preserved in
the ported code but unused; do NOT add UI to load a second artifact.
In single-graph mode every node uses the per-resource-type fill +
border + icon from `TYPE_STYLES`; the diff palette is reserved for
when a future diff mode lands.
53 changes: 53 additions & 0 deletions .github/skills/app-graph/references/artifact-path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Building the Graph Artifact Locally

The `rad graph build` subcommand has two output modes:

1. **Local file** (this skill's mode). Default output path is
`.radius/static/app.json`. Override with `--output <path>`.
2. **Orphan branch** (CI mode, NOT used by this skill). Commits the JSON
to `{source-branch}/app.json` on a configurable orphan branch.

## Invocation

```bash
rad graph build --bicep ./app.bicep --output ./.radius/static/app.json
```

`--bicep` defaults to `app.bicep` and `--output` defaults to
`.radius/static/app.json`, so the bare command also works when the
file is named `app.bicep` in the current directory:

```bash
rad graph build
```

## Prerequisites

- `bicep` CLI on PATH (or `az bicep` as fallback). `rad graph build`
shells out to `bicep build --outfile <tmp>` to compile to ARM JSON
before parsing.
- `rad` CLI from `radius-project/radius` at a ref that contains the
`graph build` subcommand. Until the feature merges to `main`, build
from `features/radius-graph`:

```bash
git clone -b features/radius-graph https://github.com/radius-project/radius
cd radius
go build -o rad ./cmd/rad
```

## Output shape

The output is a `StaticGraphArtifact` (see [schema.md](schema.md)).

## Errors

If `rad graph build` exits non-zero, surface stderr verbatim and stop.
Common failures:

- `bicep build failed` — Bicep CLI not installed or `app.bicep` has
compile errors.
- `compiling Bicep file` — the Bicep file path is wrong or unreadable.
- `building static graph` — the compiled ARM JSON is missing required
Radius resource metadata; usually means the input Bicep is not a
Radius application.
35 changes: 35 additions & 0 deletions .github/skills/app-graph/references/mermaid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Inline Mermaid Preview

The skill emits a mermaid `flowchart` block in chat alongside the full
HTML viewer. It is a structural preview only — no diff coloring, no
popup, no zoom. The full Cytoscape viewer is the source of truth.

## Rules

1. Use `flowchart TD` (top-down).
2. One node per resource in `application.resources`.
3. Node id = a sanitized form of `resource.name` (replace any char
matching `/[^A-Za-z0-9_]/` with `_`). If two resources collide,
append `_2`, `_3`, ...
4. Node label = `<name><br/><shortType>` where
`shortType = resource.type.split('/').pop()`.
5. Use rectangle shape: `id["label"]`.
6. Edge rule (mirrors the HTML renderer): for every resource, iterate
`connections` and emit `source --> target` for each connection where
`direction === 'Outbound'` AND the target `id` is present in
`application.resources`. Use the same sanitized ids.
Comment on lines +13 to +20
7. Do NOT add a legend, do NOT add classDefs, do NOT add diff styling.

## Example

For an application with two resources `frontend` (a container) connected
to `db` (a postgres database), emit:

```mermaid
flowchart TD
frontend["frontend<br/>containers"]
db["db<br/>postgreSqlDatabases"]
frontend --> db
```

That's the entire mermaid block.
145 changes: 145 additions & 0 deletions .github/skills/app-graph/references/rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Rendering Conventions

These conventions are a verbatim port of
`radius-project/github-extension` `src/content/graph-renderer.ts`. The
template file already encodes them — this file is for reference only.

## Library versions

The template loads from CDN:

- `cytoscape` v3 (`https://unpkg.com/cytoscape@3/dist/cytoscape.min.js`)
- `cytoscape-dagre` (`https://unpkg.com/cytoscape-dagre@2/cytoscape-dagre.js`)
- `dagre` (peer dep of cytoscape-dagre, also from unpkg)

## Layout

```js
{
name: 'dagre',
rankDir: 'TB',
nodeSep: 60,
rankSep: 80,
edgeSep: 20,
padding: 48,
animate: false,
}
```

## Cytoscape core options

```js
{
userZoomingEnabled: true,
userPanningEnabled: true,
boxSelectionEnabled: false,
autoungrabify: true,
minZoom: 0.3,
maxZoom: 3,
}
```

After init, call `cy.resize(); cy.fit(cy.elements(), 48); cy.center();`
inside two nested `requestAnimationFrame` calls so the graph re-fits
once the container has its final size.

## Node style

```js
{
selector: 'node',
style: {
label: 'data(label)',
'text-valign': 'center',
'text-halign': 'center',
'font-size': '12px',
'font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif',
color: '#1f2328',
'background-color': 'data(bgColor)',
'border-color': 'data(borderColor)',
'border-width': 'data(borderWidth)',
shape: 'roundrectangle',
width: 140,
height: 55,
'text-wrap': 'wrap',
'text-max-width': '120px',
},
}
```

## Edge style

```js
{
selector: 'edge',
style: {
width: 2,
'line-color': '#8c959f',
'target-arrow-color': '#8c959f',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
'arrow-scale': 0.8,
},
}
```

## Diff color tables (Primer)

Border colors and widths:

| Status | Border | Width |
| --------- | --------- | ----- |
| added | `#1a7f37` | 3 |
| removed | `#cf222e` | 3 |
| modified | `#9a6700` | 3 |
| unchanged | `#57606a` | 2 |

Background fills:

| Status | Fill |
| --------- | --------- |
| added | `#dafbe1` |
| removed | `#ffebe9` |
| modified | `#fff8c5` |
| unchanged | `#f6f8fa` |

In single-graph mode (this skill's default), every node has diff status
`unchanged`. The diff fill/border are then replaced by per-resource-type
colors and the label is prefixed with a type icon — see
[visual-style.md](visual-style.md). In a future diff mode the diff
status fill/border wins so the diff signal stays dominant.

## Node label

`label = `${resource.name}\n${shortType}`` where
`shortType = resource.type.split('/').pop() ?? resource.type`.

## Edge rule

Iterate every resource. For each `Outbound` connection, add an edge from
the owning resource to the connection's `id` **only if** the target `id`
appears in `application.resources`. Skip otherwise. Edge id is
`${source}-->${target}`.

## Popup behavior

On node tap: read the node's rendered position, anchor a floating div
at `position + (10, 10)`, show:

1. Title = `resource.name`
2. Subtitle = `resource.type`
3. `📄 Source code` link to `resource.codeReference` (only if present
and parseable). For the standalone HTML viewer, the link target is a
`file://` URL or plain path — no GitHub context is available.
4. `📐 App definition` link to `sourceFile#L<appDefinitionLine>`.
5. `×` close button.

Close on outside click, ESC, or close-button click.

Critically: the popup div must call `stopPropagation` on `mousedown`,
`mouseup`, `touchstart`, `touchend`, `pointerdown`, `pointerup` so
Cytoscape's container-walking handler does not intercept link clicks.

## Tap-on-background closes popup

`cy.on('tap', e => { if (e.target === cy) closeGraphPopup(); })`.
Loading
Loading