mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* feat(install): add guided Claude plugin setup * fix: support Claude command shims on Windows * feat: support safe Claude plugin scope migration * fix(install): preserve interactive setup terminal * fix(install): auto-migrate setup scope changes * feat(install): add guided multi-harness installer * fix(install): sync Yarn binary metadata * fix(install): handle wizard EOF on Node 18 * ci: allow installer matrix tests to finish * test(install): allow slower PowerShell delegation * fix(install): harden guided provider reconciliation * test(install): harden packaged and local compatibility * chore: prepare guided installer release 2.2.0 * fix(install): report refreshed Codex marketplace state * fix(install): verify managed content provenance * test(install): allow empty Yarn smoke fixture * test(install): invoke Windows package shims safely * fix(install): close cross-platform release gaps * fix(install): require trusted GitHub origins * fix(install): preserve hook profile precedence * refactor(install): centralize trusted GitHub origins * ci: retrigger workflow run after merge of main Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
147 lines
4.7 KiB
JavaScript
147 lines
4.7 KiB
JavaScript
'use strict';
|
|
|
|
const { version: ECC_VERSION } = require('../../package.json');
|
|
|
|
const COMMUNITY_LINKS = Object.freeze({
|
|
github: 'https://github.com/affaan-m/ECC',
|
|
discord: 'https://discord.gg/36yGMHGFbR',
|
|
documentation: 'https://github.com/affaan-m/ECC#readme',
|
|
githubApp: 'https://github.com/apps/ecc-tools',
|
|
});
|
|
|
|
const SUCCESS_ACTIONS = Object.freeze([
|
|
'installed',
|
|
'updated',
|
|
'migrated',
|
|
'resumed',
|
|
'already-migrated',
|
|
'configured',
|
|
]);
|
|
const SUCCESS_MESSAGES = Object.freeze({
|
|
installed: 'Welcome to ECC!',
|
|
updated: 'ECC is updated — thank you for using ECC!',
|
|
migrated: 'ECC is configured — thank you for using ECC!',
|
|
resumed: 'ECC is configured — thank you for using ECC!',
|
|
'already-migrated': 'ECC is configured — thank you for using ECC!',
|
|
configured: 'ECC is configured — thank you for using ECC!',
|
|
});
|
|
// CFonts' default "block" face: https://github.com/dominikwilkowski/cfonts
|
|
const ECC_WORDMARK = Object.freeze([
|
|
' ███████╗ ██████╗ ██████╗',
|
|
' ██╔════╝ ██╔════╝ ██╔════╝',
|
|
' █████╗ ██║ ██║',
|
|
' ██╔══╝ ██║ ██║',
|
|
' ███████╗ ╚██████╗ ╚██████╗',
|
|
' ╚══════╝ ╚═════╝ ╚═════╝',
|
|
]);
|
|
const ECC_GRADIENT = Object.freeze({
|
|
start: Object.freeze({ red: 215, green: 151, blue: 107 }),
|
|
end: Object.freeze({ red: 100, green: 131, blue: 160 }),
|
|
});
|
|
const ECC_VERSION_PATTERN = /^[0-9]+(?:\.[0-9]+){2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
const WORDMARK_START_COLUMN = Math.min(...ECC_WORDMARK.map(line => line.search(/\S/)));
|
|
const WORDMARK_END_COLUMN = Math.max(
|
|
...ECC_WORDMARK.map(line => line.trimEnd().length - 1)
|
|
);
|
|
|
|
function colorize(value, code, enabled) {
|
|
return enabled ? `\x1b[${code}m${value}\x1b[0m` : value;
|
|
}
|
|
|
|
function interpolateChannel(start, end, ratio) {
|
|
return Math.round(start + ((end - start) * ratio));
|
|
}
|
|
|
|
function gradientColorAt(column) {
|
|
const span = WORDMARK_END_COLUMN - WORDMARK_START_COLUMN;
|
|
const ratio = span === 0 ? 0 : (column - WORDMARK_START_COLUMN) / span;
|
|
return {
|
|
red: interpolateChannel(ECC_GRADIENT.start.red, ECC_GRADIENT.end.red, ratio),
|
|
green: interpolateChannel(ECC_GRADIENT.start.green, ECC_GRADIENT.end.green, ratio),
|
|
blue: interpolateChannel(ECC_GRADIENT.start.blue, ECC_GRADIENT.end.blue, ratio),
|
|
};
|
|
}
|
|
|
|
function renderWordmark(color) {
|
|
if (!color) return ECC_WORDMARK.join('\n');
|
|
|
|
return ECC_WORDMARK.map(line => (
|
|
[...line].map((character, column) => {
|
|
if (character === ' ') return character;
|
|
const value = gradientColorAt(column);
|
|
return `\x1b[38;2;${value.red};${value.green};${value.blue}m${character}`;
|
|
}).join('') + '\x1b[0m'
|
|
)).join('\n');
|
|
}
|
|
|
|
function renderCommunityLinks() {
|
|
const rows = Object.freeze([
|
|
`GitHub: ${COMMUNITY_LINKS.github}`,
|
|
`Discord: ${COMMUNITY_LINKS.discord}`,
|
|
`Documentation: ${COMMUNITY_LINKS.documentation}`,
|
|
`GitHub App: ${COMMUNITY_LINKS.githubApp}`,
|
|
]);
|
|
const contentWidth = Math.max(...rows.map(row => row.length));
|
|
const border = '─'.repeat(contentWidth + 2);
|
|
|
|
return [
|
|
` ╭${border}╮`,
|
|
...rows.map(row => ` │ ${row.padEnd(contentWidth)} │`),
|
|
` ╰${border}╯`,
|
|
];
|
|
}
|
|
|
|
function renderTerminalWelcome(options = {}) {
|
|
const color = options.color === true;
|
|
const installedVersion = options.version || ECC_VERSION;
|
|
if (!ECC_VERSION_PATTERN.test(installedVersion)) {
|
|
throw new Error(`Invalid ECC version: ${installedVersion}`);
|
|
}
|
|
const graphic = renderWordmark(color);
|
|
const successMessage = SUCCESS_MESSAGES[options.action] || SUCCESS_MESSAGES.installed;
|
|
const welcomeMessage = colorize(successMessage, '1;35', color);
|
|
const version = colorize(`v${installedVersion}`, '2', color);
|
|
const versionLine = color ? `\x1b[1G ${version}` : ` ${version}`;
|
|
|
|
return [
|
|
'',
|
|
graphic,
|
|
'',
|
|
` ${welcomeMessage}`,
|
|
versionLine,
|
|
'',
|
|
...renderCommunityLinks(),
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
function showTerminalWelcome(options = {}) {
|
|
const {
|
|
action,
|
|
dryRun = false,
|
|
env = process.env,
|
|
interactive = false,
|
|
json = false,
|
|
output = process.stdout,
|
|
} = options;
|
|
const shouldShow = (
|
|
interactive
|
|
&& output.isTTY === true
|
|
&& !dryRun
|
|
&& !json
|
|
&& SUCCESS_ACTIONS.includes(action)
|
|
);
|
|
if (!shouldShow) return false;
|
|
|
|
const color = env.NO_COLOR === undefined && env.TERM !== 'dumb';
|
|
output.write(renderTerminalWelcome({ action, color }));
|
|
return true;
|
|
}
|
|
|
|
module.exports = {
|
|
COMMUNITY_LINKS,
|
|
ECC_VERSION_PATTERN,
|
|
renderTerminalWelcome,
|
|
showTerminalWelcome,
|
|
};
|