@just-every/design 0.1.23 → 0.1.24
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/dist/cli.js +404 -118
- package/dist/cli.js.map +1 -1
- package/dist/install.d.ts +34 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +334 -70
- package/dist/install.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import { parseArtifactsDownloadPositional, parseArtifactsListPositional } from '
|
|
|
16
16
|
import { clearConfig, readConfig, resolveConfigPath, writeConfig } from './config.js';
|
|
17
17
|
import { DesignAppClient } from './design-client.js';
|
|
18
18
|
import { parseJsonOrThrow } from './json.js';
|
|
19
|
-
import { detectClients, detectDefaultClients, runInstall, runRemove, } from './install.js';
|
|
19
|
+
import { checkIntegrations, detectClients, detectDefaultClients, ensureLocalLauncher, resolveLocalInstallLayout, runInstall, runRemove, } from './install.js';
|
|
20
20
|
import { startMcpServer } from './server.js';
|
|
21
21
|
import { buildCreateRunRequest, NOT_AUTHENTICATED_HELP, watchRun } from './tool-logic.js';
|
|
22
22
|
function parseArgs(argv) {
|
|
@@ -476,6 +476,185 @@ async function promptYesNo(question, defaultYes = false) {
|
|
|
476
476
|
rl.close();
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
|
+
function createInstallChecklistUi(args) {
|
|
480
|
+
const stream = process.stderr;
|
|
481
|
+
const enabled = args.enabled;
|
|
482
|
+
const title = args.title ?? 'Every Design setup';
|
|
483
|
+
const state = {
|
|
484
|
+
auth: 'todo',
|
|
485
|
+
cli: 'todo',
|
|
486
|
+
integrations: 'todo',
|
|
487
|
+
};
|
|
488
|
+
let mode = 'loading';
|
|
489
|
+
let loadingMessage = 'Checking…';
|
|
490
|
+
let workingOn = null;
|
|
491
|
+
function color(code, text) {
|
|
492
|
+
if (!enabled)
|
|
493
|
+
return text;
|
|
494
|
+
return `\x1b[${code}m${text}\x1b[0m`;
|
|
495
|
+
}
|
|
496
|
+
function iconFor(s) {
|
|
497
|
+
if (s === 'ok')
|
|
498
|
+
return color('32', '✔');
|
|
499
|
+
if (s === 'working')
|
|
500
|
+
return color('33', '▣');
|
|
501
|
+
return color('90', '☐');
|
|
502
|
+
}
|
|
503
|
+
function clear() {
|
|
504
|
+
if (!enabled)
|
|
505
|
+
return;
|
|
506
|
+
// Clear screen + home.
|
|
507
|
+
stream.write('\x1b[2J\x1b[H');
|
|
508
|
+
}
|
|
509
|
+
function render() {
|
|
510
|
+
clear();
|
|
511
|
+
stream.write(`${title}\n\n`);
|
|
512
|
+
if (mode === 'loading') {
|
|
513
|
+
stream.write(`${loadingMessage}\n`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const line = (key, label) => {
|
|
517
|
+
const s = state[key];
|
|
518
|
+
stream.write(` ${iconFor(s)} ${label}\n`);
|
|
519
|
+
};
|
|
520
|
+
line('auth', 'Authentication');
|
|
521
|
+
line('cli', 'Design CLI');
|
|
522
|
+
line('integrations', 'Integrations');
|
|
523
|
+
if (workingOn) {
|
|
524
|
+
stream.write(`\n${color('90', 'Working on:')} ${workingOn}\n`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function setLoading(message) {
|
|
528
|
+
mode = 'loading';
|
|
529
|
+
loadingMessage = message;
|
|
530
|
+
if (enabled)
|
|
531
|
+
render();
|
|
532
|
+
else
|
|
533
|
+
stream.write(`${title}: ${message}\n`);
|
|
534
|
+
}
|
|
535
|
+
function setChecklist(next) {
|
|
536
|
+
mode = 'checklist';
|
|
537
|
+
workingOn = null;
|
|
538
|
+
Object.assign(state, next);
|
|
539
|
+
if (enabled)
|
|
540
|
+
render();
|
|
541
|
+
else {
|
|
542
|
+
stream.write('== System Status ==\n');
|
|
543
|
+
stream.write(`- Authentication: ${state.auth}\n`);
|
|
544
|
+
stream.write(`- Design CLI: ${state.cli}\n`);
|
|
545
|
+
stream.write(`- Integrations: ${state.integrations}\n`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function setWorking(key, label) {
|
|
549
|
+
mode = 'checklist';
|
|
550
|
+
state[key] = 'working';
|
|
551
|
+
workingOn = label;
|
|
552
|
+
if (enabled)
|
|
553
|
+
render();
|
|
554
|
+
else
|
|
555
|
+
stream.write(`Working on: ${label}\n`);
|
|
556
|
+
}
|
|
557
|
+
function log(line) {
|
|
558
|
+
if (!enabled) {
|
|
559
|
+
stream.write(`${line}\n`);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
// Preserve the UI by printing below it.
|
|
563
|
+
stream.write(`\n${line}\n`);
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
setLoading,
|
|
567
|
+
setChecklist,
|
|
568
|
+
setWorking,
|
|
569
|
+
log,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function resolveNpmLatestVersion(packageName) {
|
|
573
|
+
const npm = which('npm');
|
|
574
|
+
if (!npm)
|
|
575
|
+
return null;
|
|
576
|
+
const res = spawnSync(npm, ['view', packageName, 'version', '--json'], { encoding: 'utf8' });
|
|
577
|
+
if (res.status !== 0)
|
|
578
|
+
return null;
|
|
579
|
+
const raw = String(res.stdout || '').trim();
|
|
580
|
+
if (!raw)
|
|
581
|
+
return null;
|
|
582
|
+
try {
|
|
583
|
+
const parsed = JSON.parse(raw);
|
|
584
|
+
if (typeof parsed === 'string')
|
|
585
|
+
return parsed.trim() || null;
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
// Some npm versions print a bare string.
|
|
589
|
+
}
|
|
590
|
+
return raw.split(/\r?\n/)[0]?.trim() || null;
|
|
591
|
+
}
|
|
592
|
+
function readJsonVersionMaybe(raw) {
|
|
593
|
+
try {
|
|
594
|
+
const parsed = JSON.parse(raw);
|
|
595
|
+
const v = typeof parsed?.version === 'string' ? parsed.version.trim() : '';
|
|
596
|
+
return v || null;
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
async function readLocalInstalledDesignCliVersion(homeDir) {
|
|
603
|
+
const local = resolveLocalInstallLayout(homeDir, 'xdg');
|
|
604
|
+
const pkgPath = path.join(local.installDir, 'node_modules', '@just-every', 'design', 'package.json');
|
|
605
|
+
try {
|
|
606
|
+
const raw = await readFile(pkgPath, 'utf8');
|
|
607
|
+
return readJsonVersionMaybe(raw);
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async function checkAuthValidity(args) {
|
|
614
|
+
const cfg = await loadMergedConfig(args.configPath, { designOrigin: args.designOrigin, loginOrigin: args.loginOrigin });
|
|
615
|
+
const sessionToken = (process.env.DESIGN_MCP_SESSION_TOKEN ?? cfg.sessionToken ?? '').trim();
|
|
616
|
+
if (!sessionToken) {
|
|
617
|
+
return { ok: false, cfg: null, reason: 'missing' };
|
|
618
|
+
}
|
|
619
|
+
const client = new DesignAppClient({
|
|
620
|
+
designOrigin: cfg.designOrigin,
|
|
621
|
+
sessionToken,
|
|
622
|
+
activeAccountSlug: cfg.activeAccountSlug,
|
|
623
|
+
});
|
|
624
|
+
try {
|
|
625
|
+
await client.getMe();
|
|
626
|
+
return { ok: true, cfg: cfg };
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
return { ok: false, cfg: cfg, reason: 'invalid' };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async function checkDesignCliStatus(args) {
|
|
633
|
+
if (args.launcher === 'npx') {
|
|
634
|
+
// Using npx means users can always run `npx -y <pkg>@latest` without a local launcher.
|
|
635
|
+
return { ok: true };
|
|
636
|
+
}
|
|
637
|
+
const local = resolveLocalInstallLayout(args.homeDir, 'xdg');
|
|
638
|
+
const launcherExists = existsSync(local.launcherPath);
|
|
639
|
+
const cliJsExists = existsSync(local.cliJsPath);
|
|
640
|
+
const installed = launcherExists && cliJsExists;
|
|
641
|
+
const needsPath = args.updatePath ? !pathHasEntry(local.binDir) : false;
|
|
642
|
+
const installedVersion = installed ? await readLocalInstalledDesignCliVersion(args.homeDir) : null;
|
|
643
|
+
const latestVersion = resolveNpmLatestVersion(args.packageName);
|
|
644
|
+
const outdated = Boolean(installed && latestVersion && installedVersion && latestVersion !== installedVersion);
|
|
645
|
+
const ok = installed && !needsPath && !outdated;
|
|
646
|
+
if (ok) {
|
|
647
|
+
return { ok: true, installedVersion, latestVersion };
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
ok: false,
|
|
651
|
+
missing: !installed,
|
|
652
|
+
outdated,
|
|
653
|
+
needsPath,
|
|
654
|
+
installedVersion,
|
|
655
|
+
latestVersion,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
479
658
|
function pathHasEntry(entry) {
|
|
480
659
|
const list = (process.env.PATH ?? '')
|
|
481
660
|
.split(path.delimiter)
|
|
@@ -660,7 +839,7 @@ function recommendedMode(client, skillsEnabled) {
|
|
|
660
839
|
return 'skill';
|
|
661
840
|
return 'mcp';
|
|
662
841
|
}
|
|
663
|
-
async function promptInstallPlan(clients, skillsEnabled) {
|
|
842
|
+
async function promptInstallPlan(clients, skillsEnabled, opts) {
|
|
664
843
|
const installed = clients.filter((c) => c.installed);
|
|
665
844
|
const missing = clients.filter((c) => !c.installed);
|
|
666
845
|
console.error('Detected clients:');
|
|
@@ -674,7 +853,7 @@ async function promptInstallPlan(clients, skillsEnabled) {
|
|
|
674
853
|
console.error('Not detected:');
|
|
675
854
|
missing.forEach((c) => console.error(` - ${c.label}`));
|
|
676
855
|
}
|
|
677
|
-
const useRecommended = await promptYesNo('Use recommended defaults for detected clients?', true);
|
|
856
|
+
const useRecommended = opts?.mode === 'edit' ? false : await promptYesNo('Use recommended defaults for detected clients?', true);
|
|
678
857
|
const plan = Object.create(null);
|
|
679
858
|
for (const c of clients) {
|
|
680
859
|
plan[c.client] = useRecommended ? recommendedMode(c, skillsEnabled) : 'disabled';
|
|
@@ -790,48 +969,16 @@ async function main() {
|
|
|
790
969
|
console.error(`Invalid --launcher value: ${launcherFlag}. Expected npx|local.`);
|
|
791
970
|
process.exit(1);
|
|
792
971
|
}
|
|
793
|
-
console.error('Every Design setup');
|
|
794
|
-
console.error('');
|
|
795
|
-
console.error('Step 1/3: Authenticate');
|
|
796
|
-
const existingAuth = await readConfig(configPath);
|
|
797
|
-
let authedCfg = existingAuth;
|
|
798
|
-
if (!hasAuth(existingAuth)) {
|
|
799
|
-
if (dryRun) {
|
|
800
|
-
console.error(`Dry run: no auth found at ${configPath}. Install would run auth login first.`);
|
|
801
|
-
}
|
|
802
|
-
else if (!isTtyInteractive()) {
|
|
803
|
-
console.error(`No auth found at ${configPath}.`);
|
|
804
|
-
console.error('Run `every-design auth login` (or `npx -y @just-every/design@latest auth login`) first, then re-run install.');
|
|
805
|
-
process.exit(1);
|
|
806
|
-
}
|
|
807
|
-
if (!dryRun) {
|
|
808
|
-
const proceed = yes ? true : await promptYesNo('No auth found. Login now?', true);
|
|
809
|
-
if (!proceed) {
|
|
810
|
-
console.error('Aborted.');
|
|
811
|
-
process.exit(1);
|
|
812
|
-
}
|
|
813
|
-
authedCfg = await runAuthLoginFlow({ configPath, designOrigin, loginOrigin, flags });
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
else {
|
|
817
|
-
console.error('Auth already configured.');
|
|
818
|
-
if (looksExpired(existingAuth?.sessionExpiresAt)) {
|
|
819
|
-
console.error('Warning: saved session looks expired; you may need to re-run auth login.');
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
console.error('');
|
|
823
|
-
console.error('Step 2/3: Configure launcher');
|
|
824
972
|
const launcher = launcherFromFlag ?? 'local';
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const clientList = resolveFlagString(flags, 'client')?.trim() || resolveFlagString(flags, 'clients')?.trim();
|
|
973
|
+
const ui = createInstallChecklistUi({ enabled: isTtyInteractive(), title: 'Every Design setup' });
|
|
974
|
+
ui.setLoading('Checking…');
|
|
975
|
+
const authCheck = await checkAuthValidity({ configPath, designOrigin, loginOrigin });
|
|
976
|
+
const cliCheck = await checkDesignCliStatus({
|
|
977
|
+
launcher,
|
|
978
|
+
updatePath,
|
|
979
|
+
packageName: '@just-every/design',
|
|
980
|
+
homeDir: os.homedir(),
|
|
981
|
+
});
|
|
835
982
|
const detections = detectClients();
|
|
836
983
|
const allClients = [
|
|
837
984
|
'code',
|
|
@@ -842,7 +989,8 @@ async function main() {
|
|
|
842
989
|
'gemini',
|
|
843
990
|
'qwen',
|
|
844
991
|
];
|
|
845
|
-
|
|
992
|
+
const clientList = resolveFlagString(flags, 'client')?.trim() || resolveFlagString(flags, 'clients')?.trim();
|
|
993
|
+
const defaultPlan = Object.create(null);
|
|
846
994
|
if (clientList) {
|
|
847
995
|
const requested = clientList
|
|
848
996
|
.split(',')
|
|
@@ -855,107 +1003,245 @@ async function main() {
|
|
|
855
1003
|
return detectDefaultClients();
|
|
856
1004
|
return requested.filter((c) => allClients.includes(c));
|
|
857
1005
|
})();
|
|
858
|
-
plan = Object.create(null);
|
|
859
1006
|
for (const c of allClients) {
|
|
860
|
-
|
|
1007
|
+
defaultPlan[c] = selected.includes(c)
|
|
861
1008
|
? (c === 'code' || c === 'codex') && skillsEnabled
|
|
862
1009
|
? 'skill'
|
|
863
1010
|
: 'mcp'
|
|
864
1011
|
: 'disabled';
|
|
865
1012
|
}
|
|
866
1013
|
}
|
|
867
|
-
else
|
|
868
|
-
|
|
1014
|
+
else {
|
|
1015
|
+
for (const c of allClients)
|
|
1016
|
+
defaultPlan[c] = 'disabled';
|
|
869
1017
|
for (const c of detections) {
|
|
870
|
-
|
|
1018
|
+
defaultPlan[c.client] = recommendedMode(c, skillsEnabled);
|
|
871
1019
|
}
|
|
872
1020
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1021
|
+
const splitPlan = (plan) => {
|
|
1022
|
+
const mcpClients = [];
|
|
1023
|
+
const skillClients = [];
|
|
1024
|
+
for (const c of allClients) {
|
|
1025
|
+
const mode = plan[c] ?? 'disabled';
|
|
1026
|
+
if (mode === 'disabled')
|
|
1027
|
+
continue;
|
|
1028
|
+
if (mode === 'skill') {
|
|
1029
|
+
if (c === 'code' || c === 'codex' || c === 'claude-code') {
|
|
1030
|
+
skillClients.push(c);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
mcpClients.push(c);
|
|
1034
|
+
}
|
|
885
1035
|
continue;
|
|
886
1036
|
}
|
|
887
1037
|
mcpClients.push(c);
|
|
888
|
-
continue;
|
|
889
1038
|
}
|
|
890
|
-
mcpClients
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
console.error('Aborted.');
|
|
899
|
-
process.exit(1);
|
|
900
|
-
}
|
|
901
|
-
const results = [];
|
|
902
|
-
if (skillClients.length) {
|
|
903
|
-
results.push({
|
|
904
|
-
title: 'Skill + MCP (recommended)',
|
|
905
|
-
result: await runInstall({
|
|
906
|
-
clients: skillClients,
|
|
1039
|
+
return { mcpClients, skillClients, selected: [...new Set([...skillClients, ...mcpClients])] };
|
|
1040
|
+
};
|
|
1041
|
+
const { mcpClients: defaultMcpClients, skillClients: defaultSkillClients, selected: defaultSelected } = splitPlan(defaultPlan);
|
|
1042
|
+
const integrationsChecks = await (async () => {
|
|
1043
|
+
const out = [];
|
|
1044
|
+
if (defaultSkillClients.length) {
|
|
1045
|
+
out.push(await checkIntegrations({
|
|
1046
|
+
clients: defaultSkillClients,
|
|
907
1047
|
serverName,
|
|
908
1048
|
packageName: '@just-every/design',
|
|
909
1049
|
installSkills: true,
|
|
910
|
-
dryRun,
|
|
911
|
-
force,
|
|
912
1050
|
launcher,
|
|
913
|
-
})
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
title: 'MCP only',
|
|
919
|
-
result: await runInstall({
|
|
920
|
-
clients: mcpClients,
|
|
1051
|
+
}));
|
|
1052
|
+
}
|
|
1053
|
+
if (defaultMcpClients.length) {
|
|
1054
|
+
out.push(await checkIntegrations({
|
|
1055
|
+
clients: defaultMcpClients,
|
|
921
1056
|
serverName,
|
|
922
1057
|
packageName: '@just-every/design',
|
|
923
1058
|
installSkills: false,
|
|
924
|
-
dryRun,
|
|
925
|
-
force,
|
|
926
1059
|
launcher,
|
|
927
|
-
})
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1060
|
+
}));
|
|
1061
|
+
}
|
|
1062
|
+
return out;
|
|
1063
|
+
})();
|
|
1064
|
+
const integrationsOk = defaultSelected.length === 0
|
|
1065
|
+
? false
|
|
1066
|
+
: integrationsChecks.every((c) => c.ok);
|
|
1067
|
+
let finalAuthOk = authCheck.ok;
|
|
1068
|
+
let finalCliOk = cliCheck.ok;
|
|
1069
|
+
let finalIntegrationsOk = integrationsOk;
|
|
1070
|
+
ui.setChecklist({
|
|
1071
|
+
auth: authCheck.ok ? 'ok' : 'todo',
|
|
1072
|
+
cli: cliCheck.ok ? 'ok' : 'todo',
|
|
1073
|
+
integrations: integrationsOk ? 'ok' : 'todo',
|
|
1074
|
+
});
|
|
939
1075
|
if (dryRun) {
|
|
940
|
-
|
|
1076
|
+
ui.log('Dry run mode: no changes will be applied.');
|
|
1077
|
+
if (!authCheck.ok) {
|
|
1078
|
+
ui.log(`[dry-run] Would run: every-design auth login (config: ${configPath})`);
|
|
1079
|
+
}
|
|
1080
|
+
if (!cliCheck.ok && launcher === 'local') {
|
|
1081
|
+
const local = resolveLocalInstallLayout(os.homedir(), 'xdg');
|
|
1082
|
+
if (updatePath) {
|
|
1083
|
+
const ensured = await ensurePathEntryOnSystem({ entry: local.binDir, dryRun: true, yes: true });
|
|
1084
|
+
if (ensured.note)
|
|
1085
|
+
ui.log(ensured.note);
|
|
1086
|
+
}
|
|
1087
|
+
ui.log(`[dry-run] Would ensure local launcher at ${local.launcherPath}`);
|
|
1088
|
+
}
|
|
1089
|
+
if (!integrationsOk && defaultSelected.length) {
|
|
1090
|
+
const results = [];
|
|
1091
|
+
if (defaultSkillClients.length) {
|
|
1092
|
+
results.push({
|
|
1093
|
+
title: 'Skill + MCP',
|
|
1094
|
+
result: await runInstall({
|
|
1095
|
+
clients: defaultSkillClients,
|
|
1096
|
+
serverName,
|
|
1097
|
+
packageName: '@just-every/design',
|
|
1098
|
+
installSkills: true,
|
|
1099
|
+
dryRun: true,
|
|
1100
|
+
force,
|
|
1101
|
+
launcher,
|
|
1102
|
+
}),
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
if (defaultMcpClients.length) {
|
|
1106
|
+
results.push({
|
|
1107
|
+
title: 'MCP only',
|
|
1108
|
+
result: await runInstall({
|
|
1109
|
+
clients: defaultMcpClients,
|
|
1110
|
+
serverName,
|
|
1111
|
+
packageName: '@just-every/design',
|
|
1112
|
+
installSkills: false,
|
|
1113
|
+
dryRun: true,
|
|
1114
|
+
force,
|
|
1115
|
+
launcher,
|
|
1116
|
+
}),
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
for (const note of results.flatMap((r) => r.result.notes))
|
|
1120
|
+
ui.log(note);
|
|
1121
|
+
}
|
|
1122
|
+
process.exit(0);
|
|
941
1123
|
}
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
console.error(`- ${p}`);
|
|
1124
|
+
if (authCheck.ok && cliCheck.ok && integrationsOk) {
|
|
1125
|
+
ui.log('All checks passed — everything is up-to-date.');
|
|
1126
|
+
process.exit(0);
|
|
946
1127
|
}
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
console.error(
|
|
1128
|
+
if (!authCheck.ok) {
|
|
1129
|
+
if (!isTtyInteractive()) {
|
|
1130
|
+
console.error(`Authentication is missing/invalid at ${configPath}.`);
|
|
1131
|
+
console.error('Run `every-design auth login` (or `npx -y @just-every/design@latest auth login`) first, then re-run install.');
|
|
1132
|
+
process.exit(1);
|
|
1133
|
+
}
|
|
1134
|
+
const proceed = yes ? true : await promptYesNo('Authentication missing/invalid. Login now?', true);
|
|
1135
|
+
if (!proceed) {
|
|
1136
|
+
console.error('Aborted.');
|
|
1137
|
+
process.exit(1);
|
|
1138
|
+
}
|
|
1139
|
+
ui.setWorking('auth', 'Authentication');
|
|
1140
|
+
await runAuthLoginFlow({ configPath, designOrigin, loginOrigin, flags });
|
|
1141
|
+
ui.setChecklist({ auth: 'ok' });
|
|
1142
|
+
finalAuthOk = true;
|
|
951
1143
|
}
|
|
952
|
-
if (
|
|
953
|
-
|
|
1144
|
+
if (!cliCheck.ok && launcher === 'local') {
|
|
1145
|
+
const local = resolveLocalInstallLayout(os.homedir(), 'xdg');
|
|
1146
|
+
const summary = (() => {
|
|
1147
|
+
if (cliCheck.missing)
|
|
1148
|
+
return 'Install Design CLI (every-design)?';
|
|
1149
|
+
if (cliCheck.outdated) {
|
|
1150
|
+
const latest = cliCheck.latestVersion ? ` to v${cliCheck.latestVersion}` : '';
|
|
1151
|
+
const cur = cliCheck.installedVersion ? ` (current v${cliCheck.installedVersion})` : '';
|
|
1152
|
+
return `Update Design CLI${latest}?${cur}`;
|
|
1153
|
+
}
|
|
1154
|
+
if (cliCheck.needsPath)
|
|
1155
|
+
return `Add ${local.binDir} to PATH?`;
|
|
1156
|
+
return 'Update Design CLI?';
|
|
1157
|
+
})();
|
|
1158
|
+
const proceed = yes ? true : await promptYesNo(summary, true);
|
|
1159
|
+
if (!proceed) {
|
|
1160
|
+
if (cliCheck.missing) {
|
|
1161
|
+
console.error('Design CLI is not installed; exiting.');
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
1164
|
+
// Installed but outdated: keep going.
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
ui.setWorking('cli', 'Design CLI');
|
|
1168
|
+
if (updatePath) {
|
|
1169
|
+
const ensured = await ensurePathEntryOnSystem({ entry: local.binDir, dryRun: false, yes: true });
|
|
1170
|
+
if (ensured.note)
|
|
1171
|
+
ui.log(ensured.note);
|
|
1172
|
+
}
|
|
1173
|
+
if (cliCheck.missing || cliCheck.outdated) {
|
|
1174
|
+
const res = await ensureLocalLauncher({
|
|
1175
|
+
packageName: '@just-every/design',
|
|
1176
|
+
dryRun: false,
|
|
1177
|
+
force: cliCheck.outdated,
|
|
1178
|
+
});
|
|
1179
|
+
for (const n of res.notes)
|
|
1180
|
+
ui.log(n);
|
|
1181
|
+
}
|
|
1182
|
+
ui.setChecklist({ cli: 'ok' });
|
|
1183
|
+
finalCliOk = true;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
if (!integrationsOk) {
|
|
1187
|
+
if (defaultSelected.length === 0) {
|
|
1188
|
+
console.error('No supported clients detected. Provide --client to select a target.');
|
|
1189
|
+
process.exit(1);
|
|
1190
|
+
}
|
|
1191
|
+
const proceedRecommended = yes ? true : await promptYesNo('Install/update integrations (recommended)?', true);
|
|
1192
|
+
const planToApply = proceedRecommended
|
|
1193
|
+
? defaultPlan
|
|
1194
|
+
: await promptInstallPlan(detections, skillsEnabled, { mode: 'edit' });
|
|
1195
|
+
const { mcpClients, skillClients, selected } = splitPlan(planToApply);
|
|
1196
|
+
if (selected.length === 0) {
|
|
1197
|
+
console.error('No clients selected. Nothing to do.');
|
|
1198
|
+
process.exit(1);
|
|
1199
|
+
}
|
|
1200
|
+
ui.setWorking('integrations', 'Integrations');
|
|
1201
|
+
const results = [];
|
|
1202
|
+
if (skillClients.length) {
|
|
1203
|
+
results.push({
|
|
1204
|
+
title: 'Skill + MCP',
|
|
1205
|
+
result: await runInstall({
|
|
1206
|
+
clients: skillClients,
|
|
1207
|
+
serverName,
|
|
1208
|
+
packageName: '@just-every/design',
|
|
1209
|
+
installSkills: true,
|
|
1210
|
+
dryRun: false,
|
|
1211
|
+
force,
|
|
1212
|
+
launcher,
|
|
1213
|
+
}),
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
if (mcpClients.length) {
|
|
1217
|
+
results.push({
|
|
1218
|
+
title: 'MCP only',
|
|
1219
|
+
result: await runInstall({
|
|
1220
|
+
clients: mcpClients,
|
|
1221
|
+
serverName,
|
|
1222
|
+
packageName: '@just-every/design',
|
|
1223
|
+
installSkills: false,
|
|
1224
|
+
dryRun: false,
|
|
1225
|
+
force,
|
|
1226
|
+
launcher,
|
|
1227
|
+
}),
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
const changed = results.flatMap((r) => r.result.changed);
|
|
1231
|
+
const notes = results.flatMap((r) => r.result.notes);
|
|
1232
|
+
if (changed.length)
|
|
1233
|
+
ui.log(`Updated: ${changed.join(', ')}`);
|
|
954
1234
|
for (const n of notes)
|
|
955
|
-
|
|
1235
|
+
ui.log(n);
|
|
1236
|
+
ui.setChecklist({ integrations: 'ok' });
|
|
1237
|
+
finalIntegrationsOk = true;
|
|
1238
|
+
}
|
|
1239
|
+
if (finalAuthOk && finalCliOk && finalIntegrationsOk) {
|
|
1240
|
+
ui.log('All set. Restart your client(s) so they pick up the new MCP config.');
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
ui.log('Install finished (some steps were skipped). Restart your client(s) so they pick up the new MCP config.');
|
|
956
1244
|
}
|
|
957
|
-
console.error('');
|
|
958
|
-
console.error('Next steps: restart your client(s) so they pick up the new MCP config.');
|
|
959
1245
|
process.exit(0);
|
|
960
1246
|
}
|
|
961
1247
|
// CLI helpers (non-MCP). These are mostly useful for Skills/playbooks and scripting.
|