[android][c][cocos2dx][cpp][csharp][flutter][glfw][godot][haxe][ios][libgdx][monogame][sdl][sfml][ts][ue][unity][xna] Add AGENTS and pi PR/issue templates

This commit is contained in:
Mario Zechner 2026-03-03 13:30:03 +01:00
parent 8f0aab9cfa
commit 4b72bb4a54
5 changed files with 304 additions and 0 deletions

View File

@ -0,0 +1,158 @@
import { DynamicBorder, type ExtensionAPI, type ExtensionContext } from "@mariozechner/pi-coding-agent";
import { Container, Text } from "@mariozechner/pi-tui";
const PR_PROMPT_PATTERN = /^\s*You are given one or more GitHub PR URLs:\s*(\S+)/im;
const ISSUE_PROMPT_PATTERN = /^\s*Analyze GitHub issue\(s\):\s*(\S+)/im;
type PromptMatch = {
kind: "pr" | "issue";
url: string;
};
type GhMetadata = {
title?: string;
author?: {
login?: string;
name?: string | null;
};
};
function extractPromptMatch(prompt: string): PromptMatch | undefined {
const prMatch = prompt.match(PR_PROMPT_PATTERN);
if (prMatch?.[1]) {
return { kind: "pr", url: prMatch[1].trim() };
}
const issueMatch = prompt.match(ISSUE_PROMPT_PATTERN);
if (issueMatch?.[1]) {
return { kind: "issue", url: issueMatch[1].trim() };
}
return undefined;
}
async function fetchGhMetadata(
pi: ExtensionAPI,
kind: PromptMatch["kind"],
url: string,
): Promise<GhMetadata | undefined> {
const args =
kind === "pr" ? ["pr", "view", url, "--json", "title,author"] : ["issue", "view", url, "--json", "title,author"];
try {
const result = await pi.exec("gh", args);
if (result.code !== 0 || !result.stdout) return undefined;
return JSON.parse(result.stdout) as GhMetadata;
} catch {
return undefined;
}
}
function formatAuthor(author?: GhMetadata["author"]): string | undefined {
if (!author) return undefined;
const name = author.name?.trim();
const login = author.login?.trim();
if (name && login) return `${name} (@${login})`;
if (login) return `@${login}`;
if (name) return name;
return undefined;
}
export default function promptUrlWidgetExtension(pi: ExtensionAPI) {
const setWidget = (ctx: ExtensionContext, match: PromptMatch, title?: string, authorText?: string) => {
ctx.ui.setWidget("prompt-url", (_tui, thm) => {
const titleText = title ? thm.fg("accent", title) : thm.fg("accent", match.url);
const authorLine = authorText ? thm.fg("muted", authorText) : undefined;
const urlLine = thm.fg("dim", match.url);
const lines = [titleText];
if (authorLine) lines.push(authorLine);
lines.push(urlLine);
const container = new Container();
container.addChild(new DynamicBorder((s: string) => thm.fg("muted", s)));
container.addChild(new Text(lines.join("\n"), 1, 0));
return container;
});
};
const applySessionName = (ctx: ExtensionContext, match: PromptMatch, title?: string) => {
const label = match.kind === "pr" ? "PR" : "Issue";
const trimmedTitle = title?.trim();
const fallbackName = `${label}: ${match.url}`;
const desiredName = trimmedTitle ? `${label}: ${trimmedTitle} (${match.url})` : fallbackName;
const currentName = pi.getSessionName()?.trim();
if (!currentName) {
pi.setSessionName(desiredName);
return;
}
if (currentName === match.url || currentName === fallbackName) {
pi.setSessionName(desiredName);
}
};
pi.on("before_agent_start", async (event, ctx) => {
if (!ctx.hasUI) return;
const match = extractPromptMatch(event.prompt);
if (!match) {
return;
}
setWidget(ctx, match);
applySessionName(ctx, match);
void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
const title = meta?.title?.trim();
const authorText = formatAuthor(meta?.author);
setWidget(ctx, match, title, authorText);
applySessionName(ctx, match, title);
});
});
pi.on("session_switch", async (_event, ctx) => {
rebuildFromSession(ctx);
});
const getUserText = (content: string | { type: string; text?: string }[] | undefined): string => {
if (!content) return "";
if (typeof content === "string") return content;
return (
content
.filter((block): block is { type: "text"; text: string } => block.type === "text")
.map((block) => block.text)
.join("\n") ?? ""
);
};
const rebuildFromSession = (ctx: ExtensionContext) => {
if (!ctx.hasUI) return;
const entries = ctx.sessionManager.getEntries();
const lastMatch = [...entries].reverse().find((entry) => {
if (entry.type !== "message" || entry.message.role !== "user") return false;
const text = getUserText(entry.message.content);
return !!extractPromptMatch(text);
});
const content =
lastMatch?.type === "message" && lastMatch.message.role === "user" ? lastMatch.message.content : undefined;
const text = getUserText(content);
const match = text ? extractPromptMatch(text) : undefined;
if (!match) {
ctx.ui.setWidget("prompt-url", undefined);
return;
}
setWidget(ctx, match);
applySessionName(ctx, match);
void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
const title = meta?.title?.trim();
const authorText = formatAuthor(meta?.author);
setWidget(ctx, match, title, authorText);
applySessionName(ctx, match, title);
});
};
pi.on("session_start", async (_event, ctx) => {
rebuildFromSession(ctx);
});
}

41
.pi/prompts/cl.md Normal file
View File

@ -0,0 +1,41 @@
---
description: Audit changelog entries before release
---
Audit changelog entries for all commits since the last release.
## Process
1. **Find the last release tag:**
```bash
git tag --sort=-version:refname | head -1
```
2. **List all commits since that tag:**
```bash
git log <tag>..HEAD --oneline
```
3. **Read the current top release section in `CHANGELOG.md`:**
- Treat this section as the release target.
- Identify the runtime subsections that exist (for example `C`, `C++`, `C#`, `Java`, `TypeScript`, `Unity`, `UE`, etc.).
4. **For each commit, check:**
- Skip: changelog-only updates, doc-only changes, or release housekeeping.
- Determine affected runtime(s) and folders (`git show <hash> --stat`).
- Verify a changelog entry exists in `CHANGELOG.md` under the correct runtime subsection.
- Verify breaking API/behavior changes are listed under a breaking changes subsection for that runtime when applicable.
5. **Cross-runtime coverage rule:**
- If a shared or cross-cutting change impacts multiple runtimes, ensure each impacted runtime subsection has an entry.
6. **Report:**
- List commits with missing entries.
- List entries that are misplaced (wrong runtime subsection or wrong change type).
- Add any missing entries directly.
## Changelog structure reference
- Top-level section is the release version (for example `# 4.2`).
- Runtime subsections are grouped under that version.
- Runtime subsections may contain grouped headings such as `Additions`, `Breaking changes`, `Fixes`, etc.
- Keep wording concise and runtime-specific.

23
.pi/prompts/is.md Normal file
View File

@ -0,0 +1,23 @@
---
description: Analyze GitHub issues (bugs or feature requests)
---
Analyze GitHub issue(s): $ARGUMENTS
For each issue:
1. Read the issue in full, including all comments and linked issues/PRs.
2. Do not trust analysis written in the issue. Independently verify behavior and derive your own analysis from the code and execution path.
3. **For bugs**:
- Ignore any root cause analysis in the issue (likely wrong).
- Read all related code files in full (no truncation).
- Trace the code path and identify the actual root cause.
- Propose a fix and list affected runtime directories/files.
4. **For feature requests**:
- Do not trust implementation proposals in the issue without verification.
- Read all related code files in full (no truncation).
- Propose the most concise implementation approach.
- List affected files and runtime directories.
Do NOT implement unless explicitly asked. Analyze and propose only.

41
.pi/prompts/pr.md Normal file
View File

@ -0,0 +1,41 @@
---
description: Review PRs from URLs with structured issue and code analysis
---
You are given one or more GitHub PR URLs: $@
For each PR URL, do the following in order:
1. Read the PR page in full. Include description, all comments, all commits, and all changed files.
2. Identify any linked issues referenced in the PR body, comments, commit messages, or cross links. Read each issue in full, including all comments.
3. Analyze the PR diff (`gh pr diff <url>`). Read all relevant code files in full with no truncation from the current main branch and compare against the diff. Include related code paths that are not in the diff but are required to validate behavior.
4. Check for a changelog entry in `CHANGELOG.md` in the current release section and the affected runtime subsection(s). Report whether an entry exists. If missing, state that a changelog entry is required before merge and that you will add it if the user decides to merge. Verify:
- Entry is placed under the correct runtime subsection(s).
- Breaking changes are listed under a breaking changes subsection when applicable.
5. Check whether docs/examples need updates. This is usually required when APIs or behavior changed. Inspect at least:
- `README.md`
- Runtime-specific docs/README files under affected runtime directories
- Relevant example projects under `examples/`
6. Provide a structured review with these sections:
- Good: solid choices or improvements
- Bad: concrete issues, regressions, missing tests, or risks
- Ugly: subtle or high impact problems
7. Add Questions or Assumptions if anything is unclear.
8. Add Change summary and Tests.
Output format per PR:
PR: <url>
Changelog:
- ...
Good:
- ...
Bad:
- ...
Ugly:
- ...
Questions or Assumptions:
- ...
Change summary:
- ...
Tests:
- ...
If no issues are found, say so under Bad and Ugly.

41
AGENTS.md Normal file
View File

@ -0,0 +1,41 @@
# Spine Runtimes Agent Rules
## Minimal operating rules
- Keep changes scoped to the requested task.
- Do not commit unless the user explicitly asks.
- Before editing, read files in full, especially if the read tool truncates them.
- Follow existing code style in touched files (naming, type usage, control flow, and error handling patterns).
## Git commit subject prefix (required)
Every commit subject must start with a runtime prefix.
Format:
- Single runtime: `[unity] Fix clipping regression`
- Multiple runtimes: `[c][cpp] Sync physics constraint handling`
Use lowercase prefixes exactly as listed below.
### Runtime prefixes
- `[android]` -> `spine-android`
- `[c]` -> `spine-c`
- `[cocos2dx]` -> `spine-cocos2dx`
- `[cpp]` -> `spine-cpp`
- `[csharp]` -> `spine-csharp`
- `[flutter]` -> `spine-flutter`
- `[glfw]` -> `spine-glfw`
- `[godot]` -> `spine-godot`
- `[haxe]` -> `spine-haxe`
- `[ios]` -> `spine-ios`
- `[libgdx]` -> `spine-libgdx`
- `[monogame]` -> `spine-monogame`
- `[sdl]` -> `spine-sdl`
- `[sfml]` -> `spine-sfml`
- `[ts]` -> `spine-ts`
- `[ue]` -> `spine-ue`
- `[unity]` -> `spine-unity`
- `[xna]` -> `spine-xna`
### Prefix selection rules
- If one runtime is changed, use one prefix.
- If multiple runtimes are changed, include multiple prefixes.
- If shared files at repo root are changed, include the runtime prefix(es) impacted by that change.