create-agentic-pdlc 2.4.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agentic-pdlc/hooks/pdlc-stage-gate.sh +37 -10
- package/.claude/settings.json +18 -0
- package/.coderabbit.yaml +41 -0
- package/.github/workflows/add-to-board.yml +55 -7
- package/.github/workflows/agent-trigger.yml +57 -25
- package/.github/workflows/board-reconciliation.yml +176 -0
- package/.github/workflows/pdlc-health-check.yml +81 -81
- package/.github/workflows/project-automation.yml +252 -259
- package/CLAUDE.md +1 -1
- package/README.md +33 -32
- package/adapters/claude-code/skill.md +12 -8
- package/bin/cli.js +607 -213
- package/docs/superpowers/plans/2026-06-04-spec-format-issue-template.md +160 -0
- package/docs/superpowers/plans/2026-06-04-two-tier-installer.md +1056 -0
- package/docs/superpowers/plans/2026-06-05-archive-card-on-issue-close.md +105 -0
- package/docs/superpowers/plans/2026-06-05-project-id-actions-variable.md +336 -0
- package/docs/superpowers/specs/2026-06-04-spec-format-issue-template-design.md +46 -0
- package/docs/superpowers/specs/2026-06-05-project-id-actions-variable-design.md +114 -0
- package/package.json +2 -2
- package/scripts/derive-column.js +20 -0
- package/templates/.github/workflows/add-to-board.yml +2 -2
- package/templates/.github/workflows/agent-trigger.yml +2 -2
- package/templates/.github/workflows/pdlc-health-check.yml +2 -2
- package/templates/.github/workflows/project-automation.yml +47 -8
- package/templates/full/CLAUDE.md +30 -0
- package/templates/lite/AGENTS.md +121 -0
- package/templates/lite/CLAUDE.md +44 -0
- package/tests/cli.test.js +118 -0
- package/.github/workflows/agentic-metrics.yml +0 -545
- package/.github/workflows/qa-agent.yml +0 -139
- package/.github/workflows/qa-gate.yml +0 -51
- /package/templates/{AGENTS.md → full/AGENTS.md} +0 -0
- /package/templates/{docs → full/docs}/pdlc.md +0 -0
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
name: AI QA Agent
|
|
2
|
-
on:
|
|
3
|
-
pull_request:
|
|
4
|
-
types: [opened, synchronize, reopened]
|
|
5
|
-
|
|
6
|
-
permissions:
|
|
7
|
-
pull-requests: write
|
|
8
|
-
contents: read
|
|
9
|
-
issues: read
|
|
10
|
-
models: read
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
qa:
|
|
14
|
-
name: AC Coverage Verification (GitHub Models)
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
env:
|
|
17
|
-
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
18
|
-
PROJECT_ID: "PVT_kwHODpFFL84BXg7h"
|
|
19
|
-
STATUS_FIELD_ID: "PVTSSF_lAHODpFFL84BXg7hzhStRHI"
|
|
20
|
-
STATUS_CODE_REVIEW_PR: "86ca9720"
|
|
21
|
-
steps:
|
|
22
|
-
- uses: actions/checkout@v5.0.1
|
|
23
|
-
with:
|
|
24
|
-
fetch-depth: 0
|
|
25
|
-
|
|
26
|
-
- name: Verify AC Coverage via GitHub Models
|
|
27
|
-
env:
|
|
28
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
-
run: |
|
|
30
|
-
set -e
|
|
31
|
-
|
|
32
|
-
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
33
|
-
BASE="${{ github.event.pull_request.base.sha }}"
|
|
34
|
-
HEAD="${{ github.event.pull_request.head.sha }}"
|
|
35
|
-
|
|
36
|
-
# Get PR diff (truncated to 8000 chars to stay within context limits)
|
|
37
|
-
DIFF=$(git diff "$BASE" "$HEAD" | head -c 8000)
|
|
38
|
-
|
|
39
|
-
# Extract linked issues from PR body
|
|
40
|
-
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // ""')
|
|
41
|
-
ISSUE_NUMS=$(echo "$PR_BODY" | grep -oiE '(Closes?|Fixes?|Resolves?)\s+#([0-9]+)' | grep -oE '[0-9]+' || true)
|
|
42
|
-
|
|
43
|
-
# Build acceptance criteria context
|
|
44
|
-
AC_CONTEXT=""
|
|
45
|
-
if [ -n "$ISSUE_NUMS" ]; then
|
|
46
|
-
for NUM in $ISSUE_NUMS; do
|
|
47
|
-
ISSUE_BODY=$(gh issue view "$NUM" --json body --jq '.body // ""' 2>/dev/null || echo "")
|
|
48
|
-
AC_CONTEXT="${AC_CONTEXT}\\n\\n--- Issue #${NUM} ---\\n${ISSUE_BODY}"
|
|
49
|
-
done
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
if [ -z "$AC_CONTEXT" ]; then
|
|
53
|
-
AC_CONTEXT="No linked issue found. Evaluate if the PR description is self-contained."
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# Serialize prompt as JSON string and call GitHub Models API (3 attempts, 20s backoff)
|
|
57
|
-
PROMPT_JSON=$(printf '%s' "You are a senior QA engineer. Review whether this PR diff satisfies the Acceptance Criteria below.\n\nACCEPTANCE CRITERIA:\n${AC_CONTEXT}\n\nPR DIFF:\n${DIFF}\n\nFirst line of your response must be exactly one word: PASS or FAIL. Second line: brief explanation (max 3 sentences)." | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
58
|
-
|
|
59
|
-
RESPONSE="API_ERROR"
|
|
60
|
-
for attempt in 1 2 3; do
|
|
61
|
-
RESULT=$(curl -s -X POST \
|
|
62
|
-
"https://models.github.ai/inference/chat/completions" \
|
|
63
|
-
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
64
|
-
-H "Content-Type: application/json" \
|
|
65
|
-
-d "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}]}" \
|
|
66
|
-
-w "\n__HTTP_STATUS__:%{http_code}" \
|
|
67
|
-
--max-time 45 2>/dev/null)
|
|
68
|
-
HTTP_STATUS=$(echo "$RESULT" | grep -o '__HTTP_STATUS__:[0-9]*' | cut -d: -f2)
|
|
69
|
-
BODY=$(echo "$RESULT" | sed 's/__HTTP_STATUS__:[0-9]*$//')
|
|
70
|
-
echo "Attempt $attempt: HTTP $HTTP_STATUS"
|
|
71
|
-
if [ "$HTTP_STATUS" = "200" ]; then RESPONSE="$BODY"; break; fi
|
|
72
|
-
[ $attempt -lt 3 ] && sleep 20
|
|
73
|
-
done
|
|
74
|
-
|
|
75
|
-
if [ "$RESPONSE" = "API_ERROR" ]; then
|
|
76
|
-
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
77
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach GitHub Models API. Manual review required."
|
|
78
|
-
# exit 1 marks this check as failed; qa-gate.yml enforces the merge block via infra:qa-broken label
|
|
79
|
-
exit 1
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys,re; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip(); first=t.split("\n")[0].upper() if t else ""; print("FAIL" if re.search(r"\bFAIL\b",first) else "PASS" if re.search(r"\bPASS\b",first) else "API_ERROR")')
|
|
83
|
-
EXPLANATION=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip(); lines=t.split("\n",1); print(lines[1].strip() if len(lines)>1 else "")')
|
|
84
|
-
|
|
85
|
-
if echo "$VERDICT" | grep -q "^PASS"; then
|
|
86
|
-
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=qa:approved'
|
|
87
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage verified. ${EXPLANATION}"
|
|
88
|
-
elif echo "$VERDICT" | grep -q "^FAIL"; then
|
|
89
|
-
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=qa:needs-work'
|
|
90
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage insufficient. ${EXPLANATION}"
|
|
91
|
-
else
|
|
92
|
-
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
93
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse GitHub Models response. Manual review required."
|
|
94
|
-
exit 1
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
- name: Move board card to Code Review on qa:approved
|
|
98
|
-
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
99
|
-
uses: actions/github-script@v8
|
|
100
|
-
with:
|
|
101
|
-
github-token: ${{ env.PROJECT_TOKEN }}
|
|
102
|
-
script: |
|
|
103
|
-
const prNumber = context.payload.pull_request.number;
|
|
104
|
-
const { owner, repo } = context.repo;
|
|
105
|
-
|
|
106
|
-
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
107
|
-
if (!pr.labels.some(l => l.name === 'qa:approved')) {
|
|
108
|
-
console.log('qa:approved not on PR — skipping board move');
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const body = pr.body ?? '';
|
|
113
|
-
const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)]
|
|
114
|
-
.map(m => parseInt(m[1]));
|
|
115
|
-
|
|
116
|
-
const moveItem = async (nodeId) => {
|
|
117
|
-
const { addProjectV2ItemById: { item } } = await github.graphql(`
|
|
118
|
-
mutation($p: ID!, $c: ID!) {
|
|
119
|
-
addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
|
|
120
|
-
}`, { p: process.env.PROJECT_ID, c: nodeId });
|
|
121
|
-
await github.graphql(`
|
|
122
|
-
mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
|
|
123
|
-
updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
|
|
124
|
-
projectV2Item { id }
|
|
125
|
-
}
|
|
126
|
-
}`, { p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
|
|
127
|
-
v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR } });
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
if (linkedIssues.length > 0) {
|
|
131
|
-
for (const n of linkedIssues) {
|
|
132
|
-
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
133
|
-
await moveItem(issue.node_id);
|
|
134
|
-
console.log(`Issue #${n} → Code Review / PR`);
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
await moveItem(pr.node_id);
|
|
138
|
-
console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
|
|
139
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
name: QA Gate
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
types: [opened, synchronize, reopened, labeled, unlabeled]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
pull-requests: read
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
qa-gate:
|
|
12
|
-
name: QA Gate
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- name: Check QA status label
|
|
16
|
-
env:
|
|
17
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
-
run: |
|
|
19
|
-
set -e
|
|
20
|
-
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
21
|
-
REPO="${{ github.repository }}"
|
|
22
|
-
|
|
23
|
-
PR_LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name')
|
|
24
|
-
|
|
25
|
-
if echo "$PR_LABELS" | grep -qx "hotfix"; then
|
|
26
|
-
echo "✅ QA Gate: hotfix label — bypassed."
|
|
27
|
-
exit 0
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
if echo "$PR_LABELS" | grep -qx "human-approved"; then
|
|
31
|
-
echo "✅ QA Gate: human-approved label — manual QA sign-off, bypassed."
|
|
32
|
-
exit 0
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
if echo "$PR_LABELS" | grep -qx "qa:approved"; then
|
|
36
|
-
echo "✅ QA Gate: qa:approved — merge allowed."
|
|
37
|
-
exit 0
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
if echo "$PR_LABELS" | grep -qx "infra:qa-broken"; then
|
|
41
|
-
echo "❌ QA Gate: infra:qa-broken — GitHub Models API unreachable. Manual QA review required before merge."
|
|
42
|
-
exit 1
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
if echo "$PR_LABELS" | grep -qx "qa:needs-work"; then
|
|
46
|
-
echo "❌ QA Gate: qa:needs-work — acceptance criteria not fully met. Fix required before merge."
|
|
47
|
-
exit 1
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
echo "❌ QA Gate: no QA label found — AC Coverage Verification has not completed. Wait for the check to finish."
|
|
51
|
-
exit 1
|
|
File without changes
|
|
File without changes
|