@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 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
- console.error(`Using launcher: ${launcher}${launcherFromFlag ? ' (from flag)' : ''}.`);
826
- if (launcher === 'local' && updatePath) {
827
- const localBin = path.join(os.homedir(), '.local', 'bin');
828
- const ensured = await ensurePathEntryOnSystem({ entry: localBin, dryRun, yes });
829
- if (ensured.note)
830
- console.error(ensured.note);
831
- }
832
- console.error('');
833
- console.error('Step 3/3: Configure clients');
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
- let plan;
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
- plan[c] = selected.includes(c)
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 if (yes) {
868
- plan = Object.create(null);
1014
+ else {
1015
+ for (const c of allClients)
1016
+ defaultPlan[c] = 'disabled';
869
1017
  for (const c of detections) {
870
- plan[c.client] = recommendedMode(c, skillsEnabled);
1018
+ defaultPlan[c.client] = recommendedMode(c, skillsEnabled);
871
1019
  }
872
1020
  }
873
- else {
874
- plan = await promptInstallPlan(detections, skillsEnabled);
875
- }
876
- const mcpClients = [];
877
- const skillClients = [];
878
- for (const c of allClients) {
879
- const mode = plan[c] ?? 'disabled';
880
- if (mode === 'disabled')
881
- continue;
882
- if (mode === 'skill') {
883
- if (c === 'code' || c === 'codex' || c === 'claude-code') {
884
- skillClients.push(c);
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.push(c);
891
- }
892
- if (mcpClients.length === 0 && skillClients.length === 0) {
893
- console.error('No clients selected. Nothing to do.');
894
- process.exit(1);
895
- }
896
- const confirm = yes ? true : await promptYesNo('Apply these changes?', true);
897
- if (!confirm) {
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
- if (mcpClients.length) {
917
- results.push({
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
- const changed = results.flatMap((r) => r.result.changed);
931
- const skipped = results.flatMap((r) => r.result.skipped);
932
- const notes = results.flatMap((r) => r.result.notes);
933
- console.error('');
934
- console.error('Summary');
935
- console.error(`- Auth: ${hasAuth(authedCfg) ? 'configured' : 'missing'}`);
936
- console.error(`- Server name: ${serverName}`);
937
- console.error(`- Launcher: ${launcher}`);
938
- console.error(`- Clients configured: ${[...new Set([...skillClients, ...mcpClients])].join(', ')}`);
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
- console.error('- Dry run: no files were modified');
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 (changed.length) {
943
- console.error('Updated:');
944
- for (const p of changed)
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 (skipped.length) {
948
- console.error('Skipped:');
949
- for (const p of skipped)
950
- console.error(`- ${p}`);
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 (notes.length) {
953
- console.error('Notes:');
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
- console.error(`- ${n}`);
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.