create-agentic-pdlc 2.1.6 → 2.2.1

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 (46) 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/SETUP.md +28 -0
  35. package/adapters/claude-code/skill.md +63 -15
  36. package/adapters/hooks/pdlc-stage-gate.sh +44 -0
  37. package/bin/cli.js +113 -11
  38. package/docs/pdlc.md +15 -5
  39. package/package.json +1 -1
  40. package/pr_comments.json +20 -0
  41. package/templates/.github/workflows/add-to-board.yml +38 -0
  42. package/templates/.github/workflows/agent-trigger.yml +34 -4
  43. package/templates/.github/workflows/pdlc-stage-gate.yml +51 -0
  44. package/templates/.github/workflows/project-automation.yml +78 -54
  45. package/templates/.github/workflows/qa-agent.yml +14 -13
  46. package/templates/docs/pdlc.md +15 -5
@@ -6,22 +6,123 @@ on:
6
6
  permissions:
7
7
  pull-requests: write
8
8
  contents: read
9
+ issues: read
10
+ models: read
9
11
 
10
12
  jobs:
11
13
  qa:
12
- name: Run AI QA Agent
14
+ name: AC Coverage Verification (GitHub Models)
13
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"
14
21
  steps:
15
22
  - uses: actions/checkout@v4
16
- - name: Execute QA Tests
17
- run: |
18
- echo "Run your QA Agent here."
19
- echo "This could be QAWolf, a custom LLM script, or a secondary agent."
20
- echo "If tests pass: gh pr edit $PR_URL --add-label 'qa:pass'"
21
- echo "If tests fail: gh pr edit $PR_URL --add-label 'qa:fail'"
22
-
23
- # Example success:
24
- # gh pr edit ${{ github.event.pull_request.html_url }} --add-label "qa:pass"
23
+ with:
24
+ fetch-depth: 0
25
+
26
+ - name: Verify AC Coverage via GitHub Models
25
27
  env:
26
28
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
- PR_URL: ${{ github.event.pull_request.html_url }}
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 64000)
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 (30s timeout)
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=$(curl -sf -X POST \
60
+ "https://models.github.ai/inference/chat/completions" \
61
+ -H "Authorization: Bearer ${GITHUB_TOKEN}" \
62
+ -H "Content-Type: application/json" \
63
+ -d "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}]}" \
64
+ --max-time 30 || echo "API_ERROR")
65
+
66
+ if [ "$RESPONSE" = "API_ERROR" ]; then
67
+ gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
68
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach GitHub Models API. Manual review required."
69
+ exit 0
70
+ fi
71
+
72
+ 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")')
73
+ 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 "")')
74
+
75
+ if echo "$VERDICT" | grep -q "^PASS"; then
76
+ gh pr edit "$PR_NUMBER" --add-label "qa:approved"
77
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage verified. ${EXPLANATION}"
78
+ elif echo "$VERDICT" | grep -q "^FAIL"; then
79
+ gh pr edit "$PR_NUMBER" --add-label "qa:needs-work"
80
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage insufficient. ${EXPLANATION}"
81
+ else
82
+ gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
83
+ gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse GitHub Models response. Manual review required."
84
+ fi
85
+
86
+ - name: Move board card to Code Review on qa:approved
87
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
88
+ uses: actions/github-script@v7
89
+ with:
90
+ github-token: ${{ env.PROJECT_TOKEN }}
91
+ script: |
92
+ const prNumber = context.payload.pull_request.number;
93
+ const { owner, repo } = context.repo;
94
+
95
+ const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
96
+ if (!pr.labels.some(l => l.name === 'qa:approved')) {
97
+ console.log('qa:approved not on PR — skipping board move');
98
+ return;
99
+ }
100
+
101
+ const body = pr.body ?? '';
102
+ const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)]
103
+ .map(m => parseInt(m[1]));
104
+
105
+ const moveItem = async (nodeId) => {
106
+ const { addProjectV2ItemById: { item } } = await github.graphql(`
107
+ mutation($p: ID!, $c: ID!) {
108
+ addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
109
+ }`, { p: process.env.PROJECT_ID, c: nodeId });
110
+ await github.graphql(`
111
+ mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
112
+ updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
113
+ projectV2Item { id }
114
+ }
115
+ }`, { p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
116
+ v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR } });
117
+ };
118
+
119
+ if (linkedIssues.length > 0) {
120
+ for (const n of linkedIssues) {
121
+ const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
122
+ await moveItem(issue.node_id);
123
+ console.log(`Issue #${n} → Code Review / PR`);
124
+ }
125
+ } else {
126
+ await moveItem(pr.node_id);
127
+ console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
128
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,9 @@
1
+ # PDLC Stage Gate
2
+
3
+ NEVER run `gh pr create` unless one of these is true:
4
+ - The linked issue has label `stage:approval`
5
+ - The branch name starts with `hotfix/`
6
+
7
+ Advance stages first: `exploration` → `brainstorming` → `detailing` → `approval`
8
+
9
+ The PreToolUse hook will block the action automatically if this rule is violated.
package/SETUP.md CHANGED
@@ -149,6 +149,34 @@ If you don't use this, you can safely delete `templates/.github/workflows/qa-age
149
149
 
150
150
  ---
151
151
 
152
+ ## (Optional) Agentic Metrics — Weekly Report
153
+
154
+ Every Sunday a `📊 Agentic Pulse` issue is created automatically in your repository with agentic-specific insights no off-the-shelf tool provides:
155
+
156
+ - **Orphan issues** — open 14+ days with no linked PR (forgotten or blocked work)
157
+ - **PR merge time trend** — week-over-week comparison (flow acceleration or slowdown)
158
+ - **Rework rate** — PRs merged without extra commit sessions (agent or human first-shot rate)
159
+ - **Unlinked PRs** — merged without `Closes #N` (traceability gaps)
160
+ - **Stage Residence Time** — included automatically if your team uses `stage:*` labels _(Milestone 2)_
161
+
162
+ The issue appears in your GitHub notifications automatically — zero extra setup, zero extra secrets.
163
+
164
+ **To activate:**
165
+
166
+ 1. Copy the workflow to your repository:
167
+ ```bash
168
+ cp .agentic-pdlc/templates/.github/workflows/agentic-metrics.yml .github/workflows/agentic-metrics.yml
169
+ ```
170
+
171
+ 2. Commit and push. The first pulse runs next Sunday, or trigger it manually:
172
+ ```bash
173
+ gh workflow run agentic-metrics.yml
174
+ ```
175
+
176
+ > **No additional secrets needed.** The workflow uses the standard `GITHUB_TOKEN` and creates a `metrics:weekly` label automatically on first run.
177
+
178
+ ---
179
+
152
180
  ## Final Verification Checklist
153
181
 
154
182
  - [ ] Board has 10 columns fully configured
@@ -25,7 +25,7 @@ If any of these files are missing, you are in **Setup Mode**. Do not proceed wit
25
25
  1. **Language Detection:** Analyze the user's previous prompts and preferred language. Conduct this entire Setup Mode and ask all your interactive questions in that same language.
26
26
  2. Acknowledge that the framework is not yet set up.
27
27
  3. **Pre-filled Context:** Before asking any questions, read the following files if they exist:
28
- - `.agentic-pdlc/cli-context.json` — written by the CLI. Contains `projectName`, `repoOwner`, `repoName`. Use these values directly and skip the corresponding questions.
28
+ - `.agentic-pdlc/cli-context.json` — written by the CLI. Contains `projectName`, `repoOwner`, `repoName`, `projectNumber`, `isOrg`, `boardUrl`, and `patAutoSet` (boolean). Use these values directly and skip the corresponding questions. Honor `patAutoSet` in Step 7 and `boardUrl` in Step 10.
29
29
  - `.agentic-pdlc/templates/docs/pdlc.md` — the CLI pre-fills PROJECT_ID, STATUS_FIELD_ID, REPO_OWNER, REPO_NAME, and all 9 column option IDs. If none of the values still contain `{{...}}` placeholders, skip the entire Board IDs question group.
30
30
  - `.agentic-pdlc/templates/.github/workflows/project-automation.yml` — the CLI also pre-fills all ID placeholders here. When writing the workflow file, the remaining `{{...}}` placeholders are only non-ID ones (project name, commands, etc.).
31
31
  4. Interactively ask the user only for the **missing values**, **one group at a time**:
@@ -42,7 +42,7 @@ If any of these files are missing, you are in **Setup Mode**. Do not proceed wit
42
42
  - c) **Yes, activate** — *Uncomment the `move-violation-to-board` job in `project-automation.yml`.* → Activate immediately.
43
43
  - **QA Agent:** Ask: *"Do you want to use a QA agent to verify PRs automatically before Code Review?"* Present the options:
44
44
  - a) **No (Variant A)** — *PRs go straight to Code Review. Standard and simpler.*
45
- - b) **Yes (Variant B), but I need help configuring it** — *PRs pass through a QA Agent before being reviewed. Requires a `GEMINI_API_KEY` secret (free tier available at ai.google.dev).* → Guide the user through configuration.
45
+ - b) **Yes (Variant B), but I need help configuring it** — *PRs pass through a QA Agent before being reviewed. Uses `GITHUB_TOKEN` zero additional secrets.* → Guide the user through configuration.
46
46
  - c) **Yes (Variant B), I already have it configured** — *PRs pass through a QA Agent before being reviewed.* → Activate Variant B immediately: change `STATUS_CODE_REVIEW_PR` to `STATUS_TESTING` in the `move-card-on-pr-open` job and uncomment the `move-card-on-qa-pass` job in `project-automation.yml`.
47
47
  - **Implementation Agent:** Ask: *"Do you use an autonomous implementation agent? (It implements the features you approve for development)"* Present the options:
48
48
  - a) **No** — *No autonomous implementation agent.*
@@ -50,23 +50,33 @@ If any of these files are missing, you are in **Setup Mode**. Do not proceed wit
50
50
  - c) **Other** — *Enter the agent's handle.*
51
51
  5. Generate and write the missing files replacing the `{{SCREAMING_SNAKE_CASE}}` placeholders using the templates in `.agentic-pdlc/templates/`.
52
52
  6. Offer to run the `gh` commands for labels (`spec:approved`, `pr:in-review`, `pr:approved`, `architecture-violation`).
53
- 7. **Set up the `PROJECT_PAT` secret (required for board automation):**
54
- The board automation workflows need a GitHub Personal Access Token (classic) with `project` scope. Without it, all board card movements will silently skip — no error, no cards moving.
55
- - Go to: **github.com/settings/tokens** → *Generate new token (classic)*
56
- - Select scopes: ✅ `repo` + ✅ `project`
57
- - Copy the token, then run:
58
- ```
59
- gh secret set PROJECT_PAT --body "<your-token>"
60
- ```
61
- Wait for the user to confirm the secret is set before continuing.
53
+ 7. **`PROJECT_PAT` secret (required for board automation):**
54
+
55
+ Read `patAutoSet` from `.agentic-pdlc/cli-context.json`:
56
+
57
+ **If `patAutoSet === true`:** The CLI already configured this secret automatically. Print `✅ PROJECT_PAT is configured.` and continue to Step 8 — do not ask the user anything.
58
+
59
+ **If `patAutoSet === false` (org repo):** Show the block below and wait for the user to reply "done" or "secret set" before continuing:
60
+
61
+ > Your repo is in an organization. For security, `PROJECT_PAT` must be a dedicated PAT (not your personal OAuth token). Without it, all board card movements in CI will silently skip — no error surfaced.
62
+ >
63
+ > 1. Open: **github.com/settings/tokens** → *Generate new token (classic)*
64
+ > 2. Name: `PROJECT_PAT — <repo-name>`
65
+ > 3. Select scopes: ✅ `repo` + ✅ `project`
66
+ > 4. Copy the token, then run:
67
+ > ```
68
+ > gh secret set PROJECT_PAT --body "<your-token>" --repo <owner>/<repo>
69
+ > ```
70
+ > 5. Reply **"done"** when finished.
62
71
  8. **IMPORTANT:** Delete the setup prompt file by running exactly:
63
72
  ```
64
73
  rm -f .agentic-setup.md .agentic-setup-prompt.md .agentic-pdlc/SETUP_PROMPT.md
65
74
  ```
66
75
  **Do NOT run `git add` or any other git command.** These files were never committed and do not exist in the git index. This command must run **before** the commit step.
67
76
  9. Commit everything with the message: `chore: setup agentic-pdlc framework`.
68
- 10. Conclude Setup Mode. Read `projectNumber` from `.agentic-pdlc/cli-context.json` and show the user their board URL:
69
- `https://github.com/users/{repoOwner}/projects/{projectNumber}/views/1?layout=board`
77
+ 10. Conclude Setup Mode. Read `boardUrl` from `.agentic-pdlc/cli-context.json` and show the user exactly this (do not reconstruct the URL — `boardUrl` already includes the correct `users/` or `orgs/` path segment and `?layout=board`):
78
+
79
+ `🎉 Setup complete! Your board: <boardUrl>`
70
80
 
71
81
  ---
72
82
 
@@ -86,7 +96,32 @@ This detects which optional agents (Jules, QA Agent, Sentinel) are already confi
86
96
 
87
97
  If `AGENTS.md` and `docs/pdlc.md` are present, you are in **Execution Mode**.
88
98
 
89
- ### 0. Board Labels Mandatory at Every State Transition
99
+ ### 0. [FIRST] Issue Type Identification
100
+
101
+ **Run before anything else — before `stage:exploration`, before reading code.**
102
+
103
+ Reading the issue title and body for type inference is exempt from the `stage:exploration` requirement: it is metadata already present in the request, not code reading or skill invocation.
104
+
105
+ 1. Check if issue already has a `type:*` label (`type:us`, `type:task`, `type:bug`, `type:spike`) → if yes, skip to Section 0.1.
106
+ 2. Read issue title + body (metadata only — no code reading at this step).
107
+ 3. Classify using these rules:
108
+ - `type:task` — operational change, config, rename, docs update, non-functional (no user-facing behavior change)
109
+ - `type:bug` — something broken that should work
110
+ - `type:spike` — research/evaluation spike, never reaches Development
111
+ - `type:us` — new feature, behavioral change, anything product-facing
112
+ 4. Confidence ≥ 85% → add inferred label: `gh issue edit <N> --add-label "type:<inferred>"`
113
+ 5. Confidence < 85% → default to `type:us`: `gh issue edit <N> --add-label "type:us"`
114
+
115
+ **Type drives the PDLC flow:**
116
+
117
+ | Type | Flow |
118
+ |---|---|
119
+ | `type:us` | Full flow: exploration → brainstorming → Gate 1 → detailing → approval |
120
+ | `type:task` | Skip brainstorming: exploration → detailing → approval |
121
+ | `type:bug` | Skip brainstorming: exploration → detailing → approval |
122
+ | `type:spike` | Skip brainstorming: exploration → detailing → conclusion comment (never reaches Development) |
123
+
124
+ ### 0.1 Board Labels — Mandatory at Every State Transition
90
125
 
91
126
  These label commands are non-negotiable. They run **before** the activity they announce — before reading code, before invoking any skill, before any other action.
92
127
 
@@ -98,8 +133,21 @@ These label commands are non-negotiable. They run **before** the activity they a
98
133
 
99
134
  No investigation, no skill invocation, no code reading happens before `stage:exploration` is applied. No architecture presentation starts before `stage:brainstorming` is set (and `stage:exploration` removed). No spec writing starts before `stage:detailing` is set (and `stage:brainstorming` removed).
100
135
 
136
+ ### 0.1 PR Stage Gate — Non-Negotiable
137
+
138
+ **NEVER run `gh pr create` unless the linked issue has label `stage:approval`.**
139
+
140
+ The PreToolUse hook enforces this automatically and will block the command. The only bypass is a branch prefixed with `hotfix/` — which requires explicit PM instruction, never agent self-authorization.
141
+
142
+ Hotfix flow (only when PM explicitly requests it):
143
+ ```bash
144
+ gh issue edit <N> --add-label "hotfix"
145
+ git checkout -b hotfix/<N>-<description>
146
+ # implement → gh pr create --label hotfix
147
+ ```
148
+
101
149
  ### 1. Daily Upstream Loop
102
- Your job is to move issues from "Idea" to "Detail Solution".
150
+ Your job is to move issues from "💡 Idea - don't move manually to Exploration" to "📐 Detail Solution".
103
151
  When asked to work on a feature, you will:
104
152
  - Explore the code context.
105
153
  - Present architectural approaches (Brainstorming).
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # PDLC Stage Gate — blocks gh pr create without stage:approval on linked issue.
3
+ # Bypass: branch prefix hotfix/ skips all checks.
4
+
5
+ INPUT=$(cat)
6
+ COMMAND=$(echo "$INPUT" | node -e "const d=JSON.parse(require('fs').readFileSync(0)); console.log(d.command || '')" 2>/dev/null || echo "")
7
+
8
+ if ! echo "$COMMAND" | grep -q "gh pr create"; then
9
+ exit 0
10
+ fi
11
+
12
+ BRANCH=$(git branch --show-current 2>/dev/null || echo "")
13
+
14
+ if echo "$BRANCH" | grep -qE "^hotfix/"; then
15
+ echo "✅ PDLC: Hotfix branch — stage gate bypassed."
16
+ exit 0
17
+ fi
18
+
19
+ ISSUE_NUM=$(echo "$BRANCH" | grep -oE '[0-9]+' | head -1)
20
+
21
+ if [ -z "$ISSUE_NUM" ]; then
22
+ echo "❌ PDLC Stage Gate: Cannot determine issue from branch '$BRANCH'."
23
+ echo " Use: feat/<issue-number>-<description> or hotfix/<issue-number>-<description>"
24
+ exit 1
25
+ fi
26
+
27
+ LABELS=$(gh issue view "$ISSUE_NUM" --json labels --jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
28
+
29
+ if echo "$LABELS" | grep -qw "stage:approval"; then
30
+ echo "✅ PDLC: Issue #$ISSUE_NUM approved — gate passed."
31
+ exit 0
32
+ fi
33
+
34
+ if echo "$LABELS" | grep -qw "stage:development"; then
35
+ echo "✅ PDLC: Issue #$ISSUE_NUM in development (spec already approved) — gate passed."
36
+ exit 0
37
+ fi
38
+
39
+ STAGE=$(echo "$LABELS" | tr ' ' '\n' | grep "^stage:" | head -1 || echo "none")
40
+ echo "❌ PDLC Stage Gate: Issue #$ISSUE_NUM is not approved."
41
+ echo " Current stage: $STAGE"
42
+ echo " Required: stage:approval or stage:development label on the issue."
43
+ echo " Emergency bypass: rename branch to hotfix/<issue-number>-<description>."
44
+ exit 1
package/bin/cli.js CHANGED
@@ -68,8 +68,8 @@ const i18n = {
68
68
  update_jules_header: t('— Jules (autonomous implementation agent) —', '— Jules (agente de implementação autônomo) —', '— Jules (agente de implementación autónomo) —'),
69
69
  update_jules_ask: t(' Which agent? (a) @google-labs-jules (b) Other (c) Skip: ', ' Qual agente? (a) @google-labs-jules (b) Outro (c) Pular: ', ' ¿Qué agente? (a) @google-labs-jules (b) Otro (c) Omitir: '),
70
70
  update_jules_ask_handle: t(' Agent handle (e.g. @my-agent): ', ' Handle do agente (ex: @meu-agente): ', ' Handle del agente (ej: @mi-agente): '),
71
- update_qa_header: t('— QA Agent (AC verification via Geminifree tier) —', '— QA Agent (verificação de ACs via Geminifree tier) —', '— QA Agent (verificación de ACs via Geminifree tier) —'),
72
- update_qa_ask: t(' Activate? Requires GEMINI_API_KEY secret. (Y/n): ', ' Ativar? Requer secret GEMINI_API_KEY. (S/n): ', ' ¿Activar? Requiere el secret GEMINI_API_KEY. (S/n): '),
71
+ update_qa_header: t('— QA Agent (AC verification via GitHub Models zero secrets) —', '— QA Agent (verificação de ACs via GitHub Models zero secrets) —', '— QA Agent (verificación de ACs via GitHub Models zero secrets) —'),
72
+ update_qa_ask: t(' Activate? Uses GITHUB_TOKEN — no extra secrets needed. (Y/n): ', ' Ativar? Usa GITHUB_TOKEN — nenhum secret extra necessário. (S/n): ', ' ¿Activar? Usa GITHUB_TOKEN sin secrets adicionales. (S/n): '),
73
73
  update_sentinel_header: t('— Sentinel (architecture audit via Gemini Code Assist) —', '— Sentinel (auditoria de arquitetura via Gemini Code Assist) —', '— Sentinel (auditoría de arquitectura via Gemini Code Assist) —'),
74
74
  update_sentinel_ask: t(' Activate? Requires Gemini Code Assist CI job. (Y/n): ', ' Ativar? Requer CI job do Gemini Code Assist. (S/n): ', ' ¿Activar? Requiere CI job de Gemini Code Assist. (S/n): '),
75
75
  };
@@ -84,6 +84,11 @@ console.log(`${cyan}============================================================
84
84
  console.log(`${cyan}${i18n.welcome}${reset}`);
85
85
  console.log(`${cyan}================================================================${reset}\n`);
86
86
 
87
+ function buildBoardUrl(repoOwner, projectNumber, isOrg) {
88
+ const segment = isOrg ? 'orgs' : 'users';
89
+ return `https://github.com/${segment}/${repoOwner}/projects/${projectNumber}/views/1?layout=board`;
90
+ }
91
+
87
92
  // Helper function to recursively copy directories
88
93
  function copyDirSync(src, dest) {
89
94
  if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
@@ -121,6 +126,43 @@ async function runSetup() {
121
126
  process.exit(1);
122
127
  }
123
128
 
129
+ function getScopes() {
130
+ try {
131
+ const out = execFileSync('gh', ['api', 'user', '-i'], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
132
+ const line = out.split('\n').find(l => l.toLowerCase().startsWith('x-oauth-scopes:'));
133
+ return line ? line.split(':').slice(1).join(':').split(',').map(s => s.trim()) : [];
134
+ } catch (e) {
135
+ return [];
136
+ }
137
+ }
138
+
139
+ const scopesBefore = getScopes();
140
+ if (scopesBefore.length > 0 && !scopesBefore.includes('project')) {
141
+ console.log(`${yellow}⚠️ Token missing 'project' scope — required for GitHub Projects board.${reset}`);
142
+ console.log(`${yellow} Refreshing token now (browser may open)...${reset}\n`);
143
+ try {
144
+ execSync('gh auth refresh -h github.com -s project', { stdio: 'inherit' });
145
+ } catch (e) {
146
+ console.log(`${red}❌ Token refresh failed. Run manually: gh auth refresh -h github.com -s project${reset}`);
147
+ rl.close();
148
+ process.exit(1);
149
+ }
150
+ const scopesAfter = getScopes();
151
+ if (scopesAfter.length > 0 && !scopesAfter.includes('project')) {
152
+ console.log(`\n${red}❌ 'project' scope still missing after refresh.${reset}`);
153
+ console.log(`${yellow} Active scopes: ${scopesAfter.join(', ')}${reset}`);
154
+ console.log(`${yellow} Note: gh uses OAuth tokens — visible at github.com/settings/applications, not /settings/tokens${reset}`);
155
+ console.log(`${yellow} Try manually: gh auth refresh -h github.com -s project${reset}`);
156
+ rl.close();
157
+ process.exit(1);
158
+ }
159
+ if (scopesAfter.length > 0) {
160
+ console.log(`\n${green}✅ Token refreshed. Active scopes: ${scopesAfter.join(', ')}${reset}\n`);
161
+ } else {
162
+ console.log(`\n${green}✅ Token refreshed with 'project' scope.${reset}\n`);
163
+ }
164
+ }
165
+
124
166
  const agentAnswer = await askQuestion(i18n.ask_agent);
125
167
  const agent = agentAnswer.trim().toLowerCase();
126
168
  if (!['claude', 'cursor', 'copilot'].includes(agent)) {
@@ -190,14 +232,14 @@ async function runSetup() {
190
232
  let ownerId, projectId, projectNumber;
191
233
  try {
192
234
  if (isOrg) {
193
- const orgOutput = execFileSync('gh', ['api', 'graphql', '-f', 'query=query($login: String!) { organization(login: $login) { id } }', '-f', `login=${repoOwner}`, '--jq', '.data.organization.id']).toString().trim();
235
+ const orgOutput = execFileSync('gh', ['api', 'graphql', '-f', 'query=query($login: String!) { organization(login: $login) { id } }', '-f', `login=${repoOwner}`, '--jq', '.data.organization.id'], { stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
194
236
  ownerId = orgOutput;
195
237
  } else {
196
- const userOutput = execFileSync('gh', ['api', 'graphql', '-f', 'query={ viewer { id } }', '--jq', '.data.viewer.id']).toString().trim();
238
+ const userOutput = execFileSync('gh', ['api', 'graphql', '-f', 'query={ viewer { id } }', '--jq', '.data.viewer.id'], { stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
197
239
  ownerId = userOutput;
198
240
  }
199
241
 
200
- const projectCreateRaw = execFileSync('gh', ['api', 'graphql', '-f', 'query=mutation($owner: ID!, $title: String!) { createProjectV2(input: {ownerId: $owner, title: $title}) { projectV2 { id number } } }', '-f', `owner=${ownerId}`, '-f', `title=${boardName}`]).toString().trim();
242
+ const projectCreateRaw = execFileSync('gh', ['api', 'graphql', '-f', 'query=mutation($owner: ID!, $title: String!) { createProjectV2(input: {ownerId: $owner, title: $title}) { projectV2 { id number } } }', '-f', `owner=${ownerId}`, '-f', `title=${boardName}`], { stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
201
243
  const projectCreateResponse = JSON.parse(projectCreateRaw);
202
244
  if (projectCreateResponse.errors) {
203
245
  throw new Error(projectCreateResponse.errors.map(e => e.message).join('; '));
@@ -217,7 +259,14 @@ async function runSetup() {
217
259
  }
218
260
 
219
261
  } catch (err) {
220
- console.log(` ${i18n.project_err}${err.message}`);
262
+ const isScopeError = (err.message || '').includes("required scopes") || (err.stderr || '').toString().includes("required scopes");
263
+ if (isScopeError) {
264
+ console.log(` ${red}❌ Token missing 'project' scope.${reset}`);
265
+ console.log(` ${yellow}Fix: gh auth refresh -s project${reset}`);
266
+ console.log(` ${yellow}Then re-run: npx create-agentic-pdlc${reset}`);
267
+ } else {
268
+ console.log(` ${i18n.project_err}${err.message}`);
269
+ }
221
270
  }
222
271
 
223
272
  let statusFieldId;
@@ -278,6 +327,23 @@ async function runSetup() {
278
327
  }
279
328
  }
280
329
 
330
+ // Auto-provision PROJECT_PAT for personal repos
331
+ let patAutoSet = false;
332
+ if (projectId && !isOrg) {
333
+ try {
334
+ const tokenOut = execFileSync('gh', ['auth', 'token'], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' }).trim();
335
+ if (tokenOut) {
336
+ execFileSync('gh', ['secret', 'set', 'PROJECT_PAT', '--body', tokenOut, '--repo', repo], { stdio: ['ignore', 'pipe', 'pipe'] });
337
+ patAutoSet = true;
338
+ console.log(`\n${green}✅ PROJECT_PAT secret set automatically (uses your gh OAuth token).${reset}`);
339
+ }
340
+ } catch (err) {
341
+ console.log(`\n${yellow}⚠️ Could not auto-set PROJECT_PAT. Agent will guide manual setup.${reset}`);
342
+ }
343
+ } else if (projectId && isOrg) {
344
+ console.log(`\n${yellow}ℹ️ Org repo detected — PROJECT_PAT will require manual setup for security.${reset}`);
345
+ }
346
+
281
347
  console.log(`\n${yellow}${i18n.scaffolding}${reset}`);
282
348
 
283
349
  // We copy the templates folder so the agent has the real text logic to replace and rename
@@ -311,7 +377,11 @@ async function runSetup() {
311
377
  }
312
378
 
313
379
  fs.writeFileSync(pdlcDest, pdlcContent);
314
- console.log(`${i18n.pdlc_prefilled}`);
380
+ if (projectId && statusFieldId && Object.keys(optionMap).length > 0) {
381
+ console.log(`${i18n.pdlc_prefilled}`);
382
+ } else {
383
+ console.log(`${yellow}⚠️ pdlc.md copied — Project IDs not filled (board creation failed). Re-run after fixing token.${reset}`);
384
+ }
315
385
  }
316
386
 
317
387
  // Pre-fill project-automation.yml with the same IDs so the agent doesn't need to map them
@@ -338,11 +408,43 @@ async function runSetup() {
338
408
  // Write CLI context for the agent to consume in Setup Mode
339
409
  try {
340
410
  const cliContextPath = path.join(targetDir, '.agentic-pdlc', 'cli-context.json');
341
- fs.writeFileSync(cliContextPath, JSON.stringify({ projectName, repoOwner, repoName, projectNumber }, null, 2));
411
+ const boardUrl = projectNumber ? buildBoardUrl(repoOwner, projectNumber, isOrg) : null;
412
+ fs.writeFileSync(cliContextPath, JSON.stringify({
413
+ projectName,
414
+ repoOwner,
415
+ repoName,
416
+ projectNumber,
417
+ isOrg,
418
+ boardUrl,
419
+ patAutoSet
420
+ }, null, 2));
342
421
  } catch (err) {
343
422
  // Non-fatal — agent will ask for the values instead
344
423
  }
345
424
 
425
+ // Install PDLC stage gate hook (all agents)
426
+ const hookSrc = path.join(sourceDir, 'adapters', 'hooks', 'pdlc-stage-gate.sh');
427
+ const hookDir = path.join(targetDir, '.agentic-pdlc', 'hooks');
428
+ const hookDest = path.join(hookDir, 'pdlc-stage-gate.sh');
429
+ if (fs.existsSync(hookSrc)) {
430
+ fs.mkdirSync(hookDir, { recursive: true });
431
+ fs.copyFileSync(hookSrc, hookDest);
432
+ fs.chmodSync(hookDest, '755');
433
+ }
434
+ const claudeSettingsDir = path.join(targetDir, '.claude');
435
+ const claudeSettingsPath = path.join(claudeSettingsDir, 'settings.json');
436
+ if (!fs.existsSync(claudeSettingsPath)) {
437
+ fs.mkdirSync(claudeSettingsDir, { recursive: true });
438
+ fs.writeFileSync(claudeSettingsPath, JSON.stringify({
439
+ hooks: {
440
+ PreToolUse: [{
441
+ matcher: 'Bash',
442
+ hooks: [{ type: 'command', command: 'bash .agentic-pdlc/hooks/pdlc-stage-gate.sh' }]
443
+ }]
444
+ }
445
+ }, null, 2) + '\n');
446
+ }
447
+
346
448
  // Handle the specific setup instructions target
347
449
  const claudeSetupSrc = path.join(sourceDir, 'adapters', 'claude-code', 'skill.md');
348
450
  const cursorSetupSrc = path.join(sourceDir, 'adapters', 'cursor', 'rules.md');
@@ -536,9 +638,9 @@ async function runUpdate() {
536
638
  if (!['n', 'no', 'não', 'nao'].includes(answer)) {
537
639
  activateQaAgent(paPath);
538
640
  results.push(t(
539
- '✅ QA Agent configured — Variant B activated\n Next: gh secret set GEMINI_API_KEY --body "<your-key>"',
540
- '✅ QA Agent configurado — Variant B ativado\n Próximo: gh secret set GEMINI_API_KEY --body "<sua-chave>"',
541
- '✅ QA Agent configurado — Variant B activado\n Siguiente: gh secret set GEMINI_API_KEY --body "<tu-clave>"'
641
+ '✅ QA Agent configured — Variant B activated (uses GITHUB_TOKEN, no extra secrets needed)',
642
+ '✅ QA Agent configurado — Variant B ativado (usa GITHUB_TOKEN, nenhum secret extra necessário)',
643
+ '✅ QA Agent configurado — Variant B activado (usa GITHUB_TOKEN, sin secrets adicionales)'
542
644
  ));
543
645
  } else {
544
646
  results.push(t('⏭ QA Agent — skipped', '⏭ QA Agent — pulado', '⏭ QA Agent — omitido'));
package/docs/pdlc.md CHANGED
@@ -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 | AI is analyzing code and context | Label `stage:exploration` |
9
9
  | 🧠 Brainstorming | AI proposed approaches and trade-offs | Label `stage:brainstorming` |
10
10
  | 📐 Detail Solution | AI is writing the technical spec | Label `stage:detailing` |
@@ -72,6 +72,10 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
72
72
  | `spec:approved` | Issue | Green | Gate 2 — agent is cleared to implement |
73
73
  | `pr:in-review` | PR | Yellow | Awaiting code review |
74
74
  | `pr:approved` | PR | Green | Code review approved |
75
+ | `type:us` | Issue | Blue | New feature or behavioral change — full flow |
76
+ | `type:task` | Issue | Yellow | Operational/non-functional change — skips brainstorming |
77
+ | `type:bug` | Issue | Red | Something broken — skips brainstorming |
78
+ | `type:spike` | Issue | Gray | Research/evaluation — never reaches Development |
75
79
 
76
80
  ## Approval Gates
77
81
 
@@ -85,10 +89,16 @@ This triggers the implementation agent via `agent-trigger.yml`.
85
89
 
86
90
  ## Shortcuts by Type
87
91
 
88
- - **BUG**Skips Brainstorming; enters Detail Solution with diagnostics + fix.
89
- - **TASK** — Skips Brainstorming; enters Detail Solution with operational steps.
90
- - **SPIKE** Never reaches Development; delivery is a concluding comment.
91
- - **US** — Full flow observing both gates.
92
+ 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.
93
+
94
+ | Label | Flow |
95
+ |---|---|
96
+ | `type:us` | Full flow — exploration → brainstorming → Gate 1 → detailing → approval |
97
+ | `type:task` | Skips brainstorming — exploration → detailing → approval |
98
+ | `type:bug` | Skips brainstorming — exploration → detailing → approval |
99
+ | `type:spike` | Skips brainstorming — exploration → detailing → conclusion comment (never reaches Development) |
100
+
101
+ If no `type:*` label present and agent confidence < 85%, defaults to `type:us` (safe fallback — never skips gates by omission).
92
102
 
93
103
  ## Definition of Done
94
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-pdlc",
3
- "version": "2.1.6",
3
+ "version": "2.2.1",
4
4
  "description": "Agentic PDLC Framework - Conversational setup for your AI coding assistants",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -0,0 +1,20 @@
1
+ {
2
+ "body": "![security-high](https://www.gstatic.com/codereviewagent/security-high-priority.svg) ![high](https://www.gstatic.com/codereviewagent/high-priority.svg)\n\nThe use of `execSync` with template literals containing user-provided variables (such as `${repo}`, `${repoOwner}`, `${repoName}`, and `${branchName}`) is vulnerable to command injection. If a user provides a malicious repository URL or branch name containing shell metacharacters (e.g., `; rm -rf /`), they could execute arbitrary shell commands. It is highly recommended to use `spawnSync` with an arguments array to avoid shell interpretation, or at least sanitize and quote the inputs strictly.",
3
+ "path": "bin/cli.js",
4
+ "line": 155
5
+ }
6
+ {
7
+ "body": "![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)\n\nThe required status check context `\"Sentinel / CI\"` does not match any of the workflow or job names defined in the provided templates (e.g., `PDLC Board Automation` or `Detect Project Board Drift`). This will cause pull requests to be permanently blocked as the required check will never be reported. Based on the provided templates, `\"Detect Project Board Drift\"` seems to be the intended health check job name.\n\n```suggestion\n required_status_checks: { strict: true, checks: [{ context: \"Detect Project Board Drift\" }] },\n```",
8
+ "path": "bin/cli.js",
9
+ "line": 165
10
+ }
11
+ {
12
+ "body": "![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)\n\nParsing `updateOutput` directly can throw a `SyntaxError` if the command fails or returns an empty/invalid response. Although it's inside a `try...catch` block, checking if the output is truthy before parsing and using optional chaining provides better resilience and prevents the script from crashing on unexpected API responses.\n\n```suggestion\n const jsonResponse = updateOutput ? JSON.parse(updateOutput) : null;\n const returnedOptions = jsonResponse?.data?.updateProjectV2SingleSelectField?.projectV2SingleSelectField?.options || [];\n```",
13
+ "path": "bin/cli.js",
14
+ "line": 248
15
+ }
16
+ {
17
+ "body": "![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)\n\nTypo in Spanish translation: \"arquivo\" is Portuguese. In Spanish, it should be \"archivo\".\n\n```suggestion\n console.log(`${cyan}>>> Español: \"Lea el archivo .agentic-setup.md e inicie el Setup Mode\"${reset}`);\n```",
18
+ "path": "bin/cli.js",
19
+ "line": 314
20
+ }