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
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Identify the source and license of any third-party material. Describe any AI ass
- [ ] The change is focused and links an accepted proposal when required.
- [ ] No file under `Documentation/` is added, edited, moved, renamed, or deleted.
- [ ] Every added or changed skill was reviewed with `verifying-skills`.
- [ ] All P1 and P2 verification findings are resolved.
- [ ] All P1 and P2 verification findings are resolved; applicable reports state `P1 findings open: 0` and `P2 findings open: 0`.
- [ ] Applicable verification reports under `Verification/` are added or updated.
- [ ] Affected AI tool manifests contain valid JSON and the same version.
- [ ] The plugin version was incremented when required.
Expand Down
36 changes: 32 additions & 4 deletions .github/scripts/validate-contribution.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ function Add-ValidationError {
$script:errors.Add($Message)
}

function Test-NoOpenFindings {
param(
[Parameter(Mandatory)]
[string]$ReportText,

[Parameter(Mandatory)]
[ValidateSet("P1", "P2")]
[string]$Severity
)

$escapedSeverity = [regex]::Escape($Severity)
# Prefer the explicit open-count field while keeping existing reports and
# in-flight pull requests compatible with their earlier zero-finding wording.
$acceptedPatterns = @(
"(?im)^\s*(?:[-*]\s+)?(?:\*\*)?$escapedSeverity\s+findings\s+open(?:\*\*)?\s*:\s*0\.?\s*$",
"(?im)^\s*(?:[-*]\s+)?No(?:\s+open)?\s+$escapedSeverity\s+findings\.?\s*$",
"(?im)^\s*(?:[-*]\s+)?All\s+$escapedSeverity\s+findings\s+(?:are|have been)\s+resolved\.?\s*$"
)

foreach ($pattern in $acceptedPatterns) {
if ([regex]::IsMatch($ReportText, $pattern)) {
return $true
}
}

return $false
}

function Get-RepositoryRelativePath {
param([string]$Path)
$fullPath = [System.IO.Path]::GetFullPath($Path)
Expand Down Expand Up @@ -207,11 +235,11 @@ foreach ($skillFile in $skillFiles) {
}
else {
$reportText = Get-Content -LiteralPath $reportPath -Raw -Encoding utf8
if ($reportText -notmatch '(?m)^No P1 findings\.$') {
Add-ValidationError "Verification/$skillName-review.md does not confirm that P1 findings are resolved."
if (-not (Test-NoOpenFindings -ReportText $reportText -Severity "P1")) {
Add-ValidationError "Verification/$skillName-review.md does not confirm that no P1 findings remain open. Add 'P1 findings open: 0' after the P1 heading when all P1 findings are resolved."
}
if ($reportText -notmatch '(?m)^No P2 findings\.$') {
Add-ValidationError "Verification/$skillName-review.md does not confirm that P2 findings are resolved."
if (-not (Test-NoOpenFindings -ReportText $reportText -Severity "P2")) {
Add-ValidationError "Verification/$skillName-review.md does not confirm that no P2 findings remain open. Add 'P2 findings open: 0' after the P2 heading when all P2 findings are resolved."
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion .github/skills/distill/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: distill
description: Extract friction patterns from the current conversation into reusable skills.
description: Extracts recurring friction from completed work into reusable skills. Use when a conversation exposed corrections, failed assumptions, or non-obvious guidance worth preserving for future tasks.
---

# Distill
Expand Down
2 changes: 1 addition & 1 deletion .github/skills/verifying-skills/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: verifying-skills
description: Verifies a skill against authoritative skill-authoring guidance and produces a severity-bucketed review report with actionable findings. Use when asked to review a skill, verify a skill, audit a SKILL.md, check skill quality, or validate a skill against best practices.
description: Verifies a skill against four authoritative sources — Best Practices, Skills Docs, Complete Guide, and Skill Creator — and produces a severity-bucketed review report with actionable findings. Use when asked to review a skill, verify a skill, audit a SKILL.md, check skill quality, or validate a skill against best practices.
---

# Verify Skills
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Plain Markdown prompts and reference files are accepted but discouraged when a d
3. Add only the references, scripts, assets, and agent metadata the skill requires.
4. Verify the skill with the `verifying-skills` skill from `Common/agent-tooling`.
5. Commit the resulting report as `Verification/{skill-name}-review.md`.
6. Resolve all P1 and P2 findings before requesting review. Document any intentionally retained P3 finding in the pull request.
6. Resolve all P1 and P2 findings before requesting review. The report must state `P1 findings open: 0` and `P2 findings open: 0`; clearly labeled closed findings may remain for context. Document any intentionally retained P3 finding in the pull request.
7. Add or update `.github/skills/{skill-name}/SKILL.md` as a GitHub Copilot discovery adapter. Keep its name and description aligned with the canonical skill and link it to the canonical `SKILL.md`; do not duplicate the canonical workflow or resources.
8. Increment the plugin version in its Claude, Cursor, and Codex manifests.
9. Add the skill to the plugin's README.
Expand Down Expand Up @@ -145,7 +145,7 @@ Before opening a pull request:

1. Confirm that the contribution is based on and targets the latest published Acumatica release branch.
2. Run the `verifying-skills` skill from `Common/agent-tooling` for every added or changed skill.
3. Resolve all P1 and P2 findings.
3. Resolve all P1 and P2 findings and confirm the report states `P1 findings open: 0` and `P2 findings open: 0`.
4. Commit or update the applicable report under `Verification/`.
5. Confirm that local links resolve and referenced files exist.
6. Confirm that all affected plugin manifests contain valid JSON and matching versions.
Expand Down
2 changes: 1 addition & 1 deletion Common/agent-tooling/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"description": "Tools for authoring and verifying agent skills: friction-pattern extraction and skill quality verification",
"author": { "name": "Acumatica" },
"license": "GPL-3.0-only",
"version": "1.0.0"
"version": "1.0.1"
}
2 changes: 1 addition & 1 deletion Common/agent-tooling/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agent-tooling",
"version": "1.0.0",
"version": "1.0.1",
"description": "Tools for authoring and verifying agent skills",
"author": {
"name": "Acumatica"
Expand Down
2 changes: 1 addition & 1 deletion Common/agent-tooling/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"description": "Tools for authoring and verifying agent skills: friction-pattern extraction and skill quality verification",
"keywords": ["distill", "skill-verification"],
"license": "GPL-3.0-only",
"version": "1.0.0"
"version": "1.0.1"
}
2 changes: 1 addition & 1 deletion Common/agent-tooling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
Plugin with tools for authoring and verifying agent skills: friction-pattern extraction and skill quality verification.

## Skills
- **[distill](skills/distill/)** — Extracts friction patterns from the current conversation into reusable skills
- **[distill](skills/distill/)** — Extracts recurring friction from completed work into reusable skills
- **[verifying-skills](skills/verifying-skills/)** — Verifies a skill against best practices, skills docs, and a complete guide, producing a severity-bucketed review report
40 changes: 21 additions & 19 deletions Common/agent-tooling/skills/distill/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
name: distill
description: Extract friction patterns from the current conversation into reusable skills
description: Extracts recurring friction from completed work into reusable skills. Use when a conversation exposed corrections, failed assumptions, or non-obvious guidance worth preserving for future tasks.
metadata:
version: 1.0.0
version: 1.0.1
---

You are a friction extractor. You read the conversation that just happened, find where default AI behavior failed or would fail, and produce an executable skill that prevents that friction from recurring. If default behavior would handle it correctly, there is nothing to distill.
Act as a friction extractor. Read the conversation that just happened, find where default agent behavior failed or would fail, and produce an executable skill that prevents that friction from recurring. If default behavior would handle it correctly, there is nothing to distill.

The user invoking `/distill` is an implicit signal that this conversation contained friction worth preserving. Your job is to identify that friction, frame it as a reusable skill, and write it — with the user's approval.
Treat the user's request to distill the conversation as a signal that it contained friction worth preserving. Identify that friction, frame it as a reusable skill, and write it with the user's approval.

`$ARGUMENTS` — Optional focus guidance from the user. If provided, weight your analysis accordingly.
If the user supplies optional focus guidance, weight the analysis accordingly.

---

Expand All @@ -27,17 +27,19 @@ If the user redirects you, continue. If they confirm there is nothing, stop.

**Skill framing.** Can the friction be framed as a single coherent skill — a task-level behavioral specification with trigger conditions and prescriptions? If the patterns are project-contextual (codebase descriptions, project conventions) rather than task-level, say:

> "These patterns describe the project, not a reusable task behavior. They may belong in CLAUDE.md: [specific suggestions]. No skill to extract."
> "These patterns describe the project, not a reusable task behavior. They may belong in the project's persistent agent instructions: [specific suggestions]. No skill to extract."

Stop. `/distill` does not write to CLAUDE.md.
Stop. Do not write project-level instruction files as part of this workflow.

**Elicitation.** In most cases, the conversation contains enough. Only if trigger conditions are genuinely ambiguous or multiple conflicting patterns exist, ask up to 2-3 short-answer questions. Expected frequency: under 10% of invocations.

---

## Phase 2: Propose

**Check existing skills.** Read `.claude/skills/distill-index.md` if it exists. Read any listed skills with overlapping trigger conditions.
**Resolve the skills root.** Use the user-specified destination when provided. Otherwise use the current project's existing canonical skills directory. If the project has no established skills directory and the destination cannot be inferred safely, ask where the reusable skill should live.

**Check existing skills.** Read `[skills-root]/distill-index.md` if it exists. Read any listed skills with overlapping trigger conditions.

**Mode detection:**
- No overlap with existing skills -> **CREATE**. Say: "No existing skill covers this friction. I will create a new skill."
Expand All @@ -55,14 +57,14 @@ Say: "Confirm to write, or tell me what to change. Say 'abort' to cancel."
## Phase 3: Write

**For CREATE:**
1. Write skill to `.claude/skills/[skill-name].md` (kebab-case name derived from friction description)
2. Update or create `.claude/skills/distill-index.md`
1. Create `[skills-root]/[skill-name]/SKILL.md` (kebab-case name derived from the friction description).
2. Update or create `[skills-root]/distill-index.md`.
3. Say: "Skill written to [path]. Index updated."

**For UPDATE:**
1. Copy existing skill to `.claude/skills/[skill-name].backup.md` (overwrites any previous backup)
2. Write modified skill
3. Update index (new last-updated date)
1. Copy the existing `SKILL.md` to `SKILL.backup.md` in the same skill directory (overwrites any previous backup).
2. Write the modified `SKILL.md`.
3. Update the index with the new last-updated date.
4. Say: "Original backed up to [backup-path]. Skill updated. Index updated."

If the project uses git, note that the original is also recoverable from version control.
Expand All @@ -76,7 +78,7 @@ Skills you create use this structure. Not every section is required — omit sec
```
---
name: [skill-name]
description: [One line: what friction this addresses]
description: [One line in third person: what friction this addresses and when the skill should be used]
---

## When to Use This Skill
Expand Down Expand Up @@ -104,7 +106,7 @@ origin: [brief description of source conversation]

**Quality standard.** Before writing, check your produced skill against these criteria:
- Trigger conditions are observable from conversation context (not "when debugging" but "when the user reports a test failure and the error message references...")
- Every prescription is a delta from default AI behavior — if Claude would already do it, cut it
- Every prescription is a delta from default agent behavior — if a capable agent would already do it, cut it
- Anti-patterns are specific enough that a future AI would recognize the situation (not "avoid assumptions" but "do not assume X when Y")

**What BAD output looks like** (do not produce skills like these):
Expand All @@ -116,14 +118,14 @@ origin: [brief description of source conversation]

## Index Format

The index at `.claude/skills/distill-index.md`:
The index at `[skills-root]/distill-index.md`:

```
# Distill Skill Index

| Skill | Path | Created | Updated | Trigger Summary |
|-------|------|---------|---------|-----------------|
| [name] | .claude/skills/[name].md | YYYY-MM-DD | YYYY-MM-DD | [1-line trigger] |
| [name] | [skills-root]/[name]/SKILL.md | YYYY-MM-DD | YYYY-MM-DD | [1-line trigger] |
```

If the index does not exist, create it on first invocation. If it references a skill file that no longer exists, note the discrepancy and remove the stale entry. If a distill-created skill file exists but is not in the index, add it.
Expand All @@ -137,7 +139,7 @@ This is what a realistic produced skill looks like — use it as a quality refer
```
---
name: csv-column-mapping
description: Prevent silent column misalignment when parsing user-provided CSV files
description: Prevents silent column misalignment. Use when parsing user-provided CSV files whose requested columns have not yet been verified against the actual headers.
---

## When to Use This Skill
Expand All @@ -164,7 +166,7 @@ origin: CSV parsing session where column name mismatch caused silent data corrup

## Hard Constraints

- **Never write to CLAUDE.md.** Suggest project-contextual patterns verbally if identified. Do not execute the write.
- **Never write project-level instruction files.** Suggest project-contextual patterns verbally if identified. Do not execute the write because this workflow is authorized only to create or update a reusable skill.
- **Never silently modify an existing skill.** All modifications require preview and user confirmation.
- **Always backup before update.** Copy the original file before writing changes.
- **Never fabricate specificity.** If evidence is thin, say so. Lower your confidence — do not invent details.
Expand Down
Loading