dlw-machine-setup 0.4.6 → 0.4.7

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.
Files changed (2) hide show
  1. package/bin/installer.js +142 -85
  2. package/package.json +1 -1
package/bin/installer.js CHANGED
@@ -3244,6 +3244,47 @@ var import_fs5 = require("fs");
3244
3244
  var import_readline = require("readline");
3245
3245
  var import_path5 = require("path");
3246
3246
 
3247
+ // src/utils/fetch.ts
3248
+ var DEFAULT_TIMEOUT_MS = 3e4;
3249
+ var DEFAULT_MAX_RETRIES = 3;
3250
+ var DEFAULT_RETRY_DELAY_MS = 2e3;
3251
+ async function fetchWithTimeout(url, options = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
3252
+ const controller = new AbortController();
3253
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
3254
+ try {
3255
+ return await fetch(url, { ...options, signal: controller.signal });
3256
+ } catch (error) {
3257
+ if (error instanceof Error && error.name === "AbortError") {
3258
+ throw new Error(`Request timed out after ${timeoutMs / 1e3}s`);
3259
+ }
3260
+ throw error;
3261
+ } finally {
3262
+ clearTimeout(timer);
3263
+ }
3264
+ }
3265
+ async function fetchWithRetry(url, options = {}, maxRetries = DEFAULT_MAX_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS) {
3266
+ let lastError = null;
3267
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
3268
+ try {
3269
+ const response = await fetchWithTimeout(url, options);
3270
+ if (!response.ok && response.status >= 400 && response.status < 500 && response.status !== 429) {
3271
+ return response;
3272
+ }
3273
+ if (response.ok) {
3274
+ return response;
3275
+ }
3276
+ lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
3277
+ } catch (error) {
3278
+ lastError = error instanceof Error ? error : new Error(String(error));
3279
+ }
3280
+ if (attempt < maxRetries) {
3281
+ const delay = retryDelayMs * Math.pow(2, attempt - 1);
3282
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
3283
+ }
3284
+ }
3285
+ throw lastError || new Error("Failed after maximum retries");
3286
+ }
3287
+
3247
3288
  // src/utils/data/fetch-wizard-options.ts
3248
3289
  async function fetchWizardOptions(token, repo) {
3249
3290
  try {
@@ -3251,7 +3292,7 @@ async function fetchWizardOptions(token, repo) {
3251
3292
  "Accept": "application/vnd.github+json",
3252
3293
  "Authorization": `Bearer ${token}`
3253
3294
  };
3254
- const releaseRes = await fetch(
3295
+ const releaseRes = await fetchWithRetry(
3255
3296
  `https://api.github.com/repos/${repo}/releases/latest`,
3256
3297
  { headers }
3257
3298
  );
@@ -3259,7 +3300,7 @@ async function fetchWizardOptions(token, repo) {
3259
3300
  const release = await releaseRes.json();
3260
3301
  const asset = release.assets?.find((a) => a.name === "wizard-options.json");
3261
3302
  if (!asset) return null;
3262
- const assetRes = await fetch(asset.url, {
3303
+ const assetRes = await fetchWithRetry(asset.url, {
3263
3304
  headers: { ...headers, "Accept": "application/octet-stream" }
3264
3305
  });
3265
3306
  if (!assetRes.ok) return null;
@@ -3267,7 +3308,7 @@ async function fetchWizardOptions(token, repo) {
3267
3308
  if (!Array.isArray(data.agents) || !Array.isArray(data.personas?.personas)) {
3268
3309
  return null;
3269
3310
  }
3270
- return data;
3311
+ return { options: data, releaseVersion: release.tag_name ?? "unknown" };
3271
3312
  } catch {
3272
3313
  return null;
3273
3314
  }
@@ -3276,8 +3317,6 @@ async function fetchWizardOptions(token, repo) {
3276
3317
  // src/utils/data/discover-repo.ts
3277
3318
  var GITHUB_ORG = "DLW-INT-SAP-DEV";
3278
3319
  var REPO_TOPIC = "one-shot-data";
3279
- var MAX_RETRIES = 3;
3280
- var RETRY_DELAY_MS = 2e3;
3281
3320
  async function discoverRepo(token) {
3282
3321
  const headers = {
3283
3322
  "Accept": "application/vnd.github+json",
@@ -3339,25 +3378,6 @@ async function tryOrgReposList(headers) {
3339
3378
  return null;
3340
3379
  }
3341
3380
  }
3342
- async function fetchWithRetry(url, options) {
3343
- let lastError = null;
3344
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
3345
- try {
3346
- const response = await fetch(url, options);
3347
- if (!response.ok && response.status >= 400 && response.status < 500 && response.status !== 429) {
3348
- return response;
3349
- }
3350
- if (response.ok) return response;
3351
- lastError = new Error(`HTTP ${response.status}`);
3352
- } catch (error) {
3353
- lastError = error instanceof Error ? error : new Error(String(error));
3354
- }
3355
- if (attempt < MAX_RETRIES) {
3356
- await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS * Math.pow(2, attempt - 1)));
3357
- }
3358
- }
3359
- return new Response(null, { status: 503, statusText: "Retry exhausted" });
3360
- }
3361
3381
 
3362
3382
  // src/utils/data/fetch-contexts.ts
3363
3383
  var import_fs2 = require("fs");
@@ -3409,7 +3429,7 @@ function clearTokenCache() {
3409
3429
  }
3410
3430
  }
3411
3431
  async function requestDeviceCode() {
3412
- const response = await fetch(GITHUB_DEVICE_CODE_URL, {
3432
+ const response = await fetchWithTimeout(GITHUB_DEVICE_CODE_URL, {
3413
3433
  method: "POST",
3414
3434
  headers: {
3415
3435
  "Accept": "application/json",
@@ -3431,7 +3451,7 @@ async function pollForToken(deviceCode, interval) {
3431
3451
  let attempts = 0;
3432
3452
  while (attempts < maxAttempts) {
3433
3453
  await new Promise((resolve3) => setTimeout(resolve3, interval * 1e3));
3434
- const response = await fetch(GITHUB_TOKEN_URL, {
3454
+ const response = await fetchWithTimeout(GITHUB_TOKEN_URL, {
3435
3455
  method: "POST",
3436
3456
  headers: {
3437
3457
  "Accept": "application/json",
@@ -3472,7 +3492,11 @@ async function pollForToken(deviceCode, interval) {
3472
3492
  async function authenticateWithGitHub() {
3473
3493
  const envToken = process.env.GITHUB_TOKEN;
3474
3494
  if (envToken) {
3475
- return envToken;
3495
+ const isValid = await verifyToken(envToken);
3496
+ if (isValid) {
3497
+ return envToken;
3498
+ }
3499
+ console.log(" GITHUB_TOKEN env var is invalid or expired, falling back to authentication...\n");
3476
3500
  }
3477
3501
  const cachedToken = loadCachedToken();
3478
3502
  if (cachedToken) {
@@ -3504,7 +3528,7 @@ async function authenticateWithGitHub() {
3504
3528
  }
3505
3529
  async function verifyToken(token) {
3506
3530
  try {
3507
- const response = await fetch("https://api.github.com/user", {
3531
+ const response = await fetchWithTimeout("https://api.github.com/user", {
3508
3532
  headers: {
3509
3533
  "Authorization": `Bearer ${token}`,
3510
3534
  "Accept": "application/vnd.github+json"
@@ -3520,8 +3544,6 @@ async function getGitHubToken() {
3520
3544
  }
3521
3545
 
3522
3546
  // src/utils/data/fetch-contexts.ts
3523
- var MAX_RETRIES2 = 3;
3524
- var RETRY_DELAY_MS2 = 2e3;
3525
3547
  var MIN_FILE_SIZE = 1024;
3526
3548
  async function fetchContexts(options = {}) {
3527
3549
  const { domains = [], targetDir = process.cwd(), force = false, token, repo } = options;
@@ -3545,7 +3567,7 @@ async function fetchContexts(options = {}) {
3545
3567
  "Accept": "application/vnd.github+json",
3546
3568
  "Authorization": `Bearer ${githubToken}`
3547
3569
  };
3548
- const releaseResponse = await fetchWithRetry2(releaseUrl, { headers }, MAX_RETRIES2);
3570
+ const releaseResponse = await fetchWithRetry(releaseUrl, { headers });
3549
3571
  if (!releaseResponse.ok) {
3550
3572
  throw new Error(
3551
3573
  `GitHub API error (${releaseResponse.status}): ${getReadableError(releaseResponse.status)}`
@@ -3583,7 +3605,7 @@ async function fetchContexts(options = {}) {
3583
3605
  "Authorization": `Bearer ${githubToken}`
3584
3606
  };
3585
3607
  const downloadUrl = asset.url;
3586
- const response = await fetchWithRetry2(downloadUrl, { headers: downloadHeaders }, MAX_RETRIES2);
3608
+ const response = await fetchWithRetry(downloadUrl, { headers: downloadHeaders });
3587
3609
  if (!response.ok) {
3588
3610
  throw new Error(`Download failed: ${getReadableError(response.status)}`);
3589
3611
  }
@@ -3605,7 +3627,10 @@ async function fetchContexts(options = {}) {
3605
3627
  throw new Error(`Archive contains unsafe path: ${entry}`);
3606
3628
  }
3607
3629
  }
3608
- (0, import_child_process.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
3630
+ const extractResult = (0, import_child_process.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
3631
+ if (extractResult.status !== 0) {
3632
+ throw new Error("Archive extraction failed");
3633
+ }
3609
3634
  const extractedEntries = (0, import_fs2.readdirSync)(tempDir);
3610
3635
  const extractedFolder = extractedEntries.find((e) => e.toLowerCase() === domain.toLowerCase());
3611
3636
  if (!extractedFolder) {
@@ -3646,28 +3671,6 @@ async function checkPrerequisites() {
3646
3671
  );
3647
3672
  }
3648
3673
  }
3649
- async function fetchWithRetry2(url, options, maxRetries) {
3650
- let lastError = null;
3651
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
3652
- try {
3653
- const response = await fetch(url, options);
3654
- if (!response.ok && response.status >= 400 && response.status < 500 && response.status !== 429) {
3655
- return response;
3656
- }
3657
- if (response.ok) {
3658
- return response;
3659
- }
3660
- lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
3661
- } catch (error) {
3662
- lastError = error instanceof Error ? error : new Error(String(error));
3663
- }
3664
- if (attempt < maxRetries) {
3665
- const delay = RETRY_DELAY_MS2 * Math.pow(2, attempt - 1);
3666
- await new Promise((resolve3) => setTimeout(resolve3, delay));
3667
- }
3668
- }
3669
- throw lastError || new Error("Failed after maximum retries");
3670
- }
3671
3674
  function getReadableError(status) {
3672
3675
  switch (status) {
3673
3676
  case 401:
@@ -3735,12 +3738,25 @@ async function setupMCPConfiguration(projectPath, mcpConfig, agent) {
3735
3738
  (0, import_fs3.mkdirSync)(dir, { recursive: true });
3736
3739
  }
3737
3740
  }
3741
+ const red2 = (text) => `\x1B[31m${text}\x1B[0m`;
3738
3742
  let existingFile = {};
3739
3743
  if ((0, import_fs3.existsSync)(mcpJsonPath)) {
3740
3744
  try {
3741
3745
  const content = (0, import_fs3.readFileSync)(mcpJsonPath, "utf-8");
3742
3746
  existingFile = JSON.parse(content);
3743
3747
  } catch {
3748
+ const box = [
3749
+ "",
3750
+ red2(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
3751
+ red2(" \u2551 \u26A0 MCP CONFIG HAS INVALID JSON \u2551"),
3752
+ red2(" \u2551 \u2551"),
3753
+ red2(` \u2551 ${target.filePath} could not be parsed.`.padEnd(52) + "\u2551"),
3754
+ red2(" \u2551 Existing MCP servers may be lost. \u2551"),
3755
+ red2(" \u2551 Check the file after installation completes. \u2551"),
3756
+ red2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
3757
+ ""
3758
+ ].join("\n");
3759
+ console.log(box);
3744
3760
  }
3745
3761
  }
3746
3762
  const existing = existingFile[target.rootKey] ?? {};
@@ -3780,9 +3796,9 @@ function buildMCPConfiguration(selectedItems, baseMcpServers, allMcpServers) {
3780
3796
  // src/utils/setup/setup-instructions.ts
3781
3797
  var import_fs4 = require("fs");
3782
3798
  var import_path4 = require("path");
3783
- var CONTEXTS_DIR = (0, import_path4.join)(process.cwd(), "_ai-context");
3784
3799
  var MARKER_START = "<!-- one-shot-installer:start -->";
3785
3800
  var MARKER_END = "<!-- one-shot-installer:end -->";
3801
+ var red = (text) => `\x1B[31m${text}\x1B[0m`;
3786
3802
  function upsertBlock(filePath, block) {
3787
3803
  const marked = `${MARKER_START}
3788
3804
  ${block}
@@ -3794,7 +3810,27 @@ ${MARKER_END}`;
3794
3810
  const existing = (0, import_fs4.readFileSync)(filePath, "utf-8");
3795
3811
  const start = existing.indexOf(MARKER_START);
3796
3812
  const end = existing.indexOf(MARKER_END);
3797
- if (start !== -1 && end !== -1) {
3813
+ const hasStart = start !== -1;
3814
+ const hasEnd = end !== -1;
3815
+ if (hasStart && !hasEnd || !hasStart && hasEnd || hasStart && hasEnd && end < start) {
3816
+ const fileName = filePath.split(/[/\\]/).pop() ?? filePath;
3817
+ const box = [
3818
+ "",
3819
+ red(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
3820
+ red(" \u2551 \u26A0 INSTRUCTION FILE COULD NOT BE UPDATED \u2551"),
3821
+ red(" \u2551 \u2551"),
3822
+ red(` \u2551 ${fileName} has corrupted markers.`.padEnd(52) + "\u2551"),
3823
+ red(" \u2551 Fix: delete the lines containing \u2551"),
3824
+ red(" \u2551 <!-- one-shot-installer:start --> \u2551"),
3825
+ red(" \u2551 <!-- one-shot-installer:end --> \u2551"),
3826
+ red(" \u2551 Then re-run the installer. \u2551"),
3827
+ red(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
3828
+ ""
3829
+ ].join("\n");
3830
+ console.log(box);
3831
+ throw new Error(`Corrupted markers in ${fileName} \u2014 fix manually and re-run`);
3832
+ }
3833
+ if (hasStart && hasEnd) {
3798
3834
  const updated = existing.slice(0, start) + marked + existing.slice(end + MARKER_END.length);
3799
3835
  (0, import_fs4.writeFileSync)(filePath, updated, "utf-8");
3800
3836
  } else {
@@ -3845,19 +3881,19 @@ function buildMCPSection(mcpConfig) {
3845
3881
  }
3846
3882
  return lines2.join("\n");
3847
3883
  }
3848
- function resolveDomainFolder(domain) {
3849
- if ((0, import_fs4.existsSync)(CONTEXTS_DIR)) {
3884
+ function resolveDomainFolder(domain, contextsDir) {
3885
+ if ((0, import_fs4.existsSync)(contextsDir)) {
3850
3886
  try {
3851
- const entries = (0, import_fs4.readdirSync)(CONTEXTS_DIR);
3887
+ const entries = (0, import_fs4.readdirSync)(contextsDir);
3852
3888
  const match = entries.find((e) => e.toLowerCase() === domain.toLowerCase());
3853
3889
  if (match) {
3854
- return { folderName: match, folderPath: (0, import_path4.join)(CONTEXTS_DIR, match) };
3890
+ return { folderName: match, folderPath: (0, import_path4.join)(contextsDir, match) };
3855
3891
  }
3856
3892
  } catch {
3857
3893
  }
3858
3894
  }
3859
3895
  const fallback = domain.toUpperCase();
3860
- return { folderName: fallback, folderPath: (0, import_path4.join)(CONTEXTS_DIR, fallback) };
3896
+ return { folderName: fallback, folderPath: (0, import_path4.join)(contextsDir, fallback) };
3861
3897
  }
3862
3898
  function formatPathRef(contextPath, description, agent) {
3863
3899
  if (agent === "github-copilot") {
@@ -3865,7 +3901,7 @@ function formatPathRef(contextPath, description, agent) {
3865
3901
  }
3866
3902
  return `- \`${contextPath}\` \u2014 ${description}`;
3867
3903
  }
3868
- function buildContextRefsSection(domains, agent) {
3904
+ function buildContextRefsSection(domains, agent, contextsDir) {
3869
3905
  const lines2 = [
3870
3906
  `## Context References`,
3871
3907
  ``,
@@ -3874,7 +3910,7 @@ function buildContextRefsSection(domains, agent) {
3874
3910
  ];
3875
3911
  let hasAnyFiles = false;
3876
3912
  for (const domain of domains) {
3877
- const { folderName, folderPath: domainPath } = resolveDomainFolder(domain);
3913
+ const { folderName, folderPath: domainPath } = resolveDomainFolder(domain, contextsDir);
3878
3914
  if (!(0, import_fs4.existsSync)(domainPath)) continue;
3879
3915
  const domainFiles = [];
3880
3916
  const ctxInstructions = (0, import_path4.join)(domainPath, "context-instructions.md");
@@ -3919,29 +3955,30 @@ function buildContextRefsSection(domains, agent) {
3919
3955
  }
3920
3956
  return lines2.join("\n");
3921
3957
  }
3922
- function buildCombinedInstructions(domains, mcpConfig, agent = "") {
3958
+ function buildCombinedInstructions(domains, mcpConfig, agent = "", projectPath = process.cwd()) {
3959
+ const contextsDir = (0, import_path4.join)(projectPath, "_ai-context");
3923
3960
  const lines2 = [
3924
3961
  `# AI Development Instructions`,
3925
3962
  ``,
3926
3963
  `> Generated by One-Shot Installer`,
3927
3964
  ``
3928
3965
  ];
3929
- lines2.push(buildContextRefsSection(domains, agent));
3966
+ lines2.push(buildContextRefsSection(domains, agent, contextsDir));
3930
3967
  if (mcpConfig && Object.keys(mcpConfig).length > 0) {
3931
3968
  lines2.push(buildMCPSection(mcpConfig));
3932
3969
  }
3933
3970
  return lines2.join("\n");
3934
3971
  }
3935
3972
  async function setupInstructions(config) {
3936
- const { domains, agent, mcpConfig } = config;
3973
+ const { domains, agent, mcpConfig, projectPath = process.cwd() } = config;
3937
3974
  switch (agent) {
3938
3975
  case "claude-code": {
3939
- const content = buildCombinedInstructions(domains, mcpConfig, agent);
3940
- upsertBlock((0, import_path4.join)(process.cwd(), "CLAUDE.md"), content);
3976
+ const content = buildCombinedInstructions(domains, mcpConfig, agent, projectPath);
3977
+ upsertBlock((0, import_path4.join)(projectPath, "CLAUDE.md"), content);
3941
3978
  break;
3942
3979
  }
3943
3980
  case "github-copilot": {
3944
- const agentsDir = (0, import_path4.join)(process.cwd(), ".github", "agents");
3981
+ const agentsDir = (0, import_path4.join)(projectPath, ".github", "agents");
3945
3982
  if (!(0, import_fs4.existsSync)(agentsDir)) (0, import_fs4.mkdirSync)(agentsDir, { recursive: true });
3946
3983
  const filePath = (0, import_path4.join)(agentsDir, "instructions.md");
3947
3984
  if (!(0, import_fs4.existsSync)(filePath)) {
@@ -3951,14 +3988,14 @@ applyTo: "**"
3951
3988
 
3952
3989
  `, "utf-8");
3953
3990
  }
3954
- const body = buildCombinedInstructions(domains, mcpConfig, agent);
3991
+ const body = buildCombinedInstructions(domains, mcpConfig, agent, projectPath);
3955
3992
  upsertBlock(filePath, body);
3956
3993
  break;
3957
3994
  }
3958
3995
  case "cursor": {
3959
- const cursorDir = (0, import_path4.join)(process.cwd(), ".cursor", "rules");
3996
+ const cursorDir = (0, import_path4.join)(projectPath, ".cursor", "rules");
3960
3997
  if (!(0, import_fs4.existsSync)(cursorDir)) (0, import_fs4.mkdirSync)(cursorDir, { recursive: true });
3961
- const body = buildCombinedInstructions(domains, mcpConfig, agent);
3998
+ const body = buildCombinedInstructions(domains, mcpConfig, agent, projectPath);
3962
3999
  upsertBlock((0, import_path4.join)(cursorDir, `instructions.mdc`), body);
3963
4000
  break;
3964
4001
  }
@@ -3973,18 +4010,21 @@ async function loadWizardOptions(token, repo) {
3973
4010
  if (!remote) {
3974
4011
  throw new Error("Failed to load configuration from GitHub release. Check your network and token.");
3975
4012
  }
4013
+ const { options, releaseVersion } = remote;
3976
4014
  const filteredMcpServers = Object.fromEntries(
3977
- Object.entries(remote.mcpServers).filter(([, config]) => config.active !== false)
4015
+ Object.entries(options.mcpServers).filter(([, config]) => config.active !== false)
3978
4016
  );
3979
4017
  return {
3980
- personas: remote.personas.personas.filter((p) => p.active !== false),
3981
- agents: remote.agents.filter((a) => a.active !== false),
3982
- baseMcpServers: remote.personas.baseMcpServers,
3983
- mcpServers: filteredMcpServers
4018
+ personas: options.personas.personas.filter((p) => p.active !== false),
4019
+ agents: options.agents.filter((a) => a.active !== false),
4020
+ baseMcpServers: options.personas.baseMcpServers,
4021
+ mcpServers: filteredMcpServers,
4022
+ releaseVersion
3984
4023
  };
3985
4024
  }
3986
4025
 
3987
4026
  // src/index.ts
4027
+ var INSTALLER_VERSION = "0.4.7";
3988
4028
  function getInstructionFilePath(agent) {
3989
4029
  switch (agent) {
3990
4030
  case "claude-code":
@@ -4031,7 +4071,7 @@ async function main() {
4031
4071
  const repo = await discoverRepo(token);
4032
4072
  console.log(" Loading configuration...");
4033
4073
  const options = await loadWizardOptions(token, repo);
4034
- const config = await collectInputs(options);
4074
+ const config = await collectInputs(options, options.releaseVersion);
4035
4075
  if (!config) {
4036
4076
  await waitForEnter();
4037
4077
  return;
@@ -4050,7 +4090,7 @@ async function main() {
4050
4090
  await waitForEnter();
4051
4091
  }
4052
4092
  }
4053
- async function collectInputs(options) {
4093
+ async function collectInputs(options, releaseVersion) {
4054
4094
  const selectedIds = await esm_default2({
4055
4095
  message: "Personas:",
4056
4096
  instructions: " Space to select \xB7 Enter to confirm",
@@ -4089,7 +4129,9 @@ async function collectInputs(options) {
4089
4129
  agent,
4090
4130
  azureDevOpsOrg,
4091
4131
  projectPath: (0, import_path5.resolve)(projectInput),
4092
- mcpConfig
4132
+ baseMcpServers: options.baseMcpServers,
4133
+ mcpConfig,
4134
+ releaseVersion
4093
4135
  };
4094
4136
  }
4095
4137
  async function previewAndConfirm(config, options) {
@@ -4141,7 +4183,7 @@ async function execute(config, token, repo) {
4141
4183
  const domainValues = uniqueDomains.map((d) => d.toLowerCase());
4142
4184
  console.log(` Downloading contexts...`);
4143
4185
  try {
4144
- const downloadResult = await fetchContexts({ domains: domainValues, token, repo });
4186
+ const downloadResult = await fetchContexts({ domains: domainValues, token, repo, targetDir: config.projectPath });
4145
4187
  result.domainsInstalled = downloadResult.successful;
4146
4188
  result.domainsFailed = downloadResult.failed;
4147
4189
  result.failureReasons = downloadResult.failureReasons;
@@ -4162,6 +4204,17 @@ async function execute(config, token, repo) {
4162
4204
  result.success = false;
4163
4205
  return result;
4164
4206
  }
4207
+ const successfulDomains = new Set(result.domainsInstalled);
4208
+ const successfulPersonas = config.personas.filter(
4209
+ (p) => p.domains.every((d) => successfulDomains.has(d.toLowerCase()))
4210
+ );
4211
+ const successfulMcpServers = /* @__PURE__ */ new Set([
4212
+ ...config.baseMcpServers,
4213
+ ...successfulPersonas.flatMap((p) => p.mcpServers)
4214
+ ]);
4215
+ const filteredMcpConfig = Object.fromEntries(
4216
+ Object.entries(config.mcpConfig).filter(([name]) => successfulMcpServers.has(name))
4217
+ );
4165
4218
  const statePath = (0, import_path5.join)(config.projectPath, ".one-shot-state.json");
4166
4219
  let previousState = {};
4167
4220
  try {
@@ -4176,14 +4229,15 @@ async function execute(config, token, repo) {
4176
4229
  ])];
4177
4230
  const mergedMcpConfig = {
4178
4231
  ...previousState.mcpConfigs ?? {},
4179
- ...config.mcpConfig
4232
+ ...filteredMcpConfig
4180
4233
  };
4181
4234
  process.stdout.write(` Writing ${instructionFilePath}... `);
4182
4235
  try {
4183
4236
  await setupInstructions({
4184
4237
  domains: allDomains,
4185
4238
  agent: config.agent,
4186
- mcpConfig: mergedMcpConfig
4239
+ mcpConfig: mergedMcpConfig,
4240
+ projectPath: config.projectPath
4187
4241
  });
4188
4242
  result.instructionsCreated = true;
4189
4243
  console.log("\u2713");
@@ -4196,7 +4250,7 @@ async function execute(config, token, repo) {
4196
4250
  try {
4197
4251
  const mcpResult = await setupMCPConfiguration(
4198
4252
  config.projectPath,
4199
- config.mcpConfig,
4253
+ filteredMcpConfig,
4200
4254
  config.agent
4201
4255
  );
4202
4256
  result.mcpConfigured = true;
@@ -4214,6 +4268,9 @@ async function execute(config, token, repo) {
4214
4268
  ])];
4215
4269
  const state = {
4216
4270
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
4271
+ installerVersion: INSTALLER_VERSION,
4272
+ releaseVersion: config.releaseVersion,
4273
+ projectPath: config.projectPath,
4217
4274
  agent: config.agent,
4218
4275
  personas: allPersonas,
4219
4276
  domains: allDomains,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlw-machine-setup",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "One-shot installer for The Machine toolchain",
5
5
  "bin": {
6
6
  "dlw-machine-setup": "bin/installer.js"