forgedock 1.1.2 → 1.1.4

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.
@@ -278,6 +278,40 @@ Shows what will deploy next — diff between staging and main with issue/PR summ
278
278
 
279
279
  ---
280
280
 
281
+ ### `/deploy-pr`
282
+
283
+ **PR ship orchestrator.**
284
+
285
+ Ships a branch to its deploy target — detects or creates the PR, runs CI fixing via `/fix-ci`, runs the review gate via `/review-pr`, merges after both pass, and returns a structured result. Designed to be invoked by `/autopilot` as part of the autonomous deploy loop.
286
+
287
+ ```bash
288
+ /deploy-pr staging # Ship staging → main
289
+ /deploy-pr milestone/my-feature # Ship milestone → staging
290
+ /deploy-pr feat/my-branch --target staging # Ship any branch → staging
291
+ /deploy-pr staging --dry-run # Simulate without merging
292
+ /deploy-pr staging --issue 1234 # Reference parent issue in PR body
293
+ ```
294
+
295
+ **Branch routing**: `staging` → `main` (Deploy), `milestone/*` → `staging` (Ship), other → `staging` (Merge). Never force-merges. Returns `{ pr, source, target, status, ci_fixes, review_findings }`.
296
+
297
+ ---
298
+
299
+ ### `/fix-ci`
300
+
301
+ **Automated CI failure resolution loop.**
302
+
303
+ Diagnoses CI failures on a PR, applies targeted fixes, pushes new commits, and loops until green or max attempts reached. Invoked automatically by `/deploy-pr` as the CI gate step; can also be run directly.
304
+
305
+ ```bash
306
+ /fix-ci 123 # Fix CI on PR #123
307
+ /fix-ci 123 --max-attempts 5 # Allow up to 5 fix iterations
308
+ /fix-ci 123 --repo owner/repo # Explicit repo
309
+ ```
310
+
311
+ Returns `{ pr, status, attempts, fixes_applied }`.
312
+
313
+ ---
314
+
281
315
  #### `/rollback`
282
316
 
283
317
  **Revert PR creator.**
@@ -370,6 +404,21 @@ Cleans up stale labels, missing workflow state, project board gaps, dangling wor
370
404
 
371
405
  ---
372
406
 
407
+ ### `/recover-orphans`
408
+
409
+ **Pipeline orphan recovery.**
410
+
411
+ Scans for issues stuck in intermediate workflow states (building, investigating, in-review) where the agent died mid-pipeline. Diagnoses each orphan's actual GitHub state and applies the appropriate recovery action — re-entering the pipeline via `/work-on` or `/review-pr`.
412
+
413
+ ```bash
414
+ /recover-orphans # Scan and recover all orphans
415
+ /recover-orphans --dry-run # Show orphans without recovering
416
+ /recover-orphans --since 24 # Only orphans stalled > 24 hours
417
+ /recover-orphans --issue 42 # Recover a specific issue
418
+ ```
419
+
420
+ ---
421
+
373
422
  #### `/pipeline-health`
374
423
 
375
424
  **Pipeline performance analyzer.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgedock",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Autonomous dev pipeline for Claude Code. GitHub as institutional memory — every issue, PR, and annotation survives context resets and makes the next agent smarter.",
5
5
  "bin": {
6
6
  "forgedock": "bin/forgedock.mjs"
@@ -98,6 +98,31 @@ function escapeRegExp(str) {
98
98
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
99
99
  }
100
100
 
101
+ /**
102
+ * Reverse the HTML entity escapes applied by emit()'s sanitizeFieldValue().
103
+ *
104
+ * emit() escapes three HTML comment delimiter sequences to protect GitHub's
105
+ * renderer from comment injection:
106
+ * 1. `<!--` → `&lt;!--` (forge#1638)
107
+ * 2. `--!>` → `--!&gt;` (forge#1594)
108
+ * 3. `-->` → `--&gt;` (forge#1594)
109
+ *
110
+ * parse() must apply the inverse so that round-tripping through emit → parse
111
+ * is lossless. Unescape order: `--!&gt;` before `--&gt;` (most-specific first)
112
+ * to avoid the `!` being left dangling by an overly-greedy `--&gt;` pass.
113
+ * The `&lt;!--` unescape is independent and can run in any relative order.
114
+ * (forge#1662)
115
+ *
116
+ * @param {string} raw
117
+ * @returns {string}
118
+ */
119
+ function unescapeFieldValue(raw) {
120
+ return raw
121
+ .replace(/--!&gt;/g, '--!>')
122
+ .replace(/--&gt;/g, '-->')
123
+ .replace(/&lt;!--/g, '<!--');
124
+ }
125
+
101
126
  /**
102
127
  * Extract **Key**: value fields from an annotation body.
103
128
  *
@@ -109,7 +134,7 @@ function extractFields(body) {
109
134
  for (const line of body.split('\n')) {
110
135
  const m = line.trim().match(BOLD_FIELD_RE);
111
136
  if (m) {
112
- fields[m[1].trim()] = m[2].trim();
137
+ fields[unescapeFieldValue(m[1].trim())] = unescapeFieldValue(m[2].trim());
113
138
  }
114
139
  }
115
140
  return fields;
@@ -169,7 +194,7 @@ export function parse(commentBody) {
169
194
  continue;
170
195
  }
171
196
 
172
- const inlineValue = rawInlineValue;
197
+ const inlineValue = rawInlineValue !== null ? unescapeFieldValue(rawInlineValue) : null;
173
198
  const typeDef = typeDefForTag;
174
199
  const isControl = typeDef?.controlMarker === true;
175
200
  const isReserved = RESERVED_TYPE_NAMES.has(type);