@theholocron/cli 3.13.1 → 3.14.0

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 CHANGED
@@ -264,7 +264,8 @@ function resolveConfig(raw) {
264
264
  doctor: raw.doctor ?? {},
265
265
  agent: raw.agent,
266
266
  skills: raw.skills,
267
- docs
267
+ docs,
268
+ env: raw.env
268
269
  };
269
270
  }
270
271
  //#endregion
@@ -4443,6 +4444,86 @@ async function runSyncGithub(input) {
4443
4444
  };
4444
4445
  }
4445
4446
  //#endregion
4447
+ //#region src/commands/sync-readme.ts
4448
+ const README_INSTALL_START = "<!-- holocron:installation -->";
4449
+ const README_INSTALL_END = "<!-- /holocron:installation -->";
4450
+ function generateInstallBlock(pkg) {
4451
+ const { name = "", bin, peerDependencies = {} } = pkg;
4452
+ const isCli = Boolean(bin);
4453
+ const isReact = Boolean(peerDependencies["react"]);
4454
+ const lines = [];
4455
+ lines.push("## Installation", "");
4456
+ if (isCli) lines.push("```bash", `npm install --global ${name}`, "```");
4457
+ else lines.push("```bash", `pnpm install ${name}`, "```");
4458
+ lines.push("", "## Usage", "");
4459
+ if (isCli) {
4460
+ const commands = typeof bin === "string" ? [name.split("/").at(-1)] : Object.keys(bin ?? {});
4461
+ lines.push("```bash");
4462
+ for (const cmd of commands) lines.push(`${cmd} --help`);
4463
+ lines.push("```");
4464
+ } else if (isReact) lines.push("```tsx", `import { } from "${name}";`, "", "function App() {", ` return <></>;`, "}", "```");
4465
+ else lines.push("```typescript", `import { } from "${name}";`, "```");
4466
+ return lines.join("\n");
4467
+ }
4468
+ async function updateReadmeInstallation(repoRoot, block, dryRun, readFileFn, writeFileFn) {
4469
+ const readmePath = join(repoRoot, "README.md");
4470
+ let content;
4471
+ try {
4472
+ content = await readFileFn(readmePath, "utf8");
4473
+ } catch {
4474
+ return false;
4475
+ }
4476
+ const lines = content.split("\n");
4477
+ const startIdx = lines.findIndex((l) => l.trim() === README_INSTALL_START);
4478
+ const endIdx = lines.findIndex((l) => l.trim() === README_INSTALL_END);
4479
+ const blockLines = block.split("\n");
4480
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
4481
+ if (dryRun) return true;
4482
+ lines.splice(startIdx + 1, endIdx - startIdx - 1, "", ...blockLines, "");
4483
+ await writeFileFn(readmePath, lines.join("\n"), "utf8");
4484
+ return true;
4485
+ }
4486
+ const descEnd = lines.findIndex((l) => l.trim() === "<!-- /holocron:description -->");
4487
+ const insertAfter = descEnd !== -1 ? descEnd : lines.findIndex((l) => /^# /.test(l));
4488
+ if (insertAfter === -1) return false;
4489
+ if (dryRun) return true;
4490
+ lines.splice(insertAfter + 1, 0, "", README_INSTALL_START, "", ...blockLines, "", README_INSTALL_END);
4491
+ await writeFileFn(readmePath, lines.join("\n"), "utf8");
4492
+ return true;
4493
+ }
4494
+ async function runSyncReadme(input) {
4495
+ const { loaded, context, print = console.log, readFileFn = readFile, writeFileFn = writeFile } = input;
4496
+ const { repoRoot, dryRun = false } = context;
4497
+ const namespaces = loaded.resolved.env?.namespaces ?? [];
4498
+ print(style.header(`Syncing README installation block${dryRun ? " (dry-run)" : ""}…`));
4499
+ if (namespaces.length > 0) print(style.hint(` namespaces: ${namespaces.join(", ")}`));
4500
+ let pkg;
4501
+ try {
4502
+ const raw = await readFileFn(join(repoRoot, "package.json"), "utf8");
4503
+ pkg = JSON.parse(raw);
4504
+ } catch {
4505
+ return {
4506
+ status: "fail",
4507
+ updated: false,
4508
+ message: "Could not read package.json"
4509
+ };
4510
+ }
4511
+ const updated = await updateReadmeInstallation(repoRoot, generateInstallBlock(pkg), dryRun, readFileFn, writeFileFn);
4512
+ if (!updated) {
4513
+ print(style.warn("README.md not found or has no writable location for installation block"));
4514
+ return {
4515
+ status: "fail",
4516
+ updated: false,
4517
+ message: "README.md update failed"
4518
+ };
4519
+ }
4520
+ print(dryRun ? style.hint(" would update README.md") : style.success(" updated README.md"));
4521
+ return {
4522
+ status: dryRun ? "dry-run" : "ok",
4523
+ updated
4524
+ };
4525
+ }
4526
+ //#endregion
4446
4527
  //#region src/commands/upgrade-node.ts
4447
4528
  const SKIP_DIRS = /* @__PURE__ */ new Set([
4448
4529
  "node_modules",
@@ -5219,6 +5300,18 @@ try {
5219
5300
  ...argv.message ? { message: argv.message } : {},
5220
5301
  ...outputDir ? { outputDir } : {}
5221
5302
  })).status === "fail") process.exitCode = 1;
5303
+ }).command("sync-readme", "Sync the Installation + Usage block in README.md from package.json", (y) => y.option("dry-run", {
5304
+ type: "boolean",
5305
+ describe: "Print what would change without writing",
5306
+ default: false
5307
+ }), async (argv) => {
5308
+ if ((await runSyncReadme({
5309
+ loaded: await loadConfig(argv.cwd),
5310
+ context: {
5311
+ repoRoot: argv.cwd,
5312
+ dryRun: argv.dryRun
5313
+ }
5314
+ })).status === "fail") process.exitCode = 1;
5222
5315
  }).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
5223
5316
  const loaded = await loadConfig(argv.cwd);
5224
5317
  console.log(JSON.stringify(loaded.resolved, null, 2));