forgedock 1.0.15 → 1.0.17
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 +82 -60
- package/bin/forgedock.mjs +1992 -540
- package/bin/github-app-manifest.json.license +2 -0
- package/bin/hooks/session-start.mjs +4 -5
- package/bin/registry.mjs +20 -0
- package/bin/tui.mjs +172 -96
- package/commands/adopt.md +433 -0
- package/commands/analytics.md +2 -0
- package/commands/audit-agents.md +2 -0
- package/commands/audit.md +2 -0
- package/commands/changelog.md +354 -0
- package/commands/cleanup.md +2 -0
- package/commands/compat-audit.md +330 -0
- package/commands/deploy-info.md +6 -5
- package/commands/diagnose.md +483 -0
- package/commands/explain.md +349 -0
- package/commands/forge-stats.md +2 -0
- package/commands/forgedock-init.md +2 -0
- package/commands/geo-audit.md +2 -0
- package/commands/incident-response.md +2 -0
- package/commands/issue.md +2 -0
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +118 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +2 -0
- package/commands/quality-gate.md +37 -0
- package/commands/replay.md +263 -0
- package/commands/resume.md +264 -0
- package/commands/review-pr-agents.md +5 -1
- package/commands/review-pr-staging.md +2 -0
- package/commands/review-pr.md +85 -3
- package/commands/rollback.md +2 -0
- package/commands/scope.md +280 -0
- package/commands/security-audit.md +2 -0
- package/commands/status.md +262 -0
- package/commands/sync-ecosystem.md +2 -0
- package/commands/validate.md +2 -0
- package/commands/work-on/build/architect.md +128 -18
- package/commands/work-on/build/context.md +130 -17
- package/commands/work-on/build/implement.md +2 -0
- package/commands/work-on/build/validate.md +2 -0
- package/commands/work-on/build.md +32 -7
- package/commands/work-on/close.md +85 -0
- package/commands/work-on/decompose.md +2 -0
- package/commands/work-on/investigate.md +30 -3
- package/commands/work-on/review.md +13 -1
- package/commands/work-on-monolithic.md +2 -0
- package/commands/work-on.md +624 -36
- package/docs/BRAND.md +51 -0
- package/docs/CONFIG.md +94 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/site/getting-started.md +21 -0
- package/package.json +10 -2
- package/scripts/classify-lane.sh +145 -0
- package/scripts/transition-label.sh +161 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/templates/devdocs/README.md +72 -0
- package/templates/devdocs/agent/using-forgedock.md +3 -0
- package/templates/devdocs/agent/using-github.md +3 -0
- package/templates/devdocs/index.yaml +162 -0
- package/templates/devdocs/project/architecture.md +3 -0
- package/templates/devdocs/project/conventions.md +3 -0
- package/templates/devdocs/project/custom-instructions.md +3 -0
- package/templates/devdocs/project/glossary.md +3 -0
- package/templates/devdocs/project/stack.md +3 -0
- package/templates/devdocs/templates/commit-convention.md +3 -0
- package/templates/devdocs/templates/issue.md +3 -0
- package/templates/devdocs/templates/pull-request.md +3 -0
- package/templates/devdocs/templates/review-finding.md +3 -0
|
@@ -245,7 +245,7 @@ ${nameNote}${repoNote}${descNote}${milestoneNote}
|
|
|
245
245
|
| /autopilot | Autonomous platform improvement cycle |
|
|
246
246
|
|
|
247
247
|
Run \`/help\` to see the full command list.
|
|
248
|
-
To
|
|
248
|
+
To remove ForgeDock commands: \`npx forgedock uninstall\``;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
/**
|
|
@@ -282,12 +282,11 @@ function buildNudgeContext(dir) {
|
|
|
282
282
|
<!-- ForgeDock: unmanaged nudge -->
|
|
283
283
|
**ForgeDock** is installed but not active in this directory (${safeDir}).
|
|
284
284
|
|
|
285
|
-
To
|
|
286
|
-
\`npx forgedock
|
|
287
|
-
\`npx forgedock init\` — immediately generate forge.yaml with AI autopilot
|
|
285
|
+
To activate the autonomous development pipeline here:
|
|
286
|
+
\`npx forgedock init\` — generate forge.yaml and activate ForgeDock for this project
|
|
288
287
|
|
|
289
288
|
This message will not appear again in this directory.
|
|
290
|
-
To
|
|
289
|
+
To remove ForgeDock commands globally, run: \`npx forgedock uninstall\``;
|
|
291
290
|
}
|
|
292
291
|
|
|
293
292
|
// ---------------------------------------------------------------------------
|
package/bin/registry.mjs
CHANGED
|
@@ -356,3 +356,23 @@ export async function markNudgeSeen(dir) {
|
|
|
356
356
|
registry.nudgeSeen[absDir] = { at: new Date().toISOString() };
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Remove the nudgeSeen entry for a directory from the registry.
|
|
362
|
+
*
|
|
363
|
+
* Called by `npx forgedock uninstall` to clean up per-directory state
|
|
364
|
+
* written by the session-start hook. After uninstall, the nudge will fire
|
|
365
|
+
* once more if ForgeDock is later reinstalled in the same directory —
|
|
366
|
+
* consistent with a fresh install experience.
|
|
367
|
+
*
|
|
368
|
+
* Best-effort: a failed write is non-fatal and leaves the registry as-is.
|
|
369
|
+
*
|
|
370
|
+
* @param {string} dir - Absolute path to the directory.
|
|
371
|
+
* @returns {Promise<void>}
|
|
372
|
+
*/
|
|
373
|
+
export async function clearNudgeSeen(dir) {
|
|
374
|
+
const absDir = normalizeDir(dir);
|
|
375
|
+
await writeRegistry((registry) => {
|
|
376
|
+
delete registry.nudgeSeen[absDir];
|
|
377
|
+
});
|
|
378
|
+
}
|
package/bin/tui.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* bin/tui.mjs — Zero-dependency TUI primitives for ForgeDock
|
|
3
6
|
*
|
|
@@ -1347,19 +1350,17 @@ export async function runSteps(steps, opts = {}) {
|
|
|
1347
1350
|
|
|
1348
1351
|
drawActive();
|
|
1349
1352
|
|
|
1350
|
-
// Move back to bottom
|
|
1351
|
-
//
|
|
1352
|
-
|
|
1353
|
-
if (
|
|
1353
|
+
// Move back to bottom (the empty line AFTER the last step row).
|
|
1354
|
+
// drawActive() leaves the cursor on row i with no trailing newline,
|
|
1355
|
+
// so we need rowsFromBottom (not -1) to reach the line below row count-1.
|
|
1356
|
+
if (rowsFromBottom > 0) stream.write(`\x1b[${rowsFromBottom}B`);
|
|
1354
1357
|
cursorAtBottom = true;
|
|
1355
1358
|
|
|
1356
1359
|
// Spinner interval updates row i in-place
|
|
1357
1360
|
const timer = setInterval(() => {
|
|
1358
|
-
const savedCursorAtBottom = cursorAtBottom;
|
|
1359
1361
|
cursorUp(rowsFromBottom);
|
|
1360
1362
|
drawActive();
|
|
1361
|
-
if (
|
|
1362
|
-
cursorAtBottom = savedCursorAtBottom;
|
|
1363
|
+
if (rowsFromBottom > 0) stream.write(`\x1b[${rowsFromBottom}B`);
|
|
1363
1364
|
}, spinnerInterval);
|
|
1364
1365
|
timer.unref();
|
|
1365
1366
|
|
|
@@ -1407,8 +1408,9 @@ export async function runSteps(steps, opts = {}) {
|
|
|
1407
1408
|
cursorAtBottom = false;
|
|
1408
1409
|
overwriteRow(i);
|
|
1409
1410
|
stream.write("\n");
|
|
1410
|
-
//
|
|
1411
|
-
|
|
1411
|
+
// After the \n, cursor is on row i+1. Distance to bottom = rowsFromBottom - 1.
|
|
1412
|
+
const rowsBelowAfterNl = rowsFromBottom - 1;
|
|
1413
|
+
if (rowsBelowAfterNl > 0) stream.write(`\x1b[${rowsBelowAfterNl}B`);
|
|
1412
1414
|
cursorAtBottom = true;
|
|
1413
1415
|
|
|
1414
1416
|
if (stepError) {
|
|
@@ -1426,11 +1428,11 @@ export async function runSteps(steps, opts = {}) {
|
|
|
1426
1428
|
|
|
1427
1429
|
if (result) {
|
|
1428
1430
|
// Failed
|
|
1429
|
-
stream.write(
|
|
1431
|
+
stream.write(`\r\x1b[K${red("✖")} ${dim("Failed in")} ${elapsed}\n`);
|
|
1430
1432
|
return result;
|
|
1431
1433
|
}
|
|
1432
1434
|
|
|
1433
|
-
stream.write(
|
|
1435
|
+
stream.write(`\r\x1b[K${green("✔")} ${dim("Done in")} ${elapsed}\n`);
|
|
1434
1436
|
return { ok: true, elapsed: Date.now() - t0 };
|
|
1435
1437
|
}
|
|
1436
1438
|
|
|
@@ -1447,105 +1449,152 @@ const USE_TRUECOLOR =
|
|
|
1447
1449
|
USE_ANSI &&
|
|
1448
1450
|
(process.env.COLORTERM === "truecolor" ||
|
|
1449
1451
|
process.env.COLORTERM === "24bit" ||
|
|
1450
|
-
// Windows Terminal
|
|
1451
|
-
|
|
1452
|
+
// Windows Terminal sets WT_SESSION but not always COLORTERM
|
|
1453
|
+
!!process.env.WT_SESSION ||
|
|
1452
1454
|
process.env.TERM_PROGRAM === "iTerm.app" ||
|
|
1453
|
-
process.env.TERM_PROGRAM === "vscode"
|
|
1455
|
+
process.env.TERM_PROGRAM === "vscode" ||
|
|
1456
|
+
process.env.TERM_PROGRAM === "WezTerm" ||
|
|
1457
|
+
process.env.TERM_PROGRAM === "Hyper" ||
|
|
1458
|
+
// Ghostty, Alacritty, kitty — modern GPU-accelerated terminals
|
|
1459
|
+
process.env.TERM_PROGRAM === "ghostty" ||
|
|
1460
|
+
process.env.TERM === "xterm-kitty");
|
|
1454
1461
|
|
|
1455
1462
|
/**
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
* Each line uses ▀ (U+2580 UPPER HALF BLOCK) with ANSI 24-bit color:
|
|
1459
|
-
* \x1b[38;2;R;G;Bm — foreground (upper pixel pair)
|
|
1460
|
-
* \x1b[48;2;R;G;Bm — background (lower pixel pair)
|
|
1461
|
-
* \x1b[0m — reset
|
|
1463
|
+
* Angular F-monogram pixel map — hand-crafted to match the ForgeDock brand mark.
|
|
1462
1464
|
*
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1465
|
+
* Two forward-leaning parallelogram arms forming a stylized italic F:
|
|
1466
|
+
* - Upper arm: wide, sweeps from center-left to upper-right (rows 0-6)
|
|
1467
|
+
* - Notch: triangular gap opening right between the two arms (rows 7-8)
|
|
1468
|
+
* - Lower arm: shorter, same lean angle (rows 9-12)
|
|
1469
|
+
* - Stem: tapers to a point at bottom-left (rows 13-19)
|
|
1465
1470
|
*
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
*
|
|
1469
|
-
* To regenerate this art from a source PNG: scripts/gen-logo.mjs <input.png>
|
|
1471
|
+
* 18 columns × 20 rows → 18 wide × 10 terminal rows (half-block pairs).
|
|
1472
|
+
* 1 = filled pixel, 0 = transparent (uses terminal default background).
|
|
1470
1473
|
*/
|
|
1471
1474
|
// prettier-ignore
|
|
1472
|
-
const
|
|
1473
|
-
//
|
|
1474
|
-
//
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
//
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1475
|
+
const LOGO_PIXELS = [
|
|
1476
|
+
//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
|
|
1477
|
+
[0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0], // 0: upper arm top
|
|
1478
|
+
[0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0], // 1
|
|
1479
|
+
[0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0], // 2
|
|
1480
|
+
[0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0], // 3
|
|
1481
|
+
[0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], // 4
|
|
1482
|
+
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0], // 5
|
|
1483
|
+
[0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0], // 6: upper arm bottom
|
|
1484
|
+
[0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], // 7: stem only (notch)
|
|
1485
|
+
[0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 8: stem only (notch)
|
|
1486
|
+
[1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0], // 9: lower arm top
|
|
1487
|
+
[1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0], // 10
|
|
1488
|
+
[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0], // 11
|
|
1489
|
+
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], // 12: lower arm bottom
|
|
1490
|
+
[1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], // 13: stem
|
|
1491
|
+
[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 14
|
|
1492
|
+
[1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 15
|
|
1493
|
+
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 16
|
|
1494
|
+
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 17
|
|
1495
|
+
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 18
|
|
1496
|
+
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 19
|
|
1497
|
+
];
|
|
1485
1498
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
//
|
|
1494
|
-
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1497
|
-
//
|
|
1498
|
-
//
|
|
1499
|
-
//
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
//
|
|
1503
|
-
|
|
1504
|
-
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], // 0: top border
|
|
1505
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1], // 1: top inner
|
|
1506
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 2: left bar
|
|
1507
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 3: left bar
|
|
1508
|
-
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0], // 4: top crossbar
|
|
1509
|
-
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0], // 5: top crossbar (repeat)
|
|
1510
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 6: left bar
|
|
1511
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 7: left bar
|
|
1512
|
-
[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], // 8: lower crossbar
|
|
1513
|
-
[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], // 9: lower crossbar (repeat)
|
|
1514
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 10: left bar
|
|
1515
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 11: left bar
|
|
1516
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 12: left bar
|
|
1517
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 13: left bar
|
|
1518
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 14: left bar
|
|
1519
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 15: left bar
|
|
1520
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 16: left bar
|
|
1521
|
-
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], // 17: left bar
|
|
1522
|
-
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], // 18: bottom border
|
|
1523
|
-
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], // 19: bottom border (repeat)
|
|
1524
|
-
];
|
|
1499
|
+
/**
|
|
1500
|
+
* Gradient palette for the F-monogram — interpolates from bright ice-blue
|
|
1501
|
+
* at the top to deep ocean-blue at the bottom, creating a metallic sheen.
|
|
1502
|
+
*
|
|
1503
|
+
* Each entry is [R, G, B] for the corresponding pixel row pair.
|
|
1504
|
+
*/
|
|
1505
|
+
const LOGO_GRADIENT = [
|
|
1506
|
+
[150, 215, 255], // rows 0-1: ice blue (highlight)
|
|
1507
|
+
[140, 208, 255], // rows 2-3
|
|
1508
|
+
[128, 198, 255], // rows 4-5
|
|
1509
|
+
[115, 185, 255], // rows 6-7
|
|
1510
|
+
[100, 172, 250], // rows 8-9: brand blue
|
|
1511
|
+
[85, 158, 240], // rows 10-11
|
|
1512
|
+
[68, 140, 228], // rows 12-13
|
|
1513
|
+
[52, 122, 215], // rows 14-15
|
|
1514
|
+
[40, 108, 200], // rows 16-17
|
|
1515
|
+
[35, 95, 190], // rows 18-19: deep blue
|
|
1516
|
+
];
|
|
1525
1517
|
|
|
1518
|
+
/**
|
|
1519
|
+
* Render the F-monogram as half-block art with per-row gradient colors.
|
|
1520
|
+
* Uses transparent background (no bg color set — inherits terminal default).
|
|
1521
|
+
*
|
|
1522
|
+
* @returns {string[]} Array of ANSI-decorated terminal lines (10 lines)
|
|
1523
|
+
*/
|
|
1524
|
+
function renderLogoArt() {
|
|
1525
|
+
const RST = "\x1b[0m";
|
|
1526
1526
|
const lines = [];
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
const
|
|
1527
|
+
|
|
1528
|
+
for (let r = 0; r < LOGO_PIXELS.length; r += 2) {
|
|
1529
|
+
const top = LOGO_PIXELS[r];
|
|
1530
|
+
const bot = LOGO_PIXELS[r + 1];
|
|
1531
|
+
const pairIdx = r / 2; // 0..7
|
|
1532
|
+
const [tR, tG, tB] = LOGO_GRADIENT[pairIdx];
|
|
1533
|
+
|
|
1534
|
+
// Slightly darker shade for the lower pixel row (depth effect)
|
|
1535
|
+
const bR = Math.max(0, tR - 15);
|
|
1536
|
+
const bG = Math.max(0, tG - 15);
|
|
1537
|
+
const bB = Math.max(0, tB - 15);
|
|
1538
|
+
|
|
1530
1539
|
let line = "";
|
|
1531
1540
|
for (let c = 0; c < top.length; c++) {
|
|
1532
|
-
const
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1541
|
+
const hasTop = top[c];
|
|
1542
|
+
const hasBot = bot[c];
|
|
1543
|
+
|
|
1544
|
+
if (hasTop && hasBot) {
|
|
1545
|
+
// Both filled: fg=top color, bg=bot color, print ▀
|
|
1546
|
+
line += `\x1b[38;2;${tR};${tG};${tB}m\x1b[48;2;${bR};${bG};${bB}m▀${RST}`;
|
|
1547
|
+
} else if (hasTop && !hasBot) {
|
|
1548
|
+
// Only top filled: fg=top color, no bg (transparent), print ▀
|
|
1549
|
+
line += `\x1b[38;2;${tR};${tG};${tB}m▀${RST}`;
|
|
1550
|
+
} else if (!hasTop && hasBot) {
|
|
1551
|
+
// Only bottom filled: fg=bot color, no bg, print ▄ (lower half block)
|
|
1552
|
+
line += `\x1b[38;2;${bR};${bG};${bB}m▄${RST}`;
|
|
1553
|
+
} else {
|
|
1554
|
+
// Both empty: space
|
|
1555
|
+
line += " ";
|
|
1556
|
+
}
|
|
1535
1557
|
}
|
|
1536
1558
|
lines.push(line);
|
|
1537
1559
|
}
|
|
1538
1560
|
return lines;
|
|
1539
|
-
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Render text with a horizontal gradient (per-character 24-bit color).
|
|
1565
|
+
*
|
|
1566
|
+
* @param {string} text
|
|
1567
|
+
* @param {[number,number,number]} start - Start RGB
|
|
1568
|
+
* @param {[number,number,number]} end - End RGB
|
|
1569
|
+
* @returns {string} ANSI-colored string
|
|
1570
|
+
*/
|
|
1571
|
+
function gradientText(text, start, end) {
|
|
1572
|
+
const RST = "\x1b[0m";
|
|
1573
|
+
let result = "\x1b[1m"; // bold
|
|
1574
|
+
const len = text.length;
|
|
1575
|
+
for (let i = 0; i < len; i++) {
|
|
1576
|
+
if (text[i] === " ") {
|
|
1577
|
+
result += " ";
|
|
1578
|
+
continue;
|
|
1579
|
+
}
|
|
1580
|
+
const t = len === 1 ? 0 : i / (len - 1);
|
|
1581
|
+
const r = Math.round(start[0] + (end[0] - start[0]) * t);
|
|
1582
|
+
const g = Math.round(start[1] + (end[1] - start[1]) * t);
|
|
1583
|
+
const b = Math.round(start[2] + (end[2] - start[2]) * t);
|
|
1584
|
+
result += `\x1b[38;2;${r};${g};${b}m${text[i]}`;
|
|
1585
|
+
}
|
|
1586
|
+
return result + RST;
|
|
1587
|
+
}
|
|
1540
1588
|
|
|
1541
1589
|
/**
|
|
1542
1590
|
* Render the ForgeDock logo for display in the terminal.
|
|
1543
1591
|
*
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1592
|
+
* On truecolor TTY: angular F-monogram with gradient, brand name with gradient
|
|
1593
|
+
* text, and tagline — rendered side-by-side (logo left, text right).
|
|
1594
|
+
* On non-TTY / NO_COLOR / 256-color: plain text.
|
|
1546
1595
|
*
|
|
1547
1596
|
* @param {object} [opts]
|
|
1548
|
-
* @param {string} [opts.version] - Package version string (e.g. "1.0.
|
|
1597
|
+
* @param {string} [opts.version] - Package version string (e.g. "1.0.14")
|
|
1549
1598
|
* @returns {string} Rendered logo string (may contain ANSI sequences)
|
|
1550
1599
|
*/
|
|
1551
1600
|
export function renderLogo({ version = "" } = {}) {
|
|
@@ -1557,14 +1606,41 @@ export function renderLogo({ version = "" } = {}) {
|
|
|
1557
1606
|
return `${versionStr}\n${tagline}`;
|
|
1558
1607
|
}
|
|
1559
1608
|
|
|
1560
|
-
// Truecolor:
|
|
1561
|
-
const
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1609
|
+
// Truecolor: side-by-side layout — logo on left, text on right
|
|
1610
|
+
const art = renderLogoArt(); // 10 lines, each 18 chars wide (visible)
|
|
1611
|
+
const pad = " "; // gap between logo and text
|
|
1612
|
+
|
|
1613
|
+
// Text lines to appear to the right of the logo (vertically centered)
|
|
1614
|
+
const brandLine = gradientText(
|
|
1615
|
+
"FORGEDOCK",
|
|
1616
|
+
[150, 215, 255], // ice blue
|
|
1617
|
+
[35, 95, 190], // deep blue
|
|
1618
|
+
);
|
|
1619
|
+
const versionLine = version
|
|
1620
|
+
? `\x1b[2m\x1b[38;2;88;166;255mv${version}\x1b[0m`
|
|
1621
|
+
: "";
|
|
1622
|
+
const taglineLine = `\x1b[2m${tagline}\x1b[0m`;
|
|
1623
|
+
|
|
1624
|
+
// Place text starting at row 3 (0-indexed) for vertical centering
|
|
1625
|
+
// art has 10 rows; text block occupies rows 3-6
|
|
1626
|
+
const textRows = [
|
|
1627
|
+
"", // row 0
|
|
1628
|
+
"", // row 1
|
|
1629
|
+
"", // row 2
|
|
1630
|
+
brandLine, // row 3: FORGEDOCK
|
|
1631
|
+
versionLine, // row 4: v1.0.14
|
|
1632
|
+
"", // row 5
|
|
1633
|
+
taglineLine, // row 6: tagline
|
|
1634
|
+
"", // row 7
|
|
1635
|
+
"", // row 8
|
|
1636
|
+
"", // row 9
|
|
1568
1637
|
];
|
|
1638
|
+
|
|
1639
|
+
const lines = [""];
|
|
1640
|
+
for (let i = 0; i < art.length; i++) {
|
|
1641
|
+
const textPart = textRows[i] || "";
|
|
1642
|
+
lines.push(` ${art[i]}${pad}${textPart}`);
|
|
1643
|
+
}
|
|
1644
|
+
lines.push("");
|
|
1569
1645
|
return lines.join("\n");
|
|
1570
1646
|
}
|