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.
- package/bin/installer.js +497 -543
- 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
|
|
3247
|
+
var import_fs8 = require("fs");
|
|
3244
3248
|
var import_readline = require("readline");
|
|
3245
|
-
var
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
const
|
|
3551
|
-
|
|
3552
|
-
|
|
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
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
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
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3582
|
-
|
|
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 (
|
|
3585
|
-
|
|
3602
|
+
if (result.detail) {
|
|
3603
|
+
console.log(` ${result.detail}`);
|
|
3586
3604
|
}
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
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 ${
|
|
3702
|
+
"Authorization": `Bearer ${token}`
|
|
3607
3703
|
};
|
|
3608
|
-
const
|
|
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
|
-
|
|
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] =
|
|
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
|
-
}
|
|
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
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
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/
|
|
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
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
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
|
|
3741
|
-
|
|
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
|
|
3759
|
-
"Accept": "application/octet-stream",
|
|
3760
|
-
|
|
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/
|
|
3989
|
+
// src/steps/setup/write-instructions.ts
|
|
3932
3990
|
var import_fs4 = require("fs");
|
|
3933
3991
|
var import_path4 = require("path");
|
|
3934
|
-
|
|
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
|
|
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
|
|
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,
|
|
4017
|
-
(0,
|
|
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,
|
|
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,
|
|
4106
|
+
(0, import_fs4.writeFileSync)(filePath, updated, "utf-8");
|
|
4046
4107
|
} else {
|
|
4047
4108
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4048
|
-
(0,
|
|
4109
|
+
(0, import_fs4.writeFileSync)(filePath, existing + separator + marked, "utf-8");
|
|
4049
4110
|
}
|
|
4050
4111
|
}
|
|
4051
4112
|
function collectMdFiles(dir) {
|
|
4052
|
-
if (!(0,
|
|
4053
|
-
const entries = (0,
|
|
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,
|
|
4056
|
-
return entry.endsWith(".md") && (0,
|
|
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,
|
|
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
|
|
4073
|
-
|
|
4074
|
-
|
|
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
|
|
4137
|
+
return `- \`${contextPath}\` \u2014 ${description}`;
|
|
4093
4138
|
}
|
|
4094
4139
|
function resolveDomainFolder(domain, contextsDir) {
|
|
4095
|
-
if ((0,
|
|
4140
|
+
if ((0, import_fs4.existsSync)(contextsDir)) {
|
|
4096
4141
|
try {
|
|
4097
|
-
const entries = (0,
|
|
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,
|
|
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,
|
|
4161
|
+
if (!(0, import_fs4.existsSync)(domainPath)) continue;
|
|
4125
4162
|
const domainFiles = [];
|
|
4126
|
-
const ctxInstructions = (0,
|
|
4127
|
-
if ((0,
|
|
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,
|
|
4132
|
-
if ((0,
|
|
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,
|
|
4137
|
-
if ((0,
|
|
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,
|
|
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,
|
|
4145
|
-
if ((0,
|
|
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,
|
|
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
|
|
4187
|
-
const contextsDir = (0,
|
|
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 (
|
|
4196
|
-
|
|
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
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
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
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
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
|
-
|
|
4308
|
+
return { filePath: ".vscode/mcp.json", rootKey: "servers", dir: ".vscode" };
|
|
4244
4309
|
}
|
|
4245
4310
|
}
|
|
4246
4311
|
|
|
4247
|
-
// src/
|
|
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
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
(0,
|
|
4284
|
-
|
|
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
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
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
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
4352
|
-
const content = (0,
|
|
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,
|
|
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,
|
|
4362
|
-
const contextDirLower = (0,
|
|
4363
|
-
if ((0,
|
|
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
|
|
4402
|
-
|
|
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,
|
|
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,
|
|
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 =
|
|
4458
|
-
const mcpConfigFile =
|
|
4577
|
+
const instructionFile = getInstructionFilePath3(config.agent);
|
|
4578
|
+
const mcpConfigFile = getMCPConfigPath2(config.agent);
|
|
4459
4579
|
const serverEntries = Object.entries(config.mcpConfig);
|
|
4460
|
-
const instructionFilePath = (0,
|
|
4461
|
-
const mcpConfigFilePath = (0,
|
|
4462
|
-
const gitignorePath = (0,
|
|
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,
|
|
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
|
-
|
|
4505
|
-
const
|
|
4506
|
-
const
|
|
4507
|
-
const
|
|
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
|
-
|
|
4647
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
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
|
}
|