forgedock 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine/invariants.mjs +361 -0
  4. package/bin/engine-cli.mjs +137 -11
  5. package/bin/env-detect.mjs +249 -0
  6. package/bin/forge-utils.mjs +364 -4
  7. package/bin/forgedock.mjs +1064 -135
  8. package/bin/hooks/pre-tool-use.mjs +182 -0
  9. package/bin/hooks/session-start.mjs +68 -3
  10. package/bin/init-detect.mjs +101 -12
  11. package/bin/journey.mjs +917 -16
  12. package/bin/labels.json +10 -0
  13. package/bin/recall.mjs +557 -0
  14. package/bin/registry.mjs +91 -3
  15. package/bin/runner.mjs +38 -2
  16. package/bin/tui.mjs +49 -3
  17. package/commands/adopt.md +1 -1
  18. package/commands/analytics.md +8 -8
  19. package/commands/audit.md +1 -1
  20. package/commands/autopilot.md +958 -77
  21. package/commands/ci-audit.md +1 -1
  22. package/commands/cleanup.md +139 -55
  23. package/commands/compat-audit.md +1 -1
  24. package/commands/deploy-info.md +1 -1
  25. package/commands/deploy-pr.md +1 -1
  26. package/commands/diagnose.md +1 -1
  27. package/commands/explain.md +1 -1
  28. package/commands/fix-ci.md +1 -1
  29. package/commands/forgedock-init.md +1 -1
  30. package/commands/incident-response.md +3 -3
  31. package/commands/issue.md +1 -1
  32. package/commands/optimize.md +586 -1
  33. package/commands/orchestrate/config.md +13 -3
  34. package/commands/orchestrate/phase-1-resolve.md +36 -26
  35. package/commands/orchestrate/phase-2-triage.md +1 -1
  36. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  37. package/commands/orchestrate/phase-3-dependency.md +587 -48
  38. package/commands/orchestrate/phase-4-execution.md +1031 -57
  39. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  40. package/commands/orchestrate/phase-6-report.md +248 -11
  41. package/commands/orchestrate/safety.md +2 -2
  42. package/commands/pipeline-health/phase-2-metrics.md +305 -1
  43. package/commands/pipeline-health/phase-4-proposals.md +127 -0
  44. package/commands/qa-sweep.md +1 -1
  45. package/commands/quality-gate.md +418 -1
  46. package/commands/recover-orphans.md +5 -5
  47. package/commands/replay.md +1 -1
  48. package/commands/review-pr-agents/api.md +2 -2
  49. package/commands/review-pr-agents/auth.md +2 -2
  50. package/commands/review-pr-agents/billing.md +2 -2
  51. package/commands/review-pr-agents/concurrency.md +2 -2
  52. package/commands/review-pr-agents/database.md +2 -2
  53. package/commands/review-pr-agents/frontend.md +2 -2
  54. package/commands/review-pr-agents/infra.md +2 -2
  55. package/commands/review-pr-agents/protocols.md +1 -1
  56. package/commands/review-pr-agents/scraper.md +2 -2
  57. package/commands/review-pr-agents/security.md +2 -2
  58. package/commands/review-pr-staging.md +41 -3
  59. package/commands/review-pr.md +462 -19
  60. package/commands/rollback.md +1 -1
  61. package/commands/scope.md +1 -1
  62. package/commands/security-audit.md +1 -1
  63. package/commands/spec-doctor.md +429 -0
  64. package/commands/test-gate.md +1 -1
  65. package/commands/validate.md +53 -1
  66. package/commands/work-on/build/architect.md +78 -1
  67. package/commands/work-on/build/context.md +360 -22
  68. package/commands/work-on/build/implement.md +3 -1
  69. package/commands/work-on/build/validate.md +29 -1
  70. package/commands/work-on/build.md +75 -6
  71. package/commands/work-on/close.md +624 -10
  72. package/commands/work-on/decompose.md +1 -1
  73. package/commands/work-on/investigate.md +235 -5
  74. package/commands/work-on/remediate.md +331 -0
  75. package/commands/work-on/review.md +1 -1
  76. package/commands/work-on-monolithic.md +1 -1
  77. package/commands/work-on.md +205 -66
  78. package/docs/CI.md +2 -2
  79. package/docs/CODEX.md +1 -1
  80. package/docs/CONFIG.md +186 -0
  81. package/docs/FORGE-PROTOCOL.md +101 -0
  82. package/docs/site/command-reference.md +31 -1
  83. package/docs/site/getting-started.md +9 -34
  84. package/docs/site/index.md +1 -1
  85. package/docs/spec/forge-protocol-v1.md +97 -5
  86. package/docs/spec/label-state-machine.md +6 -1
  87. package/package.json +3 -2
  88. package/packages/protocol/src/cli.js +374 -84
  89. package/packages/protocol/src/types.js +75 -1
  90. package/scripts/build-knowledge-index.mjs +1761 -0
  91. package/scripts/calibration.mjs +996 -0
  92. package/scripts/check-command-docs-drift.sh +4 -2
  93. package/scripts/danger-zones.mjs +789 -0
  94. package/scripts/doctor-pipeline-state.sh +88 -2
  95. package/scripts/select-fix-targets.sh +234 -0
  96. package/scripts/transition-label.sh +245 -6
  97. package/scripts/refresh-bot-token.sh +0 -97
package/bin/registry.mjs CHANGED
@@ -11,6 +11,8 @@
11
11
  * setOptOut(dir, optedOut) → Promise<void> (adds/removes dir from opt-out set)
12
12
  * nudgeSeen(dir) → boolean (true if nudge was already shown for dir)
13
13
  * markNudgeSeen(dir) → Promise<void> (records that nudge was shown for dir)
14
+ * getPersistedHomeState() → { path, version, updatedAt } | null (reads persistedHome)
15
+ * setPersistedHomeState(s) → Promise<void> (writes persistedHome; see #1943)
14
16
  *
15
17
  * Registry file: ~/.claude/forgedock/registry.json
16
18
  * Registry schema:
@@ -21,7 +23,12 @@
21
23
  * },
22
24
  * "nudgeSeen": {
23
25
  * "/absolute/path/to/dir": { "at": "<ISO-8601 timestamp>" }
24
- * }
26
+ * },
27
+ * "persistedHome": {
28
+ * "path": "/absolute/path/to/~/.forge",
29
+ * "version": "1.2.3",
30
+ * "updatedAt": "<ISO-8601 timestamp>"
31
+ * } | null
25
32
  * }
26
33
  *
27
34
  * State model:
@@ -33,6 +40,12 @@
33
40
  * registry always fails open (never blocks a Claude Code session).
34
41
  * - A nudge is shown at most once per unmanaged directory; `nudgeSeen` tracks
35
42
  * which directories have already received the nudge.
43
+ * - `persistedHome` records the last known state of the persisted-toolset
44
+ * copy (bin/journey.mjs: persistHome(), issue #1943) written under
45
+ * `~/.forge/`. `null` means persistHome() has never run (or was skipped —
46
+ * e.g. a git-clone install) for this user. This is metadata ABOUT that
47
+ * copy — the copy itself is not stored here; only registry.mjs's own
48
+ * `~/.claude/forgedock/registry.json` lives in this file.
36
49
  *
37
50
  * Contract guarantees:
38
51
  * - Safe: every try/catch degrades gracefully; resolveState never throws
@@ -77,10 +90,10 @@ const REGISTRY_PATH = join(REGISTRY_DIR, "registry.json");
77
90
  * Empty registry structure — returned whenever the file is missing or corrupt.
78
91
  * Using a factory function avoids shared mutable state across calls.
79
92
  *
80
- * @returns {{ version: number, optedOut: Record<string, { at: string }>, nudgeSeen: Record<string, { at: string }> }}
93
+ * @returns {{ version: number, optedOut: Record<string, { at: string }>, nudgeSeen: Record<string, { at: string }>, persistedHome: null }}
81
94
  */
82
95
  function emptyRegistry() {
83
- return { version: 1, optedOut: {}, nudgeSeen: {} };
96
+ return { version: 1, optedOut: {}, nudgeSeen: {}, persistedHome: null };
84
97
  }
85
98
 
86
99
  // ---------------------------------------------------------------------------
@@ -162,6 +175,11 @@ function readRegistry() {
162
175
  if (!parsed.nudgeSeen || typeof parsed.nudgeSeen !== "object") {
163
176
  parsed.nudgeSeen = {};
164
177
  }
178
+ // Ensure persistedHome is present (absent in registry files written
179
+ // before #1943 added persisted-home tracking).
180
+ if (parsed.persistedHome === undefined) {
181
+ parsed.persistedHome = null;
182
+ }
165
183
  return parsed;
166
184
  }
167
185
  // Schema mismatch — treat as empty (fail open)
@@ -377,6 +395,76 @@ export async function clearNudgeSeen(dir) {
377
395
  });
378
396
  }
379
397
 
398
+ // ---------------------------------------------------------------------------
399
+ // Persisted-home state tracking (issue #1943)
400
+ // ---------------------------------------------------------------------------
401
+ // bin/journey.mjs's persistHome() copies ForgeDock's own toolset (bin/,
402
+ // commands/, scripts/, templates/) into a stable `~/.forge/` home so
403
+ // ~/.claude/commands/ symlinks and the SessionStart hook's script path
404
+ // survive npm/npx cache eviction. These two functions are the single place
405
+ // doctor()/update() read and write what persistHome() last did, so both
406
+ // commands agree on "where does the persisted copy live and what version is
407
+ // it" instead of each re-deriving it independently (forge#1589's split-brain
408
+ // precedent — install/doctor/status previously disagreed about where
409
+ // ForgeDock lived, and this issue must not reintroduce that).
410
+
411
+ /**
412
+ * Read the last recorded persisted-home state from the registry.
413
+ *
414
+ * NOT pure: reads `~/.claude/forgedock/registry.json` from disk on every
415
+ * call. Fail-open — a missing/corrupt registry file, or a registry written
416
+ * before #1943, both resolve to `null` (equivalent to "persistHome() has
417
+ * never recorded state for this user") rather than throwing.
418
+ *
419
+ * @returns {{ path: string, version: string, updatedAt: string } | null}
420
+ */
421
+ export function getPersistedHomeState() {
422
+ try {
423
+ const registry = readRegistry();
424
+ return registry.persistedHome ?? null;
425
+ } catch {
426
+ return null;
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Record the current persisted-home state in the registry.
432
+ *
433
+ * NOT pure: performs a disk write (via the atomic `.tmp` + `renameSync`
434
+ * pattern in `_doWriteRegistryWith`) and, when the caller omits `updatedAt`,
435
+ * calls `new Date()` to stamp one — this function has real side effects, it
436
+ * does not merely compute a value (see forge#462: a prior JSDoc/behavior
437
+ * mismatch in this file claimed a writer was a "pure mutation function" when
438
+ * it in fact touched the clock and the filesystem — documenting that
439
+ * honestly here rather than repeating it).
440
+ *
441
+ * Best-effort: write failures are swallowed by writeRegistry()'s existing
442
+ * fail-open contract; a failed write leaves the previous persistedHome value
443
+ * (or null) in place rather than throwing.
444
+ *
445
+ * @param {{ path: string, version: string, updatedAt?: string }} state
446
+ * @returns {Promise<void>}
447
+ */
448
+ export async function setPersistedHomeState({ path, version, updatedAt } = {}) {
449
+ // Normalize the same way normalizeDir() does elsewhere in this file
450
+ // (lowercase Windows drive letter) so a later lookup never mismatches on
451
+ // drive-letter casing alone (forge#412 precedent). Falls back to the raw
452
+ // path if it doesn't exist yet or can't be resolved — never throws.
453
+ let normalizedPath = path;
454
+ try {
455
+ if (path) normalizedPath = normalizeDir(path);
456
+ } catch {
457
+ normalizedPath = path;
458
+ }
459
+ await writeRegistry((registry) => {
460
+ registry.persistedHome = {
461
+ path: normalizedPath,
462
+ version: version || "",
463
+ updatedAt: updatedAt || new Date().toISOString(),
464
+ };
465
+ });
466
+ }
467
+
380
468
  // ---------------------------------------------------------------------------
381
469
  // Claude Code version detection and breakpoints resolution (issue #1252)
382
470
  // ---------------------------------------------------------------------------
package/bin/runner.mjs CHANGED
@@ -23,6 +23,7 @@
23
23
  * getToolHandlers(cwd) → Record<string, fn>
24
24
  * renderDryRun(ctx) → string
25
25
  * renderSummaryCard(ctx) → string
26
+ * resolveConfiguredDefaultModel(cwd) → string|null (forge.yaml agents.default_model, resolved)
26
27
  * runCommand(opts) → Promise<{status, ...}>
27
28
  *
28
29
  * Design notes:
@@ -45,6 +46,7 @@ import {
45
46
  } from "fs";
46
47
  import { join, dirname, basename, relative, isAbsolute } from "path";
47
48
  import { execSync, spawnSync } from "child_process";
49
+ import { parseForgeYaml, resolveModelAlias } from "./forge-utils.mjs";
48
50
 
49
51
  const DEFAULT_MODEL = "claude-sonnet-5";
50
52
  const DEFAULT_MAX_ITERATIONS = 50;
@@ -74,6 +76,36 @@ export function truncateToolResult(content) {
74
76
  return str.slice(0, MAX_TOOL_RESULT_CHARS) + TRUNCATION_MARKER;
75
77
  }
76
78
 
79
+ /**
80
+ * Resolve the project-configured default model from forge.yaml's
81
+ * `agents.default_model` field, if present.
82
+ *
83
+ * Reads `{cwd}/forge.yaml` (a fixed, non-agent-controlled filename — not the
84
+ * LLM-facing `resolveConfinedPath` path-confinement helper used for tool-call
85
+ * paths below), parses it with the shared `parseForgeYaml`, and resolves the
86
+ * `agents.default_model` alias (`sonnet`/`opus`/`haiku`, or a pass-through
87
+ * full model ID) via `resolveModelAlias`.
88
+ *
89
+ * Fail-soft: returns `null` on any failure (forge.yaml missing, unreadable,
90
+ * malformed, or the field absent) so the headless runner keeps working with
91
+ * zero configuration — this is a fallback tier, not a requirement.
92
+ *
93
+ * @param {string} cwd - Working directory to look for forge.yaml in.
94
+ * @returns {string|null}
95
+ */
96
+ export function resolveConfiguredDefaultModel(cwd) {
97
+ try {
98
+ const forgeYamlPath = join(cwd, "forge.yaml");
99
+ if (!existsSync(forgeYamlPath)) return null;
100
+ const raw = readFileSync(forgeYamlPath, "utf-8");
101
+ const parsed = parseForgeYaml(raw);
102
+ const configured = parsed?.agents?.default_model;
103
+ return resolveModelAlias(configured);
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
77
109
  // ---------------------------------------------------------------------------
78
110
  // Command spec resolution
79
111
  // ---------------------------------------------------------------------------
@@ -734,7 +766,9 @@ export function renderSummaryCard(ctx) {
734
766
  * @param {string[]} [opts.args] - Command arguments.
735
767
  * @param {string} [opts.cwd] - Working directory (default cwd).
736
768
  * @param {string} [opts.apiKey] - Anthropic API key (default env).
737
- * @param {string} [opts.model] - Model id.
769
+ * @param {string} [opts.model] - Model id. Resolution order when
770
+ * omitted: $FORGEDOCK_MODEL env > forge.yaml `agents.default_model` (in
771
+ * `cwd`) > hardcoded DEFAULT_MODEL.
738
772
  * @param {number} [opts.maxIterations] - Tool-loop bound.
739
773
  * @param {boolean} [opts.dryRun] - Preview without an API call.
740
774
  * @param {{log: Function, error?: Function}} [opts.logger] - Output sink.
@@ -747,7 +781,9 @@ export async function runCommand(opts = {}) {
747
781
  args = [],
748
782
  cwd = process.cwd(),
749
783
  apiKey = process.env.ANTHROPIC_API_KEY,
750
- model = process.env.FORGEDOCK_MODEL || DEFAULT_MODEL,
784
+ model = process.env.FORGEDOCK_MODEL ||
785
+ resolveConfiguredDefaultModel(cwd) ||
786
+ DEFAULT_MODEL,
751
787
  maxIterations = DEFAULT_MAX_ITERATIONS,
752
788
  dryRun = false,
753
789
  logger = console,
package/bin/tui.mjs CHANGED
@@ -631,7 +631,9 @@ const STEP_STATUS = {
631
631
  * their own ASCII symbols.
632
632
  */
633
633
  export function stepHeader(current, total, label, status = "active") {
634
- const icon = STEP_STATUS[status] || STEP_STATUS.active;
634
+ const icon = Object.hasOwn(STEP_STATUS, status)
635
+ ? STEP_STATUS[status]
636
+ : STEP_STATUS.active;
635
637
  const counter = dim(`Step ${current} of ${total}`);
636
638
  const dash = dim(" — ");
637
639
  const name =
@@ -1589,6 +1591,44 @@ function gradientText(text, start, end) {
1589
1591
  return result + RST;
1590
1592
  }
1591
1593
 
1594
+ /**
1595
+ * Default tagline — used when no context is given, or the given context has
1596
+ * no mapped entry in LOGO_TAGLINES.
1597
+ */
1598
+ const DEFAULT_TAGLINE = "GitHub as a knowledge graph for AI agents";
1599
+
1600
+ /**
1601
+ * Context-specific taglines shown under the logo. Keyed by the invoking
1602
+ * command/context name. Any context not listed here falls back to
1603
+ * DEFAULT_TAGLINE — this map is additive, never a requirement to cover every
1604
+ * caller.
1605
+ */
1606
+ const LOGO_TAGLINES = {
1607
+ install: "lighting the forge",
1608
+ update: "tempering the blade",
1609
+ uninstall: "banking the coals",
1610
+ doctor: "inspecting the anvil",
1611
+ status: "reading the heat",
1612
+ };
1613
+
1614
+ /**
1615
+ * Look up the tagline for a given invocation context. Exposed so callers
1616
+ * outside renderLogo() (e.g. plain-text command headers that don't render
1617
+ * the full logo) can still surface the same context-specific tagline text.
1618
+ *
1619
+ * Uses an own-property guard rather than a plain `LOGO_TAGLINES[context]`
1620
+ * lookup: `context` is a raw, unsanitized command-name string, and a plain
1621
+ * bracket lookup on an object literal resolves inherited Object.prototype
1622
+ * members (e.g. context === "constructor") instead of falling through to
1623
+ * the default tagline. See forge#1955.
1624
+ *
1625
+ * @param {string} [context]
1626
+ * @returns {string}
1627
+ */
1628
+ export function getLogoTagline(context = "") {
1629
+ return Object.hasOwn(LOGO_TAGLINES, context) ? LOGO_TAGLINES[context] : DEFAULT_TAGLINE;
1630
+ }
1631
+
1592
1632
  /**
1593
1633
  * Render the ForgeDock logo for display in the terminal.
1594
1634
  *
@@ -1598,10 +1638,16 @@ function gradientText(text, start, end) {
1598
1638
  *
1599
1639
  * @param {object} [opts]
1600
1640
  * @param {string} [opts.version] - Package version string (e.g. "1.0.14")
1641
+ * @param {string} [opts.context] - Invocation context (e.g. "install",
1642
+ * "update", "uninstall", "doctor", "status"). Selects the tagline shown
1643
+ * under the logo. Unmapped or omitted contexts fall back to the default
1644
+ * tagline.
1601
1645
  * @returns {string} Rendered logo string (may contain ANSI sequences)
1602
1646
  */
1603
- export function renderLogo({ version = "" } = {}) {
1604
- const tagline = "GitHub as a knowledge graph for AI agents";
1647
+ export function renderLogo({ version = "", context = "" } = {}) {
1648
+ const tagline = Object.hasOwn(LOGO_TAGLINES, context)
1649
+ ? LOGO_TAGLINES[context]
1650
+ : DEFAULT_TAGLINE;
1605
1651
  const versionStr = version ? `ForgeDock · v${version}` : "ForgeDock";
1606
1652
 
1607
1653
  if (!USE_TRUECOLOR) {
package/commands/adopt.md CHANGED
@@ -12,7 +12,7 @@ You onboard an existing repo into the ForgeDock pipeline. Unlike `/forgedock-ini
12
12
 
13
13
  Run `/adopt` once after `npx forgedock init` + `/forgedock-init` to make a legacy backlog pipeline-ready without touching the issues themselves.
14
14
 
15
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
15
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
16
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
18
  <!-- FORGE:SPEC_LOADED — adopt.md loaded and active. Agent is bound by this spec. -->
@@ -13,7 +13,7 @@ You are the analytics orchestrator. Pull data from ALL available analytics platf
13
13
 
14
14
  **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
15
15
 
16
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Pass model explicitly in every `Agent` tool call. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
16
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Pass model explicitly in every `Agent` tool call. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
17
17
 
18
18
  <!-- FORGE:SPEC_LOADED — analytics.md loaded and active. Agent is bound by this spec. -->
19
19
 
@@ -163,7 +163,7 @@ Launch ALL 7 data collection agents simultaneously using `run_in_background=true
163
163
 
164
164
  ### Agent 1: GSC Data Collector
165
165
 
166
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
166
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
167
167
 
168
168
  **Mission**: Collect Google Search Console data for `{DOMAIN}`. Use the MCP GSC tools directly.
169
169
 
@@ -183,7 +183,7 @@ All queries use `siteUrl: "{SITE_URL}"`.
183
183
 
184
184
  ### Agent 2: Clarity Data Collector
185
185
 
186
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
186
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
187
187
 
188
188
  **Mission**: Collect Microsoft Clarity behavior analytics. Use `mcp__clarity__query-analytics-dashboard` and `mcp__clarity__list-session-recordings` tools.
189
189
 
@@ -211,7 +211,7 @@ Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
211
211
 
212
212
  ### Agent 3: Umami Data Collector
213
213
 
214
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
214
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
215
215
 
216
216
  **Mission**: Collect Umami analytics via REST API.
217
217
 
@@ -238,7 +238,7 @@ python3 -c "import yaml; c=yaml.safe_load(open('{CREDENTIALS_FILE}')); print(c['
238
238
 
239
239
  ### Agent 4: Cloudflare Data Collector
240
240
 
241
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
241
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
242
242
 
243
243
  **Mission**: Collect Cloudflare traffic/performance data via GraphQL API.
244
244
 
@@ -261,7 +261,7 @@ CF_ZONE=$(python3 -c "import yaml; print(yaml.safe_load(open('{CREDENTIALS_FILE}
261
261
 
262
262
  ### Agent 5: Bing Webmaster Data Collector
263
263
 
264
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
264
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
265
265
 
266
266
  **Mission**: Collect Bing Webmaster Tools data for `{DOMAIN}` via REST API. Bing powers ChatGPT search, so Bing indexing/ranking data = AI discoverability signal.
267
267
 
@@ -288,7 +288,7 @@ BING_BASE=$(python3 -c "import yaml; print(yaml.safe_load(open('{CREDENTIALS_FIL
288
288
 
289
289
  ### Agent 6: Stripe Revenue Collector
290
290
 
291
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
291
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
292
292
 
293
293
  **Mission**: Collect Stripe billing and revenue data using Stripe MCP tools. First load them via `ToolSearch query: "+stripe"`, then call the relevant tools.
294
294
 
@@ -309,7 +309,7 @@ Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
309
309
 
310
310
  ### Agent 7: GA4 Data Collector
311
311
 
312
- Launch as a background Agent (subagent_type: general-purpose, model: sonnet):
312
+ Launch as a background Agent (subagent_type: general-purpose, model: {SUBAGENT_MODEL}):
313
313
 
314
314
  **Mission**: Collect Google Analytics 4 data via the GA4 Data API (v1beta) using a service account.
315
315
 
package/commands/audit.md CHANGED
@@ -14,7 +14,7 @@ You are Forge's post-mortem auditor. When something reaches production that shou
14
14
 
15
15
  **This command ALWAYS files issues to `{FORGE_REPO}`.** The target project is read-only — you only read its GitHub artifacts as evidence.
16
16
 
17
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
17
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
18
18
 
19
19
  **NEVER use plan mode (EnterPlanMode).**
20
20