dlw-machine-setup 0.6.0 → 0.6.1
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 +329 -405
- package/package.json +1 -1
package/bin/installer.js
CHANGED
|
@@ -2753,13 +2753,13 @@ var PromisePolyfill = class extends Promise {
|
|
|
2753
2753
|
// Available starting from Node 22
|
|
2754
2754
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
|
|
2755
2755
|
static withResolver() {
|
|
2756
|
-
let
|
|
2756
|
+
let resolve4;
|
|
2757
2757
|
let reject;
|
|
2758
2758
|
const promise = new Promise((res, rej) => {
|
|
2759
|
-
|
|
2759
|
+
resolve4 = res;
|
|
2760
2760
|
reject = rej;
|
|
2761
2761
|
});
|
|
2762
|
-
return { promise, resolve:
|
|
2762
|
+
return { promise, resolve: resolve4, reject };
|
|
2763
2763
|
}
|
|
2764
2764
|
};
|
|
2765
2765
|
|
|
@@ -2776,7 +2776,7 @@ function createPrompt(view) {
|
|
|
2776
2776
|
output
|
|
2777
2777
|
});
|
|
2778
2778
|
const screen = new ScreenManager(rl);
|
|
2779
|
-
const { promise, resolve:
|
|
2779
|
+
const { promise, resolve: resolve4, reject } = PromisePolyfill.withResolver();
|
|
2780
2780
|
const cancel = () => reject(new CancelPromptError());
|
|
2781
2781
|
if (signal) {
|
|
2782
2782
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
@@ -2800,7 +2800,7 @@ function createPrompt(view) {
|
|
|
2800
2800
|
cycle(() => {
|
|
2801
2801
|
try {
|
|
2802
2802
|
const nextView = view(config, (value) => {
|
|
2803
|
-
setImmediate(() =>
|
|
2803
|
+
setImmediate(() => resolve4(value));
|
|
2804
2804
|
});
|
|
2805
2805
|
const [content, bottomContent] = typeof nextView === "string" ? [nextView] : nextView;
|
|
2806
2806
|
screen.render(content, bottomContent);
|
|
@@ -3244,14 +3244,32 @@ ${page}${helpTipBottom}${choiceDescription}${import_ansi_escapes3.default.cursor
|
|
|
3244
3244
|
});
|
|
3245
3245
|
|
|
3246
3246
|
// src/index.ts
|
|
3247
|
-
var
|
|
3247
|
+
var import_fs11 = require("fs");
|
|
3248
3248
|
var import_readline = require("readline");
|
|
3249
|
-
var
|
|
3249
|
+
var import_path12 = require("path");
|
|
3250
3250
|
|
|
3251
3251
|
// src/utils/fetch.ts
|
|
3252
3252
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
3253
3253
|
var DEFAULT_MAX_RETRIES = 3;
|
|
3254
3254
|
var DEFAULT_RETRY_DELAY_MS = 2e3;
|
|
3255
|
+
function getReadableError(status) {
|
|
3256
|
+
switch (status) {
|
|
3257
|
+
case 401:
|
|
3258
|
+
return "Authentication failed. Check your GitHub token.";
|
|
3259
|
+
case 403:
|
|
3260
|
+
return "Access denied. Verify token permissions and rate limits.";
|
|
3261
|
+
case 404:
|
|
3262
|
+
return "Resource not found. Verify the repository and release exist.";
|
|
3263
|
+
case 429:
|
|
3264
|
+
return "Rate limit exceeded. Please wait and try again.";
|
|
3265
|
+
case 500:
|
|
3266
|
+
case 502:
|
|
3267
|
+
case 503:
|
|
3268
|
+
return "GitHub server error. Please try again later.";
|
|
3269
|
+
default:
|
|
3270
|
+
return `Unexpected error (${status})`;
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3255
3273
|
async function fetchWithTimeout(url, options = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
3256
3274
|
const controller = new AbortController();
|
|
3257
3275
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -3283,7 +3301,7 @@ async function fetchWithRetry(url, options = {}, maxRetries = DEFAULT_MAX_RETRIE
|
|
|
3283
3301
|
}
|
|
3284
3302
|
if (attempt < maxRetries) {
|
|
3285
3303
|
const delay = retryDelayMs * Math.pow(2, attempt - 1);
|
|
3286
|
-
await new Promise((
|
|
3304
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
3287
3305
|
}
|
|
3288
3306
|
}
|
|
3289
3307
|
throw lastError || new Error("Failed after maximum retries");
|
|
@@ -3451,7 +3469,7 @@ async function pollForToken(deviceCode, interval) {
|
|
|
3451
3469
|
const maxAttempts = 60;
|
|
3452
3470
|
let attempts = 0;
|
|
3453
3471
|
while (attempts < maxAttempts) {
|
|
3454
|
-
await new Promise((
|
|
3472
|
+
await new Promise((resolve4) => setTimeout(resolve4, interval * 1e3));
|
|
3455
3473
|
const response = await fetchWithTimeout(GITHUB_TOKEN_URL, {
|
|
3456
3474
|
method: "POST",
|
|
3457
3475
|
headers: {
|
|
@@ -3671,10 +3689,86 @@ __export(steps_exports, {
|
|
|
3671
3689
|
});
|
|
3672
3690
|
|
|
3673
3691
|
// src/steps/resources/fetch-contexts.ts
|
|
3692
|
+
var import_fs4 = require("fs");
|
|
3693
|
+
var import_path4 = require("path");
|
|
3694
|
+
|
|
3695
|
+
// src/utils/fs-copy.ts
|
|
3674
3696
|
var import_fs2 = require("fs");
|
|
3675
3697
|
var import_path2 = require("path");
|
|
3698
|
+
function copyDirectory(source, target) {
|
|
3699
|
+
if (!(0, import_fs2.existsSync)(target)) (0, import_fs2.mkdirSync)(target, { recursive: true });
|
|
3700
|
+
for (const entry of (0, import_fs2.readdirSync)(source, { withFileTypes: true })) {
|
|
3701
|
+
const s = (0, import_path2.join)(source, entry.name);
|
|
3702
|
+
const t = (0, import_path2.join)(target, entry.name);
|
|
3703
|
+
if (entry.isDirectory()) copyDirectory(s, t);
|
|
3704
|
+
else (0, import_fs2.copyFileSync)(s, t);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
// src/utils/download-archive.ts
|
|
3709
|
+
var import_fs3 = require("fs");
|
|
3710
|
+
var import_path3 = require("path");
|
|
3676
3711
|
var import_child_process2 = require("child_process");
|
|
3677
3712
|
var MIN_FILE_SIZE = 1024;
|
|
3713
|
+
async function fetchLatestRelease(token, repo) {
|
|
3714
|
+
const headers = {
|
|
3715
|
+
"Accept": "application/vnd.github+json",
|
|
3716
|
+
"Authorization": `Bearer ${token}`
|
|
3717
|
+
};
|
|
3718
|
+
const res = await fetchWithRetry(`https://api.github.com/repos/${repo}/releases/latest`, { headers });
|
|
3719
|
+
if (!res.ok) {
|
|
3720
|
+
throw new Error(`GitHub API error (${res.status}): ${getReadableError(res.status)}`);
|
|
3721
|
+
}
|
|
3722
|
+
const data = await res.json();
|
|
3723
|
+
return { tagName: data.tag_name ?? "unknown", assets: data.assets ?? [] };
|
|
3724
|
+
}
|
|
3725
|
+
async function downloadAndExtractAsset(token, asset, projectPath, tempDirName) {
|
|
3726
|
+
const tarCheck = (0, import_child_process2.spawnSync)("tar", ["--version"], { stdio: "ignore" });
|
|
3727
|
+
if (tarCheck.status !== 0) {
|
|
3728
|
+
throw new Error("tar command not found. Please ensure tar is installed and available in your PATH.");
|
|
3729
|
+
}
|
|
3730
|
+
const tempDir = (0, import_path3.join)(projectPath, tempDirName);
|
|
3731
|
+
if ((0, import_fs3.existsSync)(tempDir)) (0, import_fs3.rmSync)(tempDir, { recursive: true, force: true });
|
|
3732
|
+
(0, import_fs3.mkdirSync)(tempDir, { recursive: true });
|
|
3733
|
+
const cleanup = () => {
|
|
3734
|
+
if ((0, import_fs3.existsSync)(tempDir)) {
|
|
3735
|
+
try {
|
|
3736
|
+
(0, import_fs3.rmSync)(tempDir, { recursive: true, force: true });
|
|
3737
|
+
} catch {
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
};
|
|
3741
|
+
try {
|
|
3742
|
+
const dlRes = await fetchWithRetry(asset.url, {
|
|
3743
|
+
headers: { "Accept": "application/octet-stream", "Authorization": `Bearer ${token}` }
|
|
3744
|
+
});
|
|
3745
|
+
if (!dlRes.ok) {
|
|
3746
|
+
throw new Error(`Download failed: ${getReadableError(dlRes.status)}`);
|
|
3747
|
+
}
|
|
3748
|
+
const archivePath = (0, import_path3.join)(tempDir, asset.name);
|
|
3749
|
+
(0, import_fs3.writeFileSync)(archivePath, Buffer.from(await dlRes.arrayBuffer()));
|
|
3750
|
+
if ((0, import_fs3.statSync)(archivePath).size < MIN_FILE_SIZE) {
|
|
3751
|
+
throw new Error("File corrupted");
|
|
3752
|
+
}
|
|
3753
|
+
const list = (0, import_child_process2.spawnSync)("tar", ["-tzf", archivePath], { encoding: "utf8" });
|
|
3754
|
+
if (list.status !== 0) throw new Error("Failed to read archive contents");
|
|
3755
|
+
const resolvedTemp = (0, import_path3.resolve)(tempDir);
|
|
3756
|
+
for (const entry of list.stdout.split("\n").filter(Boolean)) {
|
|
3757
|
+
const path = (0, import_path3.resolve)((0, import_path3.join)(tempDir, entry.replace(/\/$/, "")));
|
|
3758
|
+
if (!path.startsWith(resolvedTemp + import_path3.sep)) {
|
|
3759
|
+
throw new Error(`Archive contains unsafe path: ${entry}`);
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
const ex = (0, import_child_process2.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
|
|
3763
|
+
if (ex.status !== 0) throw new Error("Archive extraction failed");
|
|
3764
|
+
return { extractedRoot: tempDir, cleanup };
|
|
3765
|
+
} catch (err) {
|
|
3766
|
+
cleanup();
|
|
3767
|
+
throw err;
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
// src/steps/resources/fetch-contexts.ts
|
|
3678
3772
|
var fetch_contexts_default = defineStep({
|
|
3679
3773
|
name: "fetch-contexts",
|
|
3680
3774
|
label: "Downloading contexts",
|
|
@@ -3683,9 +3777,6 @@ var fetch_contexts_default = defineStep({
|
|
|
3683
3777
|
const uniqueDomains = [...new Set(ctx.config.technologies.flatMap((p) => p.domains))];
|
|
3684
3778
|
const domainValues = uniqueDomains.map((d) => d.toLowerCase());
|
|
3685
3779
|
if (!ctx.contextRepo) {
|
|
3686
|
-
for (const domain of domainValues) {
|
|
3687
|
-
ctx.installed.domainsFailed = domainValues;
|
|
3688
|
-
}
|
|
3689
3780
|
return {
|
|
3690
3781
|
status: "failed",
|
|
3691
3782
|
detail: "Context repo not found (topic: context-data)"
|
|
@@ -3693,8 +3784,6 @@ var fetch_contexts_default = defineStep({
|
|
|
3693
3784
|
}
|
|
3694
3785
|
const downloadResult = await fetchContexts(domainValues, ctx.token, ctx.contextRepo, ctx.config.projectPath);
|
|
3695
3786
|
ctx.installed.domainsInstalled = downloadResult.successful;
|
|
3696
|
-
ctx.installed.domainsFailed = downloadResult.failed;
|
|
3697
|
-
ctx.installed.failureReasons = downloadResult.failureReasons;
|
|
3698
3787
|
ctx.installed.contextReleaseVersion = downloadResult.releaseVersion;
|
|
3699
3788
|
const domainColWidth = Math.max(...domainValues.map((d) => d.length), 8) + 2;
|
|
3700
3789
|
console.log("");
|
|
@@ -3719,24 +3808,9 @@ var fetch_contexts_default = defineStep({
|
|
|
3719
3808
|
async function fetchContexts(domains, token, repo, targetDir) {
|
|
3720
3809
|
const result = { successful: [], failed: [], failureReasons: {} };
|
|
3721
3810
|
if (domains.length === 0) return result;
|
|
3722
|
-
const
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
}
|
|
3726
|
-
const headers = {
|
|
3727
|
-
"Accept": "application/vnd.github+json",
|
|
3728
|
-
"Authorization": `Bearer ${token}`
|
|
3729
|
-
};
|
|
3730
|
-
const releaseResponse = await fetchWithRetry(
|
|
3731
|
-
`https://api.github.com/repos/${repo}/releases/latest`,
|
|
3732
|
-
{ headers }
|
|
3733
|
-
);
|
|
3734
|
-
if (!releaseResponse.ok) {
|
|
3735
|
-
throw new Error(`GitHub API error (${releaseResponse.status}): ${getReadableError(releaseResponse.status)}`);
|
|
3736
|
-
}
|
|
3737
|
-
const releaseData = await releaseResponse.json();
|
|
3738
|
-
result.releaseVersion = releaseData.tag_name;
|
|
3739
|
-
const asset = releaseData.assets?.find((a) => a.name.endsWith(".tar.gz"));
|
|
3811
|
+
const release = await fetchLatestRelease(token, repo);
|
|
3812
|
+
result.releaseVersion = release.tagName;
|
|
3813
|
+
const asset = release.assets.find((a) => a.name.endsWith(".tar.gz"));
|
|
3740
3814
|
if (!asset) {
|
|
3741
3815
|
for (const domain of domains) {
|
|
3742
3816
|
result.failed.push(domain);
|
|
@@ -3744,39 +3818,11 @@ async function fetchContexts(domains, token, repo, targetDir) {
|
|
|
3744
3818
|
}
|
|
3745
3819
|
return result;
|
|
3746
3820
|
}
|
|
3747
|
-
const contextsDir = (0,
|
|
3748
|
-
|
|
3821
|
+
const contextsDir = (0, import_path4.join)(targetDir, "_ai-context");
|
|
3822
|
+
if (!(0, import_fs4.existsSync)(contextsDir)) (0, import_fs4.mkdirSync)(contextsDir, { recursive: true });
|
|
3823
|
+
const archive = await downloadAndExtractAsset(token, asset, targetDir, ".temp-download");
|
|
3749
3824
|
try {
|
|
3750
|
-
|
|
3751
|
-
if ((0, import_fs2.existsSync)(tempDir)) (0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
|
|
3752
|
-
(0, import_fs2.mkdirSync)(tempDir, { recursive: true });
|
|
3753
|
-
const downloadHeaders = {
|
|
3754
|
-
"Accept": "application/octet-stream",
|
|
3755
|
-
"Authorization": `Bearer ${token}`
|
|
3756
|
-
};
|
|
3757
|
-
const response = await fetchWithRetry(asset.url, { headers: downloadHeaders });
|
|
3758
|
-
if (!response.ok) {
|
|
3759
|
-
throw new Error(`Download failed: ${getReadableError(response.status)}`);
|
|
3760
|
-
}
|
|
3761
|
-
const archivePath = (0, import_path2.join)(tempDir, asset.name);
|
|
3762
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
3763
|
-
(0, import_fs2.writeFileSync)(archivePath, Buffer.from(arrayBuffer));
|
|
3764
|
-
const stats = (0, import_fs2.statSync)(archivePath);
|
|
3765
|
-
if (stats.size < MIN_FILE_SIZE) {
|
|
3766
|
-
throw new Error("File corrupted");
|
|
3767
|
-
}
|
|
3768
|
-
const listResult = (0, import_child_process2.spawnSync)("tar", ["-tzf", archivePath], { encoding: "utf8" });
|
|
3769
|
-
if (listResult.status !== 0) throw new Error("Failed to read archive contents");
|
|
3770
|
-
const resolvedTempDir = (0, import_path2.resolve)(tempDir);
|
|
3771
|
-
for (const entry of listResult.stdout.split("\n").filter(Boolean)) {
|
|
3772
|
-
const entryPath = (0, import_path2.resolve)((0, import_path2.join)(tempDir, entry.replace(/\/$/, "")));
|
|
3773
|
-
if (!entryPath.startsWith(resolvedTempDir + import_path2.sep)) {
|
|
3774
|
-
throw new Error(`Archive contains unsafe path: ${entry}`);
|
|
3775
|
-
}
|
|
3776
|
-
}
|
|
3777
|
-
const extractResult = (0, import_child_process2.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
|
|
3778
|
-
if (extractResult.status !== 0) throw new Error("Archive extraction failed");
|
|
3779
|
-
const extractedFolders = (0, import_fs2.readdirSync)(tempDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3825
|
+
const extractedFolders = (0, import_fs4.readdirSync)(archive.extractedRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3780
3826
|
for (const domain of domains) {
|
|
3781
3827
|
try {
|
|
3782
3828
|
const match = extractedFolders.find((f) => f.toLowerCase() === domain.toLowerCase());
|
|
@@ -3785,8 +3831,8 @@ async function fetchContexts(domains, token, repo, targetDir) {
|
|
|
3785
3831
|
result.failureReasons[domain] = "Not found in archive";
|
|
3786
3832
|
continue;
|
|
3787
3833
|
}
|
|
3788
|
-
const domainPath = (0,
|
|
3789
|
-
copyDirectory((0,
|
|
3834
|
+
const domainPath = (0, import_path4.join)(contextsDir, domain);
|
|
3835
|
+
copyDirectory((0, import_path4.join)(archive.extractedRoot, match), domainPath);
|
|
3790
3836
|
result.successful.push(domain);
|
|
3791
3837
|
} catch (error) {
|
|
3792
3838
|
result.failed.push(domain);
|
|
@@ -3794,59 +3840,51 @@ async function fetchContexts(domains, token, repo, targetDir) {
|
|
|
3794
3840
|
}
|
|
3795
3841
|
}
|
|
3796
3842
|
} finally {
|
|
3797
|
-
|
|
3798
|
-
try {
|
|
3799
|
-
(0, import_fs2.rmSync)(tempDir, { recursive: true, force: true });
|
|
3800
|
-
} catch {
|
|
3801
|
-
}
|
|
3802
|
-
}
|
|
3843
|
+
archive.cleanup();
|
|
3803
3844
|
}
|
|
3804
3845
|
return result;
|
|
3805
3846
|
}
|
|
3806
|
-
function copyDirectory(source, target) {
|
|
3807
|
-
if (!(0, import_fs2.existsSync)(target)) (0, import_fs2.mkdirSync)(target, { recursive: true });
|
|
3808
|
-
const entries = (0, import_fs2.readdirSync)(source, { withFileTypes: true });
|
|
3809
|
-
for (const entry of entries) {
|
|
3810
|
-
const sourcePath = (0, import_path2.join)(source, entry.name);
|
|
3811
|
-
const targetPath = (0, import_path2.join)(target, entry.name);
|
|
3812
|
-
if (entry.isDirectory()) {
|
|
3813
|
-
copyDirectory(sourcePath, targetPath);
|
|
3814
|
-
} else {
|
|
3815
|
-
(0, import_fs2.copyFileSync)(sourcePath, targetPath);
|
|
3816
|
-
}
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
function getReadableError(status) {
|
|
3820
|
-
switch (status) {
|
|
3821
|
-
case 401:
|
|
3822
|
-
return "Authentication failed. Check your GitHub token.";
|
|
3823
|
-
case 403:
|
|
3824
|
-
return "Access denied. Verify token permissions and rate limits.";
|
|
3825
|
-
case 404:
|
|
3826
|
-
return "Resource not found. Verify the repository and release exist.";
|
|
3827
|
-
case 429:
|
|
3828
|
-
return "Rate limit exceeded. Please wait and try again.";
|
|
3829
|
-
case 500:
|
|
3830
|
-
case 502:
|
|
3831
|
-
case 503:
|
|
3832
|
-
return "GitHub server error. Please try again later.";
|
|
3833
|
-
default:
|
|
3834
|
-
return `Unexpected error (${status})`;
|
|
3835
|
-
}
|
|
3836
|
-
}
|
|
3837
3847
|
|
|
3838
3848
|
// src/steps/resources/fetch-factory.ts
|
|
3839
|
-
var
|
|
3840
|
-
var
|
|
3841
|
-
var import_child_process3 = require("child_process");
|
|
3849
|
+
var import_fs7 = require("fs");
|
|
3850
|
+
var import_path7 = require("path");
|
|
3842
3851
|
|
|
3843
3852
|
// src/bundles/run-bundle.ts
|
|
3844
|
-
var
|
|
3845
|
-
var
|
|
3853
|
+
var import_fs6 = require("fs");
|
|
3854
|
+
var import_path6 = require("path");
|
|
3846
3855
|
|
|
3847
3856
|
// src/bundles/types.ts
|
|
3848
3857
|
var SCHEMA_VERSION = 1;
|
|
3849
3858
|
|
|
3859
|
+
// src/utils/marker-block.ts
|
|
3860
|
+
var import_fs5 = require("fs");
|
|
3861
|
+
var import_path5 = require("path");
|
|
3862
|
+
function upsertMarkerBlock(filePath, startMarker, endMarker, body, opts = {}) {
|
|
3863
|
+
const block = `${startMarker}
|
|
3864
|
+
${body}
|
|
3865
|
+
${endMarker}`;
|
|
3866
|
+
(0, import_fs5.mkdirSync)((0, import_path5.dirname)(filePath), { recursive: true });
|
|
3867
|
+
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
3868
|
+
(0, import_fs5.writeFileSync)(filePath, block + "\n", "utf-8");
|
|
3869
|
+
return;
|
|
3870
|
+
}
|
|
3871
|
+
const existing = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
3872
|
+
const s = existing.indexOf(startMarker);
|
|
3873
|
+
const e = existing.indexOf(endMarker);
|
|
3874
|
+
const hasS = s !== -1;
|
|
3875
|
+
const hasE = e !== -1;
|
|
3876
|
+
if (hasS && !hasE || !hasS && hasE || hasS && hasE && e < s) {
|
|
3877
|
+
opts.onCorrupt?.(filePath, startMarker, endMarker);
|
|
3878
|
+
}
|
|
3879
|
+
if (hasS && hasE && e > s) {
|
|
3880
|
+
const updated = existing.slice(0, s) + block + existing.slice(e + endMarker.length);
|
|
3881
|
+
(0, import_fs5.writeFileSync)(filePath, updated, "utf-8");
|
|
3882
|
+
} else {
|
|
3883
|
+
const sep3 = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
3884
|
+
(0, import_fs5.writeFileSync)(filePath, existing + sep3 + block + "\n", "utf-8");
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3850
3888
|
// src/bundles/run-bundle.ts
|
|
3851
3889
|
var OWNER_KEY = "_bundleOwner";
|
|
3852
3890
|
async function runBundle(manifest, ctx) {
|
|
@@ -3864,7 +3902,7 @@ async function runBundle(manifest, ctx) {
|
|
|
3864
3902
|
}
|
|
3865
3903
|
if (manifest.gitignore?.length) {
|
|
3866
3904
|
upsertMarkerBlock(
|
|
3867
|
-
(0,
|
|
3905
|
+
(0, import_path6.join)(ctx.projectPath, ".gitignore"),
|
|
3868
3906
|
`# ${manifest.name}:start`,
|
|
3869
3907
|
`# ${manifest.name}:end`,
|
|
3870
3908
|
manifest.gitignore.join("\n")
|
|
@@ -3876,7 +3914,7 @@ async function runBundle(manifest, ctx) {
|
|
|
3876
3914
|
result.instructionsSnippet = snippet;
|
|
3877
3915
|
if (!ctx.skipInstructions) {
|
|
3878
3916
|
upsertMarkerBlock(
|
|
3879
|
-
(0,
|
|
3917
|
+
(0, import_path6.join)(ctx.projectPath, ctx.instructionsFile),
|
|
3880
3918
|
`<!-- ${manifest.name}:start -->`,
|
|
3881
3919
|
`<!-- ${manifest.name}:end -->`,
|
|
3882
3920
|
snippet
|
|
@@ -3903,12 +3941,12 @@ function executeOp(op, owner, ctx, result) {
|
|
|
3903
3941
|
function runCopy(op, ctx, result) {
|
|
3904
3942
|
const source = resolveBundlePath(op.from, ctx);
|
|
3905
3943
|
const target = resolveProjectPath(op.to, ctx);
|
|
3906
|
-
if (!(0,
|
|
3907
|
-
if ((0,
|
|
3908
|
-
|
|
3944
|
+
if (!(0, import_fs6.existsSync)(source)) return;
|
|
3945
|
+
if ((0, import_fs6.statSync)(source).isDirectory()) {
|
|
3946
|
+
copyDirectory(source, target);
|
|
3909
3947
|
} else {
|
|
3910
|
-
(0,
|
|
3911
|
-
(0,
|
|
3948
|
+
(0, import_fs6.mkdirSync)((0, import_path6.dirname)(target), { recursive: true });
|
|
3949
|
+
(0, import_fs6.copyFileSync)(source, target);
|
|
3912
3950
|
}
|
|
3913
3951
|
result.filesTouched.push(op.to);
|
|
3914
3952
|
}
|
|
@@ -3918,14 +3956,14 @@ function runMergeJson(op, owner, ctx, result) {
|
|
|
3918
3956
|
const stripped = stripOwnedEntries(existing, owner);
|
|
3919
3957
|
const tagged = tagEntries(op.patch, owner);
|
|
3920
3958
|
const merged = deepMerge2(stripped, tagged);
|
|
3921
|
-
(0,
|
|
3922
|
-
(0,
|
|
3959
|
+
(0, import_fs6.mkdirSync)((0, import_path6.dirname)(filePath), { recursive: true });
|
|
3960
|
+
(0, import_fs6.writeFileSync)(filePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
3923
3961
|
result.filesTouched.push(op.file);
|
|
3924
3962
|
}
|
|
3925
3963
|
function readJsonOrEmpty(path) {
|
|
3926
|
-
if (!(0,
|
|
3964
|
+
if (!(0, import_fs6.existsSync)(path)) return {};
|
|
3927
3965
|
try {
|
|
3928
|
-
return JSON.parse((0,
|
|
3966
|
+
return JSON.parse((0, import_fs6.readFileSync)(path, "utf-8"));
|
|
3929
3967
|
} catch {
|
|
3930
3968
|
return {};
|
|
3931
3969
|
}
|
|
@@ -3970,38 +4008,18 @@ function deepMerge2(a, b) {
|
|
|
3970
4008
|
}
|
|
3971
4009
|
return b;
|
|
3972
4010
|
}
|
|
3973
|
-
function upsertMarkerBlock(filePath, startMarker, endMarker, body) {
|
|
3974
|
-
const block = `${startMarker}
|
|
3975
|
-
${body}
|
|
3976
|
-
${endMarker}`;
|
|
3977
|
-
(0, import_fs3.mkdirSync)((0, import_path3.dirname)(filePath), { recursive: true });
|
|
3978
|
-
if (!(0, import_fs3.existsSync)(filePath)) {
|
|
3979
|
-
(0, import_fs3.writeFileSync)(filePath, block + "\n", "utf-8");
|
|
3980
|
-
return;
|
|
3981
|
-
}
|
|
3982
|
-
const existing = (0, import_fs3.readFileSync)(filePath, "utf-8");
|
|
3983
|
-
const s = existing.indexOf(startMarker);
|
|
3984
|
-
const e = existing.indexOf(endMarker);
|
|
3985
|
-
if (s !== -1 && e !== -1 && e > s) {
|
|
3986
|
-
const updated = existing.slice(0, s) + block + existing.slice(e + endMarker.length);
|
|
3987
|
-
(0, import_fs3.writeFileSync)(filePath, updated, "utf-8");
|
|
3988
|
-
} else {
|
|
3989
|
-
const joiner = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
3990
|
-
(0, import_fs3.writeFileSync)(filePath, existing + joiner + block + "\n", "utf-8");
|
|
3991
|
-
}
|
|
3992
|
-
}
|
|
3993
4011
|
function resolveBundlePath(rel, ctx) {
|
|
3994
|
-
const base = (0,
|
|
3995
|
-
const full = (0,
|
|
3996
|
-
if (full !== base && !full.startsWith(base +
|
|
4012
|
+
const base = (0, import_path6.resolve)(ctx.bundleRoot);
|
|
4013
|
+
const full = (0, import_path6.resolve)((0, import_path6.join)(base, rel));
|
|
4014
|
+
if (full !== base && !full.startsWith(base + import_path6.sep)) {
|
|
3997
4015
|
throw new Error(`Bundle op "from" escapes bundle root: ${rel}`);
|
|
3998
4016
|
}
|
|
3999
4017
|
return full;
|
|
4000
4018
|
}
|
|
4001
4019
|
function resolveProjectPath(rel, ctx) {
|
|
4002
|
-
const base = (0,
|
|
4003
|
-
const full = (0,
|
|
4004
|
-
if (full !== base && !full.startsWith(base +
|
|
4020
|
+
const base = (0, import_path6.resolve)(ctx.projectPath);
|
|
4021
|
+
const full = (0, import_path6.resolve)((0, import_path6.join)(base, rel));
|
|
4022
|
+
if (full !== base && !full.startsWith(base + import_path6.sep)) {
|
|
4005
4023
|
throw new Error(`Bundle op "to" escapes project path: ${rel}`);
|
|
4006
4024
|
}
|
|
4007
4025
|
return full;
|
|
@@ -4019,22 +4037,12 @@ function assertSchemaCompatible(manifest) {
|
|
|
4019
4037
|
}
|
|
4020
4038
|
}
|
|
4021
4039
|
function assertBundleRootExists(ctx) {
|
|
4022
|
-
if (!(0,
|
|
4040
|
+
if (!(0, import_fs6.existsSync)(ctx.bundleRoot) || !(0, import_fs6.statSync)(ctx.bundleRoot).isDirectory()) {
|
|
4023
4041
|
throw new Error(`Bundle root not found or not a directory: ${ctx.bundleRoot}`);
|
|
4024
4042
|
}
|
|
4025
4043
|
}
|
|
4026
|
-
function copyDirectory2(source, target) {
|
|
4027
|
-
if (!(0, import_fs3.existsSync)(target)) (0, import_fs3.mkdirSync)(target, { recursive: true });
|
|
4028
|
-
for (const entry of (0, import_fs3.readdirSync)(source, { withFileTypes: true })) {
|
|
4029
|
-
const s = (0, import_path3.join)(source, entry.name);
|
|
4030
|
-
const t = (0, import_path3.join)(target, entry.name);
|
|
4031
|
-
if (entry.isDirectory()) copyDirectory2(s, t);
|
|
4032
|
-
else (0, import_fs3.copyFileSync)(s, t);
|
|
4033
|
-
}
|
|
4034
|
-
}
|
|
4035
4044
|
|
|
4036
4045
|
// src/steps/resources/fetch-factory.ts
|
|
4037
|
-
var MIN_FILE_SIZE2 = 1024;
|
|
4038
4046
|
function getFactoryAsset(agent) {
|
|
4039
4047
|
switch (agent) {
|
|
4040
4048
|
case "github-copilot":
|
|
@@ -4065,76 +4073,31 @@ var fetch_factory_default = defineStep({
|
|
|
4065
4073
|
});
|
|
4066
4074
|
async function fetchFactory(token, repo, targetDir, agent) {
|
|
4067
4075
|
const result = { success: false, filesInstalled: [] };
|
|
4068
|
-
const tarCheck = (0, import_child_process3.spawnSync)("tar", ["--version"], { stdio: "ignore" });
|
|
4069
|
-
if (tarCheck.status !== 0) {
|
|
4070
|
-
result.failureReason = "tar command not found";
|
|
4071
|
-
return result;
|
|
4072
|
-
}
|
|
4073
4076
|
const { assetName, rootFolder } = getFactoryAsset(agent);
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
}
|
|
4078
|
-
|
|
4079
|
-
`https://api.github.com/repos/${repo}/releases/latest`,
|
|
4080
|
-
{ headers }
|
|
4081
|
-
);
|
|
4082
|
-
if (!releaseResponse.ok) {
|
|
4083
|
-
result.failureReason = `GitHub API error (${releaseResponse.status})`;
|
|
4077
|
+
let release;
|
|
4078
|
+
try {
|
|
4079
|
+
release = await fetchLatestRelease(token, repo);
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
result.failureReason = err instanceof Error ? err.message : String(err);
|
|
4084
4082
|
return result;
|
|
4085
4083
|
}
|
|
4086
|
-
const
|
|
4087
|
-
const asset = releaseData.assets?.find((a) => a.name === assetName);
|
|
4084
|
+
const asset = release.assets.find((a) => a.name === assetName);
|
|
4088
4085
|
if (!asset) {
|
|
4089
4086
|
result.failureReason = `${assetName} not found in release`;
|
|
4090
4087
|
return result;
|
|
4091
4088
|
}
|
|
4092
|
-
|
|
4089
|
+
let archive = null;
|
|
4093
4090
|
try {
|
|
4094
|
-
|
|
4095
|
-
(0,
|
|
4096
|
-
const response = await fetchWithRetry(asset.url, {
|
|
4097
|
-
headers: { "Accept": "application/octet-stream", "Authorization": `Bearer ${token}` }
|
|
4098
|
-
});
|
|
4099
|
-
if (!response.ok) {
|
|
4100
|
-
result.failureReason = `Download failed (${response.status})`;
|
|
4101
|
-
return result;
|
|
4102
|
-
}
|
|
4103
|
-
const archivePath = (0, import_path4.join)(tempDir, assetName);
|
|
4104
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
4105
|
-
(0, import_fs4.writeFileSync)(archivePath, Buffer.from(arrayBuffer));
|
|
4106
|
-
const stats = (0, import_fs4.statSync)(archivePath);
|
|
4107
|
-
if (stats.size < MIN_FILE_SIZE2) {
|
|
4108
|
-
result.failureReason = "Downloaded file corrupted (too small)";
|
|
4109
|
-
return result;
|
|
4110
|
-
}
|
|
4111
|
-
const listResult = (0, import_child_process3.spawnSync)("tar", ["-tzf", archivePath], { encoding: "utf8" });
|
|
4112
|
-
if (listResult.status !== 0) {
|
|
4113
|
-
result.failureReason = "Failed to read archive contents";
|
|
4114
|
-
return result;
|
|
4115
|
-
}
|
|
4116
|
-
const resolvedTempDir = (0, import_path4.resolve)(tempDir);
|
|
4117
|
-
for (const entry of listResult.stdout.split("\n").filter(Boolean)) {
|
|
4118
|
-
const entryPath = (0, import_path4.resolve)((0, import_path4.join)(tempDir, entry.replace(/\/$/, "")));
|
|
4119
|
-
if (!entryPath.startsWith(resolvedTempDir + import_path4.sep)) {
|
|
4120
|
-
result.failureReason = `Archive contains unsafe path: ${entry}`;
|
|
4121
|
-
return result;
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
const extractResult = (0, import_child_process3.spawnSync)("tar", ["-xzf", archivePath, "-C", tempDir], { stdio: "ignore" });
|
|
4125
|
-
if (extractResult.status !== 0) {
|
|
4126
|
-
result.failureReason = "Archive extraction failed";
|
|
4127
|
-
return result;
|
|
4128
|
-
}
|
|
4129
|
-
const extractedEntries = (0, import_fs4.readdirSync)(tempDir);
|
|
4091
|
+
archive = await downloadAndExtractAsset(token, asset, targetDir, ".temp-factory-download");
|
|
4092
|
+
const extractedEntries = (0, import_fs7.readdirSync)(archive.extractedRoot);
|
|
4130
4093
|
const extractedFolder = extractedEntries.find((e) => e.toLowerCase() === rootFolder.toLowerCase());
|
|
4131
4094
|
if (!extractedFolder) {
|
|
4132
4095
|
result.failureReason = `No ${rootFolder}/ folder in archive`;
|
|
4133
4096
|
return result;
|
|
4134
4097
|
}
|
|
4135
|
-
const extractedPath = (0,
|
|
4136
|
-
const manifestPath = (0,
|
|
4137
|
-
if ((0,
|
|
4098
|
+
const extractedPath = (0, import_path7.join)(archive.extractedRoot, extractedFolder);
|
|
4099
|
+
const manifestPath = (0, import_path7.join)(extractedPath, "install.json");
|
|
4100
|
+
if ((0, import_fs7.existsSync)(manifestPath)) {
|
|
4138
4101
|
await installViaBundle(manifestPath, extractedPath, targetDir, agent, result);
|
|
4139
4102
|
} else if (agent === "github-copilot") {
|
|
4140
4103
|
installCopilotFactory(extractedPath, targetDir, result);
|
|
@@ -4145,19 +4108,14 @@ async function fetchFactory(token, repo, targetDir, agent) {
|
|
|
4145
4108
|
} catch (error) {
|
|
4146
4109
|
result.failureReason = error instanceof Error ? error.message : String(error);
|
|
4147
4110
|
} finally {
|
|
4148
|
-
|
|
4149
|
-
try {
|
|
4150
|
-
(0, import_fs4.rmSync)(tempDir, { recursive: true, force: true });
|
|
4151
|
-
} catch {
|
|
4152
|
-
}
|
|
4153
|
-
}
|
|
4111
|
+
archive?.cleanup();
|
|
4154
4112
|
}
|
|
4155
4113
|
return result;
|
|
4156
4114
|
}
|
|
4157
4115
|
async function installViaBundle(manifestPath, extractedPath, targetDir, agent, result) {
|
|
4158
4116
|
let manifest;
|
|
4159
4117
|
try {
|
|
4160
|
-
manifest = JSON.parse((0,
|
|
4118
|
+
manifest = JSON.parse((0, import_fs7.readFileSync)(manifestPath, "utf-8"));
|
|
4161
4119
|
} catch (e) {
|
|
4162
4120
|
throw new Error(`install.json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
4163
4121
|
}
|
|
@@ -4175,43 +4133,43 @@ async function installViaBundle(manifestPath, extractedPath, targetDir, agent, r
|
|
|
4175
4133
|
result.filesInstalled = runResult.filesTouched;
|
|
4176
4134
|
}
|
|
4177
4135
|
function installClaudeFactory(extractedPath, targetDir, result) {
|
|
4178
|
-
const claudeDir = (0,
|
|
4179
|
-
const factoryDir = (0,
|
|
4180
|
-
const agentsSrc = (0,
|
|
4181
|
-
if ((0,
|
|
4182
|
-
|
|
4136
|
+
const claudeDir = (0, import_path7.join)(targetDir, ".claude");
|
|
4137
|
+
const factoryDir = (0, import_path7.join)(targetDir, "factory");
|
|
4138
|
+
const agentsSrc = (0, import_path7.join)(extractedPath, "agents");
|
|
4139
|
+
if ((0, import_fs7.existsSync)(agentsSrc)) {
|
|
4140
|
+
copyDirectory(agentsSrc, (0, import_path7.join)(claudeDir, "agents"));
|
|
4183
4141
|
result.filesInstalled.push(".claude/agents/");
|
|
4184
4142
|
}
|
|
4185
|
-
const hooksSrc = (0,
|
|
4186
|
-
if ((0,
|
|
4187
|
-
|
|
4143
|
+
const hooksSrc = (0, import_path7.join)(extractedPath, "hooks");
|
|
4144
|
+
if ((0, import_fs7.existsSync)(hooksSrc)) {
|
|
4145
|
+
copyDirectory(hooksSrc, (0, import_path7.join)(claudeDir, "hooks"));
|
|
4188
4146
|
result.filesInstalled.push(".claude/hooks/");
|
|
4189
4147
|
}
|
|
4190
|
-
const skillsSrc = (0,
|
|
4191
|
-
if ((0,
|
|
4192
|
-
|
|
4148
|
+
const skillsSrc = (0, import_path7.join)(extractedPath, "skills");
|
|
4149
|
+
if ((0, import_fs7.existsSync)(skillsSrc)) {
|
|
4150
|
+
copyDirectory(skillsSrc, (0, import_path7.join)(claudeDir, "skills"));
|
|
4193
4151
|
result.filesInstalled.push(".claude/skills/");
|
|
4194
4152
|
}
|
|
4195
|
-
const workflowSrc = (0,
|
|
4196
|
-
if ((0,
|
|
4197
|
-
|
|
4153
|
+
const workflowSrc = (0, import_path7.join)(extractedPath, "workflow");
|
|
4154
|
+
if ((0, import_fs7.existsSync)(workflowSrc)) {
|
|
4155
|
+
copyDirectory(workflowSrc, (0, import_path7.join)(factoryDir, "workflow"));
|
|
4198
4156
|
result.filesInstalled.push("factory/workflow/");
|
|
4199
4157
|
}
|
|
4200
|
-
const utilsSrc = (0,
|
|
4201
|
-
if ((0,
|
|
4202
|
-
|
|
4158
|
+
const utilsSrc = (0, import_path7.join)(extractedPath, "utils");
|
|
4159
|
+
if ((0, import_fs7.existsSync)(utilsSrc)) {
|
|
4160
|
+
copyDirectory(utilsSrc, (0, import_path7.join)(factoryDir, "utils"));
|
|
4203
4161
|
result.filesInstalled.push("factory/utils/");
|
|
4204
4162
|
}
|
|
4205
|
-
const docsSrc = (0,
|
|
4206
|
-
if ((0,
|
|
4207
|
-
|
|
4163
|
+
const docsSrc = (0, import_path7.join)(extractedPath, "docs");
|
|
4164
|
+
if ((0, import_fs7.existsSync)(docsSrc)) {
|
|
4165
|
+
copyDirectory(docsSrc, (0, import_path7.join)(factoryDir, "docs"));
|
|
4208
4166
|
result.filesInstalled.push("factory/docs/");
|
|
4209
4167
|
}
|
|
4210
|
-
const rootEntries = (0,
|
|
4168
|
+
const rootEntries = (0, import_fs7.readdirSync)(extractedPath, { withFileTypes: true });
|
|
4211
4169
|
for (const entry of rootEntries) {
|
|
4212
4170
|
if (entry.isFile()) {
|
|
4213
|
-
if (!(0,
|
|
4214
|
-
(0,
|
|
4171
|
+
if (!(0, import_fs7.existsSync)(factoryDir)) (0, import_fs7.mkdirSync)(factoryDir, { recursive: true });
|
|
4172
|
+
(0, import_fs7.copyFileSync)((0, import_path7.join)(extractedPath, entry.name), (0, import_path7.join)(factoryDir, entry.name));
|
|
4215
4173
|
result.filesInstalled.push(`factory/${entry.name}`);
|
|
4216
4174
|
}
|
|
4217
4175
|
}
|
|
@@ -4219,9 +4177,9 @@ function installClaudeFactory(extractedPath, targetDir, result) {
|
|
|
4219
4177
|
result.filesInstalled.push(".claude/settings.json");
|
|
4220
4178
|
}
|
|
4221
4179
|
function installCopilotFactory(extractedPath, targetDir, result) {
|
|
4222
|
-
const factoryDir = (0,
|
|
4223
|
-
|
|
4224
|
-
const entries = (0,
|
|
4180
|
+
const factoryDir = (0, import_path7.join)(targetDir, "factory");
|
|
4181
|
+
copyDirectory(extractedPath, factoryDir);
|
|
4182
|
+
const entries = (0, import_fs7.readdirSync)(extractedPath, { withFileTypes: true });
|
|
4225
4183
|
for (const entry of entries) {
|
|
4226
4184
|
if (entry.isDirectory()) {
|
|
4227
4185
|
result.filesInstalled.push(`factory/${entry.name}/`);
|
|
@@ -4231,11 +4189,11 @@ function installCopilotFactory(extractedPath, targetDir, result) {
|
|
|
4231
4189
|
}
|
|
4232
4190
|
}
|
|
4233
4191
|
function configureSettings(claudeDir) {
|
|
4234
|
-
const settingsPath = (0,
|
|
4192
|
+
const settingsPath = (0, import_path7.join)(claudeDir, "settings.json");
|
|
4235
4193
|
let settings = {};
|
|
4236
|
-
if ((0,
|
|
4194
|
+
if ((0, import_fs7.existsSync)(settingsPath)) {
|
|
4237
4195
|
try {
|
|
4238
|
-
settings = JSON.parse((0,
|
|
4196
|
+
settings = JSON.parse((0, import_fs7.readFileSync)(settingsPath, "utf8"));
|
|
4239
4197
|
} catch {
|
|
4240
4198
|
}
|
|
4241
4199
|
}
|
|
@@ -4286,26 +4244,13 @@ function configureSettings(claudeDir) {
|
|
|
4286
4244
|
if (!hasMonitor) settings.hooks.UserPromptSubmit.push(contextMonitorHook);
|
|
4287
4245
|
}
|
|
4288
4246
|
settings.statusLine = { type: "command", command: "node .claude/hooks/factory-statusline.cjs" };
|
|
4289
|
-
if (!(0,
|
|
4290
|
-
(0,
|
|
4291
|
-
}
|
|
4292
|
-
function copyDirectory3(source, target) {
|
|
4293
|
-
if (!(0, import_fs4.existsSync)(target)) (0, import_fs4.mkdirSync)(target, { recursive: true });
|
|
4294
|
-
const entries = (0, import_fs4.readdirSync)(source, { withFileTypes: true });
|
|
4295
|
-
for (const entry of entries) {
|
|
4296
|
-
const sourcePath = (0, import_path4.join)(source, entry.name);
|
|
4297
|
-
const targetPath = (0, import_path4.join)(target, entry.name);
|
|
4298
|
-
if (entry.isDirectory()) {
|
|
4299
|
-
copyDirectory3(sourcePath, targetPath);
|
|
4300
|
-
} else {
|
|
4301
|
-
(0, import_fs4.copyFileSync)(sourcePath, targetPath);
|
|
4302
|
-
}
|
|
4303
|
-
}
|
|
4247
|
+
if (!(0, import_fs7.existsSync)(claudeDir)) (0, import_fs7.mkdirSync)(claudeDir, { recursive: true });
|
|
4248
|
+
(0, import_fs7.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4304
4249
|
}
|
|
4305
4250
|
|
|
4306
4251
|
// src/steps/setup/write-instructions.ts
|
|
4307
|
-
var
|
|
4308
|
-
var
|
|
4252
|
+
var import_fs8 = require("fs");
|
|
4253
|
+
var import_path8 = require("path");
|
|
4309
4254
|
|
|
4310
4255
|
// src/steps/shared.ts
|
|
4311
4256
|
function getFilteredMcpConfig(ctx) {
|
|
@@ -4340,96 +4285,60 @@ var write_instructions_default = defineStep({
|
|
|
4340
4285
|
const includeFactorySection = factoryInstalled && !factoryBundleManaged;
|
|
4341
4286
|
const content = buildCombinedInstructions(domains, filteredMcpConfig, agent, projectPath, includeFactorySection);
|
|
4342
4287
|
const target = getAgentTarget(agent);
|
|
4343
|
-
const filePath = (0,
|
|
4344
|
-
const fileDir = (0,
|
|
4345
|
-
if (!(0,
|
|
4346
|
-
if (agent === "cursor" && !(0,
|
|
4347
|
-
(0,
|
|
4288
|
+
const filePath = (0, import_path8.join)(projectPath, target.instructions);
|
|
4289
|
+
const fileDir = (0, import_path8.dirname)(filePath);
|
|
4290
|
+
if (!(0, import_fs8.existsSync)(fileDir)) (0, import_fs8.mkdirSync)(fileDir, { recursive: true });
|
|
4291
|
+
if (agent === "cursor" && !(0, import_fs8.existsSync)(filePath)) {
|
|
4292
|
+
(0, import_fs8.writeFileSync)(filePath, `---
|
|
4348
4293
|
description: AI development instructions from One-Shot Installer
|
|
4349
4294
|
alwaysApply: true
|
|
4350
4295
|
---
|
|
4351
4296
|
|
|
4352
4297
|
`, "utf-8");
|
|
4353
4298
|
}
|
|
4354
|
-
|
|
4299
|
+
upsertMarkerBlock(filePath, MARKER_START, MARKER_END, content, {
|
|
4300
|
+
onCorrupt: (file) => {
|
|
4301
|
+
const fileName = file.split(/[/\\]/).pop() ?? file;
|
|
4302
|
+
const box = [
|
|
4303
|
+
"",
|
|
4304
|
+
red(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
|
|
4305
|
+
red(" \u2551 \u26A0 INSTRUCTION FILE COULD NOT BE UPDATED \u2551"),
|
|
4306
|
+
red(" \u2551 \u2551"),
|
|
4307
|
+
red(` \u2551 ${fileName} has corrupted markers.`.padEnd(52) + "\u2551"),
|
|
4308
|
+
red(" \u2551 Fix: delete the lines containing \u2551"),
|
|
4309
|
+
red(" \u2551 <!-- one-shot-installer:start --> \u2551"),
|
|
4310
|
+
red(" \u2551 <!-- one-shot-installer:end --> \u2551"),
|
|
4311
|
+
red(" \u2551 Then re-run the installer. \u2551"),
|
|
4312
|
+
red(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
|
|
4313
|
+
""
|
|
4314
|
+
].join("\n");
|
|
4315
|
+
console.log(box);
|
|
4316
|
+
throw new Error(`Corrupted markers in ${fileName} \u2014 fix manually and re-run`);
|
|
4317
|
+
}
|
|
4318
|
+
});
|
|
4355
4319
|
const bundleSnippets = ctx.installed.bundleInstructions ?? {};
|
|
4356
4320
|
for (const bundleName of Object.keys(bundleSnippets).sort()) {
|
|
4357
|
-
|
|
4321
|
+
upsertMarkerBlock(
|
|
4322
|
+
filePath,
|
|
4323
|
+
`<!-- ${bundleName}:start -->`,
|
|
4324
|
+
`<!-- ${bundleName}:end -->`,
|
|
4325
|
+
bundleSnippets[bundleName]
|
|
4326
|
+
);
|
|
4358
4327
|
}
|
|
4359
4328
|
return { status: "success", message: target.instructions };
|
|
4360
4329
|
}
|
|
4361
4330
|
});
|
|
4362
|
-
function upsertBlock(filePath, block) {
|
|
4363
|
-
const marked = `${MARKER_START}
|
|
4364
|
-
${block}
|
|
4365
|
-
${MARKER_END}`;
|
|
4366
|
-
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
4367
|
-
(0, import_fs5.writeFileSync)(filePath, marked, "utf-8");
|
|
4368
|
-
return;
|
|
4369
|
-
}
|
|
4370
|
-
const existing = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
4371
|
-
const start = existing.indexOf(MARKER_START);
|
|
4372
|
-
const end = existing.indexOf(MARKER_END);
|
|
4373
|
-
const hasStart = start !== -1;
|
|
4374
|
-
const hasEnd = end !== -1;
|
|
4375
|
-
if (hasStart && !hasEnd || !hasStart && hasEnd || hasStart && hasEnd && end < start) {
|
|
4376
|
-
const fileName = filePath.split(/[/\\]/).pop() ?? filePath;
|
|
4377
|
-
const box = [
|
|
4378
|
-
"",
|
|
4379
|
-
red(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
|
|
4380
|
-
red(" \u2551 \u26A0 INSTRUCTION FILE COULD NOT BE UPDATED \u2551"),
|
|
4381
|
-
red(" \u2551 \u2551"),
|
|
4382
|
-
red(` \u2551 ${fileName} has corrupted markers.`.padEnd(52) + "\u2551"),
|
|
4383
|
-
red(" \u2551 Fix: delete the lines containing \u2551"),
|
|
4384
|
-
red(" \u2551 <!-- one-shot-installer:start --> \u2551"),
|
|
4385
|
-
red(" \u2551 <!-- one-shot-installer:end --> \u2551"),
|
|
4386
|
-
red(" \u2551 Then re-run the installer. \u2551"),
|
|
4387
|
-
red(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
|
|
4388
|
-
""
|
|
4389
|
-
].join("\n");
|
|
4390
|
-
console.log(box);
|
|
4391
|
-
throw new Error(`Corrupted markers in ${fileName} \u2014 fix manually and re-run`);
|
|
4392
|
-
}
|
|
4393
|
-
if (hasStart && hasEnd) {
|
|
4394
|
-
const updated = existing.slice(0, start) + marked + existing.slice(end + MARKER_END.length);
|
|
4395
|
-
(0, import_fs5.writeFileSync)(filePath, updated, "utf-8");
|
|
4396
|
-
} else {
|
|
4397
|
-
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4398
|
-
(0, import_fs5.writeFileSync)(filePath, existing + separator + marked, "utf-8");
|
|
4399
|
-
}
|
|
4400
|
-
}
|
|
4401
|
-
function upsertBundleBlock(filePath, bundleName, snippet) {
|
|
4402
|
-
const startMarker = `<!-- ${bundleName}:start -->`;
|
|
4403
|
-
const endMarker = `<!-- ${bundleName}:end -->`;
|
|
4404
|
-
const block = `${startMarker}
|
|
4405
|
-
${snippet}
|
|
4406
|
-
${endMarker}`;
|
|
4407
|
-
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
4408
|
-
(0, import_fs5.writeFileSync)(filePath, block + "\n", "utf-8");
|
|
4409
|
-
return;
|
|
4410
|
-
}
|
|
4411
|
-
const existing = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
4412
|
-
const s = existing.indexOf(startMarker);
|
|
4413
|
-
const e = existing.indexOf(endMarker);
|
|
4414
|
-
if (s !== -1 && e !== -1 && e > s) {
|
|
4415
|
-
const updated = existing.slice(0, s) + block + existing.slice(e + endMarker.length);
|
|
4416
|
-
(0, import_fs5.writeFileSync)(filePath, updated, "utf-8");
|
|
4417
|
-
} else {
|
|
4418
|
-
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4419
|
-
(0, import_fs5.writeFileSync)(filePath, existing + separator + block + "\n", "utf-8");
|
|
4420
|
-
}
|
|
4421
|
-
}
|
|
4422
4331
|
function collectMdFiles(dir) {
|
|
4423
|
-
if (!(0,
|
|
4424
|
-
const entries = (0,
|
|
4332
|
+
if (!(0, import_fs8.existsSync)(dir)) return [];
|
|
4333
|
+
const entries = (0, import_fs8.readdirSync)(dir, { recursive: true });
|
|
4425
4334
|
return entries.filter((entry) => {
|
|
4426
|
-
const fullPath = (0,
|
|
4427
|
-
return entry.endsWith(".md") && (0,
|
|
4335
|
+
const fullPath = (0, import_path8.join)(dir, entry);
|
|
4336
|
+
return entry.endsWith(".md") && (0, import_fs8.statSync)(fullPath).isFile();
|
|
4428
4337
|
}).map((entry) => entry.replace(/\\/g, "/")).sort();
|
|
4429
4338
|
}
|
|
4430
4339
|
function extractFirstHeading(filePath) {
|
|
4431
4340
|
try {
|
|
4432
|
-
const content = (0,
|
|
4341
|
+
const content = (0, import_fs8.readFileSync)(filePath, "utf-8");
|
|
4433
4342
|
const withoutFrontmatter = content.replace(/^---[\s\S]*?---\s*/, "");
|
|
4434
4343
|
const match = withoutFrontmatter.match(/^#\s+(.+)/m);
|
|
4435
4344
|
if (match) {
|
|
@@ -4447,16 +4356,16 @@ function formatPathRef(contextPath, description, agent) {
|
|
|
4447
4356
|
return `- \`${contextPath}\` \u2014 ${description}`;
|
|
4448
4357
|
}
|
|
4449
4358
|
function resolveDomainFolder(domain, contextsDir) {
|
|
4450
|
-
if ((0,
|
|
4359
|
+
if ((0, import_fs8.existsSync)(contextsDir)) {
|
|
4451
4360
|
try {
|
|
4452
|
-
const entries = (0,
|
|
4361
|
+
const entries = (0, import_fs8.readdirSync)(contextsDir);
|
|
4453
4362
|
const match = entries.find((e) => e.toLowerCase() === domain.toLowerCase());
|
|
4454
|
-
if (match) return { folderName: match, folderPath: (0,
|
|
4363
|
+
if (match) return { folderName: match, folderPath: (0, import_path8.join)(contextsDir, match) };
|
|
4455
4364
|
} catch {
|
|
4456
4365
|
}
|
|
4457
4366
|
}
|
|
4458
4367
|
const fallback = domain.toUpperCase();
|
|
4459
|
-
return { folderName: fallback, folderPath: (0,
|
|
4368
|
+
return { folderName: fallback, folderPath: (0, import_path8.join)(contextsDir, fallback) };
|
|
4460
4369
|
}
|
|
4461
4370
|
function buildContextRefsSection(domains, agent, contextsDir) {
|
|
4462
4371
|
const lines2 = [
|
|
@@ -4468,34 +4377,34 @@ function buildContextRefsSection(domains, agent, contextsDir) {
|
|
|
4468
4377
|
let hasAnyFiles = false;
|
|
4469
4378
|
for (const domain of domains) {
|
|
4470
4379
|
const { folderName, folderPath: domainPath } = resolveDomainFolder(domain, contextsDir);
|
|
4471
|
-
if (!(0,
|
|
4380
|
+
if (!(0, import_fs8.existsSync)(domainPath)) continue;
|
|
4472
4381
|
const domainFiles = [];
|
|
4473
|
-
const ctxInstructions = (0,
|
|
4474
|
-
if ((0,
|
|
4382
|
+
const ctxInstructions = (0, import_path8.join)(domainPath, "context-instructions.md");
|
|
4383
|
+
if ((0, import_fs8.existsSync)(ctxInstructions)) {
|
|
4475
4384
|
const desc = extractFirstHeading(ctxInstructions);
|
|
4476
4385
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/context-instructions.md`, desc, agent));
|
|
4477
4386
|
}
|
|
4478
|
-
const instructionsMd = (0,
|
|
4479
|
-
if ((0,
|
|
4387
|
+
const instructionsMd = (0, import_path8.join)(domainPath, "core", "instructions.md");
|
|
4388
|
+
if ((0, import_fs8.existsSync)(instructionsMd)) {
|
|
4480
4389
|
const desc = extractFirstHeading(instructionsMd);
|
|
4481
4390
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/instructions.md`, `${desc} (start here)`, agent));
|
|
4482
4391
|
}
|
|
4483
|
-
const coreDir = (0,
|
|
4484
|
-
if ((0,
|
|
4392
|
+
const coreDir = (0, import_path8.join)(domainPath, "core");
|
|
4393
|
+
if ((0, import_fs8.existsSync)(coreDir)) {
|
|
4485
4394
|
const coreFiles = collectMdFiles(coreDir).filter((f) => f !== "instructions.md" && !f.startsWith("instructions/"));
|
|
4486
4395
|
for (const file of coreFiles) {
|
|
4487
|
-
const desc = extractFirstHeading((0,
|
|
4396
|
+
const desc = extractFirstHeading((0, import_path8.join)(coreDir, file));
|
|
4488
4397
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/core/${file}`, desc, agent));
|
|
4489
4398
|
}
|
|
4490
4399
|
}
|
|
4491
|
-
const refDir = (0,
|
|
4492
|
-
if ((0,
|
|
4400
|
+
const refDir = (0, import_path8.join)(domainPath, "reference");
|
|
4401
|
+
if ((0, import_fs8.existsSync)(refDir)) {
|
|
4493
4402
|
const refFiles = collectMdFiles(refDir);
|
|
4494
4403
|
if (refFiles.length > 0) {
|
|
4495
4404
|
domainFiles.push(``);
|
|
4496
4405
|
domainFiles.push(`**Reference & cheat sheets:**`);
|
|
4497
4406
|
for (const file of refFiles) {
|
|
4498
|
-
const desc = extractFirstHeading((0,
|
|
4407
|
+
const desc = extractFirstHeading((0, import_path8.join)(refDir, file));
|
|
4499
4408
|
domainFiles.push(formatPathRef(`_ai-context/${folderName}/reference/${file}`, desc, agent));
|
|
4500
4409
|
}
|
|
4501
4410
|
}
|
|
@@ -4573,7 +4482,7 @@ function buildFactorySection(agent) {
|
|
|
4573
4482
|
].join("\n");
|
|
4574
4483
|
}
|
|
4575
4484
|
function buildCombinedInstructions(domains, mcpConfig, agent, projectPath, factoryInstalled) {
|
|
4576
|
-
const contextsDir = (0,
|
|
4485
|
+
const contextsDir = (0, import_path8.join)(projectPath, "_ai-context");
|
|
4577
4486
|
const lines2 = [`# AI Development Instructions`, ``, `> Generated by One-Shot Installer`, ``];
|
|
4578
4487
|
lines2.push(buildContextRefsSection(domains, agent, contextsDir));
|
|
4579
4488
|
if (Object.keys(mcpConfig).length > 0) lines2.push(buildMCPSection(mcpConfig));
|
|
@@ -4582,8 +4491,8 @@ function buildCombinedInstructions(domains, mcpConfig, agent, projectPath, facto
|
|
|
4582
4491
|
}
|
|
4583
4492
|
|
|
4584
4493
|
// src/steps/setup/write-mcp-config.ts
|
|
4585
|
-
var
|
|
4586
|
-
var
|
|
4494
|
+
var import_fs9 = require("fs");
|
|
4495
|
+
var import_path9 = require("path");
|
|
4587
4496
|
var red2 = (text) => `\x1B[31m${text}\x1B[0m`;
|
|
4588
4497
|
var write_mcp_config_default = defineStep({
|
|
4589
4498
|
name: "write-mcp-config",
|
|
@@ -4592,15 +4501,15 @@ var write_mcp_config_default = defineStep({
|
|
|
4592
4501
|
execute: async (ctx) => {
|
|
4593
4502
|
const filteredMcpConfig = getFilteredMcpConfig(ctx);
|
|
4594
4503
|
const target = getAgentTarget(ctx.config.agent);
|
|
4595
|
-
const mcpJsonPath = (0,
|
|
4504
|
+
const mcpJsonPath = (0, import_path9.join)(ctx.config.projectPath, target.mcpConfig);
|
|
4596
4505
|
if (target.mcpDir) {
|
|
4597
|
-
const dir = (0,
|
|
4598
|
-
if (!(0,
|
|
4506
|
+
const dir = (0, import_path9.join)(ctx.config.projectPath, target.mcpDir);
|
|
4507
|
+
if (!(0, import_fs9.existsSync)(dir)) (0, import_fs9.mkdirSync)(dir, { recursive: true });
|
|
4599
4508
|
}
|
|
4600
4509
|
let existingFile = {};
|
|
4601
|
-
if ((0,
|
|
4510
|
+
if ((0, import_fs9.existsSync)(mcpJsonPath)) {
|
|
4602
4511
|
try {
|
|
4603
|
-
existingFile = JSON.parse((0,
|
|
4512
|
+
existingFile = JSON.parse((0, import_fs9.readFileSync)(mcpJsonPath, "utf-8"));
|
|
4604
4513
|
} catch {
|
|
4605
4514
|
const box = [
|
|
4606
4515
|
"",
|
|
@@ -4626,7 +4535,7 @@ var write_mcp_config_default = defineStep({
|
|
|
4626
4535
|
}
|
|
4627
4536
|
const mergedServers = { ...existingServers, ...newServers };
|
|
4628
4537
|
const outputFile = { ...existingFile, [target.mcpRootKey]: mergedServers };
|
|
4629
|
-
(0,
|
|
4538
|
+
(0, import_fs9.writeFileSync)(mcpJsonPath, JSON.stringify(outputFile, null, 2), "utf-8");
|
|
4630
4539
|
ctx.installed.mcpServersAdded = addedServers;
|
|
4631
4540
|
return {
|
|
4632
4541
|
status: "success",
|
|
@@ -4636,7 +4545,7 @@ var write_mcp_config_default = defineStep({
|
|
|
4636
4545
|
});
|
|
4637
4546
|
|
|
4638
4547
|
// src/steps/setup/run-mcp-install-commands.ts
|
|
4639
|
-
var
|
|
4548
|
+
var import_child_process3 = require("child_process");
|
|
4640
4549
|
var run_mcp_install_commands_default = defineStep({
|
|
4641
4550
|
name: "run-mcp-install-commands",
|
|
4642
4551
|
label: "Registering MCP servers with Claude Code",
|
|
@@ -4658,7 +4567,7 @@ var run_mcp_install_commands_default = defineStep({
|
|
|
4658
4567
|
for (const [name, cfg] of Object.entries(filtered)) {
|
|
4659
4568
|
const cmd = cfg.installCommand;
|
|
4660
4569
|
if (typeof cmd !== "string" || cmd.length === 0) continue;
|
|
4661
|
-
const result = (0,
|
|
4570
|
+
const result = (0, import_child_process3.spawnSync)(cmd, {
|
|
4662
4571
|
shell: true,
|
|
4663
4572
|
stdio: "pipe",
|
|
4664
4573
|
encoding: "utf-8",
|
|
@@ -4672,7 +4581,6 @@ var run_mcp_install_commands_default = defineStep({
|
|
|
4672
4581
|
failed.push({ name, reason });
|
|
4673
4582
|
}
|
|
4674
4583
|
}
|
|
4675
|
-
ctx.installed.mcpInstallCommandsRun = { succeeded, failed };
|
|
4676
4584
|
if (failed.length === 0) {
|
|
4677
4585
|
return {
|
|
4678
4586
|
status: "success",
|
|
@@ -4691,13 +4599,12 @@ var run_mcp_install_commands_default = defineStep({
|
|
|
4691
4599
|
}
|
|
4692
4600
|
});
|
|
4693
4601
|
function isClaudeCliAvailable() {
|
|
4694
|
-
const check2 = (0,
|
|
4602
|
+
const check2 = (0, import_child_process3.spawnSync)("claude --version", { shell: true, stdio: "ignore" });
|
|
4695
4603
|
return check2.status === 0;
|
|
4696
4604
|
}
|
|
4697
4605
|
|
|
4698
4606
|
// src/steps/setup/update-gitignore.ts
|
|
4699
|
-
var
|
|
4700
|
-
var import_path7 = require("path");
|
|
4607
|
+
var import_path10 = require("path");
|
|
4701
4608
|
var MARKER_START2 = "# one-shot-installer:start";
|
|
4702
4609
|
var MARKER_END2 = "# one-shot-installer:end";
|
|
4703
4610
|
var CORE_GITIGNORE_ENTRIES = [
|
|
@@ -4732,40 +4639,52 @@ var update_gitignore_default = defineStep({
|
|
|
4732
4639
|
name: "update-gitignore",
|
|
4733
4640
|
label: "Updating .gitignore",
|
|
4734
4641
|
execute: async (ctx) => {
|
|
4735
|
-
const gitignorePath = (0,
|
|
4642
|
+
const gitignorePath = (0, import_path10.join)(ctx.config.projectPath, ".gitignore");
|
|
4736
4643
|
const factoryInstalled = ctx.installed.factoryInstalled ?? false;
|
|
4737
4644
|
const factoryBundleManaged = ctx.installed.factoryBundleManaged ?? false;
|
|
4738
4645
|
const includeLegacyFactory = factoryInstalled && !factoryBundleManaged;
|
|
4739
4646
|
const entries = includeLegacyFactory ? [...CORE_GITIGNORE_ENTRIES, "", ...LEGACY_FACTORY_GITIGNORE_ENTRIES] : CORE_GITIGNORE_ENTRIES;
|
|
4740
|
-
|
|
4741
|
-
if (!(0, import_fs7.existsSync)(gitignorePath)) {
|
|
4742
|
-
(0, import_fs7.writeFileSync)(gitignorePath, block + "\n", "utf-8");
|
|
4743
|
-
return { status: "success" };
|
|
4744
|
-
}
|
|
4745
|
-
const existing = (0, import_fs7.readFileSync)(gitignorePath, "utf-8");
|
|
4746
|
-
const start = existing.indexOf(MARKER_START2);
|
|
4747
|
-
const end = existing.indexOf(MARKER_END2);
|
|
4748
|
-
if (start !== -1 && end !== -1 && end > start) {
|
|
4749
|
-
const updated = existing.slice(0, start) + block + existing.slice(end + MARKER_END2.length);
|
|
4750
|
-
(0, import_fs7.writeFileSync)(gitignorePath, updated, "utf-8");
|
|
4751
|
-
} else {
|
|
4752
|
-
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4753
|
-
(0, import_fs7.writeFileSync)(gitignorePath, existing + separator + block + "\n", "utf-8");
|
|
4754
|
-
}
|
|
4647
|
+
upsertMarkerBlock(gitignorePath, MARKER_START2, MARKER_END2, entries.join("\n"));
|
|
4755
4648
|
return { status: "success" };
|
|
4756
4649
|
}
|
|
4757
4650
|
});
|
|
4758
4651
|
|
|
4759
4652
|
// src/steps/setup/write-state.ts
|
|
4760
|
-
var
|
|
4761
|
-
var
|
|
4653
|
+
var import_fs10 = require("fs");
|
|
4654
|
+
var import_path11 = require("path");
|
|
4762
4655
|
var import_os2 = require("os");
|
|
4763
|
-
|
|
4656
|
+
|
|
4657
|
+
// package.json
|
|
4658
|
+
var package_default = {
|
|
4659
|
+
name: "one-shot-setup",
|
|
4660
|
+
version: "0.6.0",
|
|
4661
|
+
description: "One-shot installation tool for The Machine toolchain",
|
|
4662
|
+
type: "module",
|
|
4663
|
+
main: "src/index.ts",
|
|
4664
|
+
scripts: {
|
|
4665
|
+
dev: "tsx src/index.ts",
|
|
4666
|
+
build: 'esbuild src/index.ts --bundle --platform=node --banner:js="#!/usr/bin/env node" --outfile=wrapper/bin/installer.js',
|
|
4667
|
+
"publish:wrapper": "cd wrapper && npm publish"
|
|
4668
|
+
},
|
|
4669
|
+
dependencies: {
|
|
4670
|
+
"@inquirer/prompts": "^5.0.0"
|
|
4671
|
+
},
|
|
4672
|
+
devDependencies: {
|
|
4673
|
+
"@types/node": "^20.11.0",
|
|
4674
|
+
esbuild: "latest",
|
|
4675
|
+
tsx: "latest"
|
|
4676
|
+
},
|
|
4677
|
+
engines: {
|
|
4678
|
+
node: ">=18.0.0"
|
|
4679
|
+
}
|
|
4680
|
+
};
|
|
4681
|
+
|
|
4682
|
+
// src/steps/setup/write-state.ts
|
|
4764
4683
|
var write_state_default = defineStep({
|
|
4765
4684
|
name: "write-state",
|
|
4766
4685
|
label: "Saving installation state",
|
|
4767
4686
|
execute: async (ctx) => {
|
|
4768
|
-
const statePath = (0,
|
|
4687
|
+
const statePath = (0, import_path11.join)(ctx.config.projectPath, ".one-shot-state.json");
|
|
4769
4688
|
const mcpServersAdded = ctx.installed.mcpServersAdded ?? [];
|
|
4770
4689
|
const filteredMcpConfig = Object.fromEntries(
|
|
4771
4690
|
Object.entries(ctx.config.mcpConfig).filter(([name]) => mcpServersAdded.includes(name))
|
|
@@ -4773,7 +4692,7 @@ var write_state_default = defineStep({
|
|
|
4773
4692
|
const target = getAgentTarget(ctx.config.agent);
|
|
4774
4693
|
const state = {
|
|
4775
4694
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4776
|
-
installerVersion:
|
|
4695
|
+
installerVersion: package_default.version,
|
|
4777
4696
|
releaseVersion: ctx.config.releaseVersion,
|
|
4778
4697
|
contextReleaseVersion: ctx.installed.contextReleaseVersion ?? null,
|
|
4779
4698
|
projectPath: ctx.config.projectPath,
|
|
@@ -4788,10 +4707,10 @@ var write_state_default = defineStep({
|
|
|
4788
4707
|
mcpConfig: target.mcpConfig,
|
|
4789
4708
|
contexts: "_ai-context/",
|
|
4790
4709
|
factory: ctx.installed.factoryInstalled ? "factory/" : null,
|
|
4791
|
-
globalConfig: (0,
|
|
4710
|
+
globalConfig: (0, import_path11.join)((0, import_os2.homedir)(), ".one-shot-installer")
|
|
4792
4711
|
}
|
|
4793
4712
|
};
|
|
4794
|
-
(0,
|
|
4713
|
+
(0, import_fs10.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
4795
4714
|
return { status: "success" };
|
|
4796
4715
|
}
|
|
4797
4716
|
});
|
|
@@ -4806,27 +4725,27 @@ var dim = (text) => `\x1B[2m${text}\x1B[0m`;
|
|
|
4806
4725
|
var yellow = (text) => `\x1B[33m${text}\x1B[0m`;
|
|
4807
4726
|
var green = (text) => `\x1B[32m${text}\x1B[0m`;
|
|
4808
4727
|
function detectMarkerFileMode(filePath, markerStart) {
|
|
4809
|
-
if (!(0,
|
|
4810
|
-
const content = (0,
|
|
4728
|
+
if (!(0, import_fs11.existsSync)(filePath)) return green("create");
|
|
4729
|
+
const content = (0, import_fs11.readFileSync)(filePath, "utf-8");
|
|
4811
4730
|
if (content.includes(markerStart)) return yellow("update");
|
|
4812
4731
|
return yellow("append");
|
|
4813
4732
|
}
|
|
4814
4733
|
function detectMCPFileMode(filePath) {
|
|
4815
|
-
if (!(0,
|
|
4734
|
+
if (!(0, import_fs11.existsSync)(filePath)) return green("create");
|
|
4816
4735
|
return yellow("merge");
|
|
4817
4736
|
}
|
|
4818
4737
|
function detectContextMode(projectPath, domain) {
|
|
4819
|
-
const contextDir = (0,
|
|
4820
|
-
const contextDirLower = (0,
|
|
4821
|
-
if ((0,
|
|
4738
|
+
const contextDir = (0, import_path12.join)(projectPath, "_ai-context", domain.toUpperCase());
|
|
4739
|
+
const contextDirLower = (0, import_path12.join)(projectPath, "_ai-context", domain);
|
|
4740
|
+
if ((0, import_fs11.existsSync)(contextDir) || (0, import_fs11.existsSync)(contextDirLower)) return yellow("overwrite");
|
|
4822
4741
|
return green("create");
|
|
4823
4742
|
}
|
|
4824
4743
|
function waitForEnter() {
|
|
4825
4744
|
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
4826
|
-
return new Promise((
|
|
4745
|
+
return new Promise((resolve4) => {
|
|
4827
4746
|
rl.question(" Press Enter to close...", () => {
|
|
4828
4747
|
rl.close();
|
|
4829
|
-
|
|
4748
|
+
resolve4();
|
|
4830
4749
|
});
|
|
4831
4750
|
});
|
|
4832
4751
|
}
|
|
@@ -4916,13 +4835,13 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
|
|
|
4916
4835
|
}
|
|
4917
4836
|
const projectInput = await esm_default4({
|
|
4918
4837
|
message: "Project directory:",
|
|
4919
|
-
default: (0,
|
|
4838
|
+
default: (0, import_path12.resolve)(process.cwd())
|
|
4920
4839
|
});
|
|
4921
4840
|
return {
|
|
4922
4841
|
technologies: selectedTechnologies,
|
|
4923
4842
|
agent,
|
|
4924
4843
|
azureDevOpsOrg,
|
|
4925
|
-
projectPath: (0,
|
|
4844
|
+
projectPath: (0, import_path12.resolve)(projectInput),
|
|
4926
4845
|
baseMcpServers: options.baseMcpServers,
|
|
4927
4846
|
mcpConfig,
|
|
4928
4847
|
releaseVersion,
|
|
@@ -4936,9 +4855,9 @@ async function previewAndConfirm(config, options) {
|
|
|
4936
4855
|
const instructionFile = target.instructions;
|
|
4937
4856
|
const mcpConfigFile = target.mcpConfig;
|
|
4938
4857
|
const serverEntries = Object.entries(config.mcpConfig);
|
|
4939
|
-
const instructionFilePath = (0,
|
|
4940
|
-
const mcpConfigFilePath = (0,
|
|
4941
|
-
const gitignorePath = (0,
|
|
4858
|
+
const instructionFilePath = (0, import_path12.join)(config.projectPath, instructionFile);
|
|
4859
|
+
const mcpConfigFilePath = (0, import_path12.join)(config.projectPath, mcpConfigFile);
|
|
4860
|
+
const gitignorePath = (0, import_path12.join)(config.projectPath, ".gitignore");
|
|
4942
4861
|
const instructionMode = detectMarkerFileMode(instructionFilePath, "<!-- one-shot-installer:start -->");
|
|
4943
4862
|
const mcpMode = detectMCPFileMode(mcpConfigFilePath);
|
|
4944
4863
|
const gitignoreMode = detectMarkerFileMode(gitignorePath, "# one-shot-installer:start");
|
|
@@ -4964,7 +4883,7 @@ async function previewAndConfirm(config, options) {
|
|
|
4964
4883
|
console.log(` ${mcpConfigFile.padEnd(domainColWidth + 14)}${mcpMode}`);
|
|
4965
4884
|
console.log(` ${".gitignore".padEnd(domainColWidth + 14)}${gitignoreMode}`);
|
|
4966
4885
|
if (config.installFactory) {
|
|
4967
|
-
const factoryExists = (0,
|
|
4886
|
+
const factoryExists = (0, import_fs11.existsSync)((0, import_path12.join)(config.projectPath, "factory"));
|
|
4968
4887
|
const factoryMode = factoryExists ? yellow("overwrite") : green("create");
|
|
4969
4888
|
console.log(` ${"factory/".padEnd(domainColWidth + 14)}${factoryMode}`);
|
|
4970
4889
|
}
|
|
@@ -4983,6 +4902,7 @@ async function previewAndConfirm(config, options) {
|
|
|
4983
4902
|
function printSummary(result, config) {
|
|
4984
4903
|
const failed = result.entries.filter((e) => e.result.status === "failed");
|
|
4985
4904
|
const succeeded = result.entries.filter((e) => e.result.status === "success");
|
|
4905
|
+
const skipped = result.entries.filter((e) => e.result.status === "skipped");
|
|
4986
4906
|
const hasErrors = failed.length > 0;
|
|
4987
4907
|
console.log("\u2500".repeat(48));
|
|
4988
4908
|
console.log(hasErrors ? " Done (with errors).\n" : " Done.\n");
|
|
@@ -4990,6 +4910,10 @@ function printSummary(result, config) {
|
|
|
4990
4910
|
const msg = entry.result.message ? ` ${entry.result.message}` : "";
|
|
4991
4911
|
console.log(` ${entry.label}${msg}`);
|
|
4992
4912
|
}
|
|
4913
|
+
for (const entry of skipped) {
|
|
4914
|
+
const detail = entry.result.detail ? ` ${entry.result.detail}` : "";
|
|
4915
|
+
console.log(` ${entry.label} (skipped)${detail}`);
|
|
4916
|
+
}
|
|
4993
4917
|
if (hasErrors) {
|
|
4994
4918
|
console.log("\n What went wrong:");
|
|
4995
4919
|
for (const entry of failed) {
|