dlw-machine-setup 0.5.8 → 0.5.9

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 +497 -543
  2. package/package.json +1 -1
package/bin/installer.js CHANGED
@@ -9,6 +9,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __commonJS = (cb, mod) => function __require() {
10
10
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
11
  };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
12
16
  var __copyProps = (to, from, except, desc) => {
13
17
  if (from && typeof from === "object" || typeof from === "function") {
14
18
  for (let key of __getOwnPropNames(from))
@@ -3240,10 +3244,9 @@ ${page}${helpTipBottom}${choiceDescription}${import_ansi_escapes3.default.cursor
3240
3244
  });
3241
3245
 
3242
3246
  // src/index.ts
3243
- var import_fs7 = require("fs");
3247
+ var import_fs8 = require("fs");
3244
3248
  var import_readline = require("readline");
3245
- var import_path7 = require("path");
3246
- var import_os2 = require("os");
3249
+ var import_path8 = require("path");
3247
3250
 
3248
3251
  // src/utils/fetch.ts
3249
3252
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -3286,7 +3289,7 @@ async function fetchWithRetry(url, options = {}, maxRetries = DEFAULT_MAX_RETRIE
3286
3289
  throw lastError || new Error("Failed after maximum retries");
3287
3290
  }
3288
3291
 
3289
- // src/utils/data/fetch-wizard-options.ts
3292
+ // src/utils/wizard-options.ts
3290
3293
  async function fetchWizardOptions(token, repo) {
3291
3294
  try {
3292
3295
  const headers = {
@@ -3315,7 +3318,7 @@ async function fetchWizardOptions(token, repo) {
3315
3318
  }
3316
3319
  }
3317
3320
 
3318
- // src/utils/data/discover-repo.ts
3321
+ // src/utils/discover-repo.ts
3319
3322
  var GITHUB_ORG = "DLW-INT-SAP-DEV";
3320
3323
  var DEFAULT_TOPIC = "one-shot-data";
3321
3324
  async function discoverRepo(token, topic = DEFAULT_TOPIC) {
@@ -3380,12 +3383,7 @@ async function tryOrgReposList(headers, topic) {
3380
3383
  }
3381
3384
  }
3382
3385
 
3383
- // src/utils/data/fetch-contexts.ts
3384
- var import_fs2 = require("fs");
3385
- var import_path2 = require("path");
3386
- var import_child_process = require("child_process");
3387
-
3388
- // src/utils/auth/github-auth.ts
3386
+ // src/utils/github-auth.ts
3389
3387
  var import_fs = require("fs");
3390
3388
  var import_path = require("path");
3391
3389
  var import_os = require("os");
@@ -3544,56 +3542,154 @@ async function getGitHubToken() {
3544
3542
  return await authenticateWithGitHub();
3545
3543
  }
3546
3544
 
3547
- // src/utils/data/fetch-contexts.ts
3548
- var MIN_FILE_SIZE = 1024;
3549
- async function fetchContexts(options = {}) {
3550
- const { domains = [], targetDir = process.cwd(), force = false, token, repo } = options;
3551
- if (!repo) {
3552
- throw new Error("Repository not specified. Discovery may have failed.");
3545
+ // src/utils/mcp-config.ts
3546
+ function buildMCPConfiguration(selectedItems, baseMcpServers, allMcpServers) {
3547
+ const config = {};
3548
+ for (const serverName of baseMcpServers) {
3549
+ if (allMcpServers[serverName]) {
3550
+ config[serverName] = allMcpServers[serverName];
3551
+ }
3553
3552
  }
3554
- const result = {
3555
- successful: [],
3556
- failed: [],
3557
- skipped: [],
3558
- failureReasons: {}
3559
- };
3560
- if (domains.length === 0) {
3561
- return result;
3553
+ for (const item of selectedItems) {
3554
+ for (const serverName of item.mcpServers) {
3555
+ if (allMcpServers[serverName] && !config[serverName]) {
3556
+ config[serverName] = allMcpServers[serverName];
3557
+ }
3558
+ }
3562
3559
  }
3563
- await checkPrerequisites();
3564
- const githubToken = token ?? await getGitHubToken();
3565
- try {
3566
- const releaseUrl = `https://api.github.com/repos/${repo}/releases/latest`;
3567
- const headers = {
3568
- "Accept": "application/vnd.github+json",
3569
- "Authorization": `Bearer ${githubToken}`
3570
- };
3571
- const releaseResponse = await fetchWithRetry(releaseUrl, { headers });
3572
- if (!releaseResponse.ok) {
3573
- throw new Error(
3574
- `GitHub API error (${releaseResponse.status}): ${getReadableError(releaseResponse.status)}`
3575
- );
3560
+ return config;
3561
+ }
3562
+
3563
+ // src/utils/mod.ts
3564
+ async function loadWizardOptions(token, repo) {
3565
+ const remote = await fetchWizardOptions(token, repo);
3566
+ if (!remote) {
3567
+ throw new Error("Failed to load configuration from GitHub release. Check your network and token.");
3568
+ }
3569
+ const { options, releaseVersion } = remote;
3570
+ const filteredMcpServers = Object.fromEntries(
3571
+ Object.entries(options.mcpServers).filter(([, config]) => config.active !== false)
3572
+ );
3573
+ return {
3574
+ personas: options.personas.personas.filter((p) => p.active !== false),
3575
+ agents: options.agents.filter((a) => a.active !== false),
3576
+ baseMcpServers: options.personas.baseMcpServers,
3577
+ mcpServers: filteredMcpServers,
3578
+ releaseVersion
3579
+ };
3580
+ }
3581
+
3582
+ // src/pipeline.ts
3583
+ function defineStep(def) {
3584
+ return def;
3585
+ }
3586
+ async function runPipeline(steps, ctx) {
3587
+ const entries = [];
3588
+ for (const step of steps) {
3589
+ if (step.when && !step.when(ctx)) {
3590
+ entries.push({ name: step.name, label: step.label, result: { status: "skipped" } });
3591
+ continue;
3576
3592
  }
3577
- const releaseData = await releaseResponse.json();
3578
- const contextsDir = (0, import_path2.join)(targetDir, "_ai-context");
3579
- const tempDir = (0, import_path2.join)(targetDir, ".temp-download");
3593
+ process.stdout.write(` ${step.label}... `);
3580
3594
  try {
3581
- if (!(0, import_fs2.existsSync)(contextsDir)) {
3582
- (0, import_fs2.mkdirSync)(contextsDir, { recursive: true });
3595
+ const result = await step.execute(ctx);
3596
+ entries.push({ name: step.name, label: step.label, result });
3597
+ if (result.status === "success") {
3598
+ console.log("\u2713");
3599
+ } else {
3600
+ console.log("\u2717");
3583
3601
  }
3584
- if ((0, import_fs2.existsSync)(tempDir)) {
3585
- (0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
3602
+ if (result.detail) {
3603
+ console.log(` ${result.detail}`);
3586
3604
  }
3587
- (0, import_fs2.mkdirSync)(tempDir, { recursive: true });
3588
- } catch (error) {
3589
- throw new Error(`Failed to create directories: ${error instanceof Error ? error.message : "Unknown error"}`);
3605
+ } catch (err) {
3606
+ const detail = err instanceof Error ? err.message : String(err);
3607
+ entries.push({
3608
+ name: step.name,
3609
+ label: step.label,
3610
+ result: { status: "failed", detail }
3611
+ });
3612
+ console.log("\u2717");
3613
+ console.log(` ${detail}`);
3590
3614
  }
3615
+ }
3616
+ return { entries };
3617
+ }
3618
+
3619
+ // src/steps/index.ts
3620
+ var steps_exports = {};
3621
+ __export(steps_exports, {
3622
+ fetchContexts: () => fetch_contexts_default,
3623
+ fetchFactory: () => fetch_factory_default,
3624
+ updateGitignore: () => update_gitignore_default,
3625
+ writeInstructions: () => write_instructions_default,
3626
+ writeMcpConfig: () => write_mcp_config_default,
3627
+ writeState: () => write_state_default
3628
+ });
3629
+
3630
+ // src/steps/resources/fetch-contexts.ts
3631
+ var import_fs2 = require("fs");
3632
+ var import_path2 = require("path");
3633
+ var import_child_process = require("child_process");
3634
+ var MIN_FILE_SIZE = 1024;
3635
+ var fetch_contexts_default = defineStep({
3636
+ name: "fetch-contexts",
3637
+ label: "Downloading contexts",
3638
+ execute: async (ctx) => {
3639
+ const uniqueDomains = [...new Set(ctx.config.personas.flatMap((p) => p.domains))];
3640
+ const domainValues = uniqueDomains.map((d) => d.toLowerCase());
3641
+ const downloadResult = await fetchContexts(domainValues, ctx.token, ctx.repo, ctx.config.projectPath);
3642
+ ctx.installed.domainsInstalled = downloadResult.successful;
3643
+ ctx.installed.domainsFailed = downloadResult.failed;
3644
+ ctx.installed.failureReasons = downloadResult.failureReasons;
3645
+ const domainColWidth = Math.max(...domainValues.map((d) => d.length), 8) + 2;
3646
+ console.log("");
3647
+ for (const domain of domainValues) {
3648
+ const ok = downloadResult.successful.includes(domain);
3649
+ const reason = downloadResult.failureReasons[domain];
3650
+ const status = ok ? "\u2713" : `\u2717 ${reason ?? "Unknown error"}`;
3651
+ console.log(` ${domain.padEnd(domainColWidth)}${status}`);
3652
+ }
3653
+ if (downloadResult.failed.length > 0) {
3654
+ return {
3655
+ status: "failed",
3656
+ message: `${downloadResult.successful.join(", ")} succeeded; ${downloadResult.failed.join(", ")} failed`
3657
+ };
3658
+ }
3659
+ return {
3660
+ status: "success",
3661
+ message: downloadResult.successful.join(", ")
3662
+ };
3663
+ }
3664
+ });
3665
+ async function fetchContexts(domains, token, repo, targetDir) {
3666
+ const result = { successful: [], failed: [], failureReasons: {} };
3667
+ if (domains.length === 0) return result;
3668
+ const tarCheck = (0, import_child_process.spawnSync)("tar", ["--version"], { stdio: "ignore" });
3669
+ if (tarCheck.status !== 0) {
3670
+ throw new Error("tar command not found. Please ensure tar is installed and available in your PATH.");
3671
+ }
3672
+ const headers = {
3673
+ "Accept": "application/vnd.github+json",
3674
+ "Authorization": `Bearer ${token}`
3675
+ };
3676
+ const releaseResponse = await fetchWithRetry(
3677
+ `https://api.github.com/repos/${repo}/releases/latest`,
3678
+ { headers }
3679
+ );
3680
+ if (!releaseResponse.ok) {
3681
+ throw new Error(`GitHub API error (${releaseResponse.status}): ${getReadableError(releaseResponse.status)}`);
3682
+ }
3683
+ const releaseData = await releaseResponse.json();
3684
+ const contextsDir = (0, import_path2.join)(targetDir, "_ai-context");
3685
+ const tempDir = (0, import_path2.join)(targetDir, ".temp-download");
3686
+ try {
3687
+ if (!(0, import_fs2.existsSync)(contextsDir)) (0, import_fs2.mkdirSync)(contextsDir, { recursive: true });
3688
+ if ((0, import_fs2.existsSync)(tempDir)) (0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
3689
+ (0, import_fs2.mkdirSync)(tempDir, { recursive: true });
3591
3690
  for (const domain of domains) {
3592
3691
  try {
3593
3692
  const domainPath = (0, import_path2.join)(contextsDir, domain);
3594
- if ((0, import_fs2.existsSync)(domainPath) && force) {
3595
- (0, import_fs2.rmSync)(domainPath, { recursive: true, force: true });
3596
- }
3597
3693
  const archiveName = `${domain.toLowerCase()}.tar.gz`;
3598
3694
  const asset = releaseData.assets?.find((a) => a.name === archiveName);
3599
3695
  if (!asset) {
@@ -3603,10 +3699,9 @@ async function fetchContexts(options = {}) {
3603
3699
  }
3604
3700
  const downloadHeaders = {
3605
3701
  "Accept": "application/octet-stream",
3606
- "Authorization": `Bearer ${githubToken}`
3702
+ "Authorization": `Bearer ${token}`
3607
3703
  };
3608
- const downloadUrl = asset.url;
3609
- const response = await fetchWithRetry(downloadUrl, { headers: downloadHeaders });
3704
+ const response = await fetchWithRetry(asset.url, { headers: downloadHeaders });
3610
3705
  if (!response.ok) {
3611
3706
  throw new Error(`Download failed: ${getReadableError(response.status)}`);
3612
3707
  }
@@ -3618,9 +3713,7 @@ async function fetchContexts(options = {}) {
3618
3713
  throw new Error("File corrupted");
3619
3714
  }
3620
3715
  const listResult = (0, import_child_process.spawnSync)("tar", ["-tzf", archivePath], { encoding: "utf8" });
3621
- if (listResult.status !== 0) {
3622
- throw new Error("Failed to read archive contents");
3623
- }
3716
+ if (listResult.status !== 0) throw new Error("Failed to read archive contents");
3624
3717
  const resolvedTempDir = (0, import_path2.resolve)(tempDir);
3625
3718
  for (const entry of listResult.stdout.split("\n").filter(Boolean)) {
3626
3719
  const entryPath = (0, import_path2.resolve)((0, import_path2.join)(tempDir, entry.replace(/\/$/, "")));
@@ -3629,47 +3722,38 @@ async function fetchContexts(options = {}) {
3629
3722
  }
3630
3723
  }
3631
3724
  const extractResult = (0, import_child_process.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
3632
- if (extractResult.status !== 0) {
3633
- throw new Error("Archive extraction failed");
3634
- }
3725
+ if (extractResult.status !== 0) throw new Error("Archive extraction failed");
3635
3726
  const extractedEntries = (0, import_fs2.readdirSync)(tempDir);
3636
3727
  const extractedFolder = extractedEntries.find((e) => e.toLowerCase() === domain.toLowerCase());
3637
- if (!extractedFolder) {
3638
- throw new Error("Extraction failed");
3639
- }
3640
- const extractedPath = (0, import_path2.join)(tempDir, extractedFolder);
3641
- copyDirectory(extractedPath, domainPath);
3728
+ if (!extractedFolder) throw new Error("Extraction failed");
3729
+ copyDirectory((0, import_path2.join)(tempDir, extractedFolder), domainPath);
3642
3730
  result.successful.push(domain);
3643
3731
  } catch (error) {
3644
- const reason = error instanceof Error ? error.message : "Unknown error";
3645
3732
  result.failed.push(domain);
3646
- result.failureReasons[domain] = reason;
3647
- }
3648
- }
3649
- try {
3650
- if ((0, import_fs2.existsSync)(tempDir)) {
3651
- (0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
3733
+ result.failureReasons[domain] = error instanceof Error ? error.message : "Unknown error";
3652
3734
  }
3653
- } catch (error) {
3654
3735
  }
3655
- } catch (error) {
3656
- const tempDir = (0, import_path2.join)(targetDir, ".temp-download");
3736
+ } finally {
3657
3737
  if ((0, import_fs2.existsSync)(tempDir)) {
3658
3738
  try {
3659
3739
  (0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
3660
3740
  } catch {
3661
3741
  }
3662
3742
  }
3663
- throw error;
3664
3743
  }
3665
3744
  return result;
3666
3745
  }
3667
- async function checkPrerequisites() {
3668
- const check2 = (0, import_child_process.spawnSync)("tar", ["--version"], { stdio: "ignore" });
3669
- if (check2.status !== 0) {
3670
- throw new Error(
3671
- "tar command not found. Please ensure tar is installed and available in your PATH."
3672
- );
3746
+ function copyDirectory(source, target) {
3747
+ if (!(0, import_fs2.existsSync)(target)) (0, import_fs2.mkdirSync)(target, { recursive: true });
3748
+ const entries = (0, import_fs2.readdirSync)(source, { withFileTypes: true });
3749
+ for (const entry of entries) {
3750
+ const sourcePath = (0, import_path2.join)(source, entry.name);
3751
+ const targetPath = (0, import_path2.join)(target, entry.name);
3752
+ if (entry.isDirectory()) {
3753
+ copyDirectory(sourcePath, targetPath);
3754
+ } else {
3755
+ (0, import_fs2.copyFileSync)(sourcePath, targetPath);
3756
+ }
3673
3757
  }
3674
3758
  }
3675
3759
  function getReadableError(status) {
@@ -3690,44 +3774,28 @@ function getReadableError(status) {
3690
3774
  return `Unexpected error (${status})`;
3691
3775
  }
3692
3776
  }
3693
- function copyDirectory(source, target) {
3694
- let added = 0;
3695
- let overwritten = 0;
3696
- if (!(0, import_fs2.existsSync)(target)) {
3697
- (0, import_fs2.mkdirSync)(target, { recursive: true });
3698
- }
3699
- const entries = (0, import_fs2.readdirSync)(source, { withFileTypes: true });
3700
- for (const entry of entries) {
3701
- const sourcePath = (0, import_path2.join)(source, entry.name);
3702
- const targetPath = (0, import_path2.join)(target, entry.name);
3703
- if (entry.isDirectory()) {
3704
- const result = copyDirectory(sourcePath, targetPath);
3705
- added += result.added;
3706
- overwritten += result.overwritten;
3707
- } else {
3708
- if ((0, import_fs2.existsSync)(targetPath)) {
3709
- overwritten++;
3710
- } else {
3711
- added++;
3712
- }
3713
- (0, import_fs2.copyFileSync)(sourcePath, targetPath);
3714
- }
3715
- }
3716
- return { added, overwritten };
3717
- }
3718
3777
 
3719
- // src/utils/data/fetch-factory.ts
3778
+ // src/steps/resources/fetch-factory.ts
3720
3779
  var import_fs3 = require("fs");
3721
3780
  var import_path3 = require("path");
3722
3781
  var import_child_process2 = require("child_process");
3723
3782
  var MIN_FILE_SIZE2 = 1024;
3724
3783
  var FACTORY_ASSET_NAME = "factory.tar.gz";
3725
- async function fetchFactory(options) {
3726
- const { token, repo, targetDir } = options;
3727
- const result = {
3728
- success: false,
3729
- filesInstalled: []
3730
- };
3784
+ var fetch_factory_default = defineStep({
3785
+ name: "fetch-factory",
3786
+ label: "Installing Factory framework",
3787
+ when: (ctx) => ctx.config.installFactory && !!ctx.factoryRepo,
3788
+ execute: async (ctx) => {
3789
+ const result = await fetchFactory(ctx.token, ctx.factoryRepo, ctx.config.projectPath);
3790
+ ctx.installed.factoryInstalled = result.success;
3791
+ if (!result.success) {
3792
+ return { status: "failed", detail: result.failureReason };
3793
+ }
3794
+ return { status: "success", message: result.filesInstalled.join(", ") };
3795
+ }
3796
+ });
3797
+ async function fetchFactory(token, repo, targetDir) {
3798
+ const result = { success: false, filesInstalled: [] };
3731
3799
  const tarCheck = (0, import_child_process2.spawnSync)("tar", ["--version"], { stdio: "ignore" });
3732
3800
  if (tarCheck.status !== 0) {
3733
3801
  result.failureReason = "tar command not found";
@@ -3737,8 +3805,10 @@ async function fetchFactory(options) {
3737
3805
  "Accept": "application/vnd.github+json",
3738
3806
  "Authorization": `Bearer ${token}`
3739
3807
  };
3740
- const releaseUrl = `https://api.github.com/repos/${repo}/releases/latest`;
3741
- const releaseResponse = await fetchWithRetry(releaseUrl, { headers });
3808
+ const releaseResponse = await fetchWithRetry(
3809
+ `https://api.github.com/repos/${repo}/releases/latest`,
3810
+ { headers }
3811
+ );
3742
3812
  if (!releaseResponse.ok) {
3743
3813
  result.failureReason = `GitHub API error (${releaseResponse.status})`;
3744
3814
  return result;
@@ -3751,15 +3821,11 @@ async function fetchFactory(options) {
3751
3821
  }
3752
3822
  const tempDir = (0, import_path3.join)(targetDir, ".temp-factory-download");
3753
3823
  try {
3754
- if ((0, import_fs3.existsSync)(tempDir)) {
3755
- (0, import_fs3.rmSync)(tempDir, { recursive: true, force: true });
3756
- }
3824
+ if ((0, import_fs3.existsSync)(tempDir)) (0, import_fs3.rmSync)(tempDir, { recursive: true, force: true });
3757
3825
  (0, import_fs3.mkdirSync)(tempDir, { recursive: true });
3758
- const downloadHeaders = {
3759
- "Accept": "application/octet-stream",
3760
- "Authorization": `Bearer ${token}`
3761
- };
3762
- const response = await fetchWithRetry(asset.url, { headers: downloadHeaders });
3826
+ const response = await fetchWithRetry(asset.url, {
3827
+ headers: { "Accept": "application/octet-stream", "Authorization": `Bearer ${token}` }
3828
+ });
3763
3829
  if (!response.ok) {
3764
3830
  result.failureReason = `Download failed (${response.status})`;
3765
3831
  return result;
@@ -3891,31 +3957,23 @@ function configureSettings(claudeDir) {
3891
3957
  if (!hasMonitor) settings.hooks.UserPromptSubmit.push(contextMonitorHook);
3892
3958
  }
3893
3959
  settings.statusLine = { type: "command", command: "node .claude/hooks/factory-statusline.cjs" };
3894
- if (!(0, import_fs3.existsSync)(claudeDir)) {
3895
- (0, import_fs3.mkdirSync)(claudeDir, { recursive: true });
3896
- }
3960
+ if (!(0, import_fs3.existsSync)(claudeDir)) (0, import_fs3.mkdirSync)(claudeDir, { recursive: true });
3897
3961
  (0, import_fs3.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3898
3962
  }
3899
3963
  function copyAgentStubs(source, target) {
3900
- if (!(0, import_fs3.existsSync)(target)) {
3901
- (0, import_fs3.mkdirSync)(target, { recursive: true });
3902
- }
3964
+ if (!(0, import_fs3.existsSync)(target)) (0, import_fs3.mkdirSync)(target, { recursive: true });
3903
3965
  const entries = (0, import_fs3.readdirSync)(source, { withFileTypes: true });
3904
3966
  for (const entry of entries) {
3905
- if (entry.isDirectory()) continue;
3906
- if (!entry.name.endsWith(".md")) continue;
3967
+ if (entry.isDirectory() || !entry.name.endsWith(".md")) continue;
3907
3968
  const content = (0, import_fs3.readFileSync)((0, import_path3.join)(source, entry.name), "utf8");
3908
3969
  const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?\r?\n)---/);
3909
3970
  if (frontmatterMatch) {
3910
3971
  (0, import_fs3.writeFileSync)((0, import_path3.join)(target, entry.name), frontmatterMatch[0] + "\n");
3911
- } else {
3912
3972
  }
3913
3973
  }
3914
3974
  }
3915
3975
  function copyDirectory2(source, target) {
3916
- if (!(0, import_fs3.existsSync)(target)) {
3917
- (0, import_fs3.mkdirSync)(target, { recursive: true });
3918
- }
3976
+ if (!(0, import_fs3.existsSync)(target)) (0, import_fs3.mkdirSync)(target, { recursive: true });
3919
3977
  const entries = (0, import_fs3.readdirSync)(source, { withFileTypes: true });
3920
3978
  for (const entry of entries) {
3921
3979
  const sourcePath = (0, import_path3.join)(source, entry.name);
@@ -3928,96 +3986,99 @@ function copyDirectory2(source, target) {
3928
3986
  }
3929
3987
  }
3930
3988
 
3931
- // src/utils/setup/setup-mcp.ts
3989
+ // src/steps/setup/write-instructions.ts
3932
3990
  var import_fs4 = require("fs");
3933
3991
  var import_path4 = require("path");
3934
- function getAgentMCPTarget(agent) {
3992
+
3993
+ // src/steps/shared.ts
3994
+ function getFilteredMcpConfig(ctx) {
3995
+ const successfulDomains = new Set(ctx.installed.domainsInstalled ?? []);
3996
+ const successfulPersonas = ctx.config.personas.filter(
3997
+ (p) => p.domains.every((d) => successfulDomains.has(d.toLowerCase()))
3998
+ );
3999
+ const successfulMcpServers = /* @__PURE__ */ new Set([
4000
+ ...ctx.config.baseMcpServers,
4001
+ ...successfulPersonas.flatMap((p) => p.mcpServers)
4002
+ ]);
4003
+ return Object.fromEntries(
4004
+ Object.entries(ctx.config.mcpConfig).filter(([name]) => successfulMcpServers.has(name))
4005
+ );
4006
+ }
4007
+
4008
+ // src/steps/setup/write-instructions.ts
4009
+ var MARKER_START = "<!-- one-shot-installer:start -->";
4010
+ var MARKER_END = "<!-- one-shot-installer:end -->";
4011
+ var red = (text) => `\x1B[31m${text}\x1B[0m`;
4012
+ var write_instructions_default = defineStep({
4013
+ name: "write-instructions",
4014
+ label: "Writing instruction file",
4015
+ when: (ctx) => (ctx.installed.domainsInstalled?.length ?? 0) > 0,
4016
+ execute: async (ctx) => {
4017
+ const filteredMcpConfig = getFilteredMcpConfig(ctx);
4018
+ const domains = ctx.installed.domainsInstalled;
4019
+ const agent = ctx.config.agent;
4020
+ const projectPath = ctx.config.projectPath;
4021
+ const factoryInstalled = ctx.installed.factoryInstalled ?? false;
4022
+ const content = buildCombinedInstructions(domains, filteredMcpConfig, agent, projectPath, factoryInstalled);
4023
+ switch (agent) {
4024
+ case "claude-code": {
4025
+ upsertBlock((0, import_path4.join)(projectPath, "CLAUDE.md"), content);
4026
+ break;
4027
+ }
4028
+ case "github-copilot": {
4029
+ const agentsDir = (0, import_path4.join)(projectPath, ".github", "agents");
4030
+ if (!(0, import_fs4.existsSync)(agentsDir)) (0, import_fs4.mkdirSync)(agentsDir, { recursive: true });
4031
+ const filePath = (0, import_path4.join)(agentsDir, "instructions.md");
4032
+ if (!(0, import_fs4.existsSync)(filePath)) {
4033
+ (0, import_fs4.writeFileSync)(filePath, `---
4034
+ applyTo: "**"
4035
+ ---
4036
+
4037
+ `, "utf-8");
4038
+ }
4039
+ upsertBlock(filePath, content);
4040
+ break;
4041
+ }
4042
+ case "cursor": {
4043
+ const cursorDir = (0, import_path4.join)(projectPath, ".cursor", "rules");
4044
+ if (!(0, import_fs4.existsSync)(cursorDir)) (0, import_fs4.mkdirSync)(cursorDir, { recursive: true });
4045
+ const filePath = (0, import_path4.join)(cursorDir, "instructions.mdc");
4046
+ if (!(0, import_fs4.existsSync)(filePath)) {
4047
+ (0, import_fs4.writeFileSync)(filePath, `---
4048
+ description: AI development instructions from One-Shot Installer
4049
+ alwaysApply: true
4050
+ ---
4051
+
4052
+ `, "utf-8");
4053
+ }
4054
+ upsertBlock(filePath, content);
4055
+ break;
4056
+ }
4057
+ }
4058
+ return { status: "success", message: getInstructionFilePath(agent) };
4059
+ }
4060
+ });
4061
+ function getInstructionFilePath(agent) {
3935
4062
  switch (agent) {
3936
4063
  case "claude-code":
3937
- return { filePath: ".mcp.json", rootKey: "mcpServers", dir: null };
3938
- case "cursor":
3939
- return { filePath: ".cursor/mcp.json", rootKey: "mcpServers", dir: ".cursor" };
4064
+ return "CLAUDE.md";
3940
4065
  case "github-copilot":
4066
+ return ".github/agents/instructions.md";
4067
+ case "cursor":
4068
+ return ".cursor/rules/instructions.mdc";
3941
4069
  default:
3942
- return { filePath: ".vscode/mcp.json", rootKey: "servers", dir: ".vscode" };
3943
- }
3944
- }
3945
- async function setupMCPConfiguration(projectPath, mcpConfig, agent) {
3946
- const target = getAgentMCPTarget(agent);
3947
- const mcpJsonPath = (0, import_path4.join)(projectPath, target.filePath);
3948
- if (target.dir) {
3949
- const dir = (0, import_path4.join)(projectPath, target.dir);
3950
- if (!(0, import_fs4.existsSync)(dir)) {
3951
- (0, import_fs4.mkdirSync)(dir, { recursive: true });
3952
- }
3953
- }
3954
- const red2 = (text) => `\x1B[31m${text}\x1B[0m`;
3955
- let existingFile = {};
3956
- if ((0, import_fs4.existsSync)(mcpJsonPath)) {
3957
- try {
3958
- const content = (0, import_fs4.readFileSync)(mcpJsonPath, "utf-8");
3959
- existingFile = JSON.parse(content);
3960
- } catch {
3961
- const box = [
3962
- "",
3963
- 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"),
3964
- red2(" \u2551 \u26A0 MCP CONFIG HAS INVALID JSON \u2551"),
3965
- red2(" \u2551 \u2551"),
3966
- red2(` \u2551 ${target.filePath} could not be parsed.`.padEnd(52) + "\u2551"),
3967
- red2(" \u2551 Existing MCP servers may be lost. \u2551"),
3968
- red2(" \u2551 Check the file after installation completes. \u2551"),
3969
- 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"),
3970
- ""
3971
- ].join("\n");
3972
- console.log(box);
3973
- }
3974
- }
3975
- const addedServers = [];
3976
- const skippedServers = [];
3977
- const existingServers = existingFile[target.rootKey] ?? {};
3978
- const newServers = {};
3979
- for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {
3980
- const { description, useWhen, active, ...mcpFields } = serverConfig;
3981
- newServers[serverName] = mcpFields;
3982
- addedServers.push(serverName);
3983
- }
3984
- const mergedServers = { ...existingServers, ...newServers };
3985
- const outputFile = { ...existingFile, [target.rootKey]: mergedServers };
3986
- (0, import_fs4.writeFileSync)(mcpJsonPath, JSON.stringify(outputFile, null, 2), "utf-8");
3987
- return { addedServers, skippedServers };
3988
- }
3989
- function buildMCPConfiguration(selectedItems, baseMcpServers, allMcpServers) {
3990
- const config = {};
3991
- for (const serverName of baseMcpServers) {
3992
- if (allMcpServers[serverName]) {
3993
- config[serverName] = allMcpServers[serverName];
3994
- }
3995
- }
3996
- for (const item of selectedItems) {
3997
- for (const serverName of item.mcpServers) {
3998
- if (allMcpServers[serverName] && !config[serverName]) {
3999
- config[serverName] = allMcpServers[serverName];
4000
- }
4001
- }
4070
+ return "CLAUDE.md";
4002
4071
  }
4003
- return config;
4004
4072
  }
4005
-
4006
- // src/utils/setup/setup-instructions.ts
4007
- var import_fs5 = require("fs");
4008
- var import_path5 = require("path");
4009
- var MARKER_START = "<!-- one-shot-installer:start -->";
4010
- var MARKER_END = "<!-- one-shot-installer:end -->";
4011
- var red = (text) => `\x1B[31m${text}\x1B[0m`;
4012
4073
  function upsertBlock(filePath, block) {
4013
4074
  const marked = `${MARKER_START}
4014
4075
  ${block}
4015
4076
  ${MARKER_END}`;
4016
- if (!(0, import_fs5.existsSync)(filePath)) {
4017
- (0, import_fs5.writeFileSync)(filePath, marked, "utf-8");
4077
+ if (!(0, import_fs4.existsSync)(filePath)) {
4078
+ (0, import_fs4.writeFileSync)(filePath, marked, "utf-8");
4018
4079
  return;
4019
4080
  }
4020
- const existing = (0, import_fs5.readFileSync)(filePath, "utf-8");
4081
+ const existing = (0, import_fs4.readFileSync)(filePath, "utf-8");
4021
4082
  const start = existing.indexOf(MARKER_START);
4022
4083
  const end = existing.indexOf(MARKER_END);
4023
4084
  const hasStart = start !== -1;
@@ -4042,23 +4103,23 @@ ${MARKER_END}`;
4042
4103
  }
4043
4104
  if (hasStart && hasEnd) {
4044
4105
  const updated = existing.slice(0, start) + marked + existing.slice(end + MARKER_END.length);
4045
- (0, import_fs5.writeFileSync)(filePath, updated, "utf-8");
4106
+ (0, import_fs4.writeFileSync)(filePath, updated, "utf-8");
4046
4107
  } else {
4047
4108
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
4048
- (0, import_fs5.writeFileSync)(filePath, existing + separator + marked, "utf-8");
4109
+ (0, import_fs4.writeFileSync)(filePath, existing + separator + marked, "utf-8");
4049
4110
  }
4050
4111
  }
4051
4112
  function collectMdFiles(dir) {
4052
- if (!(0, import_fs5.existsSync)(dir)) return [];
4053
- const entries = (0, import_fs5.readdirSync)(dir, { recursive: true });
4113
+ if (!(0, import_fs4.existsSync)(dir)) return [];
4114
+ const entries = (0, import_fs4.readdirSync)(dir, { recursive: true });
4054
4115
  return entries.filter((entry) => {
4055
- const fullPath = (0, import_path5.join)(dir, entry);
4056
- return entry.endsWith(".md") && (0, import_fs5.statSync)(fullPath).isFile();
4116
+ const fullPath = (0, import_path4.join)(dir, entry);
4117
+ return entry.endsWith(".md") && (0, import_fs4.statSync)(fullPath).isFile();
4057
4118
  }).map((entry) => entry.replace(/\\/g, "/")).sort();
4058
4119
  }
4059
4120
  function extractFirstHeading(filePath) {
4060
4121
  try {
4061
- const content = (0, import_fs5.readFileSync)(filePath, "utf-8");
4122
+ const content = (0, import_fs4.readFileSync)(filePath, "utf-8");
4062
4123
  const withoutFrontmatter = content.replace(/^---[\s\S]*?---\s*/, "");
4063
4124
  const match = withoutFrontmatter.match(/^#\s+(.+)/m);
4064
4125
  if (match) {
@@ -4069,47 +4130,23 @@ function extractFirstHeading(filePath) {
4069
4130
  const basename = filePath.split(/[/\\]/).pop() ?? "";
4070
4131
  return basename.replace(/\.md$/i, "").replace(/[-_]+/g, " ");
4071
4132
  }
4072
- function buildMCPSection(mcpConfig) {
4073
- const entries = Object.entries(mcpConfig);
4074
- if (entries.length === 0) return "";
4075
- const lines2 = [
4076
- `## MCP Servers`,
4077
- ``,
4078
- `The following MCP servers are available. Use them proactively when relevant:`,
4079
- ``
4080
- ];
4081
- for (const [name, config] of entries) {
4082
- const description = config.description;
4083
- const useWhen = config.useWhen;
4084
- if (!description) continue;
4085
- lines2.push(`### ${name}`);
4086
- lines2.push(description);
4087
- if (useWhen) {
4088
- lines2.push(`**Use when**: ${useWhen}`);
4089
- }
4090
- lines2.push(``);
4133
+ function formatPathRef(contextPath, description, agent) {
4134
+ if (agent === "github-copilot") {
4135
+ return `- [${contextPath}](../../${contextPath}) \u2014 ${description}`;
4091
4136
  }
4092
- return lines2.join("\n");
4137
+ return `- \`${contextPath}\` \u2014 ${description}`;
4093
4138
  }
4094
4139
  function resolveDomainFolder(domain, contextsDir) {
4095
- if ((0, import_fs5.existsSync)(contextsDir)) {
4140
+ if ((0, import_fs4.existsSync)(contextsDir)) {
4096
4141
  try {
4097
- const entries = (0, import_fs5.readdirSync)(contextsDir);
4142
+ const entries = (0, import_fs4.readdirSync)(contextsDir);
4098
4143
  const match = entries.find((e) => e.toLowerCase() === domain.toLowerCase());
4099
- if (match) {
4100
- return { folderName: match, folderPath: (0, import_path5.join)(contextsDir, match) };
4101
- }
4144
+ if (match) return { folderName: match, folderPath: (0, import_path4.join)(contextsDir, match) };
4102
4145
  } catch {
4103
4146
  }
4104
4147
  }
4105
4148
  const fallback = domain.toUpperCase();
4106
- return { folderName: fallback, folderPath: (0, import_path5.join)(contextsDir, fallback) };
4107
- }
4108
- function formatPathRef(contextPath, description, agent) {
4109
- if (agent === "github-copilot") {
4110
- return `- [${contextPath}](../../${contextPath}) \u2014 ${description}`;
4111
- }
4112
- return `- \`${contextPath}\` \u2014 ${description}`;
4149
+ return { folderName: fallback, folderPath: (0, import_path4.join)(contextsDir, fallback) };
4113
4150
  }
4114
4151
  function buildContextRefsSection(domains, agent, contextsDir) {
4115
4152
  const lines2 = [
@@ -4121,34 +4158,34 @@ function buildContextRefsSection(domains, agent, contextsDir) {
4121
4158
  let hasAnyFiles = false;
4122
4159
  for (const domain of domains) {
4123
4160
  const { folderName, folderPath: domainPath } = resolveDomainFolder(domain, contextsDir);
4124
- if (!(0, import_fs5.existsSync)(domainPath)) continue;
4161
+ if (!(0, import_fs4.existsSync)(domainPath)) continue;
4125
4162
  const domainFiles = [];
4126
- const ctxInstructions = (0, import_path5.join)(domainPath, "context-instructions.md");
4127
- if ((0, import_fs5.existsSync)(ctxInstructions)) {
4163
+ const ctxInstructions = (0, import_path4.join)(domainPath, "context-instructions.md");
4164
+ if ((0, import_fs4.existsSync)(ctxInstructions)) {
4128
4165
  const desc = extractFirstHeading(ctxInstructions);
4129
4166
  domainFiles.push(formatPathRef(`_ai-context/${folderName}/context-instructions.md`, desc, agent));
4130
4167
  }
4131
- const instructionsMd = (0, import_path5.join)(domainPath, "core", "instructions.md");
4132
- if ((0, import_fs5.existsSync)(instructionsMd)) {
4168
+ const instructionsMd = (0, import_path4.join)(domainPath, "core", "instructions.md");
4169
+ if ((0, import_fs4.existsSync)(instructionsMd)) {
4133
4170
  const desc = extractFirstHeading(instructionsMd);
4134
4171
  domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/instructions.md`, `${desc} (start here)`, agent));
4135
4172
  }
4136
- const coreDir = (0, import_path5.join)(domainPath, "core");
4137
- if ((0, import_fs5.existsSync)(coreDir)) {
4173
+ const coreDir = (0, import_path4.join)(domainPath, "core");
4174
+ if ((0, import_fs4.existsSync)(coreDir)) {
4138
4175
  const coreFiles = collectMdFiles(coreDir).filter((f) => f !== "instructions.md" && !f.startsWith("instructions/"));
4139
4176
  for (const file of coreFiles) {
4140
- const desc = extractFirstHeading((0, import_path5.join)(coreDir, file));
4177
+ const desc = extractFirstHeading((0, import_path4.join)(coreDir, file));
4141
4178
  domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/${file}`, desc, agent));
4142
4179
  }
4143
4180
  }
4144
- const refDir = (0, import_path5.join)(domainPath, "reference");
4145
- if ((0, import_fs5.existsSync)(refDir)) {
4181
+ const refDir = (0, import_path4.join)(domainPath, "reference");
4182
+ if ((0, import_fs4.existsSync)(refDir)) {
4146
4183
  const refFiles = collectMdFiles(refDir);
4147
4184
  if (refFiles.length > 0) {
4148
4185
  domainFiles.push(``);
4149
4186
  domainFiles.push(`**Reference & cheat sheets:**`);
4150
4187
  for (const file of refFiles) {
4151
- const desc = extractFirstHeading((0, import_path5.join)(refDir, file));
4188
+ const desc = extractFirstHeading((0, import_path4.join)(refDir, file));
4152
4189
  domainFiles.push(formatPathRef(`_ai-context/${folderName}/reference/${file}`, desc, agent));
4153
4190
  }
4154
4191
  }
@@ -4165,6 +4202,21 @@ function buildContextRefsSection(domains, agent, contextsDir) {
4165
4202
  }
4166
4203
  return lines2.join("\n");
4167
4204
  }
4205
+ function buildMCPSection(mcpConfig) {
4206
+ const entries = Object.entries(mcpConfig);
4207
+ if (entries.length === 0) return "";
4208
+ const lines2 = [`## MCP Servers`, ``, `The following MCP servers are available. Use them proactively when relevant:`, ``];
4209
+ for (const [name, config] of entries) {
4210
+ const description = config.description;
4211
+ const useWhen = config.useWhen;
4212
+ if (!description) continue;
4213
+ lines2.push(`### ${name}`);
4214
+ lines2.push(description);
4215
+ if (useWhen) lines2.push(`**Use when**: ${useWhen}`);
4216
+ lines2.push(``);
4217
+ }
4218
+ return lines2.join("\n");
4219
+ }
4168
4220
  function buildFactorySection() {
4169
4221
  return [
4170
4222
  `## Factory Dev Workflow`,
@@ -4183,68 +4235,81 @@ function buildFactorySection() {
4183
4235
  ``
4184
4236
  ].join("\n");
4185
4237
  }
4186
- function buildCombinedInstructions(domains, mcpConfig, agent = "", projectPath = process.cwd(), factoryInstalled = false) {
4187
- const contextsDir = (0, import_path5.join)(projectPath, "_ai-context");
4188
- const lines2 = [
4189
- `# AI Development Instructions`,
4190
- ``,
4191
- `> Generated by One-Shot Installer`,
4192
- ``
4193
- ];
4238
+ function buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled) {
4239
+ const contextsDir = (0, import_path4.join)(projectPath, "_ai-context");
4240
+ const lines2 = [`# AI Development Instructions`, ``, `> Generated by One-Shot Installer`, ``];
4194
4241
  lines2.push(buildContextRefsSection(domains, agent, contextsDir));
4195
- if (mcpConfig && Object.keys(mcpConfig).length > 0) {
4196
- lines2.push(buildMCPSection(mcpConfig));
4197
- }
4198
- if (factoryInstalled) {
4199
- lines2.push(buildFactorySection());
4200
- }
4242
+ if (Object.keys(mcpConfig).length > 0) lines2.push(buildMCPSection(mcpConfig));
4243
+ if (factoryInstalled) lines2.push(buildFactorySection());
4201
4244
  return lines2.join("\n");
4202
4245
  }
4203
- async function setupInstructions(config) {
4204
- const { domains, agent, mcpConfig, projectPath = process.cwd(), factoryInstalled = false } = config;
4205
- switch (agent) {
4206
- case "claude-code": {
4207
- const content = buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled);
4208
- upsertBlock((0, import_path5.join)(projectPath, "CLAUDE.md"), content);
4209
- break;
4210
- }
4211
- case "github-copilot": {
4212
- const agentsDir = (0, import_path5.join)(projectPath, ".github", "agents");
4213
- if (!(0, import_fs5.existsSync)(agentsDir)) (0, import_fs5.mkdirSync)(agentsDir, { recursive: true });
4214
- const filePath = (0, import_path5.join)(agentsDir, "instructions.md");
4215
- if (!(0, import_fs5.existsSync)(filePath)) {
4216
- (0, import_fs5.writeFileSync)(filePath, `---
4217
- applyTo: "**"
4218
- ---
4219
4246
 
4220
- `, "utf-8");
4221
- }
4222
- const body = buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled);
4223
- upsertBlock(filePath, body);
4224
- break;
4247
+ // src/steps/setup/write-mcp-config.ts
4248
+ var import_fs5 = require("fs");
4249
+ var import_path5 = require("path");
4250
+ var red2 = (text) => `\x1B[31m${text}\x1B[0m`;
4251
+ var write_mcp_config_default = defineStep({
4252
+ name: "write-mcp-config",
4253
+ label: "Writing MCP configuration",
4254
+ when: (ctx) => (ctx.installed.domainsInstalled?.length ?? 0) > 0,
4255
+ execute: async (ctx) => {
4256
+ const filteredMcpConfig = getFilteredMcpConfig(ctx);
4257
+ const target = getAgentMCPTarget(ctx.config.agent);
4258
+ const mcpJsonPath = (0, import_path5.join)(ctx.config.projectPath, target.filePath);
4259
+ if (target.dir) {
4260
+ const dir = (0, import_path5.join)(ctx.config.projectPath, target.dir);
4261
+ if (!(0, import_fs5.existsSync)(dir)) (0, import_fs5.mkdirSync)(dir, { recursive: true });
4225
4262
  }
4226
- case "cursor": {
4227
- const cursorDir = (0, import_path5.join)(projectPath, ".cursor", "rules");
4228
- if (!(0, import_fs5.existsSync)(cursorDir)) (0, import_fs5.mkdirSync)(cursorDir, { recursive: true });
4229
- const filePath = (0, import_path5.join)(cursorDir, "instructions.mdc");
4230
- if (!(0, import_fs5.existsSync)(filePath)) {
4231
- (0, import_fs5.writeFileSync)(filePath, `---
4232
- description: AI development instructions from One-Shot Installer
4233
- alwaysApply: true
4234
- ---
4235
-
4236
- `, "utf-8");
4263
+ let existingFile = {};
4264
+ if ((0, import_fs5.existsSync)(mcpJsonPath)) {
4265
+ try {
4266
+ existingFile = JSON.parse((0, import_fs5.readFileSync)(mcpJsonPath, "utf-8"));
4267
+ } catch {
4268
+ const box = [
4269
+ "",
4270
+ 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"),
4271
+ red2(" \u2551 \u26A0 MCP CONFIG HAS INVALID JSON \u2551"),
4272
+ red2(" \u2551 \u2551"),
4273
+ red2(` \u2551 ${target.filePath} could not be parsed.`.padEnd(52) + "\u2551"),
4274
+ red2(" \u2551 Existing MCP servers may be lost. \u2551"),
4275
+ red2(" \u2551 Check the file after installation completes. \u2551"),
4276
+ 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"),
4277
+ ""
4278
+ ].join("\n");
4279
+ console.log(box);
4237
4280
  }
4238
- const body = buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled);
4239
- upsertBlock(filePath, body);
4240
- break;
4241
4281
  }
4282
+ const addedServers = [];
4283
+ const existingServers = existingFile[target.rootKey] ?? {};
4284
+ const newServers = {};
4285
+ for (const [serverName, serverConfig] of Object.entries(filteredMcpConfig)) {
4286
+ const { description, useWhen, active, ...mcpFields } = serverConfig;
4287
+ newServers[serverName] = mcpFields;
4288
+ addedServers.push(serverName);
4289
+ }
4290
+ const mergedServers = { ...existingServers, ...newServers };
4291
+ const outputFile = { ...existingFile, [target.rootKey]: mergedServers };
4292
+ (0, import_fs5.writeFileSync)(mcpJsonPath, JSON.stringify(outputFile, null, 2), "utf-8");
4293
+ ctx.installed.mcpServersAdded = addedServers;
4294
+ return {
4295
+ status: "success",
4296
+ message: addedServers.length > 0 ? addedServers.join(", ") : void 0
4297
+ };
4298
+ }
4299
+ });
4300
+ function getAgentMCPTarget(agent) {
4301
+ switch (agent) {
4302
+ case "claude-code":
4303
+ return { filePath: ".mcp.json", rootKey: "mcpServers", dir: null };
4304
+ case "cursor":
4305
+ return { filePath: ".cursor/mcp.json", rootKey: "mcpServers", dir: ".cursor" };
4306
+ case "github-copilot":
4242
4307
  default:
4243
- break;
4308
+ return { filePath: ".vscode/mcp.json", rootKey: "servers", dir: ".vscode" };
4244
4309
  }
4245
4310
  }
4246
4311
 
4247
- // src/utils/setup/setup-gitignore.ts
4312
+ // src/steps/setup/update-gitignore.ts
4248
4313
  var import_fs6 = require("fs");
4249
4314
  var import_path6 = require("path");
4250
4315
  var MARKER_START2 = "# one-shot-installer:start";
@@ -4276,47 +4341,93 @@ var GITIGNORE_ENTRIES = [
4276
4341
  "factory/summaries/",
4277
4342
  "factory/ai-context/"
4278
4343
  ];
4279
- function setupGitignore(projectPath) {
4280
- const gitignorePath = (0, import_path6.join)(projectPath, ".gitignore");
4281
- const block = [MARKER_START2, ...GITIGNORE_ENTRIES, MARKER_END2].join("\n");
4282
- if (!(0, import_fs6.existsSync)(gitignorePath)) {
4283
- (0, import_fs6.writeFileSync)(gitignorePath, block + "\n", "utf-8");
4284
- return;
4344
+ var update_gitignore_default = defineStep({
4345
+ name: "update-gitignore",
4346
+ label: "Updating .gitignore",
4347
+ execute: async (ctx) => {
4348
+ const gitignorePath = (0, import_path6.join)(ctx.config.projectPath, ".gitignore");
4349
+ const block = [MARKER_START2, ...GITIGNORE_ENTRIES, MARKER_END2].join("\n");
4350
+ if (!(0, import_fs6.existsSync)(gitignorePath)) {
4351
+ (0, import_fs6.writeFileSync)(gitignorePath, block + "\n", "utf-8");
4352
+ return { status: "success" };
4353
+ }
4354
+ const existing = (0, import_fs6.readFileSync)(gitignorePath, "utf-8");
4355
+ const start = existing.indexOf(MARKER_START2);
4356
+ const end = existing.indexOf(MARKER_END2);
4357
+ if (start !== -1 && end !== -1 && end > start) {
4358
+ const updated = existing.slice(0, start) + block + existing.slice(end + MARKER_END2.length);
4359
+ (0, import_fs6.writeFileSync)(gitignorePath, updated, "utf-8");
4360
+ } else {
4361
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
4362
+ (0, import_fs6.writeFileSync)(gitignorePath, existing + separator + block + "\n", "utf-8");
4363
+ }
4364
+ return { status: "success" };
4285
4365
  }
4286
- const existing = (0, import_fs6.readFileSync)(gitignorePath, "utf-8");
4287
- const start = existing.indexOf(MARKER_START2);
4288
- const end = existing.indexOf(MARKER_END2);
4289
- if (start !== -1 && end !== -1 && end > start) {
4290
- const updated = existing.slice(0, start) + block + existing.slice(end + MARKER_END2.length);
4291
- (0, import_fs6.writeFileSync)(gitignorePath, updated, "utf-8");
4292
- } else {
4293
- const separator = existing.endsWith("\n") ? "\n" : "\n\n";
4294
- (0, import_fs6.writeFileSync)(gitignorePath, existing + separator + block + "\n", "utf-8");
4366
+ });
4367
+
4368
+ // src/steps/setup/write-state.ts
4369
+ var import_fs7 = require("fs");
4370
+ var import_path7 = require("path");
4371
+ var import_os2 = require("os");
4372
+ var INSTALLER_VERSION = "0.5.9";
4373
+ var write_state_default = defineStep({
4374
+ name: "write-state",
4375
+ label: "Saving installation state",
4376
+ execute: async (ctx) => {
4377
+ const statePath = (0, import_path7.join)(ctx.config.projectPath, ".one-shot-state.json");
4378
+ const mcpServersAdded = ctx.installed.mcpServersAdded ?? [];
4379
+ const filteredMcpConfig = Object.fromEntries(
4380
+ Object.entries(ctx.config.mcpConfig).filter(([name]) => mcpServersAdded.includes(name))
4381
+ );
4382
+ const state = {
4383
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
4384
+ installerVersion: INSTALLER_VERSION,
4385
+ releaseVersion: ctx.config.releaseVersion,
4386
+ projectPath: ctx.config.projectPath,
4387
+ agent: ctx.config.agent,
4388
+ personas: ctx.config.personas.map((p) => p.id),
4389
+ domains: ctx.installed.domainsInstalled ?? [],
4390
+ mcpServers: mcpServersAdded,
4391
+ mcpConfigs: filteredMcpConfig,
4392
+ factoryInstalled: ctx.installed.factoryInstalled ?? false,
4393
+ files: {
4394
+ instructions: getInstructionFilePath2(ctx.config.agent),
4395
+ mcpConfig: getMCPConfigPath(ctx.config.agent),
4396
+ contexts: "_ai-context/",
4397
+ factory: ctx.installed.factoryInstalled ? "factory/" : null,
4398
+ globalConfig: (0, import_path7.join)((0, import_os2.homedir)(), ".one-shot-installer")
4399
+ }
4400
+ };
4401
+ (0, import_fs7.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf-8");
4402
+ return { status: "success" };
4403
+ }
4404
+ });
4405
+ function getInstructionFilePath2(agent) {
4406
+ switch (agent) {
4407
+ case "claude-code":
4408
+ return "CLAUDE.md";
4409
+ case "github-copilot":
4410
+ return ".github/agents/instructions.md";
4411
+ case "cursor":
4412
+ return ".cursor/rules/instructions.mdc";
4413
+ default:
4414
+ return "CLAUDE.md";
4295
4415
  }
4296
4416
  }
4297
-
4298
- // src/utils/mod.ts
4299
- async function loadWizardOptions(token, repo) {
4300
- const remote = await fetchWizardOptions(token, repo);
4301
- if (!remote) {
4302
- throw new Error("Failed to load configuration from GitHub release. Check your network and token.");
4417
+ function getMCPConfigPath(agent) {
4418
+ switch (agent) {
4419
+ case "claude-code":
4420
+ return ".mcp.json";
4421
+ case "cursor":
4422
+ return ".cursor/mcp.json";
4423
+ case "github-copilot":
4424
+ default:
4425
+ return ".vscode/mcp.json";
4303
4426
  }
4304
- const { options, releaseVersion } = remote;
4305
- const filteredMcpServers = Object.fromEntries(
4306
- Object.entries(options.mcpServers).filter(([, config]) => config.active !== false)
4307
- );
4308
- return {
4309
- personas: options.personas.personas.filter((p) => p.active !== false),
4310
- agents: options.agents.filter((a) => a.active !== false),
4311
- baseMcpServers: options.personas.baseMcpServers,
4312
- mcpServers: filteredMcpServers,
4313
- releaseVersion
4314
- };
4315
4427
  }
4316
4428
 
4317
4429
  // src/index.ts
4318
- var INSTALLER_VERSION = "0.5.7";
4319
- function getInstructionFilePath(agent) {
4430
+ function getInstructionFilePath3(agent) {
4320
4431
  switch (agent) {
4321
4432
  case "claude-code":
4322
4433
  return "CLAUDE.md";
@@ -4328,7 +4439,7 @@ function getInstructionFilePath(agent) {
4328
4439
  return "CLAUDE.md";
4329
4440
  }
4330
4441
  }
4331
- function getMCPConfigPath(agent) {
4442
+ function getMCPConfigPath2(agent) {
4332
4443
  switch (agent) {
4333
4444
  case "claude-code":
4334
4445
  return ".mcp.json";
@@ -4348,19 +4459,19 @@ var dim = (text) => `\x1B[2m${text}\x1B[0m`;
4348
4459
  var yellow = (text) => `\x1B[33m${text}\x1B[0m`;
4349
4460
  var green = (text) => `\x1B[32m${text}\x1B[0m`;
4350
4461
  function detectMarkerFileMode(filePath, markerStart) {
4351
- if (!(0, import_fs7.existsSync)(filePath)) return green("create");
4352
- const content = (0, import_fs7.readFileSync)(filePath, "utf-8");
4462
+ if (!(0, import_fs8.existsSync)(filePath)) return green("create");
4463
+ const content = (0, import_fs8.readFileSync)(filePath, "utf-8");
4353
4464
  if (content.includes(markerStart)) return yellow("update");
4354
4465
  return yellow("append");
4355
4466
  }
4356
4467
  function detectMCPFileMode(filePath) {
4357
- if (!(0, import_fs7.existsSync)(filePath)) return green("create");
4468
+ if (!(0, import_fs8.existsSync)(filePath)) return green("create");
4358
4469
  return yellow("merge");
4359
4470
  }
4360
4471
  function detectContextMode(projectPath, domain) {
4361
- const contextDir = (0, import_path7.join)(projectPath, "_ai-context", domain.toUpperCase());
4362
- const contextDirLower = (0, import_path7.join)(projectPath, "_ai-context", domain);
4363
- if ((0, import_fs7.existsSync)(contextDir) || (0, import_fs7.existsSync)(contextDirLower)) return yellow("overwrite");
4472
+ const contextDir = (0, import_path8.join)(projectPath, "_ai-context", domain.toUpperCase());
4473
+ const contextDirLower = (0, import_path8.join)(projectPath, "_ai-context", domain);
4474
+ if ((0, import_fs8.existsSync)(contextDir) || (0, import_fs8.existsSync)(contextDirLower)) return yellow("overwrite");
4364
4475
  return green("create");
4365
4476
  }
4366
4477
  function waitForEnter() {
@@ -4398,8 +4509,17 @@ async function main() {
4398
4509
  await waitForEnter();
4399
4510
  return;
4400
4511
  }
4401
- const result = await execute(config, token, repo, factoryRepo);
4402
- printSummary(result);
4512
+ const ctx = {
4513
+ config,
4514
+ token,
4515
+ repo,
4516
+ factoryRepo,
4517
+ installed: {}
4518
+ };
4519
+ const stepList = Object.values(steps_exports);
4520
+ console.log("");
4521
+ const result = await runPipeline(stepList, ctx);
4522
+ printSummary(result, config);
4403
4523
  return;
4404
4524
  } catch (error) {
4405
4525
  console.error("\n[ERROR]", error instanceof Error ? error.message : String(error));
@@ -4438,13 +4558,13 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
4438
4558
  }
4439
4559
  const projectInput = await esm_default4({
4440
4560
  message: "Project directory:",
4441
- default: (0, import_path7.resolve)(process.cwd())
4561
+ default: (0, import_path8.resolve)(process.cwd())
4442
4562
  });
4443
4563
  return {
4444
4564
  personas: selectedPersonas,
4445
4565
  agent,
4446
4566
  azureDevOpsOrg,
4447
- projectPath: (0, import_path7.resolve)(projectInput),
4567
+ projectPath: (0, import_path8.resolve)(projectInput),
4448
4568
  baseMcpServers: options.baseMcpServers,
4449
4569
  mcpConfig,
4450
4570
  releaseVersion,
@@ -4454,12 +4574,12 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
4454
4574
  async function previewAndConfirm(config, options) {
4455
4575
  const personaNames = config.personas.map((p) => p.name.replace(/ Developer$/, "")).join(", ");
4456
4576
  const agentDisplay = options.agents.find((a) => a.value === config.agent)?.name ?? config.agent;
4457
- const instructionFile = getInstructionFilePath(config.agent);
4458
- const mcpConfigFile = getMCPConfigPath(config.agent);
4577
+ const instructionFile = getInstructionFilePath3(config.agent);
4578
+ const mcpConfigFile = getMCPConfigPath2(config.agent);
4459
4579
  const serverEntries = Object.entries(config.mcpConfig);
4460
- const instructionFilePath = (0, import_path7.join)(config.projectPath, instructionFile);
4461
- const mcpConfigFilePath = (0, import_path7.join)(config.projectPath, mcpConfigFile);
4462
- const gitignorePath = (0, import_path7.join)(config.projectPath, ".gitignore");
4580
+ const instructionFilePath = (0, import_path8.join)(config.projectPath, instructionFile);
4581
+ const mcpConfigFilePath = (0, import_path8.join)(config.projectPath, mcpConfigFile);
4582
+ const gitignorePath = (0, import_path8.join)(config.projectPath, ".gitignore");
4463
4583
  const instructionMode = detectMarkerFileMode(instructionFilePath, "<!-- one-shot-installer:start -->");
4464
4584
  const mcpMode = detectMCPFileMode(mcpConfigFilePath);
4465
4585
  const gitignoreMode = detectMarkerFileMode(gitignorePath, "# one-shot-installer:start");
@@ -4485,7 +4605,7 @@ async function previewAndConfirm(config, options) {
4485
4605
  console.log(` ${mcpConfigFile.padEnd(domainColWidth + 14)}${mcpMode}`);
4486
4606
  console.log(` ${".gitignore".padEnd(domainColWidth + 14)}${gitignoreMode}`);
4487
4607
  if (config.installFactory) {
4488
- const factoryExists = (0, import_fs7.existsSync)((0, import_path7.join)(config.projectPath, "factory"));
4608
+ const factoryExists = (0, import_fs8.existsSync)((0, import_path8.join)(config.projectPath, "factory"));
4489
4609
  const factoryMode = factoryExists ? yellow("overwrite") : green("create");
4490
4610
  console.log(` ${"factory/".padEnd(domainColWidth + 14)}${factoryMode}`);
4491
4611
  }
@@ -4501,192 +4621,26 @@ async function previewAndConfirm(config, options) {
4501
4621
  console.log("\n" + "\u2500".repeat(48));
4502
4622
  return esm_default3({ message: "Proceed?", default: true });
4503
4623
  }
4504
- async function execute(config, token, repo, factoryRepo = null) {
4505
- const instructionFilePath = getInstructionFilePath(config.agent);
4506
- const mcpConfigPath = getMCPConfigPath(config.agent);
4507
- const result = {
4508
- success: false,
4509
- domainsInstalled: [],
4510
- domainsFailed: [],
4511
- failureReasons: {},
4512
- instructionsCreated: false,
4513
- instructionFilePath,
4514
- mcpConfigured: false,
4515
- mcpConfigPath,
4516
- mcpServersAdded: [],
4517
- gitignoreUpdated: false,
4518
- factoryInstalled: false
4519
- };
4520
- console.log("");
4521
- const uniqueDomains = [...new Set(config.personas.flatMap((p) => p.domains))];
4522
- const domainValues = uniqueDomains.map((d) => d.toLowerCase());
4523
- console.log(` Downloading contexts...`);
4524
- try {
4525
- const downloadResult = await fetchContexts({ domains: domainValues, token, repo, targetDir: config.projectPath });
4526
- result.domainsInstalled = downloadResult.successful;
4527
- result.domainsFailed = downloadResult.failed;
4528
- result.failureReasons = downloadResult.failureReasons;
4529
- } catch (error) {
4530
- result.domainsFailed = domainValues;
4531
- const reason = error instanceof Error ? error.message : String(error);
4532
- for (const d of domainValues) result.failureReasons[d] = reason;
4533
- }
4534
- const domainColWidth = Math.max(...domainValues.map((d) => d.length), 8) + 2;
4535
- for (const domain of domainValues) {
4536
- const ok = result.domainsInstalled.includes(domain);
4537
- const reason = result.failureReasons[domain];
4538
- const status = ok ? "\u2713" : `\u2717 ${reason ?? "Unknown error"}`;
4539
- console.log(` ${domain.padEnd(domainColWidth)}${status}`);
4540
- }
4541
- console.log("");
4542
- if (config.installFactory && factoryRepo) {
4543
- process.stdout.write(` Installing Factory framework... `);
4544
- try {
4545
- const factoryResult = await fetchFactory({ token, repo: factoryRepo, targetDir: config.projectPath });
4546
- if (factoryResult.success) {
4547
- result.factoryInstalled = true;
4548
- console.log("\u2713");
4549
- } else {
4550
- console.log("\u2717");
4551
- console.log(` ${factoryResult.failureReason}`);
4552
- }
4553
- } catch (error) {
4554
- console.log("\u2717");
4555
- console.log(` ${error instanceof Error ? error.message : String(error)}`);
4556
- }
4557
- }
4558
- if (result.domainsInstalled.length === 0) {
4559
- result.success = false;
4560
- return result;
4561
- }
4562
- const successfulDomains = new Set(result.domainsInstalled);
4563
- const successfulPersonas = config.personas.filter(
4564
- (p) => p.domains.every((d) => successfulDomains.has(d.toLowerCase()))
4565
- );
4566
- const successfulMcpServers = /* @__PURE__ */ new Set([
4567
- ...config.baseMcpServers,
4568
- ...successfulPersonas.flatMap((p) => p.mcpServers)
4569
- ]);
4570
- const filteredMcpConfig = Object.fromEntries(
4571
- Object.entries(config.mcpConfig).filter(([name]) => successfulMcpServers.has(name))
4572
- );
4573
- const statePath = (0, import_path7.join)(config.projectPath, ".one-shot-state.json");
4574
- const allDomains = result.domainsInstalled;
4575
- process.stdout.write(` Writing ${instructionFilePath}... `);
4576
- try {
4577
- await setupInstructions({
4578
- domains: allDomains,
4579
- agent: config.agent,
4580
- mcpConfig: filteredMcpConfig,
4581
- projectPath: config.projectPath,
4582
- factoryInstalled: result.factoryInstalled
4583
- });
4584
- result.instructionsCreated = true;
4585
- console.log("\u2713");
4586
- } catch (error) {
4587
- console.log("\u2717");
4588
- console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
4589
- console.log(` Path: ${instructionFilePath}`);
4590
- }
4591
- process.stdout.write(` Writing ${mcpConfigPath}... `);
4592
- try {
4593
- const mcpResult = await setupMCPConfiguration(
4594
- config.projectPath,
4595
- filteredMcpConfig,
4596
- config.agent
4597
- );
4598
- result.mcpConfigured = true;
4599
- result.mcpServersAdded = mcpResult.addedServers;
4600
- console.log("\u2713");
4601
- } catch (error) {
4602
- console.log("\u2717");
4603
- console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
4604
- console.log(` Path: ${mcpConfigPath}`);
4605
- }
4606
- process.stdout.write(` Updating .gitignore... `);
4607
- try {
4608
- setupGitignore(config.projectPath);
4609
- result.gitignoreUpdated = true;
4610
- console.log("\u2713");
4611
- } catch (error) {
4612
- console.log("\u2717");
4613
- console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
4614
- }
4615
- result.success = result.domainsFailed.length === 0 && result.instructionsCreated && result.mcpConfigured;
4616
- const allPersonas = config.personas.map((p) => p.id);
4617
- const state = {
4618
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
4619
- installerVersion: INSTALLER_VERSION,
4620
- releaseVersion: config.releaseVersion,
4621
- projectPath: config.projectPath,
4622
- agent: config.agent,
4623
- personas: allPersonas,
4624
- domains: allDomains,
4625
- mcpServers: Object.keys(filteredMcpConfig),
4626
- mcpConfigs: filteredMcpConfig,
4627
- factoryInstalled: result.factoryInstalled,
4628
- files: {
4629
- instructions: instructionFilePath,
4630
- mcpConfig: mcpConfigPath,
4631
- contexts: "_ai-context/",
4632
- factory: result.factoryInstalled ? "factory/" : null,
4633
- globalConfig: (0, import_path7.join)((0, import_os2.homedir)(), ".one-shot-installer")
4634
- }
4635
- };
4636
- try {
4637
- (0, import_fs7.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf-8");
4638
- } catch {
4639
- }
4640
- return result;
4641
- }
4642
- function printSummary(result) {
4643
- const hasErrors = !result.success;
4624
+ function printSummary(result, config) {
4625
+ const failed = result.entries.filter((e) => e.result.status === "failed");
4626
+ const succeeded = result.entries.filter((e) => e.result.status === "success");
4627
+ const hasErrors = failed.length > 0;
4644
4628
  console.log("\u2500".repeat(48));
4645
4629
  console.log(hasErrors ? " Done (with errors).\n" : " Done.\n");
4646
- if (result.domainsInstalled.length > 0) {
4647
- console.log(` _ai-context/ ${result.domainsInstalled.join(", ")}`);
4648
- }
4649
- if (result.instructionsCreated) {
4650
- console.log(` ${result.instructionFilePath} written`);
4651
- }
4652
- if (result.mcpConfigured) {
4653
- const serverList = result.mcpServersAdded.length > 0 ? ` (${result.mcpServersAdded.join(", ")})` : "";
4654
- console.log(` ${result.mcpConfigPath} written${serverList}`);
4655
- }
4656
- if (result.factoryInstalled) {
4657
- console.log(` Factory installed (.claude/ + factory/)`);
4658
- }
4659
- if (result.gitignoreUpdated) {
4660
- console.log(` .gitignore updated`);
4630
+ for (const entry of succeeded) {
4631
+ const msg = entry.result.message ? ` ${entry.result.message}` : "";
4632
+ console.log(` ${entry.label}${msg}`);
4661
4633
  }
4662
4634
  if (hasErrors) {
4663
4635
  console.log("\n What went wrong:");
4664
- if (result.domainsFailed.length > 0) {
4665
- console.log("");
4666
- console.log(" Context downloads failed:");
4667
- for (const domain of result.domainsFailed) {
4668
- const reason = result.failureReasons[domain];
4669
- console.log(` ${domain} \u2014 ${reason ?? "Unknown error"}`);
4670
- }
4671
- }
4672
- if (!result.instructionsCreated) {
4673
- console.log("");
4674
- console.log(` Instruction file not written: ${result.instructionFilePath}`);
4675
- }
4676
- if (!result.mcpConfigured) {
4677
- console.log("");
4678
- console.log(` MCP config not written: ${result.mcpConfigPath}`);
4636
+ for (const entry of failed) {
4637
+ console.log(` ${entry.label}: ${entry.result.detail ?? "Unknown error"}`);
4679
4638
  }
4680
4639
  console.log("\n Troubleshooting:");
4681
- if (result.domainsFailed.length > 0) {
4682
- console.log(" - Re-authenticate: delete ~/.one-shot-installer/github-token.json");
4683
- console.log(" - Check network connectivity");
4684
- console.log(" - Confirm your GitHub account has access to the repository");
4685
- }
4686
- if (!result.instructionsCreated || !result.mcpConfigured) {
4687
- console.log(" - Check write permissions for the project directory");
4688
- console.log(` - Project directory: ${result.instructionFilePath.includes("/") ? "check parent folders exist" : "current directory"}`);
4689
- }
4640
+ console.log(" - Re-authenticate: delete ~/.one-shot-installer/github-token.json");
4641
+ console.log(" - Check network connectivity");
4642
+ console.log(" - Confirm your GitHub account has access to the repository");
4643
+ console.log(" - Check write permissions for the project directory");
4690
4644
  }
4691
4645
  console.log("\n" + "\u2500".repeat(48) + "\n");
4692
4646
  }