@theholocron/cli 5.0.0-alpha.146 → 5.0.0-alpha.148
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/dist/cli.mjs +65 -140
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +3 -7
- package/dist/index.mjs +12 -36
- package/package.json +10 -6
package/dist/cli.mjs
CHANGED
|
@@ -17,9 +17,13 @@ import { errorMessage } from "@theholocron/misc-utils";
|
|
|
17
17
|
import { createLogger, isCI, parseLogLevel, resolveAxiomFromEnv } from "@theholocron/observability/logger";
|
|
18
18
|
import chalk from "chalk";
|
|
19
19
|
import ora from "ora";
|
|
20
|
-
import {
|
|
20
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
21
|
+
import { stringifyJson } from "@theholocron/json-utils";
|
|
22
|
+
import { readGitRemote, updateGitignore } from "@theholocron/git-utils";
|
|
23
|
+
import { readPackageJson } from "@theholocron/pkg-utils";
|
|
21
24
|
import { access, copyFile, mkdir, readFile, readdir, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
22
25
|
import { createHash, randomUUID } from "node:crypto";
|
|
26
|
+
import { findRootSync } from "@theholocron/fs-utils";
|
|
23
27
|
import { generateReadme } from "@theholocron/components-doc/markdown";
|
|
24
28
|
import { getClients, getConfigs, getDocs, getPlugins, getSkills, getThemes, getUtils } from "@theholocron/registry-doc";
|
|
25
29
|
import { createGitHubClient } from "@theholocron/github-client";
|
|
@@ -27,7 +31,6 @@ import { PostHogSink } from "@theholocron/observability/analytics";
|
|
|
27
31
|
import { SentrySink } from "@theholocron/observability/errors";
|
|
28
32
|
import load from "@commitlint/load";
|
|
29
33
|
import lint from "@commitlint/lint";
|
|
30
|
-
import { promisify } from "node:util";
|
|
31
34
|
import { ConfigFileError, loadConfigFile } from "@theholocron/datapad";
|
|
32
35
|
import search from "@inquirer/search";
|
|
33
36
|
//#region src/env.ts
|
|
@@ -2275,19 +2278,21 @@ async function runNew(input) {
|
|
|
2275
2278
|
const filesPatched = patchFiles(repoDir, variants, input.description, input.homepage, input.runtimeEnvironment, print, readFn, writeFn, walkFn);
|
|
2276
2279
|
print(` ${filesPatched.length} file${filesPatched.length === 1 ? "" : "s"} patched`);
|
|
2277
2280
|
if (input.description !== void 0 || input.homepage !== void 0) try {
|
|
2278
|
-
const
|
|
2281
|
+
const raw = readFn(pkgJsonPath);
|
|
2282
|
+
const pkg = JSON.parse(raw);
|
|
2279
2283
|
if (input.description !== void 0) pkg["description"] = input.description;
|
|
2280
2284
|
if (input.homepage !== void 0) pkg["homepage"] = input.homepage;
|
|
2281
|
-
writeFn(pkgJsonPath,
|
|
2285
|
+
writeFn(pkgJsonPath, stringifyJson(pkg, { like: raw }));
|
|
2282
2286
|
print(` ✓ package.json (description/homepage)`);
|
|
2283
2287
|
} catch {}
|
|
2284
2288
|
const displayName = input.name.split("-").map(cap).join(" ");
|
|
2285
2289
|
const rootTsconfigPath = path.join(repoDir, "tsconfig.json");
|
|
2286
2290
|
try {
|
|
2287
|
-
const
|
|
2291
|
+
const rawTsconfig = readFn(rootTsconfigPath);
|
|
2292
|
+
const tsconfig = JSON.parse(rawTsconfig);
|
|
2288
2293
|
if (typeof tsconfig["display"] === "string") {
|
|
2289
2294
|
tsconfig["display"] = displayName;
|
|
2290
|
-
writeFn(rootTsconfigPath,
|
|
2295
|
+
writeFn(rootTsconfigPath, stringifyJson(tsconfig, { like: rawTsconfig }));
|
|
2291
2296
|
print(` ✓ tsconfig.json (display)`);
|
|
2292
2297
|
}
|
|
2293
2298
|
} catch {}
|
|
@@ -2407,8 +2412,10 @@ async function runNpmBumpVersions(input) {
|
|
|
2407
2412
|
}, "npm bump-versions: start");
|
|
2408
2413
|
function bumpFile(absPath, label) {
|
|
2409
2414
|
let pkg;
|
|
2415
|
+
let raw;
|
|
2410
2416
|
try {
|
|
2411
|
-
|
|
2417
|
+
raw = readFile(absPath);
|
|
2418
|
+
pkg = JSON.parse(raw);
|
|
2412
2419
|
} catch {
|
|
2413
2420
|
print(` ✗ could not parse ${label}`);
|
|
2414
2421
|
return;
|
|
@@ -2416,7 +2423,7 @@ async function runNpmBumpVersions(input) {
|
|
|
2416
2423
|
const old = pkg.version;
|
|
2417
2424
|
if (!dryRun) {
|
|
2418
2425
|
pkg.version = version;
|
|
2419
|
-
writeFile(absPath,
|
|
2426
|
+
writeFile(absPath, stringifyJson(pkg, { like: raw }));
|
|
2420
2427
|
}
|
|
2421
2428
|
print(` ${dryRun ? "~" : "✓"} ${label}: ${old} → ${version}`);
|
|
2422
2429
|
bumped.push(label);
|
|
@@ -4154,13 +4161,11 @@ function namesInWorkspaceDir(cwd, dirName) {
|
|
|
4154
4161
|
});
|
|
4155
4162
|
}
|
|
4156
4163
|
async function resolveRepoName(cwd, exec) {
|
|
4157
|
-
|
|
4158
|
-
"
|
|
4159
|
-
"
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
if (result.exitCode !== 0) return "unknown";
|
|
4163
|
-
return /[/:]([^/:]+?)(?:\.git)?$/.exec(result.stdout.trim())?.[1] ?? "unknown";
|
|
4164
|
+
return (await readGitRemote(cwd, { exec: async (args, options) => {
|
|
4165
|
+
const result = await exec("git", args, options);
|
|
4166
|
+
if (result.exitCode !== 0) throw new Error(`git ${args.join(" ")} exited ${result.exitCode}`);
|
|
4167
|
+
return result.stdout;
|
|
4168
|
+
} }))?.repo ?? "unknown";
|
|
4164
4169
|
}
|
|
4165
4170
|
function printNextSteps(print, env, packageNames, repoName) {
|
|
4166
4171
|
print("");
|
|
@@ -4550,10 +4555,10 @@ function deriveCompliance(capabilities) {
|
|
|
4550
4555
|
*/
|
|
4551
4556
|
async function readWorkspacePackageJsons(repoRoot, workspacePackages) {
|
|
4552
4557
|
const results = [];
|
|
4553
|
-
for (const { slug, dir = "packages" } of workspacePackages)
|
|
4554
|
-
const
|
|
4555
|
-
results.push(
|
|
4556
|
-
}
|
|
4558
|
+
for (const { slug, dir = "packages" } of workspacePackages) {
|
|
4559
|
+
const pkg = await readPackageJson(join(repoRoot, dir, slug));
|
|
4560
|
+
if (pkg) results.push(pkg);
|
|
4561
|
+
}
|
|
4557
4562
|
return results;
|
|
4558
4563
|
}
|
|
4559
4564
|
//#endregion
|
|
@@ -4710,7 +4715,7 @@ var alexrc_default = { allow: [
|
|
|
4710
4715
|
//#region src/templates/configs/alexjs/create-config.ts
|
|
4711
4716
|
const { workflowHeader: workflowHeader$9 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
|
|
4712
4717
|
function createRcConfig() {
|
|
4713
|
-
return
|
|
4718
|
+
return stringifyJson(alexrc_default);
|
|
4714
4719
|
}
|
|
4715
4720
|
function createIgnoreConfig$1() {
|
|
4716
4721
|
return `${workflowHeader$9()}${alexignore_default}`;
|
|
@@ -4794,7 +4799,7 @@ var editorconfig_checker_default = {
|
|
|
4794
4799
|
* buys nothing.
|
|
4795
4800
|
*/
|
|
4796
4801
|
function createConfig$4() {
|
|
4797
|
-
return
|
|
4802
|
+
return stringifyJson(editorconfig_checker_default);
|
|
4798
4803
|
}
|
|
4799
4804
|
//#endregion
|
|
4800
4805
|
//#region src/templates/configs/pre-commit/pre-commit
|
|
@@ -5264,21 +5269,11 @@ async function installAgentPrompts({ repoRoot }) {
|
|
|
5264
5269
|
const promptsDir = join(repoRoot, AGENTS_PROMPTS_ROOT);
|
|
5265
5270
|
await mkdir(promptsDir, { recursive: true });
|
|
5266
5271
|
for (const [filename, content] of Object.entries(AGENT_PROMPTS)) await writeFile(join(promptsDir, filename), content, "utf8");
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
PROMPTS_GITIGNORE_END
|
|
5273
|
-
].join("\n");
|
|
5274
|
-
let updated;
|
|
5275
|
-
if (existing.includes(PROMPTS_GITIGNORE_START)) {
|
|
5276
|
-
const start = existing.indexOf(PROMPTS_GITIGNORE_START);
|
|
5277
|
-
const end = existing.indexOf(PROMPTS_GITIGNORE_END, start);
|
|
5278
|
-
const afterBlock = end !== -1 ? existing.slice(end + 41) : "\n";
|
|
5279
|
-
updated = existing.slice(0, start) + block + afterBlock;
|
|
5280
|
-
} else updated = (existing.trimEnd() ? existing.trimEnd() + "\n\n" : "") + block + "\n";
|
|
5281
|
-
await writeFile(gitignorePath, updated, "utf8");
|
|
5272
|
+
await updateGitignore(join(repoRoot, ".gitignore"), {
|
|
5273
|
+
start: PROMPTS_GITIGNORE_START,
|
|
5274
|
+
end: PROMPTS_GITIGNORE_END,
|
|
5275
|
+
lines: [`/${AGENTS_PROMPTS_ROOT}/`]
|
|
5276
|
+
});
|
|
5282
5277
|
return `wrote ${Object.keys(AGENT_PROMPTS).length} prompt files to ${AGENTS_PROMPTS_ROOT}/`;
|
|
5283
5278
|
}
|
|
5284
5279
|
//#endregion
|
|
@@ -5548,8 +5543,7 @@ async function installSkills({ agent, skills, repoRoot }) {
|
|
|
5548
5543
|
}
|
|
5549
5544
|
}
|
|
5550
5545
|
const gitignorePath = join(repoRoot, ".gitignore");
|
|
5551
|
-
const
|
|
5552
|
-
const previouslyInstalled = parsePreviousSkills(existingContent, symlinkFn);
|
|
5546
|
+
const previouslyInstalled = parsePreviousSkills(await readFile(gitignorePath, "utf8").catch(() => ""), symlinkFn);
|
|
5553
5547
|
const currentSet = new Set(skills);
|
|
5554
5548
|
const stale = previouslyInstalled.filter((n) => !currentSet.has(n));
|
|
5555
5549
|
for (const name of stale) {
|
|
@@ -5609,7 +5603,7 @@ async function installSkills({ agent, skills, repoRoot }) {
|
|
|
5609
5603
|
}
|
|
5610
5604
|
}
|
|
5611
5605
|
}
|
|
5612
|
-
if (installed.length > 0 || stale.length > 0 || missing.length > 0 || externalFailed.length > 0) await updateSkillsGitignore(gitignorePath,
|
|
5606
|
+
if (installed.length > 0 || stale.length > 0 || missing.length > 0 || externalFailed.length > 0) await updateSkillsGitignore(gitignorePath, [
|
|
5613
5607
|
...installed,
|
|
5614
5608
|
...missing,
|
|
5615
5609
|
...externalFailed
|
|
@@ -5621,14 +5615,24 @@ async function installSkills({ agent, skills, repoRoot }) {
|
|
|
5621
5615
|
if (missing.length > 0) parts.push(`unknown: ${missing.join(", ")}`);
|
|
5622
5616
|
return parts.join("; ");
|
|
5623
5617
|
}
|
|
5618
|
+
/**
|
|
5619
|
+
* The skill names a previous `holocron setup` recorded in the `.gitignore`
|
|
5620
|
+
* managed block. A block missing its end marker (hand-edited, truncated) runs
|
|
5621
|
+
* only to the next blank line — the same rule `updateGitignore` uses to
|
|
5622
|
+
* replace it — so user-written ignore rules further down are never mistaken
|
|
5623
|
+
* for installed skills and pruned.
|
|
5624
|
+
*/
|
|
5624
5625
|
function parsePreviousSkills(gitignoreContent, symlinkFn) {
|
|
5625
|
-
if (!gitignoreContent.includes(GITIGNORE_BLOCK_START)) return [];
|
|
5626
5626
|
const startIdx = gitignoreContent.indexOf(GITIGNORE_BLOCK_START);
|
|
5627
|
+
if (startIdx === -1) return [];
|
|
5627
5628
|
const endIdx = gitignoreContent.indexOf(GITIGNORE_BLOCK_END, startIdx);
|
|
5628
|
-
const
|
|
5629
|
+
const blankAt = /\r?\n[ \t]*\r?\n/.exec(gitignoreContent.slice(startIdx));
|
|
5630
|
+
const blankIdx = blankAt ? startIdx + blankAt.index : -1;
|
|
5631
|
+
const blockEnd = endIdx !== -1 && (blankIdx === -1 || endIdx < blankIdx) ? endIdx : blankIdx === -1 ? gitignoreContent.length : blankIdx;
|
|
5632
|
+
const block = gitignoreContent.slice(startIdx, blockEnd);
|
|
5629
5633
|
const placeholder = "__placeholder__";
|
|
5630
5634
|
const symlinkPrefix = `/${symlinkFn(placeholder)}`.replace(placeholder, "");
|
|
5631
|
-
return block.split(
|
|
5635
|
+
return block.split(/\r?\n/).filter((line) => line.startsWith(symlinkPrefix)).map((line) => line.slice(symlinkPrefix.length));
|
|
5632
5636
|
}
|
|
5633
5637
|
async function copyDirRecursive(src, dest) {
|
|
5634
5638
|
await mkdir(dest, { recursive: true });
|
|
@@ -5640,21 +5644,12 @@ async function copyDirRecursive(src, dest) {
|
|
|
5640
5644
|
else await copyFile(srcPath, destPath);
|
|
5641
5645
|
}
|
|
5642
5646
|
}
|
|
5643
|
-
async function updateSkillsGitignore(gitignorePath,
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
...
|
|
5648
|
-
|
|
5649
|
-
].join("\n");
|
|
5650
|
-
let content;
|
|
5651
|
-
if (existingContent.includes(GITIGNORE_BLOCK_START)) {
|
|
5652
|
-
const start = existingContent.indexOf(GITIGNORE_BLOCK_START);
|
|
5653
|
-
const end = existingContent.indexOf(GITIGNORE_BLOCK_END, start);
|
|
5654
|
-
const afterBlock = end !== -1 ? existingContent.slice(end + 40) : "\n";
|
|
5655
|
-
content = existingContent.slice(0, start) + block + afterBlock;
|
|
5656
|
-
} else content = (existingContent.trimEnd() ? existingContent.trimEnd() + "\n\n" : "") + block + "\n";
|
|
5657
|
-
await writeFile(gitignorePath, content, "utf8");
|
|
5647
|
+
async function updateSkillsGitignore(gitignorePath, skills, symlinkFn) {
|
|
5648
|
+
await updateGitignore(gitignorePath, {
|
|
5649
|
+
start: GITIGNORE_BLOCK_START,
|
|
5650
|
+
end: GITIGNORE_BLOCK_END,
|
|
5651
|
+
lines: [`/${AGENTS_SKILLS_ROOT}/`, ...skills.map((n) => `/${symlinkFn(n)}`)]
|
|
5652
|
+
});
|
|
5658
5653
|
}
|
|
5659
5654
|
//#endregion
|
|
5660
5655
|
//#region src/commands/setup/run-setup.ts
|
|
@@ -6359,7 +6354,7 @@ async function ensurePrepareScript(repoRoot) {
|
|
|
6359
6354
|
if (/(^|[\s&|;])husky($|[\s&|;])/.test(current)) return false;
|
|
6360
6355
|
scripts.prepare = current ? `${current} && husky` : "husky";
|
|
6361
6356
|
pkg.scripts = scripts;
|
|
6362
|
-
await writeFile(pkgPath,
|
|
6357
|
+
await writeFile(pkgPath, stringifyJson(pkg, { like: content }), "utf8");
|
|
6363
6358
|
return true;
|
|
6364
6359
|
}
|
|
6365
6360
|
//#endregion
|
|
@@ -6462,55 +6457,6 @@ function runSkillsUpdate(input) {
|
|
|
6462
6457
|
return { status: exitCode === 0 ? "ok" : "fail" };
|
|
6463
6458
|
}
|
|
6464
6459
|
//#endregion
|
|
6465
|
-
//#region src/utils/find-git-root.ts
|
|
6466
|
-
/**
|
|
6467
|
-
* Walks up from `startDir` looking for a `.git` entry — a directory for a
|
|
6468
|
-
* normal checkout, a file for a worktree/submodule (contains a `gitdir:`
|
|
6469
|
-
* pointer, never followed here; its presence is enough to mark the root).
|
|
6470
|
-
*
|
|
6471
|
-
* `--cwd` picks which `holocron.config.ts` to load, which is NOT
|
|
6472
|
-
* necessarily the git repository root — a monorepo subpackage (e.g.
|
|
6473
|
-
* `packages/sentinel`) has its own config but no `.git` of its own.
|
|
6474
|
-
* Anything inherently repo-wide (`.github/workflows`, `.github/CODEOWNERS`,
|
|
6475
|
-
* `turbo.json`, "is this repo a monorepo") must resolve against the real
|
|
6476
|
-
* root, not `--cwd`, or it silently writes into a GitHub-invisible nested
|
|
6477
|
-
* `.github/` instead (holocron#1014).
|
|
6478
|
-
*
|
|
6479
|
-
* Falls back to the resolved start dir when no `.git` is found walking all
|
|
6480
|
-
* the way to the filesystem root — soft-skip, not a hard failure; every
|
|
6481
|
-
* caller already treats `--cwd` itself as the root today, so an
|
|
6482
|
-
* unexpected non-git checkout just keeps today's behavior.
|
|
6483
|
-
*
|
|
6484
|
-
* Resolves `startDir` to an absolute, symlink-free path before walking:
|
|
6485
|
-
* a relative `--cwd` (e.g. `"packages/sentinel"`) would otherwise hit
|
|
6486
|
-
* `dirname`'s fixed point at `"."` after only two steps, far short of the
|
|
6487
|
-
* real root, and a `--cwd` reached through a symlink would walk the
|
|
6488
|
-
* symlink's own lexical ancestors instead of the physical directory
|
|
6489
|
-
* tree its target actually lives in — in both cases landing on the
|
|
6490
|
-
* wrong directory, not genuinely "no `.git` found anywhere."
|
|
6491
|
-
*
|
|
6492
|
-
* `realpathSync` itself throws when `startDir` doesn't exist on disk at
|
|
6493
|
-
* all (a real possibility in tests, which often use a synthetic path
|
|
6494
|
-
* like `"/tmp/test"` that's never actually created) — caught and treated
|
|
6495
|
-
* the same as "no `.git` found," since this function's whole contract is
|
|
6496
|
-
* soft-skip, never a hard failure.
|
|
6497
|
-
*/
|
|
6498
|
-
function findGitRoot(startDir) {
|
|
6499
|
-
let resolvedStart;
|
|
6500
|
-
try {
|
|
6501
|
-
resolvedStart = realpathSync(startDir);
|
|
6502
|
-
} catch {
|
|
6503
|
-
return startDir;
|
|
6504
|
-
}
|
|
6505
|
-
let dir = resolvedStart;
|
|
6506
|
-
for (;;) {
|
|
6507
|
-
if (existsSync(`${dir}/.git`)) return dir;
|
|
6508
|
-
const parent = dirname(dir);
|
|
6509
|
-
if (parent === dir) return resolvedStart;
|
|
6510
|
-
dir = parent;
|
|
6511
|
-
}
|
|
6512
|
-
}
|
|
6513
|
-
//#endregion
|
|
6514
6460
|
//#region src/commands/sync-readme.ts
|
|
6515
6461
|
const MARKER_INSTALL_START = "<!-- holocron:installation -->";
|
|
6516
6462
|
const MARKER_INSTALL_END = "<!-- /holocron:installation -->";
|
|
@@ -6917,7 +6863,10 @@ async function runSync(input) {
|
|
|
6917
6863
|
const dryRun = input.context.dryRun ?? false;
|
|
6918
6864
|
const requestedSteps = input.steps;
|
|
6919
6865
|
const steps = [];
|
|
6920
|
-
const gitRoot =
|
|
6866
|
+
const gitRoot = findRootSync(input.context.repoRoot, ".git", {
|
|
6867
|
+
fallback: "start",
|
|
6868
|
+
realpath: true
|
|
6869
|
+
});
|
|
6921
6870
|
let resolvedRepoRoot;
|
|
6922
6871
|
try {
|
|
6923
6872
|
resolvedRepoRoot = realpathSync(input.context.repoRoot);
|
|
@@ -7365,7 +7314,7 @@ async function writePackageJsonField(repoRoot, field, value) {
|
|
|
7365
7314
|
}
|
|
7366
7315
|
const pkg = JSON.parse(content);
|
|
7367
7316
|
pkg[field] = value;
|
|
7368
|
-
await writeFile(pkgPath,
|
|
7317
|
+
await writeFile(pkgPath, stringifyJson(pkg, { like: content }), "utf8");
|
|
7369
7318
|
return true;
|
|
7370
7319
|
}
|
|
7371
7320
|
/**
|
|
@@ -7407,7 +7356,7 @@ async function mergePackageJsonScripts(repoRoot, desired, turboEligible) {
|
|
|
7407
7356
|
}
|
|
7408
7357
|
if (changed.length === 0) return [];
|
|
7409
7358
|
pkg.scripts = scripts;
|
|
7410
|
-
await writeFile(pkgPath,
|
|
7359
|
+
await writeFile(pkgPath, stringifyJson(pkg, { like: content }), "utf8");
|
|
7411
7360
|
return changed.sort();
|
|
7412
7361
|
}
|
|
7413
7362
|
/**
|
|
@@ -8146,7 +8095,7 @@ function patchPackageJson(content, from, to) {
|
|
|
8146
8095
|
changed = true;
|
|
8147
8096
|
}
|
|
8148
8097
|
}
|
|
8149
|
-
return changed ?
|
|
8098
|
+
return changed ? stringifyJson(pkg, { like: content }) : null;
|
|
8150
8099
|
}
|
|
8151
8100
|
function patchYaml(content, from, to) {
|
|
8152
8101
|
const updated = content.replace(/node-version:\s+['"]?(\d+)['"]?/g, (match, ver) => ver === String(from) ? match.replace(String(from), String(to)) : match);
|
|
@@ -8369,7 +8318,6 @@ async function lintCommitMsgFile(filePath, cwd = process.cwd()) {
|
|
|
8369
8318
|
* Probe order is TS-first (`.ts` → `.js` → `.mjs` → `.cjs` → `.json`) —
|
|
8370
8319
|
* see ADR-0010 / issue #75 / #81.
|
|
8371
8320
|
*/
|
|
8372
|
-
const execFileAsync = promisify(execFile);
|
|
8373
8321
|
/**
|
|
8374
8322
|
* Read + parse + resolve `holocron.config.*` starting from the given
|
|
8375
8323
|
* directory, walking up through ancestors to the nearest `.git` boundary
|
|
@@ -8395,10 +8343,10 @@ async function deriveDefaults(configDir, raw) {
|
|
|
8395
8343
|
const result = { ...raw };
|
|
8396
8344
|
if (!result.name) result.name = await readPackageJsonName(configDir) ?? basename(configDir);
|
|
8397
8345
|
if (result.repo && !result.repo.name) {
|
|
8398
|
-
const
|
|
8399
|
-
if (
|
|
8346
|
+
const remote = await readGitRemote(configDir);
|
|
8347
|
+
if (remote?.host === "github.com") result.repo = {
|
|
8400
8348
|
...result.repo,
|
|
8401
|
-
name:
|
|
8349
|
+
name: remote.slug
|
|
8402
8350
|
};
|
|
8403
8351
|
}
|
|
8404
8352
|
const manifest = await loadTasksConfig(configDir);
|
|
@@ -8410,31 +8358,8 @@ async function deriveDefaults(configDir, raw) {
|
|
|
8410
8358
|
return result;
|
|
8411
8359
|
}
|
|
8412
8360
|
async function readPackageJsonName(dir) {
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
const pkg = JSON.parse(content);
|
|
8416
|
-
return typeof pkg.name === "string" ? pkg.name.replace(/^@[^/]+\//, "") : void 0;
|
|
8417
|
-
} catch {
|
|
8418
|
-
return;
|
|
8419
|
-
}
|
|
8420
|
-
}
|
|
8421
|
-
async function readGitRemote(dir) {
|
|
8422
|
-
try {
|
|
8423
|
-
const { stdout } = await execFileAsync("git", [
|
|
8424
|
-
"remote",
|
|
8425
|
-
"get-url",
|
|
8426
|
-
"origin"
|
|
8427
|
-
], { cwd: dir });
|
|
8428
|
-
return parseGitRemoteUrl(stdout.trim());
|
|
8429
|
-
} catch {
|
|
8430
|
-
return;
|
|
8431
|
-
}
|
|
8432
|
-
}
|
|
8433
|
-
function parseGitRemoteUrl(url) {
|
|
8434
|
-
const httpsMatch = url.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
8435
|
-
if (httpsMatch) return httpsMatch[1];
|
|
8436
|
-
const sshMatch = url.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
8437
|
-
if (sshMatch) return sshMatch[1];
|
|
8361
|
+
const name = (await readPackageJson(dir))?.name;
|
|
8362
|
+
return name === void 0 ? void 0 : name.replace(/^@[^/]+\//, "");
|
|
8438
8363
|
}
|
|
8439
8364
|
//#endregion
|
|
8440
8365
|
//#region src/interactive-menu.ts
|