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.
- package/.agentic-pdlc/SETUP_PROMPT.md +73 -0
- package/.agentic-pdlc/hooks/pdlc-stage-gate.sh +39 -0
- package/.agentic-pdlc/metrics/.gitkeep +0 -0
- package/.agentic-pdlc/metrics/2026-W19.md +21 -0
- package/.agentic-pdlc/metrics/raw/2026-W19.jsonl +6 -0
- package/.agentic-pdlc/metrics/raw/2026-W20.jsonl +1 -0
- package/.agentic-pdlc/templates/.github/CODEOWNERS +5 -0
- package/.agentic-pdlc/templates/.github/copilot-instructions.md +12 -0
- package/.agentic-pdlc/templates/.github/workflows/add-to-board.yml +38 -0
- package/.agentic-pdlc/templates/.github/workflows/agent-trigger.yml +146 -0
- package/.agentic-pdlc/templates/.github/workflows/agentic-metrics.yml +412 -0
- package/.agentic-pdlc/templates/.github/workflows/auto-approve.yml +16 -0
- package/.agentic-pdlc/templates/.github/workflows/ci.yml +40 -0
- package/.agentic-pdlc/templates/.github/workflows/pdlc-health-check.yml +123 -0
- package/.agentic-pdlc/templates/.github/workflows/pdlc-stage-gate.yml +51 -0
- package/.agentic-pdlc/templates/.github/workflows/project-automation.yml +278 -0
- package/.agentic-pdlc/templates/.github/workflows/protect-workflows.yml +21 -0
- package/.agentic-pdlc/templates/.github/workflows/qa-agent.yml +128 -0
- package/.agentic-pdlc/templates/AGENTS.md +81 -0
- package/.agentic-pdlc/templates/docs/pdlc.md +15 -5
- package/.agentic-setup-prompt.md +73 -0
- package/.agentic-setup.md +73 -0
- package/.claude/settings.json +15 -0
- package/.cursorrules +9 -0
- package/.github/ISSUE_TEMPLATE/pulse-feedback.md +11 -0
- package/.github/workflows/add-to-board.yml +38 -0
- package/.github/workflows/agent-trigger.yml +30 -43
- package/.github/workflows/agentic-metrics.yml +412 -0
- package/.github/workflows/pdlc-health-check.yml +10 -10
- package/.github/workflows/pdlc-stage-gate.yml +51 -0
- package/.github/workflows/project-automation.yml +68 -18
- package/.github/workflows/qa-agent.yml +112 -11
- package/CLAUDE.md +9 -0
- package/SETUP.md +28 -0
- package/adapters/claude-code/skill.md +63 -15
- package/adapters/hooks/pdlc-stage-gate.sh +44 -0
- package/bin/cli.js +113 -11
- package/docs/pdlc.md +15 -5
- package/package.json +1 -1
- package/pr_comments.json +20 -0
- package/templates/.github/workflows/add-to-board.yml +38 -0
- package/templates/.github/workflows/agent-trigger.yml +34 -4
- package/templates/.github/workflows/pdlc-stage-gate.yml +51 -0
- package/templates/.github/workflows/project-automation.yml +78 -54
- package/templates/.github/workflows/qa-agent.yml +14 -13
- 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:
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 `
|
|
69
|
-
|
|
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.
|
|
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
|
|
72
|
-
update_qa_ask: t(' Activate?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
540
|
-
'✅ QA Agent configurado — Variant B ativado
|
|
541
|
-
'✅ QA Agent configurado — Variant B activado
|
|
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 —
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
package/pr_comments.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"body": " \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": "\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": "\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": "\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
|
+
}
|