create-agentic-pdlc 2.4.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agentic-pdlc/hooks/pdlc-stage-gate.sh +37 -10
- package/.claude/settings.json +18 -0
- package/.coderabbit.yaml +41 -0
- package/.github/workflows/add-to-board.yml +55 -7
- package/.github/workflows/agent-trigger.yml +57 -25
- package/.github/workflows/board-reconciliation.yml +176 -0
- package/.github/workflows/pdlc-health-check.yml +81 -81
- package/.github/workflows/project-automation.yml +252 -259
- package/CLAUDE.md +1 -1
- package/README.md +33 -32
- package/adapters/claude-code/skill.md +12 -8
- package/bin/cli.js +607 -213
- package/docs/superpowers/plans/2026-06-04-spec-format-issue-template.md +160 -0
- package/docs/superpowers/plans/2026-06-04-two-tier-installer.md +1056 -0
- package/docs/superpowers/plans/2026-06-05-archive-card-on-issue-close.md +105 -0
- package/docs/superpowers/plans/2026-06-05-project-id-actions-variable.md +336 -0
- package/docs/superpowers/specs/2026-06-04-spec-format-issue-template-design.md +46 -0
- package/docs/superpowers/specs/2026-06-05-project-id-actions-variable-design.md +114 -0
- package/package.json +2 -2
- package/scripts/derive-column.js +20 -0
- package/templates/.github/workflows/add-to-board.yml +2 -2
- package/templates/.github/workflows/agent-trigger.yml +2 -2
- package/templates/.github/workflows/pdlc-health-check.yml +2 -2
- package/templates/.github/workflows/project-automation.yml +47 -8
- package/templates/full/CLAUDE.md +30 -0
- package/templates/lite/AGENTS.md +121 -0
- package/templates/lite/CLAUDE.md +44 -0
- package/tests/cli.test.js +118 -0
- package/.github/workflows/agentic-metrics.yml +0 -545
- package/.github/workflows/qa-agent.yml +0 -139
- package/.github/workflows/qa-gate.yml +0 -51
- /package/templates/{AGENTS.md → full/AGENTS.md} +0 -0
- /package/templates/{docs → full/docs}/pdlc.md +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Archive Board Card on Issue Close Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Archive the GitHub Project board card when an issue is closed, preventing stale cards from accumulating in active columns.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Single new job appended to `project-automation.yml` template. Triggers on the already-present `issues: closed` event. Uses `addProjectV2ItemById` (idempotent) then `archiveProjectV2Item` — handles both issues closed without a PR and issues closed via PR merge safely.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** GitHub Actions, `actions/github-script@v8`, GitHub GraphQL API (`addProjectV2ItemById`, `archiveProjectV2Item`).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Map
|
|
14
|
+
|
|
15
|
+
| Action | Path | Responsibility |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Modify | `templates/.github/workflows/project-automation.yml` | Add `move-card-on-issue-close` job at end of file |
|
|
18
|
+
|
|
19
|
+
No unit tests — YAML workflow template; correctness verified by grep and manual inspection.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### Task 1: Add `move-card-on-issue-close` job
|
|
24
|
+
|
|
25
|
+
**Files:**
|
|
26
|
+
- Modify: `templates/.github/workflows/project-automation.yml` (append after `cleanup-labels-on-close`)
|
|
27
|
+
|
|
28
|
+
- [ ] **Step 1: Verify the insertion point**
|
|
29
|
+
|
|
30
|
+
Run:
|
|
31
|
+
```bash
|
|
32
|
+
tail -5 templates/.github/workflows/project-automation.yml
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Expected: last line is `console.log(\`Issue #\${issue_number} labels cleaned up\`);` followed by closing braces. The file ends after `cleanup-labels-on-close`.
|
|
36
|
+
|
|
37
|
+
- [ ] **Step 2: Append the new job**
|
|
38
|
+
|
|
39
|
+
At the end of `templates/.github/workflows/project-automation.yml`, add:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
|
|
43
|
+
move-card-on-issue-close:
|
|
44
|
+
name: Closed issue → Archive from board
|
|
45
|
+
if: github.event_name == 'issues' && github.event.action == 'closed'
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
env:
|
|
48
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
49
|
+
steps:
|
|
50
|
+
- name: Archive board card
|
|
51
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
|
|
52
|
+
uses: actions/github-script@v8
|
|
53
|
+
with:
|
|
54
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
55
|
+
script: |
|
|
56
|
+
const nodeId = context.payload.issue.node_id;
|
|
57
|
+
let itemId;
|
|
58
|
+
try {
|
|
59
|
+
const added = await github.graphql(`
|
|
60
|
+
mutation($p: ID!, $c: ID!) {
|
|
61
|
+
addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
|
|
62
|
+
}`, { p: process.env.PROJECT_ID, c: nodeId });
|
|
63
|
+
itemId = added.addProjectV2ItemById.item.id;
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.log(`Could not add issue to project: ${e.message}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
await github.graphql(`
|
|
69
|
+
mutation($p: ID!, $i: ID!) {
|
|
70
|
+
archiveProjectV2Item(input: {projectId: $p, itemId: $i}) { item { id } }
|
|
71
|
+
}`, { p: process.env.PROJECT_ID, i: itemId });
|
|
72
|
+
console.log(`Issue #${context.payload.issue.number} archived from board`);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- [ ] **Step 3: Verify job was added and file is syntactically correct**
|
|
76
|
+
|
|
77
|
+
Run:
|
|
78
|
+
```bash
|
|
79
|
+
grep -n "move-card-on-issue-close\|archiveProjectV2Item" templates/.github/workflows/project-automation.yml
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Expected: two matches — the job name line and the mutation name.
|
|
83
|
+
|
|
84
|
+
Run:
|
|
85
|
+
```bash
|
|
86
|
+
python3 -c "import yaml, sys; yaml.safe_load(open('templates/.github/workflows/project-automation.yml'))" 2>&1
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Expected: no output (valid YAML).
|
|
90
|
+
|
|
91
|
+
- [ ] **Step 4: Verify guard condition matches existing pattern**
|
|
92
|
+
|
|
93
|
+
Run:
|
|
94
|
+
```bash
|
|
95
|
+
grep "PROJECT_TOKEN != ''" templates/.github/workflows/project-automation.yml
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Expected: multiple lines including the new job — confirms guard is consistent with other jobs.
|
|
99
|
+
|
|
100
|
+
- [ ] **Step 5: Commit**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git add templates/.github/workflows/project-automation.yml
|
|
104
|
+
git commit -m "feat(templates): archive board card when issue is closed"
|
|
105
|
+
```
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# PROJECT_ID as Actions Variable Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Replace hardcoded `PROJECT_ID: "PVT_xxx"` in installed YAML files with `${{ vars.PROJECT_ID }}`, and have the installer set the value via the GitHub Actions Variables REST API.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Three workflow templates swap their `PROJECT_ID` env value and guard condition. `bin/cli.js` gains a `setActionsVariable(repo, name, value, execFn)` helper (dependency-injected `execFn` for testability) called after board creation in both the `runFullSetup` and `runUpgradeToAgentic` flows.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js 22, `node:test` + `node:assert/strict`, `gh` CLI (`execFileSync`), GitHub Actions Variables REST API (`PATCH`/`POST /repos/{owner}/{repo}/actions/variables`).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Map
|
|
14
|
+
|
|
15
|
+
| Action | Path | Responsibility |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Modify | `templates/.github/workflows/project-automation.yml` | Remove `{{PROJECT_ID}}` placeholder, source from `vars` |
|
|
18
|
+
| Modify | `templates/.github/workflows/add-to-board.yml` | Same |
|
|
19
|
+
| Modify | `templates/.github/workflows/agent-trigger.yml` | Same |
|
|
20
|
+
| Modify | `bin/cli.js` | Add helper, wire call sites, export helper |
|
|
21
|
+
| Modify | `tests/cli.test.js` | Tests for `setActionsVariable` logic |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### Task 1: Update workflow templates
|
|
26
|
+
|
|
27
|
+
**Files:**
|
|
28
|
+
- Modify: `templates/.github/workflows/project-automation.yml`
|
|
29
|
+
- Modify: `templates/.github/workflows/add-to-board.yml`
|
|
30
|
+
- Modify: `templates/.github/workflows/agent-trigger.yml`
|
|
31
|
+
|
|
32
|
+
No tests for template content — correctness is verified by the installer integration.
|
|
33
|
+
|
|
34
|
+
- [ ] **Step 1: Replace `PROJECT_ID` env value in all three templates**
|
|
35
|
+
|
|
36
|
+
In each of the three files, find every line matching:
|
|
37
|
+
```yaml
|
|
38
|
+
PROJECT_ID: "{{PROJECT_ID}}"
|
|
39
|
+
```
|
|
40
|
+
Replace with:
|
|
41
|
+
```yaml
|
|
42
|
+
PROJECT_ID: ${{ vars.PROJECT_ID }}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`project-automation.yml` line 12, `add-to-board.yml` line 8, `agent-trigger.yml` line 21.
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 2: Replace guard conditions in all three templates**
|
|
48
|
+
|
|
49
|
+
In each of the three files, find every line matching:
|
|
50
|
+
```yaml
|
|
51
|
+
env.PROJECT_ID != '{{PROJECT_ID}}'
|
|
52
|
+
```
|
|
53
|
+
Replace with:
|
|
54
|
+
```yaml
|
|
55
|
+
env.PROJECT_ID != ''
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Do a full-file search in each — there are multiple occurrences per file (every job that conditionally runs).
|
|
59
|
+
|
|
60
|
+
- [ ] **Step 3: Verify no `{{PROJECT_ID}}` remains in workflow files**
|
|
61
|
+
|
|
62
|
+
Run:
|
|
63
|
+
```bash
|
|
64
|
+
grep -rn "{{PROJECT_ID}}" templates/.github/workflows/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Expected: no output. If any lines appear, fix them before proceeding.
|
|
68
|
+
|
|
69
|
+
- [ ] **Step 4: Commit**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git add templates/.github/workflows/project-automation.yml \
|
|
73
|
+
templates/.github/workflows/add-to-board.yml \
|
|
74
|
+
templates/.github/workflows/agent-trigger.yml
|
|
75
|
+
git commit -m "feat(templates): source PROJECT_ID from vars instead of hardcoded placeholder"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Task 2: Remove `{{PROJECT_ID}}` YAML substitution from `scaffoldFullTemplates`
|
|
81
|
+
|
|
82
|
+
**Files:**
|
|
83
|
+
- Modify: `bin/cli.js:345`
|
|
84
|
+
|
|
85
|
+
- [ ] **Step 1: Delete the single PROJECT_ID substitution line**
|
|
86
|
+
|
|
87
|
+
In `bin/cli.js`, find and remove exactly this line (currently line 345):
|
|
88
|
+
```javascript
|
|
89
|
+
if (projectId) wfContent = wfContent.replace(/\{\{PROJECT_ID\}\}/g, () => projectId);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The block around it (lines 342–356) substitutes `STATUS_FIELD_ID` and all `ID_*` column options into `project-automation.yml`. Keep all those lines. Only the `PROJECT_ID` line is removed.
|
|
93
|
+
|
|
94
|
+
- [ ] **Step 2: Verify other substitutions are intact**
|
|
95
|
+
|
|
96
|
+
Run:
|
|
97
|
+
```bash
|
|
98
|
+
grep -n "wfContent.replace" bin/cli.js
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Expected output must include lines for `STATUS_FIELD_ID`, `ID_IDEA`, `ID_BRAINSTORMING`, `ID_DETAILING`, `ID_APPROVAL`, `ID_DEVELOPMENT`, `ID_TESTING`, `ID_CODE_REVIEW_PR`, `ID_PRODUCTION`. Must NOT include `PROJECT_ID`.
|
|
102
|
+
|
|
103
|
+
- [ ] **Step 3: Commit**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git add bin/cli.js
|
|
107
|
+
git commit -m "fix(cli): remove PROJECT_ID hardcoding from scaffoldFullTemplates"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Task 3: Add `setActionsVariable` helper (TDD)
|
|
113
|
+
|
|
114
|
+
**Files:**
|
|
115
|
+
- Modify: `bin/cli.js` (add function before `scaffoldFullTemplates`, ~line 283)
|
|
116
|
+
- Modify: `tests/cli.test.js` (add describe block)
|
|
117
|
+
- Modify: `bin/cli.js:793` (add to `module.exports`)
|
|
118
|
+
|
|
119
|
+
- [ ] **Step 1: Write failing tests**
|
|
120
|
+
|
|
121
|
+
Add to `tests/cli.test.js`:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const { describe, it, mock } = require('node:test');
|
|
125
|
+
|
|
126
|
+
// ... existing imports and tests above ...
|
|
127
|
+
|
|
128
|
+
describe('setActionsVariable', () => {
|
|
129
|
+
it('calls PATCH first', () => {
|
|
130
|
+
const calls = [];
|
|
131
|
+
const execFn = (cmd, args) => { calls.push(args); };
|
|
132
|
+
const { setActionsVariable } = require('../bin/cli.js');
|
|
133
|
+
setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn);
|
|
134
|
+
assert.equal(calls.length, 1);
|
|
135
|
+
assert.ok(calls[0].includes('--method'));
|
|
136
|
+
assert.ok(calls[0].includes('PATCH'));
|
|
137
|
+
assert.ok(calls[0].some(a => a.includes('PROJECT_ID')));
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('falls back to POST on 404', () => {
|
|
141
|
+
const calls = [];
|
|
142
|
+
let callCount = 0;
|
|
143
|
+
const execFn = (cmd, args) => {
|
|
144
|
+
calls.push([...args]);
|
|
145
|
+
callCount++;
|
|
146
|
+
if (callCount === 1) {
|
|
147
|
+
const err = new Error('Not Found');
|
|
148
|
+
err.stderr = Buffer.from('Not Found');
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const { setActionsVariable } = require('../bin/cli.js');
|
|
153
|
+
setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn);
|
|
154
|
+
assert.equal(calls.length, 2);
|
|
155
|
+
assert.ok(calls[0].includes('PATCH'));
|
|
156
|
+
assert.ok(calls[1].includes('POST'));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('throws on 403', () => {
|
|
160
|
+
const execFn = () => {
|
|
161
|
+
const err = new Error('Forbidden');
|
|
162
|
+
err.stderr = Buffer.from('Forbidden');
|
|
163
|
+
throw err;
|
|
164
|
+
};
|
|
165
|
+
const { setActionsVariable } = require('../bin/cli.js');
|
|
166
|
+
assert.throws(
|
|
167
|
+
() => setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn),
|
|
168
|
+
/Forbidden/
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npm test
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Expected: 3 new test failures — `setActionsVariable` is not exported yet.
|
|
181
|
+
|
|
182
|
+
- [ ] **Step 3: Add `setActionsVariable` to `bin/cli.js`**
|
|
183
|
+
|
|
184
|
+
Insert this function before `scaffoldFullTemplates` (~line 283):
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
function setActionsVariable(repo, name, value, execFn = execFileSync) {
|
|
188
|
+
try {
|
|
189
|
+
execFn('gh', [
|
|
190
|
+
'api', `repos/${repo}/actions/variables/${name}`,
|
|
191
|
+
'--method', 'PATCH',
|
|
192
|
+
'-f', `name=${name}`,
|
|
193
|
+
'-f', `value=${value}`
|
|
194
|
+
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const msg = (err.stderr?.toString() || '') + (err.message || '');
|
|
197
|
+
if (msg.includes('404') || msg.includes('Not Found')) {
|
|
198
|
+
execFn('gh', [
|
|
199
|
+
'api', `repos/${repo}/actions/variables`,
|
|
200
|
+
'--method', 'POST',
|
|
201
|
+
'-f', `name=${name}`,
|
|
202
|
+
'-f', `value=${value}`
|
|
203
|
+
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
204
|
+
} else {
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- [ ] **Step 4: Export the function**
|
|
212
|
+
|
|
213
|
+
In `bin/cli.js` at line 793, update:
|
|
214
|
+
```javascript
|
|
215
|
+
if (typeof module !== 'undefined') module.exports = { resolveMode };
|
|
216
|
+
```
|
|
217
|
+
to:
|
|
218
|
+
```javascript
|
|
219
|
+
if (typeof module !== 'undefined') module.exports = { resolveMode, setActionsVariable };
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- [ ] **Step 5: Run tests to verify they pass**
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npm test
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Expected: all tests pass including the 3 new `setActionsVariable` tests.
|
|
229
|
+
|
|
230
|
+
- [ ] **Step 6: Commit**
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
git add bin/cli.js tests/cli.test.js
|
|
234
|
+
git commit -m "feat(cli): add setActionsVariable helper with PATCH→POST fallback"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### Task 4: Wire `setActionsVariable` into the create flow (`runFullSetup`)
|
|
240
|
+
|
|
241
|
+
**Files:**
|
|
242
|
+
- Modify: `bin/cli.js` (~line 554)
|
|
243
|
+
|
|
244
|
+
- [ ] **Step 1: Add the call site after the PROJECT_PAT block**
|
|
245
|
+
|
|
246
|
+
In `bin/cli.js`, locate the end of the `PROJECT_PAT` block in `runFullSetup` (the `} else if (projectId && isOrg)` line at ~554). Add after it:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// Set PROJECT_ID as GitHub Actions Variable
|
|
250
|
+
if (projectId) {
|
|
251
|
+
try {
|
|
252
|
+
setActionsVariable(repo, 'PROJECT_ID', projectId);
|
|
253
|
+
console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
|
|
254
|
+
} catch (_) {
|
|
255
|
+
console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Insert this block between line 554 (`} else if (projectId && isOrg) { ... }`) and line 556 (`await setBranchProtection(...)`).
|
|
261
|
+
|
|
262
|
+
- [ ] **Step 2: Verify placement**
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
grep -n "vars.PROJECT_ID\|setBranchProtection\|isOrg\)" bin/cli.js | head -20
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The `vars.PROJECT_ID` console log should appear between the `isOrg` block and `setBranchProtection`.
|
|
269
|
+
|
|
270
|
+
- [ ] **Step 3: Commit**
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
git add bin/cli.js
|
|
274
|
+
git commit -m "feat(cli): set vars.PROJECT_ID as Actions Variable in create flow"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### Task 5: Wire `setActionsVariable` into the upgrade flow (`runUpgradeToAgentic`)
|
|
280
|
+
|
|
281
|
+
**Files:**
|
|
282
|
+
- Modify: `bin/cli.js` (~line 1006)
|
|
283
|
+
|
|
284
|
+
- [ ] **Step 1: Add the call site after the PROJECT_PAT block in the upgrade flow**
|
|
285
|
+
|
|
286
|
+
In `bin/cli.js`, locate the end of the `PROJECT_PAT` block in `runUpgradeToAgentic` (the closing `}` of `if (projectId && !isOrg)` at ~line 1006). Add after it:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// Set PROJECT_ID as GitHub Actions Variable
|
|
290
|
+
if (projectId) {
|
|
291
|
+
try {
|
|
292
|
+
setActionsVariable(repo, 'PROJECT_ID', projectId);
|
|
293
|
+
console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
|
|
294
|
+
} catch (_) {
|
|
295
|
+
console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Insert between line ~1006 (`}` closing the `PROJECT_PAT` block) and line ~1008 (`console.log scaffolding`).
|
|
301
|
+
|
|
302
|
+
- [ ] **Step 2: Verify no `{{PROJECT_ID}}` placeholder survives install**
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
grep -rn "{{PROJECT_ID}}" templates/
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Expected: only `templates/full/docs/pdlc.md` (the documentation file). No workflow YAML files.
|
|
309
|
+
|
|
310
|
+
- [ ] **Step 3: Run full test suite**
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
npm test
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Expected: all tests pass.
|
|
317
|
+
|
|
318
|
+
- [ ] **Step 4: Commit**
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
git add bin/cli.js
|
|
322
|
+
git commit -m "feat(cli): set vars.PROJECT_ID as Actions Variable in upgrade flow"
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Self-Review Checklist
|
|
328
|
+
|
|
329
|
+
- Acceptance Criteria 1 (new install sets `vars.PROJECT_ID`): Task 4 ✓
|
|
330
|
+
- Acceptance Criteria 2 (`resolve-ids` works without YAML modification): Task 1 ✓
|
|
331
|
+
- Acceptance Criteria 3 (`--update` sets variable): Task 5 ✓
|
|
332
|
+
- Acceptance Criteria 4 (403 warning, non-fatal): Tasks 3, 4, 5 ✓
|
|
333
|
+
- Edge case — PATCH first, POST on 404: Task 3 ✓
|
|
334
|
+
- Edge case — `vars.PROJECT_ID` unset resolves to `''`, guard works: Task 1 ✓
|
|
335
|
+
- No `{{PROJECT_ID}}` in any YAML after install: Tasks 1 + 2 ✓
|
|
336
|
+
- All other ID substitutions preserved: Task 2 Step 2 ✓
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Design: ship spec-format issue template (lite)
|
|
2
|
+
|
|
3
|
+
**Issue:** #157
|
|
4
|
+
**Date:** 2026-06-04
|
|
5
|
+
**Status:** approved
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
`npx create-agentic-pdlc` copies templates to `.agentic-pdlc/templates/` — a path GitHub ignores entirely. Users who open "New Issue" after setup see a blank text box instead of the spec format template.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Copy `templates/.github/ISSUE_TEMPLATE/` directly to `targetDir/.github/ISSUE_TEMPLATE/` during `runSetup()` in `bin/cli.js`. No agent involvement — templates have no `{{PLACEHOLDER}}` substitution, so the CLI can write them directly.
|
|
14
|
+
|
|
15
|
+
## Change
|
|
16
|
+
|
|
17
|
+
**File:** `bin/cli.js`
|
|
18
|
+
|
|
19
|
+
After the existing `copyDirSync(sourceTemplates, targetTemplates)` block (around line 386):
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// i18n string (add to i18n object)
|
|
23
|
+
issue_templates_copied: t(
|
|
24
|
+
'✅ Issue templates copied to .github/ISSUE_TEMPLATE/',
|
|
25
|
+
'✅ Issue templates copiados para .github/ISSUE_TEMPLATE/',
|
|
26
|
+
'✅ Issue templates copiados a .github/ISSUE_TEMPLATE/'
|
|
27
|
+
),
|
|
28
|
+
|
|
29
|
+
// Copy block (add inside runSetup(), after template copy)
|
|
30
|
+
const sourceIssueTemplates = path.join(sourceDir, 'templates', '.github', 'ISSUE_TEMPLATE');
|
|
31
|
+
const targetIssueTemplates = path.join(targetDir, '.github', 'ISSUE_TEMPLATE');
|
|
32
|
+
if (fs.existsSync(sourceIssueTemplates)) {
|
|
33
|
+
copyDirSync(sourceIssueTemplates, targetIssueTemplates);
|
|
34
|
+
console.log(i18n.issue_templates_copied);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Edge Cases
|
|
39
|
+
|
|
40
|
+
- `.github/ISSUE_TEMPLATE/` already exists → `copyDirSync` overwrites. Correct — boilerplate, not user customizations.
|
|
41
|
+
- Source dir missing (corrupt install) → guarded by `if (fs.existsSync(...))`, skips silently.
|
|
42
|
+
|
|
43
|
+
## Out of Scope
|
|
44
|
+
|
|
45
|
+
- Changes to template content — existing `feature.md`, `bug.md`, `task.md` already have all required sections.
|
|
46
|
+
- Changes to `adapters/claude-code/skill.md` — agent setup not involved.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Design: Set PROJECT_ID as Actions Variable During Install
|
|
2
|
+
|
|
3
|
+
**Issue:** #179
|
|
4
|
+
**Date:** 2026-06-05
|
|
5
|
+
**Status:** Approved
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
The `create-agentic-pdlc` installer embeds the GitHub Project ID (`PVT_xxx`) directly into workflow YAML files by replacing `{{PROJECT_ID}}` placeholders during `scaffoldFullTemplates`. This means new repo installs get workflows with a hardcoded value in source-controlled files — brittle, hard to rotate, and inconsistent with how GitHub recommends storing non-secret configuration.
|
|
10
|
+
|
|
11
|
+
The fix: set `vars.PROJECT_ID` as a GitHub Actions Variable via the REST API during install, and have workflow templates source it from `vars` at runtime.
|
|
12
|
+
|
|
13
|
+
## Approach: env block sourced from `vars`
|
|
14
|
+
|
|
15
|
+
Keep the `PROJECT_ID:` entry in the workflow-level `env` block — just change its value from a hardcoded placeholder to `${{ vars.PROJECT_ID }}`. All `process.env.PROJECT_ID` references inside `actions/github-script` bodies remain unchanged. Minimal diff, maximum behavioral parity.
|
|
16
|
+
|
|
17
|
+
Rejected alternatives:
|
|
18
|
+
- **Inline `vars` everywhere**: larger diff, all script bodies change, no benefit.
|
|
19
|
+
- **Set vars + keep YAML hardcode**: adds complexity, defeats the goal.
|
|
20
|
+
|
|
21
|
+
## Changes
|
|
22
|
+
|
|
23
|
+
### 1. Templates — 3 workflow files
|
|
24
|
+
|
|
25
|
+
Files: `templates/.github/workflows/project-automation.yml`, `add-to-board.yml`, `agent-trigger.yml`
|
|
26
|
+
|
|
27
|
+
Every occurrence of:
|
|
28
|
+
|
|
29
|
+
| Before | After |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `PROJECT_ID: "{{PROJECT_ID}}"` | `PROJECT_ID: ${{ vars.PROJECT_ID }}` |
|
|
32
|
+
| `env.PROJECT_ID != '{{PROJECT_ID}}'` | `env.PROJECT_ID != ''` |
|
|
33
|
+
|
|
34
|
+
Guard correctness: when `vars.PROJECT_ID` is unset, `${{ vars.PROJECT_ID }}` resolves to `''` at workflow startup → `env.PROJECT_ID != ''` suppresses execution cleanly. Verified: `vars.*` in workflow-level `env` block resolves before jobs run.
|
|
35
|
+
|
|
36
|
+
`pdlc.md` keeps `{{PROJECT_ID}}` substitution — it is a documentation file, not a YAML workflow.
|
|
37
|
+
|
|
38
|
+
### 2. `bin/cli.js` — new helper `setActionsVariable`
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
function setActionsVariable(repo, name, value) {
|
|
42
|
+
try {
|
|
43
|
+
execFileSync('gh', ['api', `repos/${repo}/actions/variables/${name}`,
|
|
44
|
+
'--method', 'PATCH', '-f', `name=${name}`, '-f', `value=${value}`],
|
|
45
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] });
|
|
46
|
+
} catch (err) {
|
|
47
|
+
const msg = err.stderr?.toString() || '';
|
|
48
|
+
if (msg.includes('404') || msg.includes('Not Found')) {
|
|
49
|
+
execFileSync('gh', ['api', `repos/${repo}/actions/variables`,
|
|
50
|
+
'--method', 'POST', '-f', `name=${name}`, '-f', `value=${value}`],
|
|
51
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] });
|
|
52
|
+
} else {
|
|
53
|
+
throw err; // 403 bubbles up → caller emits user-visible warning
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Uses `gh api` via `execFileSync` — consistent with all other API calls in `cli.js`. PATCH on existing variable, POST on 404, throws on 403 so the caller can warn the user.
|
|
60
|
+
|
|
61
|
+
**Token scope requirement:** fine-grained PAT needs `variables:write`; classic PAT needs `repo` scope. `GITHUB_TOKEN` (workflow-issued) will return 403 — cannot set repo variables. The installer already calls `gh auth token` for `PROJECT_PAT`; the same authenticated session is used here.
|
|
62
|
+
|
|
63
|
+
### 3. `bin/cli.js` — `scaffoldFullTemplates` (line 345)
|
|
64
|
+
|
|
65
|
+
Remove the single line that substitutes `{{PROJECT_ID}}` in `project-automation.yml`:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// REMOVE this line:
|
|
69
|
+
if (projectId) wfContent = wfContent.replace(/\{\{PROJECT_ID\}\}/g, () => projectId);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All other substitutions on lines 346–355 (`STATUS_FIELD_ID`, `ID_BRAINSTORMING`, `ID_DETAILING`, etc.) are preserved unchanged.
|
|
73
|
+
|
|
74
|
+
### 4. Create flow — call site
|
|
75
|
+
|
|
76
|
+
Inside the existing `if (projectId)` block (after `PROJECT_PAT` secret is set, ~line 541):
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
try {
|
|
80
|
+
setActionsVariable(repo, 'PROJECT_ID', projectId);
|
|
81
|
+
console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
|
|
82
|
+
} catch (_) {
|
|
83
|
+
console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set it manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Non-fatal. Install continues regardless.
|
|
88
|
+
|
|
89
|
+
### 5. `--update` flow — same call site
|
|
90
|
+
|
|
91
|
+
The `--update` command is a **lite → full upgrade** — it exits early if the profile is already `full` (line 865). For lite → full, a new board is created via `createProjectV2` (line 920), producing a fresh `projectId`. Call `setActionsVariable` in the same `if (projectId)` block after board creation. Identical error handling to the create flow.
|
|
92
|
+
|
|
93
|
+
No "find existing project" query is needed. `projectId` is always available from the mutation result in both flows.
|
|
94
|
+
|
|
95
|
+
## Acceptance Criteria
|
|
96
|
+
|
|
97
|
+
- `npx create-agentic-pdlc` → `vars.PROJECT_ID` set as GitHub Actions Variable on the target repo
|
|
98
|
+
- No `PVT_xxx` value appears in any installed YAML file
|
|
99
|
+
- `resolve-ids` job resolves column IDs without any YAML modification
|
|
100
|
+
- `--update` (lite → full) → `vars.PROJECT_ID` set with the newly created board ID
|
|
101
|
+
- If token lacks scope → installer prints actionable warning with manual steps; install does not abort
|
|
102
|
+
|
|
103
|
+
## Out of Scope
|
|
104
|
+
|
|
105
|
+
- Migrating existing full installs (board already set up, YAML already hardcoded)
|
|
106
|
+
- Changing how `PROJECT_ID` is resolved during install (GraphQL flow unchanged)
|
|
107
|
+
- `--update` on an already-full install (exits early before any board logic runs)
|
|
108
|
+
|
|
109
|
+
## Files to Modify
|
|
110
|
+
|
|
111
|
+
- `templates/.github/workflows/project-automation.yml`
|
|
112
|
+
- `templates/.github/workflows/add-to-board.yml`
|
|
113
|
+
- `templates/.github/workflows/agent-trigger.yml`
|
|
114
|
+
- `bin/cli.js` — `setActionsVariable` helper, `scaffoldFullTemplates` line 345, create flow call site, update flow call site
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agentic-pdlc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Agentic PDLC Framework - Conversational setup for your AI coding assistants",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-agentic-pdlc": "./bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "node --test tests/cli.test.js"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"agentic",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Single source of truth for label → board column classification.
|
|
2
|
+
// Used by the event dispatcher (project-automation.yml) and the reconciliation cron (board-reconciliation.yml).
|
|
3
|
+
// pr:* beats stage:* — a live PR is higher-confidence signal than a stage label that may not have been cleaned up.
|
|
4
|
+
const LABEL_PRIORITY = [
|
|
5
|
+
{ label: 'pr:in-review', column: 'code_review_pr' },
|
|
6
|
+
{ label: 'pr:approved', column: 'code_review_pr' },
|
|
7
|
+
{ label: 'stage:development', column: 'development' },
|
|
8
|
+
{ label: 'stage:approval', column: 'approval' },
|
|
9
|
+
{ label: 'stage:detailing', column: 'detailing' },
|
|
10
|
+
{ label: 'stage:brainstorming', column: 'brainstorming' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function classifyItem(labelNames) {
|
|
14
|
+
for (const { label, column } of LABEL_PRIORITY) {
|
|
15
|
+
if (labelNames.includes(label)) return column;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { classifyItem, LABEL_PRIORITY };
|
|
@@ -5,7 +5,7 @@ on:
|
|
|
5
5
|
types: [opened]
|
|
6
6
|
|
|
7
7
|
env:
|
|
8
|
-
PROJECT_ID:
|
|
8
|
+
PROJECT_ID: ${{ vars.PROJECT_ID }}
|
|
9
9
|
STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
|
|
10
10
|
STATUS_IDEA: "{{ID_IDEA}}"
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
18
18
|
steps:
|
|
19
19
|
- name: Add issue to project board
|
|
20
|
-
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '
|
|
20
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
|
|
21
21
|
uses: actions/github-script@v8
|
|
22
22
|
with:
|
|
23
23
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
contents: read
|
|
19
19
|
env:
|
|
20
20
|
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
21
|
-
PROJECT_ID:
|
|
21
|
+
PROJECT_ID: ${{ vars.PROJECT_ID }}
|
|
22
22
|
STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
|
|
23
23
|
STATUS_DEVELOPMENT: "{{ID_DEVELOPMENT}}"
|
|
24
24
|
steps:
|
|
@@ -62,7 +62,7 @@ jobs:
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
- name: Move board card to Development
|
|
65
|
-
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '
|
|
65
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
|
|
66
66
|
continue-on-error: true
|
|
67
67
|
uses: actions/github-script@v8
|
|
68
68
|
with:
|