create-agentic-pdlc 2.1.5 → 2.2.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.
Files changed (48) hide show
  1. package/.agentic-pdlc/SETUP_PROMPT.md +73 -0
  2. package/.agentic-pdlc/hooks/pdlc-stage-gate.sh +39 -0
  3. package/.agentic-pdlc/metrics/.gitkeep +0 -0
  4. package/.agentic-pdlc/metrics/2026-W19.md +21 -0
  5. package/.agentic-pdlc/metrics/raw/2026-W19.jsonl +6 -0
  6. package/.agentic-pdlc/metrics/raw/2026-W20.jsonl +1 -0
  7. package/.agentic-pdlc/templates/.github/CODEOWNERS +5 -0
  8. package/.agentic-pdlc/templates/.github/copilot-instructions.md +12 -0
  9. package/.agentic-pdlc/templates/.github/workflows/add-to-board.yml +38 -0
  10. package/.agentic-pdlc/templates/.github/workflows/agent-trigger.yml +146 -0
  11. package/.agentic-pdlc/templates/.github/workflows/agentic-metrics.yml +412 -0
  12. package/.agentic-pdlc/templates/.github/workflows/auto-approve.yml +16 -0
  13. package/.agentic-pdlc/templates/.github/workflows/ci.yml +40 -0
  14. package/.agentic-pdlc/templates/.github/workflows/pdlc-health-check.yml +123 -0
  15. package/.agentic-pdlc/templates/.github/workflows/pdlc-stage-gate.yml +51 -0
  16. package/.agentic-pdlc/templates/.github/workflows/project-automation.yml +278 -0
  17. package/.agentic-pdlc/templates/.github/workflows/protect-workflows.yml +21 -0
  18. package/.agentic-pdlc/templates/.github/workflows/qa-agent.yml +128 -0
  19. package/.agentic-pdlc/templates/AGENTS.md +81 -0
  20. package/.agentic-pdlc/templates/docs/pdlc.md +15 -5
  21. package/.agentic-setup-prompt.md +73 -0
  22. package/.agentic-setup.md +73 -0
  23. package/.claude/settings.json +15 -0
  24. package/.cursorrules +9 -0
  25. package/.github/ISSUE_TEMPLATE/pulse-feedback.md +11 -0
  26. package/.github/workflows/add-to-board.yml +38 -0
  27. package/.github/workflows/agent-trigger.yml +30 -43
  28. package/.github/workflows/agentic-metrics.yml +412 -0
  29. package/.github/workflows/pdlc-health-check.yml +10 -10
  30. package/.github/workflows/pdlc-stage-gate.yml +51 -0
  31. package/.github/workflows/project-automation.yml +68 -18
  32. package/.github/workflows/qa-agent.yml +112 -11
  33. package/CLAUDE.md +9 -0
  34. package/README.md +8 -0
  35. package/SETUP.md +28 -0
  36. package/adapters/claude-code/skill.md +41 -3
  37. package/adapters/hooks/pdlc-stage-gate.sh +44 -0
  38. package/bin/cli.js +28 -5
  39. package/docs/pdlc.md +15 -5
  40. package/package.json +1 -1
  41. package/pr_comments.json +20 -0
  42. package/templates/.github/workflows/add-to-board.yml +38 -0
  43. package/templates/.github/workflows/agent-trigger.yml +34 -4
  44. package/templates/.github/workflows/pdlc-stage-gate.yml +51 -0
  45. package/templates/.github/workflows/project-automation.yml +78 -54
  46. package/templates/.github/workflows/qa-agent.yml +14 -13
  47. package/templates/AGENTS.md +10 -0
  48. package/templates/docs/pdlc.md +15 -5
@@ -6,7 +6,7 @@ on:
6
6
  pull_request_review:
7
7
  types: [submitted]
8
8
  issues:
9
- types: [labeled]
9
+ types: [labeled, edited]
10
10
 
11
11
  env:
12
12
  PROJECT_ID: "{{PROJECT_ID}}"
@@ -87,6 +87,32 @@ jobs:
87
87
  await moveItem(node_id, targetStatusId);
88
88
  console.log(`Issue #${number} moved to ${stageName}`);
89
89
 
90
+ auto-move-to-approval:
91
+ name: Spec complete → stage:approval
92
+ if: >
93
+ github.event_name == 'issues' &&
94
+ github.event.action == 'edited' &&
95
+ contains(github.event.issue.labels.*.name, 'stage:detailing')
96
+ runs-on: ubuntu-latest
97
+ env:
98
+ PROJECT_PAT: ${{ secrets.PROJECT_PAT }}
99
+ steps:
100
+ - name: Check spec markers and swap labels
101
+ if: ${{ env.PROJECT_PAT != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
102
+ uses: actions/github-script@v7
103
+ with:
104
+ github-token: ${{ env.PROJECT_PAT }}
105
+ script: |
106
+ const body = context.payload.issue.body ?? '';
107
+ if (!body.includes('## Acceptance Criteria') || !body.includes('## Files to modify')) return;
108
+
109
+ const { owner, repo } = context.repo;
110
+ const issue_number = context.payload.issue.number;
111
+
112
+ await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['stage:approval'] }).catch(() => {});
113
+ await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'stage:detailing' }).catch(() => {});
114
+ console.log(`Issue #${issue_number} auto-moved to stage:approval`);
115
+
90
116
  # OPTIONAL: Uncomment to enable architecture-violation → Idea
91
117
  # move-violation-to-board:
92
118
  # name: architecture-violation → 💡 Idea
@@ -115,16 +141,15 @@ jobs:
115
141
  # });
116
142
  # console.log(`Issue #${number} moved to Idea`);
117
143
 
118
- # PR Opened → Move linked issue to Code Review / PR
119
- # 💡 VARIANT B (QA Agent): If using an AI QA agent, change `STATUS_CODE_REVIEW_PR` to `STATUS_TESTING` on line ~158
144
+ # PR Opened → Move linked issue to Testing (Variant B — QA Agent enabled)
120
145
  move-card-on-pr-open:
121
- name: Open PR → Code Review / PR
146
+ name: Open PR → Testing
122
147
  if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened')
123
148
  runs-on: ubuntu-latest
124
149
  env:
125
150
  PROJECT_PAT: ${{ secrets.PROJECT_PAT }}
126
151
  steps:
127
- - name: Move linked issue to Code Review / PR
152
+ - name: Move linked issue to Testing
128
153
  if: ${{ env.PROJECT_PAT != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
129
154
  uses: actions/github-script@v7
130
155
  with:
@@ -153,7 +178,7 @@ jobs:
153
178
  }
154
179
  }`, {
155
180
  p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
156
- v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR }
181
+ v: { singleSelectOptionId: process.env.STATUS_TESTING }
157
182
  });
158
183
  };
159
184
 
@@ -161,15 +186,60 @@ jobs:
161
186
  for (const n of linkedIssues) {
162
187
  const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
163
188
  await moveItem(issue.node_id);
164
- console.log(`Issue #${n} → Code Review / PR`);
189
+ console.log(`Issue #${n} → Testing`);
165
190
  }
166
191
  } else {
167
192
  await moveItem(pr.node_id);
168
- console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
193
+ console.log(`PR #${prNumber} → Testing (no linked issue)`);
169
194
  }
170
195
 
171
196
  await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:in-review'] }).catch(() => {});
172
197
 
198
+ # QA Approved → Move linked issue to Code Review / PR
199
+ move-card-on-qa-pass:
200
+ name: qa:approved → Code Review / PR
201
+ if: github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'qa:approved'
202
+ runs-on: ubuntu-latest
203
+ env:
204
+ PROJECT_PAT: ${{ secrets.PROJECT_PAT }}
205
+ steps:
206
+ - name: Move linked issue to Code Review / PR
207
+ if: ${{ env.PROJECT_PAT != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
208
+ uses: actions/github-script@v7
209
+ with:
210
+ github-token: ${{ env.PROJECT_PAT }}
211
+ script: |
212
+ const prNumber = context.payload.pull_request.number;
213
+ const { owner, repo } = context.repo;
214
+ const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
215
+ const body = pr.body ?? '';
216
+ const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)].map(m => parseInt(m[1]));
217
+ const moveItem = async (nodeId) => {
218
+ const { addProjectV2ItemById: { item } } = await github.graphql(`
219
+ mutation($p: ID!, $c: ID!) {
220
+ addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
221
+ }`, { p: process.env.PROJECT_ID, c: nodeId });
222
+ await github.graphql(`
223
+ mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
224
+ updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
225
+ projectV2Item { id }
226
+ }
227
+ }`, {
228
+ p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
229
+ v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR }
230
+ });
231
+ };
232
+ if (linkedIssues.length > 0) {
233
+ for (const n of linkedIssues) {
234
+ const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
235
+ await moveItem(issue.node_id);
236
+ console.log(`Issue #${n} → Code Review / PR`);
237
+ }
238
+ } else {
239
+ await moveItem(pr.node_id);
240
+ console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
241
+ }
242
+
173
243
  # Review Approved → Add Label
174
244
  move-card-on-review-approved:
175
245
  name: Approved PR → Add Label
@@ -189,52 +259,6 @@ jobs:
189
259
  try { await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'pr:in-review' }); } catch {}
190
260
  await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:approved'] }).catch(() => {});
191
261
 
192
- # OPTIONAL: Uncomment for Variant B (QA Agent enabled)
193
- # When qa:approved is added to a PR, move linked issue to Code Review / PR
194
- # move-card-on-qa-pass:
195
- # name: qa:approved → Code Review / PR
196
- # if: github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'qa:approved'
197
- # runs-on: ubuntu-latest
198
- # env:
199
- # PROJECT_PAT: ${{ secrets.PROJECT_PAT }}
200
- # steps:
201
- # - name: Move linked issue to Code Review / PR
202
- # if: ${{ env.PROJECT_PAT != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
203
- # uses: actions/github-script@v7
204
- # with:
205
- # github-token: ${{ env.PROJECT_PAT }}
206
- # script: |
207
- # const prNumber = context.payload.pull_request.number;
208
- # const { owner, repo } = context.repo;
209
- # const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
210
- # const body = pr.body ?? '';
211
- # const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)].map(m => parseInt(m[1]));
212
- # const moveItem = async (nodeId) => {
213
- # const { addProjectV2ItemById: { item } } = await github.graphql(`
214
- # mutation($p: ID!, $c: ID!) {
215
- # addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
216
- # }`, { p: process.env.PROJECT_ID, c: nodeId });
217
- # await github.graphql(`
218
- # mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
219
- # updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
220
- # projectV2Item { id }
221
- # }
222
- # }`, {
223
- # p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
224
- # v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR }
225
- # });
226
- # };
227
- # if (linkedIssues.length > 0) {
228
- # for (const n of linkedIssues) {
229
- # const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
230
- # await moveItem(issue.node_id);
231
- # console.log(`Issue #${n} → Code Review / PR`);
232
- # }
233
- # } else {
234
- # await moveItem(pr.node_id);
235
- # console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
236
- # }
237
-
238
262
  # PR Merged → Production
239
263
  move-card-on-pr-merge:
240
264
  name: Merged PR → Production
@@ -7,20 +7,20 @@ permissions:
7
7
  pull-requests: write
8
8
  contents: read
9
9
  issues: read
10
+ models: read
10
11
 
11
12
  jobs:
12
13
  qa:
13
- name: AC Coverage Verification (Gemini)
14
+ name: AC Coverage Verification (GitHub Models)
14
15
  runs-on: ubuntu-latest
15
16
  steps:
16
17
  - uses: actions/checkout@v4
17
18
  with:
18
19
  fetch-depth: 0
19
20
 
20
- - name: Verify AC Coverage via Gemini
21
+ - name: Verify AC Coverage via GitHub Models
21
22
  env:
22
23
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23
- GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
24
24
  run: |
25
25
  set -e
26
26
 
@@ -29,7 +29,7 @@ jobs:
29
29
  HEAD="${{ github.event.pull_request.head.sha }}"
30
30
 
31
31
  # Get PR diff (truncated to 8000 chars to stay within context limits)
32
- DIFF=$(git diff "$BASE" "$HEAD" | head -c 8000)
32
+ DIFF=$(git diff "$BASE" "$HEAD" | head -c 64000)
33
33
 
34
34
  # Extract linked issues from PR body
35
35
  PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // ""')
@@ -48,23 +48,24 @@ jobs:
48
48
  AC_CONTEXT="No linked issue found. Evaluate if the PR description is self-contained."
49
49
  fi
50
50
 
51
- # Serialize prompt as JSON string and call Gemini API (30s timeout)
52
- 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\nRespond with exactly one word: PASS or FAIL, then on the next line explain briefly why (max 3 sentences)." | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
51
+ # Serialize prompt as JSON string and call GitHub Models API (30s timeout)
52
+ 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()))')
53
53
 
54
- RESPONSE=$(curl -s -X POST \
55
- "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}" \
54
+ RESPONSE=$(curl -sf -X POST \
55
+ "https://models.github.ai/inference/chat/completions" \
56
+ -H "Authorization: Bearer ${GITHUB_TOKEN}" \
56
57
  -H "Content-Type: application/json" \
57
- -d "{\"contents\":[{\"parts\":[{\"text\":${PROMPT_JSON}}]}]}" \
58
+ -d "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}]}" \
58
59
  --max-time 30 || echo "API_ERROR")
59
60
 
60
61
  if [ "$RESPONSE" = "API_ERROR" ]; then
61
62
  gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
62
- gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach Gemini API. Manual review required."
63
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach GitHub Models API. Manual review required."
63
64
  exit 0
64
65
  fi
65
66
 
66
- VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("candidates",[{}])[0].get("content",{}).get("parts",[{}])[0].get("text","").strip(); print(t.split("\n")[0].upper() if t else "API_ERROR")')
67
- EXPLANATION=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("candidates",[{}])[0].get("content",{}).get("parts",[{}])[0].get("text","").strip(); lines=t.split("\n",1); print(lines[1].strip() if len(lines)>1 else "")')
67
+ 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")')
68
+ 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 "")')
68
69
 
69
70
  if echo "$VERDICT" | grep -q "^PASS"; then
70
71
  gh pr edit "$PR_NUMBER" --add-label "qa:approved"
@@ -74,5 +75,5 @@ jobs:
74
75
  gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage insufficient. ${EXPLANATION}"
75
76
  else
76
77
  gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
77
- gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse Gemini response. Manual review required."
78
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse GitHub Models response. Manual review required."
78
79
  fi
@@ -64,6 +64,16 @@ When detailing a solution in an issue body, you must **always** include both the
64
64
  - `path/to/file.ts` — what changes
65
65
  ```
66
66
 
67
+ ## Pipeline Updates
68
+
69
+ To add or configure optional agents (Jules, QA Agent, Sentinel) at any time:
70
+
71
+ ```bash
72
+ npx create-agentic-pdlc --update
73
+ ```
74
+
75
+ Run this when the user says anything like "update the pipeline", "update the board", or "configure the agents". It detects what is already configured and interactively sets up what is missing — without touching this file or any user-owned config.
76
+
67
77
  ## What NOT to do
68
78
 
69
79
  - Never commit directly to `main`.
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Column | Meaning | Who moves the card |
6
6
  |---|---|---|
7
- | 💡 Idea | Backlog — every new issue lands here | Manual |
7
+ | 💡 Idea — don't move manually to Exploration | Backlog — tell agent: "work on issue #XX" | Don't move manually |
8
8
  | 🔍 Exploration | Claude is analyzing code and context | Label `stage:exploration` |
9
9
  | 🧠 Brainstorming | Claude proposed approaches, awaiting PM gate | Label `stage:brainstorming` |
10
10
  | 📐 Detail Solution | Claude is writing the technical spec | Label `stage:detailing` |
@@ -80,6 +80,10 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
80
80
  | `qa:approved` | PR | Green | QA Agent passed — AC coverage verified |
81
81
  | `qa:needs-work` | PR | Red | QA Agent failed — PR needs changes |
82
82
  | `infra:qa-broken` | PR | Orange | QA Agent error — manual review required |
83
+ | `type:us` | Issue | Blue | New feature or behavioral change — full flow |
84
+ | `type:task` | Issue | Yellow | Operational/non-functional change — skips brainstorming |
85
+ | `type:bug` | Issue | Red | Something broken — skips brainstorming |
86
+ | `type:spike` | Issue | Gray | Research/evaluation — never reaches Development |
83
87
 
84
88
  ## Approval Gates
85
89
 
@@ -93,10 +97,16 @@ This triggers the implementation agent via `agent-trigger.yml`.
93
97
 
94
98
  ## Shortcuts by Type
95
99
 
96
- - **BUG**Skips Brainstorming; enters Detail Solution with diagnostics + fix.
97
- - **TASK** — Skips Brainstorming; enters Detail Solution with operational steps.
98
- - **SPIKE** Never reaches Development; delivery is a concluding comment.
99
- - **US** — Full flow observing both gates.
100
+ The `type:*` label is the authoritative signal set automatically by the agent via type inference (see `adapters/claude-code/skill.md`). Title prefixes (`🔧 TASK:`, `👤 US:`) are hints for humans; the label drives the flow.
101
+
102
+ | Label | Flow |
103
+ |---|---|
104
+ | `type:us` | Full flow — exploration → brainstorming → Gate 1 → detailing → approval |
105
+ | `type:task` | Skips brainstorming — exploration → detailing → approval |
106
+ | `type:bug` | Skips brainstorming — exploration → detailing → approval |
107
+ | `type:spike` | Skips brainstorming — exploration → detailing → conclusion comment (never reaches Development) |
108
+
109
+ If no `type:*` label present and agent confidence < 85%, defaults to `type:us` (safe fallback — never skips gates by omission).
100
110
 
101
111
  ## Definition of Done
102
112