forgedock 1.1.8 → 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.
- package/README.md +10 -33
- package/bin/cinema.mjs +23 -5
- package/bin/engine-cli.mjs +137 -11
- package/bin/env-detect.mjs +249 -0
- package/bin/forge-utils.mjs +364 -4
- package/bin/forgedock.mjs +1064 -135
- package/bin/hooks/session-start.mjs +68 -3
- package/bin/init-detect.mjs +101 -12
- package/bin/journey.mjs +917 -16
- package/bin/recall.mjs +24 -7
- package/bin/registry.mjs +91 -3
- package/bin/runner.mjs +38 -2
- package/bin/tui.mjs +49 -3
- package/commands/adopt.md +1 -1
- package/commands/analytics.md +8 -8
- package/commands/audit.md +1 -1
- package/commands/autopilot.md +1 -1
- package/commands/ci-audit.md +1 -1
- package/commands/cleanup.md +87 -52
- package/commands/compat-audit.md +1 -1
- package/commands/deploy-info.md +1 -1
- package/commands/deploy-pr.md +1 -1
- package/commands/diagnose.md +1 -1
- package/commands/explain.md +1 -1
- package/commands/fix-ci.md +1 -1
- package/commands/forgedock-init.md +1 -1
- package/commands/incident-response.md +3 -3
- package/commands/issue.md +1 -1
- package/commands/optimize.md +1 -1
- package/commands/orchestrate/config.md +13 -3
- package/commands/orchestrate/phase-1-resolve.md +1 -1
- package/commands/orchestrate/phase-2-triage.md +1 -1
- package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
- package/commands/orchestrate/phase-3-dependency.md +128 -20
- package/commands/orchestrate/phase-4-execution.md +598 -43
- package/commands/orchestrate/phase-5-cleanup.md +1 -1
- package/commands/orchestrate/phase-6-report.md +51 -3
- package/commands/orchestrate/safety.md +2 -2
- package/commands/qa-sweep.md +1 -1
- package/commands/quality-gate.md +1 -1
- package/commands/recover-orphans.md +5 -5
- package/commands/replay.md +1 -1
- package/commands/review-pr-agents/api.md +2 -2
- package/commands/review-pr-agents/auth.md +2 -2
- package/commands/review-pr-agents/billing.md +2 -2
- package/commands/review-pr-agents/concurrency.md +2 -2
- package/commands/review-pr-agents/database.md +2 -2
- package/commands/review-pr-agents/frontend.md +2 -2
- package/commands/review-pr-agents/infra.md +2 -2
- package/commands/review-pr-agents/protocols.md +1 -1
- package/commands/review-pr-agents/scraper.md +2 -2
- package/commands/review-pr-agents/security.md +2 -2
- package/commands/review-pr-staging.md +41 -3
- package/commands/review-pr.md +71 -6
- package/commands/rollback.md +1 -1
- package/commands/scope.md +1 -1
- package/commands/security-audit.md +1 -1
- package/commands/test-gate.md +1 -1
- package/commands/validate.md +1 -1
- package/commands/work-on/build/architect.md +1 -1
- package/commands/work-on/build/context.md +9 -6
- package/commands/work-on/build/implement.md +1 -1
- package/commands/work-on/build/validate.md +1 -1
- package/commands/work-on/build.md +31 -6
- package/commands/work-on/close.md +3 -2
- package/commands/work-on/decompose.md +1 -1
- package/commands/work-on/investigate.md +14 -5
- package/commands/work-on/remediate.md +331 -0
- package/commands/work-on/review.md +1 -1
- package/commands/work-on-monolithic.md +1 -1
- package/commands/work-on.md +194 -56
- package/docs/CI.md +2 -2
- package/docs/CODEX.md +1 -1
- package/docs/CONFIG.md +186 -0
- package/docs/site/command-reference.md +31 -1
- package/docs/site/getting-started.md +9 -34
- package/docs/site/index.md +1 -1
- package/docs/spec/label-state-machine.md +1 -1
- package/package.json +2 -1
- package/packages/protocol/src/types.js +1 -1
- package/scripts/check-command-docs-drift.sh +4 -2
- package/scripts/danger-zones.mjs +19 -12
- package/scripts/transition-label.sh +101 -13
- package/scripts/refresh-bot-token.sh +0 -97
package/bin/recall.mjs
CHANGED
|
@@ -281,9 +281,10 @@ function buildRenameMap(indexDir) {
|
|
|
281
281
|
const lines = readFileSync(renamesPath, 'utf8').split('\n').filter(l => l.trim());
|
|
282
282
|
for (const line of lines) {
|
|
283
283
|
const entry = JSON.parse(line);
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
// One-hop record only (last entry for a given `from` wins, matching
|
|
285
|
+
// append-only log semantics). Multi-hop chains (A->B->C) are resolved
|
|
286
|
+
// transitively at lookup time in applyRenameMap — see below.
|
|
287
|
+
if (entry && entry.from) map[entry.from] = entry.to;
|
|
287
288
|
}
|
|
288
289
|
} catch (_) {
|
|
289
290
|
// Return empty map on error
|
|
@@ -293,10 +294,21 @@ function buildRenameMap(indexDir) {
|
|
|
293
294
|
|
|
294
295
|
/**
|
|
295
296
|
* Apply rename map to query file path.
|
|
296
|
-
*
|
|
297
|
+
* Follows the rename chain to a fixed point so multi-hop renames
|
|
298
|
+
* (e.g. A -> B -> C) resolve to the final current path, not just the
|
|
299
|
+
* first hop. Guards against cycles (e.g. A -> B, B -> A) with a visited
|
|
300
|
+
* set so a malformed/cyclic rename log can't cause an infinite loop.
|
|
297
301
|
*/
|
|
298
302
|
function applyRenameMap(queryPath, renameMap) {
|
|
299
|
-
|
|
303
|
+
let current = queryPath;
|
|
304
|
+
const seen = new Set([current]);
|
|
305
|
+
while (Object.prototype.hasOwnProperty.call(renameMap, current)) {
|
|
306
|
+
const next = renameMap[current];
|
|
307
|
+
if (next === undefined || next === current || seen.has(next)) break;
|
|
308
|
+
current = next;
|
|
309
|
+
seen.add(current);
|
|
310
|
+
}
|
|
311
|
+
return current;
|
|
300
312
|
}
|
|
301
313
|
|
|
302
314
|
// ---------------------------------------------------------------------------
|
|
@@ -404,7 +416,7 @@ function executeQuery(args, cards, postings) {
|
|
|
404
416
|
function formatCard(card, score, showScore = true) {
|
|
405
417
|
const lines = [];
|
|
406
418
|
const cite = `#${card.issue}`;
|
|
407
|
-
const kind = card.kind.toUpperCase();
|
|
419
|
+
const kind = typeof card.kind === 'string' && card.kind ? card.kind.toUpperCase() : 'UNKNOWN';
|
|
408
420
|
const scoreStr = showScore ? ` (score: ${score.toFixed(2)})` : '';
|
|
409
421
|
const staleStr = card.status === 'stale' ? ' [STALE]' : '';
|
|
410
422
|
|
|
@@ -537,4 +549,9 @@ function main() {
|
|
|
537
549
|
}
|
|
538
550
|
}
|
|
539
551
|
|
|
540
|
-
|
|
552
|
+
try {
|
|
553
|
+
main();
|
|
554
|
+
} catch (e) {
|
|
555
|
+
process.stderr.write(`[recall] ERROR: ${e.message}\n`);
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
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 ||
|
|
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
|
|
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 =
|
|
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: "
|
|
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. -->
|
package/commands/analytics.md
CHANGED
|
@@ -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: "
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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: "
|
|
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
|
|
package/commands/autopilot.md
CHANGED
|
@@ -30,7 +30,7 @@ You are a fully autonomous deploy loop for this project. Your job is to **detect
|
|
|
30
30
|
|
|
31
31
|
**This command resumes from wherever the pipeline is stuck.** It always reads current GitHub state before taking any action.
|
|
32
32
|
|
|
33
|
-
**Agent model policy**: `model: "
|
|
33
|
+
**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>`.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
package/commands/ci-audit.md
CHANGED
|
@@ -30,7 +30,7 @@ This command is designed to be run:
|
|
|
30
30
|
|
|
31
31
|
This is NOT a PR review — it does not approve or block. It creates issues for each CI gap found.
|
|
32
32
|
|
|
33
|
-
**Agent model policy**: `model: "
|
|
33
|
+
**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.
|
|
34
34
|
**NEVER use plan mode (EnterPlanMode).**
|
|
35
35
|
|
|
36
36
|
---
|