forgedock 1.0.1 → 1.0.3
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/README.md +19 -2
- package/bin/forgedock.mjs +3084 -80
- package/bin/github-app-manifest.json +24 -0
- package/bin/tui.mjs +702 -0
- package/commands/analytics.md +129 -52
- package/commands/audit-agents.md +29 -5
- package/commands/audit.md +55 -17
- package/commands/autopilot.md +11 -6
- package/commands/cleanup.md +44 -24
- package/commands/deploy-info.md +7 -3
- package/commands/forge-stats.md +13 -4
- package/commands/forgedock-init.md +534 -0
- package/commands/geo-audit.md +114 -26
- package/commands/incident-response.md +4 -1
- package/commands/issue.md +26 -25
- package/commands/milestone.md +80 -59
- package/commands/orchestrate.md +38 -21
- package/commands/pipeline-health.md +26 -15
- package/commands/qa-sweep.md +68 -5
- package/commands/quality-gate.md +18 -0
- package/commands/review-pr-agents.md +58 -90
- package/commands/review-pr.md +58 -16
- package/commands/rollback.md +8 -5
- package/commands/security-audit.md +68 -12
- package/commands/sync-ecosystem.md +196 -156
- package/commands/work-on/build/architect.md +2 -2
- package/commands/work-on/build/context.md +2 -2
- package/commands/work-on/build/implement.md +3 -3
- package/commands/work-on/build/validate.md +3 -3
- package/commands/work-on/build.md +6 -6
- package/commands/work-on/close.md +59 -30
- package/commands/work-on/decompose.md +6 -6
- package/commands/work-on/investigate.md +16 -20
- package/commands/work-on/review.md +4 -4
- package/commands/work-on-monolithic.md +15 -16
- package/commands/work-on.md +45 -33
- package/docs/CODEX.md +1 -1
- package/docs/CONFIG.md +398 -0
- package/docs/WORKFLOW.md +115 -72
- package/package.json +1 -1
- package/scripts/verify-host-headers.sh +14 -9
- package/scripts/verify-route-registration.sh +10 -10
- package/commands/failure-recon.md +0 -375
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: AI-powered forge.yaml config generator — scans codebase, queries GitHub, fills all optional sections interactively
|
|
3
|
+
argument-hint: [--force | --section <name>]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /forgedock-init — AI-Powered Config Generator
|
|
7
|
+
|
|
8
|
+
**Input**: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
You complete the `forge.yaml` configuration that `npx forgedock init` started. The CLI generates required sections (project, paths, branches) from auto-detection. Your job is to scan the codebase, query GitHub APIs, ask targeted clarifying questions, and produce a complete `forge.yaml` with every applicable optional section filled.
|
|
11
|
+
|
|
12
|
+
**Agent model policy**: Default `model: "sonnet"`. Fallback: `model: "opus"` if Sonnet is rate-limited.
|
|
13
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Argument Parsing
|
|
18
|
+
|
|
19
|
+
| Flag | Effect |
|
|
20
|
+
|------|--------|
|
|
21
|
+
| (none) | Fill all optional sections not yet configured |
|
|
22
|
+
| `--force` | Overwrite ALL optional sections without asking |
|
|
23
|
+
| `--section <name>` | Fill only one section: `repos`, `project_board`, `services`, `review`, or `verification` |
|
|
24
|
+
|
|
25
|
+
Parse `$ARGUMENTS` and set:
|
|
26
|
+
```
|
|
27
|
+
FORCE = true if --force present
|
|
28
|
+
TARGET_SECTION = value from --section <name>, or "all"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If `--section` was provided, validate the value immediately:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ALLOWED_SECTIONS="repos project_board services review verification"
|
|
35
|
+
if [ "$TARGET_SECTION" != "all" ]; then
|
|
36
|
+
VALID=false
|
|
37
|
+
for s in $ALLOWED_SECTIONS; do
|
|
38
|
+
[ "$TARGET_SECTION" = "$s" ] && VALID=true && break
|
|
39
|
+
done
|
|
40
|
+
if [ "$VALID" = "false" ]; then
|
|
41
|
+
echo "Error: '$TARGET_SECTION' is not a valid section name."
|
|
42
|
+
echo ""
|
|
43
|
+
echo "Valid sections: repos, project_board, services, review, verification"
|
|
44
|
+
echo ""
|
|
45
|
+
echo "Usage: /forgedock-init --section <name>"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Phase 1: Load forge.yaml
|
|
54
|
+
|
|
55
|
+
### 1A: Locate and read
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
FORGE_YAML="$(pwd)/forge.yaml"
|
|
59
|
+
if [ ! -f "$FORGE_YAML" ]; then
|
|
60
|
+
echo "forge.yaml not found in current directory."
|
|
61
|
+
echo ""
|
|
62
|
+
echo "Run first: npx forgedock init"
|
|
63
|
+
echo ""
|
|
64
|
+
echo "This generates the required skeleton (project, paths, branches)."
|
|
65
|
+
echo "Then re-run /forgedock-init to complete the optional sections."
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
cat "$FORGE_YAML"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 1B: Extract required section values
|
|
72
|
+
|
|
73
|
+
Parse these from the existing file — they are needed for all subsequent queries:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
OWNER = project.owner
|
|
77
|
+
REPO = project.repo
|
|
78
|
+
REPO_ROOT = paths.root
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
After extracting these values, validate that OWNER and REPO are non-empty and do not contain placeholder values before proceeding:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
if [ -z "$OWNER" ] || [ "$OWNER" = "your-github-org" ]; then
|
|
85
|
+
echo "Error: project.owner is not set in forge.yaml."
|
|
86
|
+
echo ""
|
|
87
|
+
echo "Open forge.yaml and fill in the 'project:' section:"
|
|
88
|
+
echo " project:"
|
|
89
|
+
echo " owner: \"your-actual-github-org-or-username\""
|
|
90
|
+
echo ""
|
|
91
|
+
echo "Then re-run /forgedock-init."
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ -z "$REPO" ] || [ "$REPO" = "your-repo-name" ]; then
|
|
96
|
+
echo "Error: project.repo is not set in forge.yaml."
|
|
97
|
+
echo ""
|
|
98
|
+
echo "Open forge.yaml and fill in the 'project:' section:"
|
|
99
|
+
echo " project:"
|
|
100
|
+
echo " repo: \"your-actual-repo-name\""
|
|
101
|
+
echo ""
|
|
102
|
+
echo "Then re-run /forgedock-init."
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 1C: Detect already-configured optional sections
|
|
108
|
+
|
|
109
|
+
For each optional section, check if it has an UNCOMMENTED entry in the file. A commented-out section (all lines starting with `#`) is NOT configured.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Check which optional sections are already active (uncommented)
|
|
113
|
+
grep -q "^repos:" "$FORGE_YAML" && HAS_REPOS=true || HAS_REPOS=false
|
|
114
|
+
grep -q "^project_board:" "$FORGE_YAML" && HAS_PROJECT_BOARD=true || HAS_PROJECT_BOARD=false
|
|
115
|
+
grep -q "^services:" "$FORGE_YAML" && HAS_SERVICES=true || HAS_SERVICES=false
|
|
116
|
+
grep -q "^review:" "$FORGE_YAML" && HAS_REVIEW=true || HAS_REVIEW=false
|
|
117
|
+
grep -q "^verification:" "$FORGE_YAML" && HAS_VERIFICATION=true || HAS_VERIFICATION=false
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If `FORCE=false` and a section is already active, skip it (unless `--section` targets it explicitly). Tell the user which sections were skipped and why.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Phase 2: Codebase Scan
|
|
125
|
+
|
|
126
|
+
Run these scans unconditionally — they populate defaults that reduce the number of clarifying questions needed.
|
|
127
|
+
|
|
128
|
+
### 2A: Tech stack detection
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Node.js / TypeScript
|
|
132
|
+
[ -f "$REPO_ROOT/package.json" ] && \
|
|
133
|
+
cat "$REPO_ROOT/package.json" | grep -E '"name"|"dependencies"|"devDependencies"' | head -30
|
|
134
|
+
|
|
135
|
+
# Python
|
|
136
|
+
[ -f "$REPO_ROOT/pyproject.toml" ] && grep -E "^\[project\]|^name|^requires-python|^dependencies" "$REPO_ROOT/pyproject.toml" | head -20
|
|
137
|
+
[ -f "$REPO_ROOT/requirements.txt" ] && head -20 "$REPO_ROOT/requirements.txt"
|
|
138
|
+
|
|
139
|
+
# Rust
|
|
140
|
+
[ -f "$REPO_ROOT/Cargo.toml" ] && grep -E "^\[package\]|^name|^edition|^\[dependencies\]" "$REPO_ROOT/Cargo.toml" | head -20
|
|
141
|
+
|
|
142
|
+
# Ruby
|
|
143
|
+
[ -f "$REPO_ROOT/Gemfile" ] && head -15 "$REPO_ROOT/Gemfile"
|
|
144
|
+
|
|
145
|
+
# Go
|
|
146
|
+
[ -f "$REPO_ROOT/go.mod" ] && head -10 "$REPO_ROOT/go.mod"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Build `TECH_STACK` description from findings (e.g., "Next.js 15, FastAPI, PostgreSQL").
|
|
150
|
+
|
|
151
|
+
### 2B: Project context
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# CLAUDE.md — primary context source
|
|
155
|
+
[ -f "$REPO_ROOT/CLAUDE.md" ] && cat "$REPO_ROOT/CLAUDE.md"
|
|
156
|
+
|
|
157
|
+
# README.md — fallback
|
|
158
|
+
[ -f "$REPO_ROOT/README.md" ] && head -60 "$REPO_ROOT/README.md"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Extract a 2–4 sentence project description for `review.context`.
|
|
162
|
+
|
|
163
|
+
### 2C: Docker / service names
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Container name prefixes for verification section
|
|
167
|
+
ls "$REPO_ROOT"/docker-compose*.yml 2>/dev/null
|
|
168
|
+
for f in "$REPO_ROOT"/docker-compose*.yml; do
|
|
169
|
+
[ -f "$f" ] || continue
|
|
170
|
+
grep -E "^\s+container_name:|^\s+image:" "$f" | head -20
|
|
171
|
+
done
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Extract container name prefixes (e.g., `acme-`, `my-app-`) for `verification.internal_service_patterns` and `verification.services`.
|
|
175
|
+
|
|
176
|
+
### 2D: Branch topology
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Staging branch detection
|
|
180
|
+
git -C "$REPO_ROOT" branch -r | grep -E "staging|develop|dev" | head -5
|
|
181
|
+
|
|
182
|
+
# CI/CD target branches
|
|
183
|
+
grep -rh "branches:" "$REPO_ROOT/.github/workflows/"*.yml 2>/dev/null | head -20
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Use to confirm or suggest `branches.staging`.
|
|
187
|
+
|
|
188
|
+
### 2E: API health endpoint hints
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Look for health route definitions
|
|
192
|
+
grep -rn "health\|/ping\|/status" "$REPO_ROOT" --include="*.py" --include="*.ts" --include="*.go" -l 2>/dev/null | head -10
|
|
193
|
+
grep -rn "healthcheck\|health_endpoint\|HEALTH_URL" "$REPO_ROOT" --include="*.yml" --include="*.yaml" -l 2>/dev/null | head -5
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Phase 3: GitHub Queries
|
|
199
|
+
|
|
200
|
+
Run these to gather live IDs. Failures are non-blocking — skip the section if the query fails.
|
|
201
|
+
|
|
202
|
+
### 3A: Check GitHub auth
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
gh auth status 2>&1
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
If not authenticated, skip all GitHub queries and note which sections will need manual completion.
|
|
209
|
+
|
|
210
|
+
### 3B: Project board discovery
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# List projects owned by this org/user
|
|
214
|
+
gh project list --owner "$OWNER" --format json 2>/dev/null \
|
|
215
|
+
| jq '.projects[] | {number: .number, title: .title, id: .id}'
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
If projects found: ask the user which one ForgeDock should use (or `none`). Record:
|
|
219
|
+
- `PROJECT_NUMBER`
|
|
220
|
+
- `PROJECT_ID` (the `PVT_...` node ID)
|
|
221
|
+
|
|
222
|
+
### 3C: Project board field IDs
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Only run if PROJECT_NUMBER is set
|
|
226
|
+
gh project field-list "$PROJECT_NUMBER" --owner "$OWNER" --format json 2>/dev/null \
|
|
227
|
+
| jq '.fields[] | {name: .name, id: .id, type: .type}'
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Map field names to their IDs:
|
|
231
|
+
- Look for fields named `Status`, `Lane`, `Component`, `Priority`, `Workflow` (case-insensitive)
|
|
232
|
+
- Record their `PVTSSF_...` IDs
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Get option IDs for single-select fields
|
|
236
|
+
gh project field-list "$PROJECT_NUMBER" --owner "$OWNER" --format json 2>/dev/null \
|
|
237
|
+
| jq '.fields[] | select(.type == "SINGLE_SELECT") | {name: .name, options: [.options[] | {name: .name, id: .id}]}'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Map option names to IDs for `status` (Todo/In Progress/Done), `lane` (Fast/Feature/Sync), `priority` (P0/P1/P2/P3), `workflow` (Investigating/Building/In Review/Merged).
|
|
241
|
+
|
|
242
|
+
### 3D: Satellite repo discovery
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# List all repos under this owner
|
|
246
|
+
gh repo list "$OWNER" --limit 50 --json name,description,url 2>/dev/null \
|
|
247
|
+
| jq '.[] | select(.name != "'"$REPO"'") | {name: .name, description: .description}'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Present the list and ask: "Do any of these serve as satellite repos that you'd route issues to with a prefix (e.g., `mcp:5`)?"
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Phase 4: Interactive Clarification
|
|
255
|
+
|
|
256
|
+
Ask only about items NOT already determined by Phases 2–3. Keep questions grouped by section. Use `read` or present multiple-choice.
|
|
257
|
+
|
|
258
|
+
### 4A: repos section
|
|
259
|
+
|
|
260
|
+
Only ask if satellite repos were found in 3D:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
The following repos were found under {OWNER}:
|
|
264
|
+
- {repo_name}: {description}
|
|
265
|
+
(...)
|
|
266
|
+
|
|
267
|
+
Do any of these serve as satellite repos for ForgeDock routing?
|
|
268
|
+
If yes, enter a prefix:repo mapping (e.g., "mcp:acme-mcp-server"), or "none":
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 4B: project_board section
|
|
272
|
+
|
|
273
|
+
If project board found in 3B but field mapping is ambiguous:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
Found project board: "{TITLE}" (#{PROJECT_NUMBER})
|
|
277
|
+
|
|
278
|
+
Field mapping (press Enter to accept detected name, or type correct field name):
|
|
279
|
+
Status field → detected: "{DETECTED}" (ID: {ID})
|
|
280
|
+
Lane field → detected: "{DETECTED}" (ID: {ID})
|
|
281
|
+
...
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 4C: services section
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
Does your project use any of the following analytics/monitoring services?
|
|
288
|
+
[1] Umami (self-hosted)
|
|
289
|
+
[2] Microsoft Clarity
|
|
290
|
+
[3] Google Analytics 4
|
|
291
|
+
[4] None / Skip this section
|
|
292
|
+
|
|
293
|
+
Enter numbers separated by commas (e.g., "1,3"), or "4" to skip:
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
If any selected, ask for the specific IDs/URLs required by each.
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
Do you have an API URL for health checks? (e.g., https://api.myproject.io)
|
|
300
|
+
Enter URL or press Enter to skip:
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 4D: review section
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
Detected tech stack: {TECH_STACK}
|
|
307
|
+
|
|
308
|
+
Is this accurate? Add/edit if needed, or press Enter to accept:
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
Any additional context for code reviewers? (unusual conventions, deploy setup, known pitfalls)
|
|
313
|
+
Press Enter to skip, or describe briefly:
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 4E: verification section
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
Detected potential health endpoint path: {DETECTED_PATH}
|
|
320
|
+
Full health URL (e.g., https://api.myproject.io/health), or press Enter to skip:
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
Detected container name prefixes: {PREFIXES}
|
|
325
|
+
Are these correct? Add/edit prefixes, or press Enter to accept:
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Phase 5: Write forge.yaml
|
|
331
|
+
|
|
332
|
+
### 5A: Preserve required sections
|
|
333
|
+
|
|
334
|
+
Read the current `forge.yaml` content. Extract the complete `project:`, `paths:`, and `branches:` blocks verbatim — they must not change.
|
|
335
|
+
|
|
336
|
+
### 5B: Build optional section YAML
|
|
337
|
+
|
|
338
|
+
For each optional section the user confirmed:
|
|
339
|
+
|
|
340
|
+
**repos** (if satellites confirmed):
|
|
341
|
+
```yaml
|
|
342
|
+
repos:
|
|
343
|
+
default:
|
|
344
|
+
repo: "{OWNER}/{REPO}"
|
|
345
|
+
staging_branch: "{branches.staging}"
|
|
346
|
+
satellites:
|
|
347
|
+
- prefix: "{PREFIX}"
|
|
348
|
+
repo: "{OWNER}/{SATELLITE_REPO}"
|
|
349
|
+
staging_branch: "main"
|
|
350
|
+
local_path: "{REPO_ROOT}/../{SATELLITE_REPO}"
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**project_board** (if board and field IDs found):
|
|
354
|
+
```yaml
|
|
355
|
+
project_board:
|
|
356
|
+
owner: "{OWNER}"
|
|
357
|
+
project_number: {PROJECT_NUMBER}
|
|
358
|
+
project_id: "{PROJECT_ID}" # Must be a PVT_... node ID — obtain via: gh project list --owner <owner> --format json | jq '.projects[] | .id'
|
|
359
|
+
field_ids:
|
|
360
|
+
status: "{STATUS_FIELD_ID}"
|
|
361
|
+
lane: "{LANE_FIELD_ID}"
|
|
362
|
+
component: "{COMPONENT_FIELD_ID}"
|
|
363
|
+
priority: "{PRIORITY_FIELD_ID}"
|
|
364
|
+
workflow: "{WORKFLOW_FIELD_ID}"
|
|
365
|
+
option_ids:
|
|
366
|
+
status:
|
|
367
|
+
todo: "{TODO_OPTION_ID}"
|
|
368
|
+
in_progress: "{IN_PROGRESS_OPTION_ID}"
|
|
369
|
+
done: "{DONE_OPTION_ID}"
|
|
370
|
+
lane:
|
|
371
|
+
fast: "{FAST_OPTION_ID}"
|
|
372
|
+
feature: "{FEATURE_OPTION_ID}"
|
|
373
|
+
priority:
|
|
374
|
+
p0: "{P0_OPTION_ID}"
|
|
375
|
+
p1: "{P1_OPTION_ID}"
|
|
376
|
+
p2: "{P2_OPTION_ID}"
|
|
377
|
+
p3: "{P3_OPTION_ID}"
|
|
378
|
+
workflow:
|
|
379
|
+
investigating: "{INVESTIGATING_OPTION_ID}"
|
|
380
|
+
building: "{BUILDING_OPTION_ID}"
|
|
381
|
+
in_review: "{IN_REVIEW_OPTION_ID}"
|
|
382
|
+
merged: "{MERGED_OPTION_ID}"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**services** (if analytics/monitoring confirmed):
|
|
386
|
+
```yaml
|
|
387
|
+
services:
|
|
388
|
+
domain: "{ROOT_DOMAIN}"
|
|
389
|
+
api_url: "{API_URL}"
|
|
390
|
+
analytics:
|
|
391
|
+
# Only include subsections the user confirmed
|
|
392
|
+
umami: # if selected
|
|
393
|
+
url: "{UMAMI_URL}"
|
|
394
|
+
website_id: "{UMAMI_WEBSITE_ID}"
|
|
395
|
+
clarity: # if selected
|
|
396
|
+
project_id: "{CLARITY_PROJECT_ID}"
|
|
397
|
+
ga4: # if selected
|
|
398
|
+
property_id: "{GA4_PROPERTY_ID}"
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**review** (if tech stack or context provided):
|
|
402
|
+
```yaml
|
|
403
|
+
review:
|
|
404
|
+
tech_stack: "{TECH_STACK}"
|
|
405
|
+
context: |
|
|
406
|
+
{PROJECT_CONTEXT}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**verification** (if health endpoint or containers found):
|
|
410
|
+
```yaml
|
|
411
|
+
verification:
|
|
412
|
+
health_endpoint: "{HEALTH_ENDPOINT}"
|
|
413
|
+
health_patterns:
|
|
414
|
+
- '"status": "ok"'
|
|
415
|
+
internal_service_patterns:
|
|
416
|
+
- "{SERVICE_PREFIX}"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### 5C: Overwrite protection
|
|
420
|
+
|
|
421
|
+
For each optional section that is ALREADY ACTIVE in forge.yaml (detected in Phase 1C):
|
|
422
|
+
|
|
423
|
+
If `FORCE=false`:
|
|
424
|
+
```
|
|
425
|
+
forge.yaml already has an active {section}: section.
|
|
426
|
+
Overwrite it with newly detected values? [y/N]
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
If user says N: keep existing content for that section.
|
|
430
|
+
|
|
431
|
+
### 5D: Write the file
|
|
432
|
+
|
|
433
|
+
Backup the existing file:
|
|
434
|
+
```bash
|
|
435
|
+
cp "$FORGE_YAML" "${FORGE_YAML}.bak"
|
|
436
|
+
echo "Backed up: forge.yaml → forge.yaml.bak"
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Write the complete new file: required sections (preserved verbatim) + active optional sections (newly generated) + inactive optional sections (left as commented-out blocks matching forge.yaml.example structure).
|
|
440
|
+
|
|
441
|
+
Print a summary of what was written:
|
|
442
|
+
```
|
|
443
|
+
forge.yaml updated:
|
|
444
|
+
|
|
445
|
+
Required sections (preserved):
|
|
446
|
+
✓ project
|
|
447
|
+
✓ paths
|
|
448
|
+
✓ branches
|
|
449
|
+
|
|
450
|
+
Optional sections (filled):
|
|
451
|
+
✓ repos — 2 satellite(s) configured
|
|
452
|
+
✓ project_board — field IDs from project "Acme Board" (#1)
|
|
453
|
+
✗ services — skipped (no analytics services)
|
|
454
|
+
✓ review — tech stack + context
|
|
455
|
+
✓ verification — health endpoint configured
|
|
456
|
+
|
|
457
|
+
Optional sections (skipped — still commented out):
|
|
458
|
+
- services
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Phase 6: Validation
|
|
464
|
+
|
|
465
|
+
### 6A: Repo access
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
gh repo view "${OWNER}/${REPO}" --json name,url 2>/dev/null \
|
|
469
|
+
|| echo "WARNING: Cannot access ${OWNER}/${REPO} — check project.owner and project.repo"
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### 6B: Path existence
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
[ -d "$(grep 'root:' "$FORGE_YAML" | head -1 | awk '{print $2}' | tr -d '"')" ] \
|
|
476
|
+
&& echo "✓ paths.root exists" \
|
|
477
|
+
|| echo "WARNING: paths.root does not exist on this machine"
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### 6C: Project board ID resolution
|
|
481
|
+
|
|
482
|
+
`project_board.project_id` must be a GitHub Projects v2 node ID with the `PVT_` prefix (e.g. `PVT_kwHOxxxxxxxxxxxxxxxx`). This value is returned by `gh project list --owner <owner> --format json | jq '.projects[] | .id'`. A manually-edited `forge.yaml` with an incorrect value (e.g. a project number instead of a node ID) will produce a confusing GraphQL error — the format check below catches this before attempting the API call.
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
# Only run if project_board section was written
|
|
486
|
+
if grep -q "^project_board:" "$FORGE_YAML"; then
|
|
487
|
+
PROJECT_ID=$(grep "project_id:" "$FORGE_YAML" | head -1 | awk '{print $2}' | tr -d '"')
|
|
488
|
+
# Validate PVT_ prefix before GraphQL call — any other format will produce a confusing API error
|
|
489
|
+
if [[ "$PROJECT_ID" != PVT_* ]]; then
|
|
490
|
+
echo "ERROR: project_board.project_id must start with 'PVT_' (got: '$PROJECT_ID')"
|
|
491
|
+
echo " Expected format: PVT_kwHOxxxxxxxxxxxxxxxx"
|
|
492
|
+
echo " Obtain the correct value with: gh project list --owner ${OWNER} --format json | jq '.projects[] | .id'"
|
|
493
|
+
else
|
|
494
|
+
gh api graphql -f query='query { node(id: "'"$PROJECT_ID"'") { id __typename } }' 2>/dev/null \
|
|
495
|
+
| jq -e '.data.node.id' > /dev/null \
|
|
496
|
+
&& echo "✓ project_board.project_id resolves" \
|
|
497
|
+
|| echo "WARNING: project_board.project_id may be invalid — verify with: gh project list --owner ${OWNER}"
|
|
498
|
+
fi
|
|
499
|
+
fi
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### 6D: Satellite repo access
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Only run if repos.satellites section was written
|
|
506
|
+
if grep -q "^repos:" "$FORGE_YAML"; then
|
|
507
|
+
# Extract satellite repo names and verify each is accessible
|
|
508
|
+
grep -A 20 "^repos:" "$FORGE_YAML" | grep "repo:" | tail -n +2 | while read -r line; do
|
|
509
|
+
SAT_REPO=$(echo "$line" | awk '{print $2}' | tr -d '"')
|
|
510
|
+
gh repo view "$SAT_REPO" --json name 2>/dev/null \
|
|
511
|
+
&& echo "✓ satellite repo ${SAT_REPO} accessible" \
|
|
512
|
+
|| echo "WARNING: satellite repo ${SAT_REPO} not accessible — check prefix and repo name"
|
|
513
|
+
done
|
|
514
|
+
fi
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 6E: Final report
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
forge.yaml is ready.
|
|
521
|
+
|
|
522
|
+
Validation results:
|
|
523
|
+
{validation output from 6A–6D}
|
|
524
|
+
|
|
525
|
+
Next steps:
|
|
526
|
+
1. If forge.yaml contains sensitive paths (credentials file, local paths):
|
|
527
|
+
echo "forge.yaml" >> .gitignore
|
|
528
|
+
2. Test the pipeline:
|
|
529
|
+
/work-on next
|
|
530
|
+
3. If any WARNING appeared above, edit forge.yaml to fix the flagged values.
|
|
531
|
+
4. Backup file: forge.yaml.bak (delete when satisfied)
|
|
532
|
+
|
|
533
|
+
ForgeDock commands now use your project's config from forge.yaml.
|
|
534
|
+
```
|