dlw-machine-setup 0.5.7 → 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 +510 -545
- 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;
|
|
@@ -3845,6 +3911,13 @@ function configureSettings(claudeDir) {
|
|
|
3845
3911
|
}
|
|
3846
3912
|
}
|
|
3847
3913
|
if (!settings.hooks) settings.hooks = {};
|
|
3914
|
+
const welcomeHook = {
|
|
3915
|
+
hooks: [{ type: "command", command: "node .claude/hooks/factory-welcome.cjs" }]
|
|
3916
|
+
};
|
|
3917
|
+
const immutableGuardHook = {
|
|
3918
|
+
matcher: "Write|Edit",
|
|
3919
|
+
hooks: [{ type: "command", command: "node .claude/hooks/factory-immutable-guard.cjs" }]
|
|
3920
|
+
};
|
|
3848
3921
|
const stateAdvanceHook = {
|
|
3849
3922
|
matcher: "Write|Edit",
|
|
3850
3923
|
hooks: [{ type: "command", command: "node .claude/hooks/factory-state-advance.cjs" }]
|
|
@@ -3855,16 +3928,20 @@ function configureSettings(claudeDir) {
|
|
|
3855
3928
|
const stateGuardHook = {
|
|
3856
3929
|
hooks: [{ type: "command", command: "node .claude/hooks/factory-state-guard.cjs" }]
|
|
3857
3930
|
};
|
|
3858
|
-
for (const event of ["PostToolUse", "UserPromptSubmit"]) {
|
|
3931
|
+
for (const event of ["SessionStart", "PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
3859
3932
|
if (settings.hooks[event]) {
|
|
3860
3933
|
settings.hooks[event] = settings.hooks[event].filter(
|
|
3861
3934
|
(h) => !h.hooks?.some(
|
|
3862
|
-
(hh) => hh.command?.includes("factory-state-advance") || hh.command?.includes("factory-context-monitor") || hh.command?.includes("factory-state-guard")
|
|
3935
|
+
(hh) => hh.command?.includes("factory-welcome") || hh.command?.includes("factory-immutable-guard") || hh.command?.includes("factory-state-advance") || hh.command?.includes("factory-context-monitor") || hh.command?.includes("factory-state-guard")
|
|
3863
3936
|
)
|
|
3864
3937
|
);
|
|
3865
3938
|
if (settings.hooks[event].length === 0) delete settings.hooks[event];
|
|
3866
3939
|
}
|
|
3867
3940
|
}
|
|
3941
|
+
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
3942
|
+
settings.hooks.SessionStart.unshift(welcomeHook);
|
|
3943
|
+
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
3944
|
+
settings.hooks.PreToolUse.unshift(immutableGuardHook);
|
|
3868
3945
|
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
3869
3946
|
settings.hooks.PostToolUse.unshift(stateAdvanceHook);
|
|
3870
3947
|
if (!settings.hooks.UserPromptSubmit) {
|
|
@@ -3880,31 +3957,23 @@ function configureSettings(claudeDir) {
|
|
|
3880
3957
|
if (!hasMonitor) settings.hooks.UserPromptSubmit.push(contextMonitorHook);
|
|
3881
3958
|
}
|
|
3882
3959
|
settings.statusLine = { type: "command", command: "node .claude/hooks/factory-statusline.cjs" };
|
|
3883
|
-
if (!(0, import_fs3.existsSync)(claudeDir)) {
|
|
3884
|
-
(0, import_fs3.mkdirSync)(claudeDir, { recursive: true });
|
|
3885
|
-
}
|
|
3960
|
+
if (!(0, import_fs3.existsSync)(claudeDir)) (0, import_fs3.mkdirSync)(claudeDir, { recursive: true });
|
|
3886
3961
|
(0, import_fs3.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
3887
3962
|
}
|
|
3888
3963
|
function copyAgentStubs(source, target) {
|
|
3889
|
-
if (!(0, import_fs3.existsSync)(target)) {
|
|
3890
|
-
(0, import_fs3.mkdirSync)(target, { recursive: true });
|
|
3891
|
-
}
|
|
3964
|
+
if (!(0, import_fs3.existsSync)(target)) (0, import_fs3.mkdirSync)(target, { recursive: true });
|
|
3892
3965
|
const entries = (0, import_fs3.readdirSync)(source, { withFileTypes: true });
|
|
3893
3966
|
for (const entry of entries) {
|
|
3894
|
-
if (entry.isDirectory()) continue;
|
|
3895
|
-
if (!entry.name.endsWith(".md")) continue;
|
|
3967
|
+
if (entry.isDirectory() || !entry.name.endsWith(".md")) continue;
|
|
3896
3968
|
const content = (0, import_fs3.readFileSync)((0, import_path3.join)(source, entry.name), "utf8");
|
|
3897
3969
|
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?\r?\n)---/);
|
|
3898
3970
|
if (frontmatterMatch) {
|
|
3899
3971
|
(0, import_fs3.writeFileSync)((0, import_path3.join)(target, entry.name), frontmatterMatch[0] + "\n");
|
|
3900
|
-
} else {
|
|
3901
3972
|
}
|
|
3902
3973
|
}
|
|
3903
3974
|
}
|
|
3904
3975
|
function copyDirectory2(source, target) {
|
|
3905
|
-
if (!(0, import_fs3.existsSync)(target)) {
|
|
3906
|
-
(0, import_fs3.mkdirSync)(target, { recursive: true });
|
|
3907
|
-
}
|
|
3976
|
+
if (!(0, import_fs3.existsSync)(target)) (0, import_fs3.mkdirSync)(target, { recursive: true });
|
|
3908
3977
|
const entries = (0, import_fs3.readdirSync)(source, { withFileTypes: true });
|
|
3909
3978
|
for (const entry of entries) {
|
|
3910
3979
|
const sourcePath = (0, import_path3.join)(source, entry.name);
|
|
@@ -3917,96 +3986,99 @@ function copyDirectory2(source, target) {
|
|
|
3917
3986
|
}
|
|
3918
3987
|
}
|
|
3919
3988
|
|
|
3920
|
-
// src/
|
|
3989
|
+
// src/steps/setup/write-instructions.ts
|
|
3921
3990
|
var import_fs4 = require("fs");
|
|
3922
3991
|
var import_path4 = require("path");
|
|
3923
|
-
|
|
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) {
|
|
3924
4062
|
switch (agent) {
|
|
3925
4063
|
case "claude-code":
|
|
3926
|
-
return
|
|
3927
|
-
case "cursor":
|
|
3928
|
-
return { filePath: ".cursor/mcp.json", rootKey: "mcpServers", dir: ".cursor" };
|
|
4064
|
+
return "CLAUDE.md";
|
|
3929
4065
|
case "github-copilot":
|
|
4066
|
+
return ".github/agents/instructions.md";
|
|
4067
|
+
case "cursor":
|
|
4068
|
+
return ".cursor/rules/instructions.mdc";
|
|
3930
4069
|
default:
|
|
3931
|
-
return
|
|
3932
|
-
}
|
|
3933
|
-
}
|
|
3934
|
-
async function setupMCPConfiguration(projectPath, mcpConfig, agent) {
|
|
3935
|
-
const target = getAgentMCPTarget(agent);
|
|
3936
|
-
const mcpJsonPath = (0, import_path4.join)(projectPath, target.filePath);
|
|
3937
|
-
if (target.dir) {
|
|
3938
|
-
const dir = (0, import_path4.join)(projectPath, target.dir);
|
|
3939
|
-
if (!(0, import_fs4.existsSync)(dir)) {
|
|
3940
|
-
(0, import_fs4.mkdirSync)(dir, { recursive: true });
|
|
3941
|
-
}
|
|
3942
|
-
}
|
|
3943
|
-
const red2 = (text) => `\x1B[31m${text}\x1B[0m`;
|
|
3944
|
-
let existingFile = {};
|
|
3945
|
-
if ((0, import_fs4.existsSync)(mcpJsonPath)) {
|
|
3946
|
-
try {
|
|
3947
|
-
const content = (0, import_fs4.readFileSync)(mcpJsonPath, "utf-8");
|
|
3948
|
-
existingFile = JSON.parse(content);
|
|
3949
|
-
} catch {
|
|
3950
|
-
const box = [
|
|
3951
|
-
"",
|
|
3952
|
-
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"),
|
|
3953
|
-
red2(" \u2551 \u26A0 MCP CONFIG HAS INVALID JSON \u2551"),
|
|
3954
|
-
red2(" \u2551 \u2551"),
|
|
3955
|
-
red2(` \u2551 ${target.filePath} could not be parsed.`.padEnd(52) + "\u2551"),
|
|
3956
|
-
red2(" \u2551 Existing MCP servers may be lost. \u2551"),
|
|
3957
|
-
red2(" \u2551 Check the file after installation completes. \u2551"),
|
|
3958
|
-
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"),
|
|
3959
|
-
""
|
|
3960
|
-
].join("\n");
|
|
3961
|
-
console.log(box);
|
|
3962
|
-
}
|
|
3963
|
-
}
|
|
3964
|
-
const addedServers = [];
|
|
3965
|
-
const skippedServers = [];
|
|
3966
|
-
const existingServers = existingFile[target.rootKey] ?? {};
|
|
3967
|
-
const newServers = {};
|
|
3968
|
-
for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {
|
|
3969
|
-
const { description, useWhen, active, ...mcpFields } = serverConfig;
|
|
3970
|
-
newServers[serverName] = mcpFields;
|
|
3971
|
-
addedServers.push(serverName);
|
|
3972
|
-
}
|
|
3973
|
-
const mergedServers = { ...existingServers, ...newServers };
|
|
3974
|
-
const outputFile = { ...existingFile, [target.rootKey]: mergedServers };
|
|
3975
|
-
(0, import_fs4.writeFileSync)(mcpJsonPath, JSON.stringify(outputFile, null, 2), "utf-8");
|
|
3976
|
-
return { addedServers, skippedServers };
|
|
3977
|
-
}
|
|
3978
|
-
function buildMCPConfiguration(selectedItems, baseMcpServers, allMcpServers) {
|
|
3979
|
-
const config = {};
|
|
3980
|
-
for (const serverName of baseMcpServers) {
|
|
3981
|
-
if (allMcpServers[serverName]) {
|
|
3982
|
-
config[serverName] = allMcpServers[serverName];
|
|
3983
|
-
}
|
|
3984
|
-
}
|
|
3985
|
-
for (const item of selectedItems) {
|
|
3986
|
-
for (const serverName of item.mcpServers) {
|
|
3987
|
-
if (allMcpServers[serverName] && !config[serverName]) {
|
|
3988
|
-
config[serverName] = allMcpServers[serverName];
|
|
3989
|
-
}
|
|
3990
|
-
}
|
|
4070
|
+
return "CLAUDE.md";
|
|
3991
4071
|
}
|
|
3992
|
-
return config;
|
|
3993
4072
|
}
|
|
3994
|
-
|
|
3995
|
-
// src/utils/setup/setup-instructions.ts
|
|
3996
|
-
var import_fs5 = require("fs");
|
|
3997
|
-
var import_path5 = require("path");
|
|
3998
|
-
var MARKER_START = "<!-- one-shot-installer:start -->";
|
|
3999
|
-
var MARKER_END = "<!-- one-shot-installer:end -->";
|
|
4000
|
-
var red = (text) => `\x1B[31m${text}\x1B[0m`;
|
|
4001
4073
|
function upsertBlock(filePath, block) {
|
|
4002
4074
|
const marked = `${MARKER_START}
|
|
4003
4075
|
${block}
|
|
4004
4076
|
${MARKER_END}`;
|
|
4005
|
-
if (!(0,
|
|
4006
|
-
(0,
|
|
4077
|
+
if (!(0, import_fs4.existsSync)(filePath)) {
|
|
4078
|
+
(0, import_fs4.writeFileSync)(filePath, marked, "utf-8");
|
|
4007
4079
|
return;
|
|
4008
4080
|
}
|
|
4009
|
-
const existing = (0,
|
|
4081
|
+
const existing = (0, import_fs4.readFileSync)(filePath, "utf-8");
|
|
4010
4082
|
const start = existing.indexOf(MARKER_START);
|
|
4011
4083
|
const end = existing.indexOf(MARKER_END);
|
|
4012
4084
|
const hasStart = start !== -1;
|
|
@@ -4031,23 +4103,23 @@ ${MARKER_END}`;
|
|
|
4031
4103
|
}
|
|
4032
4104
|
if (hasStart && hasEnd) {
|
|
4033
4105
|
const updated = existing.slice(0, start) + marked + existing.slice(end + MARKER_END.length);
|
|
4034
|
-
(0,
|
|
4106
|
+
(0, import_fs4.writeFileSync)(filePath, updated, "utf-8");
|
|
4035
4107
|
} else {
|
|
4036
4108
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4037
|
-
(0,
|
|
4109
|
+
(0, import_fs4.writeFileSync)(filePath, existing + separator + marked, "utf-8");
|
|
4038
4110
|
}
|
|
4039
4111
|
}
|
|
4040
4112
|
function collectMdFiles(dir) {
|
|
4041
|
-
if (!(0,
|
|
4042
|
-
const entries = (0,
|
|
4113
|
+
if (!(0, import_fs4.existsSync)(dir)) return [];
|
|
4114
|
+
const entries = (0, import_fs4.readdirSync)(dir, { recursive: true });
|
|
4043
4115
|
return entries.filter((entry) => {
|
|
4044
|
-
const fullPath = (0,
|
|
4045
|
-
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();
|
|
4046
4118
|
}).map((entry) => entry.replace(/\\/g, "/")).sort();
|
|
4047
4119
|
}
|
|
4048
4120
|
function extractFirstHeading(filePath) {
|
|
4049
4121
|
try {
|
|
4050
|
-
const content = (0,
|
|
4122
|
+
const content = (0, import_fs4.readFileSync)(filePath, "utf-8");
|
|
4051
4123
|
const withoutFrontmatter = content.replace(/^---[\s\S]*?---\s*/, "");
|
|
4052
4124
|
const match = withoutFrontmatter.match(/^#\s+(.+)/m);
|
|
4053
4125
|
if (match) {
|
|
@@ -4058,47 +4130,23 @@ function extractFirstHeading(filePath) {
|
|
|
4058
4130
|
const basename = filePath.split(/[/\\]/).pop() ?? "";
|
|
4059
4131
|
return basename.replace(/\.md$/i, "").replace(/[-_]+/g, " ");
|
|
4060
4132
|
}
|
|
4061
|
-
function
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
const lines2 = [
|
|
4065
|
-
`## MCP Servers`,
|
|
4066
|
-
``,
|
|
4067
|
-
`The following MCP servers are available. Use them proactively when relevant:`,
|
|
4068
|
-
``
|
|
4069
|
-
];
|
|
4070
|
-
for (const [name, config] of entries) {
|
|
4071
|
-
const description = config.description;
|
|
4072
|
-
const useWhen = config.useWhen;
|
|
4073
|
-
if (!description) continue;
|
|
4074
|
-
lines2.push(`### ${name}`);
|
|
4075
|
-
lines2.push(description);
|
|
4076
|
-
if (useWhen) {
|
|
4077
|
-
lines2.push(`**Use when**: ${useWhen}`);
|
|
4078
|
-
}
|
|
4079
|
-
lines2.push(``);
|
|
4133
|
+
function formatPathRef(contextPath, description, agent) {
|
|
4134
|
+
if (agent === "github-copilot") {
|
|
4135
|
+
return `- [${contextPath}](../../${contextPath}) \u2014 ${description}`;
|
|
4080
4136
|
}
|
|
4081
|
-
return
|
|
4137
|
+
return `- \`${contextPath}\` \u2014 ${description}`;
|
|
4082
4138
|
}
|
|
4083
4139
|
function resolveDomainFolder(domain, contextsDir) {
|
|
4084
|
-
if ((0,
|
|
4140
|
+
if ((0, import_fs4.existsSync)(contextsDir)) {
|
|
4085
4141
|
try {
|
|
4086
|
-
const entries = (0,
|
|
4142
|
+
const entries = (0, import_fs4.readdirSync)(contextsDir);
|
|
4087
4143
|
const match = entries.find((e) => e.toLowerCase() === domain.toLowerCase());
|
|
4088
|
-
if (match) {
|
|
4089
|
-
return { folderName: match, folderPath: (0, import_path5.join)(contextsDir, match) };
|
|
4090
|
-
}
|
|
4144
|
+
if (match) return { folderName: match, folderPath: (0, import_path4.join)(contextsDir, match) };
|
|
4091
4145
|
} catch {
|
|
4092
4146
|
}
|
|
4093
4147
|
}
|
|
4094
4148
|
const fallback = domain.toUpperCase();
|
|
4095
|
-
return { folderName: fallback, folderPath: (0,
|
|
4096
|
-
}
|
|
4097
|
-
function formatPathRef(contextPath, description, agent) {
|
|
4098
|
-
if (agent === "github-copilot") {
|
|
4099
|
-
return `- [${contextPath}](../../${contextPath}) \u2014 ${description}`;
|
|
4100
|
-
}
|
|
4101
|
-
return `- \`${contextPath}\` \u2014 ${description}`;
|
|
4149
|
+
return { folderName: fallback, folderPath: (0, import_path4.join)(contextsDir, fallback) };
|
|
4102
4150
|
}
|
|
4103
4151
|
function buildContextRefsSection(domains, agent, contextsDir) {
|
|
4104
4152
|
const lines2 = [
|
|
@@ -4110,34 +4158,34 @@ function buildContextRefsSection(domains, agent, contextsDir) {
|
|
|
4110
4158
|
let hasAnyFiles = false;
|
|
4111
4159
|
for (const domain of domains) {
|
|
4112
4160
|
const { folderName, folderPath: domainPath } = resolveDomainFolder(domain, contextsDir);
|
|
4113
|
-
if (!(0,
|
|
4161
|
+
if (!(0, import_fs4.existsSync)(domainPath)) continue;
|
|
4114
4162
|
const domainFiles = [];
|
|
4115
|
-
const ctxInstructions = (0,
|
|
4116
|
-
if ((0,
|
|
4163
|
+
const ctxInstructions = (0, import_path4.join)(domainPath, "context-instructions.md");
|
|
4164
|
+
if ((0, import_fs4.existsSync)(ctxInstructions)) {
|
|
4117
4165
|
const desc = extractFirstHeading(ctxInstructions);
|
|
4118
4166
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/context-instructions.md`, desc, agent));
|
|
4119
4167
|
}
|
|
4120
|
-
const instructionsMd = (0,
|
|
4121
|
-
if ((0,
|
|
4168
|
+
const instructionsMd = (0, import_path4.join)(domainPath, "core", "instructions.md");
|
|
4169
|
+
if ((0, import_fs4.existsSync)(instructionsMd)) {
|
|
4122
4170
|
const desc = extractFirstHeading(instructionsMd);
|
|
4123
4171
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/instructions.md`, `${desc} (start here)`, agent));
|
|
4124
4172
|
}
|
|
4125
|
-
const coreDir = (0,
|
|
4126
|
-
if ((0,
|
|
4173
|
+
const coreDir = (0, import_path4.join)(domainPath, "core");
|
|
4174
|
+
if ((0, import_fs4.existsSync)(coreDir)) {
|
|
4127
4175
|
const coreFiles = collectMdFiles(coreDir).filter((f) => f !== "instructions.md" && !f.startsWith("instructions/"));
|
|
4128
4176
|
for (const file of coreFiles) {
|
|
4129
|
-
const desc = extractFirstHeading((0,
|
|
4177
|
+
const desc = extractFirstHeading((0, import_path4.join)(coreDir, file));
|
|
4130
4178
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/${file}`, desc, agent));
|
|
4131
4179
|
}
|
|
4132
4180
|
}
|
|
4133
|
-
const refDir = (0,
|
|
4134
|
-
if ((0,
|
|
4181
|
+
const refDir = (0, import_path4.join)(domainPath, "reference");
|
|
4182
|
+
if ((0, import_fs4.existsSync)(refDir)) {
|
|
4135
4183
|
const refFiles = collectMdFiles(refDir);
|
|
4136
4184
|
if (refFiles.length > 0) {
|
|
4137
4185
|
domainFiles.push(``);
|
|
4138
4186
|
domainFiles.push(`**Reference & cheat sheets:**`);
|
|
4139
4187
|
for (const file of refFiles) {
|
|
4140
|
-
const desc = extractFirstHeading((0,
|
|
4188
|
+
const desc = extractFirstHeading((0, import_path4.join)(refDir, file));
|
|
4141
4189
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/reference/${file}`, desc, agent));
|
|
4142
4190
|
}
|
|
4143
4191
|
}
|
|
@@ -4154,6 +4202,21 @@ function buildContextRefsSection(domains, agent, contextsDir) {
|
|
|
4154
4202
|
}
|
|
4155
4203
|
return lines2.join("\n");
|
|
4156
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
|
+
}
|
|
4157
4220
|
function buildFactorySection() {
|
|
4158
4221
|
return [
|
|
4159
4222
|
`## Factory Dev Workflow`,
|
|
@@ -4172,68 +4235,81 @@ function buildFactorySection() {
|
|
|
4172
4235
|
``
|
|
4173
4236
|
].join("\n");
|
|
4174
4237
|
}
|
|
4175
|
-
function buildCombinedInstructions(domains, mcpConfig, agent
|
|
4176
|
-
const contextsDir = (0,
|
|
4177
|
-
const lines2 = [
|
|
4178
|
-
`# AI Development Instructions`,
|
|
4179
|
-
``,
|
|
4180
|
-
`> Generated by One-Shot Installer`,
|
|
4181
|
-
``
|
|
4182
|
-
];
|
|
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`, ``];
|
|
4183
4241
|
lines2.push(buildContextRefsSection(domains, agent, contextsDir));
|
|
4184
|
-
if (
|
|
4185
|
-
|
|
4186
|
-
}
|
|
4187
|
-
if (factoryInstalled) {
|
|
4188
|
-
lines2.push(buildFactorySection());
|
|
4189
|
-
}
|
|
4242
|
+
if (Object.keys(mcpConfig).length > 0) lines2.push(buildMCPSection(mcpConfig));
|
|
4243
|
+
if (factoryInstalled) lines2.push(buildFactorySection());
|
|
4190
4244
|
return lines2.join("\n");
|
|
4191
4245
|
}
|
|
4192
|
-
async function setupInstructions(config) {
|
|
4193
|
-
const { domains, agent, mcpConfig, projectPath = process.cwd(), factoryInstalled = false } = config;
|
|
4194
|
-
switch (agent) {
|
|
4195
|
-
case "claude-code": {
|
|
4196
|
-
const content = buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled);
|
|
4197
|
-
upsertBlock((0, import_path5.join)(projectPath, "CLAUDE.md"), content);
|
|
4198
|
-
break;
|
|
4199
|
-
}
|
|
4200
|
-
case "github-copilot": {
|
|
4201
|
-
const agentsDir = (0, import_path5.join)(projectPath, ".github", "agents");
|
|
4202
|
-
if (!(0, import_fs5.existsSync)(agentsDir)) (0, import_fs5.mkdirSync)(agentsDir, { recursive: true });
|
|
4203
|
-
const filePath = (0, import_path5.join)(agentsDir, "instructions.md");
|
|
4204
|
-
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
4205
|
-
(0, import_fs5.writeFileSync)(filePath, `---
|
|
4206
|
-
applyTo: "**"
|
|
4207
|
-
---
|
|
4208
4246
|
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
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 });
|
|
4214
4262
|
}
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
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);
|
|
4226
4280
|
}
|
|
4227
|
-
const body = buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled);
|
|
4228
|
-
upsertBlock(filePath, body);
|
|
4229
|
-
break;
|
|
4230
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":
|
|
4231
4307
|
default:
|
|
4232
|
-
|
|
4308
|
+
return { filePath: ".vscode/mcp.json", rootKey: "servers", dir: ".vscode" };
|
|
4233
4309
|
}
|
|
4234
4310
|
}
|
|
4235
4311
|
|
|
4236
|
-
// src/
|
|
4312
|
+
// src/steps/setup/update-gitignore.ts
|
|
4237
4313
|
var import_fs6 = require("fs");
|
|
4238
4314
|
var import_path6 = require("path");
|
|
4239
4315
|
var MARKER_START2 = "# one-shot-installer:start";
|
|
@@ -4265,47 +4341,93 @@ var GITIGNORE_ENTRIES = [
|
|
|
4265
4341
|
"factory/summaries/",
|
|
4266
4342
|
"factory/ai-context/"
|
|
4267
4343
|
];
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
(0,
|
|
4273
|
-
|
|
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" };
|
|
4274
4365
|
}
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
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";
|
|
4284
4415
|
}
|
|
4285
4416
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
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";
|
|
4292
4426
|
}
|
|
4293
|
-
const { options, releaseVersion } = remote;
|
|
4294
|
-
const filteredMcpServers = Object.fromEntries(
|
|
4295
|
-
Object.entries(options.mcpServers).filter(([, config]) => config.active !== false)
|
|
4296
|
-
);
|
|
4297
|
-
return {
|
|
4298
|
-
personas: options.personas.personas.filter((p) => p.active !== false),
|
|
4299
|
-
agents: options.agents.filter((a) => a.active !== false),
|
|
4300
|
-
baseMcpServers: options.personas.baseMcpServers,
|
|
4301
|
-
mcpServers: filteredMcpServers,
|
|
4302
|
-
releaseVersion
|
|
4303
|
-
};
|
|
4304
4427
|
}
|
|
4305
4428
|
|
|
4306
4429
|
// src/index.ts
|
|
4307
|
-
|
|
4308
|
-
function getInstructionFilePath(agent) {
|
|
4430
|
+
function getInstructionFilePath3(agent) {
|
|
4309
4431
|
switch (agent) {
|
|
4310
4432
|
case "claude-code":
|
|
4311
4433
|
return "CLAUDE.md";
|
|
@@ -4317,7 +4439,7 @@ function getInstructionFilePath(agent) {
|
|
|
4317
4439
|
return "CLAUDE.md";
|
|
4318
4440
|
}
|
|
4319
4441
|
}
|
|
4320
|
-
function
|
|
4442
|
+
function getMCPConfigPath2(agent) {
|
|
4321
4443
|
switch (agent) {
|
|
4322
4444
|
case "claude-code":
|
|
4323
4445
|
return ".mcp.json";
|
|
@@ -4337,19 +4459,19 @@ var dim = (text) => `\x1B[2m${text}\x1B[0m`;
|
|
|
4337
4459
|
var yellow = (text) => `\x1B[33m${text}\x1B[0m`;
|
|
4338
4460
|
var green = (text) => `\x1B[32m${text}\x1B[0m`;
|
|
4339
4461
|
function detectMarkerFileMode(filePath, markerStart) {
|
|
4340
|
-
if (!(0,
|
|
4341
|
-
const content = (0,
|
|
4462
|
+
if (!(0, import_fs8.existsSync)(filePath)) return green("create");
|
|
4463
|
+
const content = (0, import_fs8.readFileSync)(filePath, "utf-8");
|
|
4342
4464
|
if (content.includes(markerStart)) return yellow("update");
|
|
4343
4465
|
return yellow("append");
|
|
4344
4466
|
}
|
|
4345
4467
|
function detectMCPFileMode(filePath) {
|
|
4346
|
-
if (!(0,
|
|
4468
|
+
if (!(0, import_fs8.existsSync)(filePath)) return green("create");
|
|
4347
4469
|
return yellow("merge");
|
|
4348
4470
|
}
|
|
4349
4471
|
function detectContextMode(projectPath, domain) {
|
|
4350
|
-
const contextDir = (0,
|
|
4351
|
-
const contextDirLower = (0,
|
|
4352
|
-
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");
|
|
4353
4475
|
return green("create");
|
|
4354
4476
|
}
|
|
4355
4477
|
function waitForEnter() {
|
|
@@ -4387,8 +4509,17 @@ async function main() {
|
|
|
4387
4509
|
await waitForEnter();
|
|
4388
4510
|
return;
|
|
4389
4511
|
}
|
|
4390
|
-
const
|
|
4391
|
-
|
|
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);
|
|
4392
4523
|
return;
|
|
4393
4524
|
} catch (error) {
|
|
4394
4525
|
console.error("\n[ERROR]", error instanceof Error ? error.message : String(error));
|
|
@@ -4427,13 +4558,13 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
|
|
|
4427
4558
|
}
|
|
4428
4559
|
const projectInput = await esm_default4({
|
|
4429
4560
|
message: "Project directory:",
|
|
4430
|
-
default: (0,
|
|
4561
|
+
default: (0, import_path8.resolve)(process.cwd())
|
|
4431
4562
|
});
|
|
4432
4563
|
return {
|
|
4433
4564
|
personas: selectedPersonas,
|
|
4434
4565
|
agent,
|
|
4435
4566
|
azureDevOpsOrg,
|
|
4436
|
-
projectPath: (0,
|
|
4567
|
+
projectPath: (0, import_path8.resolve)(projectInput),
|
|
4437
4568
|
baseMcpServers: options.baseMcpServers,
|
|
4438
4569
|
mcpConfig,
|
|
4439
4570
|
releaseVersion,
|
|
@@ -4443,12 +4574,12 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
|
|
|
4443
4574
|
async function previewAndConfirm(config, options) {
|
|
4444
4575
|
const personaNames = config.personas.map((p) => p.name.replace(/ Developer$/, "")).join(", ");
|
|
4445
4576
|
const agentDisplay = options.agents.find((a) => a.value === config.agent)?.name ?? config.agent;
|
|
4446
|
-
const instructionFile =
|
|
4447
|
-
const mcpConfigFile =
|
|
4577
|
+
const instructionFile = getInstructionFilePath3(config.agent);
|
|
4578
|
+
const mcpConfigFile = getMCPConfigPath2(config.agent);
|
|
4448
4579
|
const serverEntries = Object.entries(config.mcpConfig);
|
|
4449
|
-
const instructionFilePath = (0,
|
|
4450
|
-
const mcpConfigFilePath = (0,
|
|
4451
|
-
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");
|
|
4452
4583
|
const instructionMode = detectMarkerFileMode(instructionFilePath, "<!-- one-shot-installer:start -->");
|
|
4453
4584
|
const mcpMode = detectMCPFileMode(mcpConfigFilePath);
|
|
4454
4585
|
const gitignoreMode = detectMarkerFileMode(gitignorePath, "# one-shot-installer:start");
|
|
@@ -4474,7 +4605,7 @@ async function previewAndConfirm(config, options) {
|
|
|
4474
4605
|
console.log(` ${mcpConfigFile.padEnd(domainColWidth + 14)}${mcpMode}`);
|
|
4475
4606
|
console.log(` ${".gitignore".padEnd(domainColWidth + 14)}${gitignoreMode}`);
|
|
4476
4607
|
if (config.installFactory) {
|
|
4477
|
-
const factoryExists = (0,
|
|
4608
|
+
const factoryExists = (0, import_fs8.existsSync)((0, import_path8.join)(config.projectPath, "factory"));
|
|
4478
4609
|
const factoryMode = factoryExists ? yellow("overwrite") : green("create");
|
|
4479
4610
|
console.log(` ${"factory/".padEnd(domainColWidth + 14)}${factoryMode}`);
|
|
4480
4611
|
}
|
|
@@ -4490,192 +4621,26 @@ async function previewAndConfirm(config, options) {
|
|
|
4490
4621
|
console.log("\n" + "\u2500".repeat(48));
|
|
4491
4622
|
return esm_default3({ message: "Proceed?", default: true });
|
|
4492
4623
|
}
|
|
4493
|
-
|
|
4494
|
-
const
|
|
4495
|
-
const
|
|
4496
|
-
const
|
|
4497
|
-
success: false,
|
|
4498
|
-
domainsInstalled: [],
|
|
4499
|
-
domainsFailed: [],
|
|
4500
|
-
failureReasons: {},
|
|
4501
|
-
instructionsCreated: false,
|
|
4502
|
-
instructionFilePath,
|
|
4503
|
-
mcpConfigured: false,
|
|
4504
|
-
mcpConfigPath,
|
|
4505
|
-
mcpServersAdded: [],
|
|
4506
|
-
gitignoreUpdated: false,
|
|
4507
|
-
factoryInstalled: false
|
|
4508
|
-
};
|
|
4509
|
-
console.log("");
|
|
4510
|
-
const uniqueDomains = [...new Set(config.personas.flatMap((p) => p.domains))];
|
|
4511
|
-
const domainValues = uniqueDomains.map((d) => d.toLowerCase());
|
|
4512
|
-
console.log(` Downloading contexts...`);
|
|
4513
|
-
try {
|
|
4514
|
-
const downloadResult = await fetchContexts({ domains: domainValues, token, repo, targetDir: config.projectPath });
|
|
4515
|
-
result.domainsInstalled = downloadResult.successful;
|
|
4516
|
-
result.domainsFailed = downloadResult.failed;
|
|
4517
|
-
result.failureReasons = downloadResult.failureReasons;
|
|
4518
|
-
} catch (error) {
|
|
4519
|
-
result.domainsFailed = domainValues;
|
|
4520
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
4521
|
-
for (const d of domainValues) result.failureReasons[d] = reason;
|
|
4522
|
-
}
|
|
4523
|
-
const domainColWidth = Math.max(...domainValues.map((d) => d.length), 8) + 2;
|
|
4524
|
-
for (const domain of domainValues) {
|
|
4525
|
-
const ok = result.domainsInstalled.includes(domain);
|
|
4526
|
-
const reason = result.failureReasons[domain];
|
|
4527
|
-
const status = ok ? "\u2713" : `\u2717 ${reason ?? "Unknown error"}`;
|
|
4528
|
-
console.log(` ${domain.padEnd(domainColWidth)}${status}`);
|
|
4529
|
-
}
|
|
4530
|
-
console.log("");
|
|
4531
|
-
if (config.installFactory && factoryRepo) {
|
|
4532
|
-
process.stdout.write(` Installing Factory framework... `);
|
|
4533
|
-
try {
|
|
4534
|
-
const factoryResult = await fetchFactory({ token, repo: factoryRepo, targetDir: config.projectPath });
|
|
4535
|
-
if (factoryResult.success) {
|
|
4536
|
-
result.factoryInstalled = true;
|
|
4537
|
-
console.log("\u2713");
|
|
4538
|
-
} else {
|
|
4539
|
-
console.log("\u2717");
|
|
4540
|
-
console.log(` ${factoryResult.failureReason}`);
|
|
4541
|
-
}
|
|
4542
|
-
} catch (error) {
|
|
4543
|
-
console.log("\u2717");
|
|
4544
|
-
console.log(` ${error instanceof Error ? error.message : String(error)}`);
|
|
4545
|
-
}
|
|
4546
|
-
}
|
|
4547
|
-
if (result.domainsInstalled.length === 0) {
|
|
4548
|
-
result.success = false;
|
|
4549
|
-
return result;
|
|
4550
|
-
}
|
|
4551
|
-
const successfulDomains = new Set(result.domainsInstalled);
|
|
4552
|
-
const successfulPersonas = config.personas.filter(
|
|
4553
|
-
(p) => p.domains.every((d) => successfulDomains.has(d.toLowerCase()))
|
|
4554
|
-
);
|
|
4555
|
-
const successfulMcpServers = /* @__PURE__ */ new Set([
|
|
4556
|
-
...config.baseMcpServers,
|
|
4557
|
-
...successfulPersonas.flatMap((p) => p.mcpServers)
|
|
4558
|
-
]);
|
|
4559
|
-
const filteredMcpConfig = Object.fromEntries(
|
|
4560
|
-
Object.entries(config.mcpConfig).filter(([name]) => successfulMcpServers.has(name))
|
|
4561
|
-
);
|
|
4562
|
-
const statePath = (0, import_path7.join)(config.projectPath, ".one-shot-state.json");
|
|
4563
|
-
const allDomains = result.domainsInstalled;
|
|
4564
|
-
process.stdout.write(` Writing ${instructionFilePath}... `);
|
|
4565
|
-
try {
|
|
4566
|
-
await setupInstructions({
|
|
4567
|
-
domains: allDomains,
|
|
4568
|
-
agent: config.agent,
|
|
4569
|
-
mcpConfig: filteredMcpConfig,
|
|
4570
|
-
projectPath: config.projectPath,
|
|
4571
|
-
factoryInstalled: result.factoryInstalled
|
|
4572
|
-
});
|
|
4573
|
-
result.instructionsCreated = true;
|
|
4574
|
-
console.log("\u2713");
|
|
4575
|
-
} catch (error) {
|
|
4576
|
-
console.log("\u2717");
|
|
4577
|
-
console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
4578
|
-
console.log(` Path: ${instructionFilePath}`);
|
|
4579
|
-
}
|
|
4580
|
-
process.stdout.write(` Writing ${mcpConfigPath}... `);
|
|
4581
|
-
try {
|
|
4582
|
-
const mcpResult = await setupMCPConfiguration(
|
|
4583
|
-
config.projectPath,
|
|
4584
|
-
filteredMcpConfig,
|
|
4585
|
-
config.agent
|
|
4586
|
-
);
|
|
4587
|
-
result.mcpConfigured = true;
|
|
4588
|
-
result.mcpServersAdded = mcpResult.addedServers;
|
|
4589
|
-
console.log("\u2713");
|
|
4590
|
-
} catch (error) {
|
|
4591
|
-
console.log("\u2717");
|
|
4592
|
-
console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
4593
|
-
console.log(` Path: ${mcpConfigPath}`);
|
|
4594
|
-
}
|
|
4595
|
-
process.stdout.write(` Updating .gitignore... `);
|
|
4596
|
-
try {
|
|
4597
|
-
setupGitignore(config.projectPath);
|
|
4598
|
-
result.gitignoreUpdated = true;
|
|
4599
|
-
console.log("\u2713");
|
|
4600
|
-
} catch (error) {
|
|
4601
|
-
console.log("\u2717");
|
|
4602
|
-
console.log(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
4603
|
-
}
|
|
4604
|
-
result.success = result.domainsFailed.length === 0 && result.instructionsCreated && result.mcpConfigured;
|
|
4605
|
-
const allPersonas = config.personas.map((p) => p.id);
|
|
4606
|
-
const state = {
|
|
4607
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4608
|
-
installerVersion: INSTALLER_VERSION,
|
|
4609
|
-
releaseVersion: config.releaseVersion,
|
|
4610
|
-
projectPath: config.projectPath,
|
|
4611
|
-
agent: config.agent,
|
|
4612
|
-
personas: allPersonas,
|
|
4613
|
-
domains: allDomains,
|
|
4614
|
-
mcpServers: Object.keys(filteredMcpConfig),
|
|
4615
|
-
mcpConfigs: filteredMcpConfig,
|
|
4616
|
-
factoryInstalled: result.factoryInstalled,
|
|
4617
|
-
files: {
|
|
4618
|
-
instructions: instructionFilePath,
|
|
4619
|
-
mcpConfig: mcpConfigPath,
|
|
4620
|
-
contexts: "_ai-context/",
|
|
4621
|
-
factory: result.factoryInstalled ? "factory/" : null,
|
|
4622
|
-
globalConfig: (0, import_path7.join)((0, import_os2.homedir)(), ".one-shot-installer")
|
|
4623
|
-
}
|
|
4624
|
-
};
|
|
4625
|
-
try {
|
|
4626
|
-
(0, import_fs7.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
4627
|
-
} catch {
|
|
4628
|
-
}
|
|
4629
|
-
return result;
|
|
4630
|
-
}
|
|
4631
|
-
function printSummary(result) {
|
|
4632
|
-
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;
|
|
4633
4628
|
console.log("\u2500".repeat(48));
|
|
4634
4629
|
console.log(hasErrors ? " Done (with errors).\n" : " Done.\n");
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
}
|
|
4638
|
-
if (result.instructionsCreated) {
|
|
4639
|
-
console.log(` ${result.instructionFilePath} written`);
|
|
4640
|
-
}
|
|
4641
|
-
if (result.mcpConfigured) {
|
|
4642
|
-
const serverList = result.mcpServersAdded.length > 0 ? ` (${result.mcpServersAdded.join(", ")})` : "";
|
|
4643
|
-
console.log(` ${result.mcpConfigPath} written${serverList}`);
|
|
4644
|
-
}
|
|
4645
|
-
if (result.factoryInstalled) {
|
|
4646
|
-
console.log(` Factory installed (.claude/ + factory/)`);
|
|
4647
|
-
}
|
|
4648
|
-
if (result.gitignoreUpdated) {
|
|
4649
|
-
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}`);
|
|
4650
4633
|
}
|
|
4651
4634
|
if (hasErrors) {
|
|
4652
4635
|
console.log("\n What went wrong:");
|
|
4653
|
-
|
|
4654
|
-
console.log("");
|
|
4655
|
-
console.log(" Context downloads failed:");
|
|
4656
|
-
for (const domain of result.domainsFailed) {
|
|
4657
|
-
const reason = result.failureReasons[domain];
|
|
4658
|
-
console.log(` ${domain} \u2014 ${reason ?? "Unknown error"}`);
|
|
4659
|
-
}
|
|
4660
|
-
}
|
|
4661
|
-
if (!result.instructionsCreated) {
|
|
4662
|
-
console.log("");
|
|
4663
|
-
console.log(` Instruction file not written: ${result.instructionFilePath}`);
|
|
4664
|
-
}
|
|
4665
|
-
if (!result.mcpConfigured) {
|
|
4666
|
-
console.log("");
|
|
4667
|
-
console.log(` MCP config not written: ${result.mcpConfigPath}`);
|
|
4636
|
+
for (const entry of failed) {
|
|
4637
|
+
console.log(` ${entry.label}: ${entry.result.detail ?? "Unknown error"}`);
|
|
4668
4638
|
}
|
|
4669
4639
|
console.log("\n Troubleshooting:");
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
}
|
|
4675
|
-
if (!result.instructionsCreated || !result.mcpConfigured) {
|
|
4676
|
-
console.log(" - Check write permissions for the project directory");
|
|
4677
|
-
console.log(` - Project directory: ${result.instructionFilePath.includes("/") ? "check parent folders exist" : "current directory"}`);
|
|
4678
|
-
}
|
|
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");
|
|
4679
4644
|
}
|
|
4680
4645
|
console.log("\n" + "\u2500".repeat(48) + "\n");
|
|
4681
4646
|
}
|