@theholocron/cli 2.0.0-alpha.56 → 2.0.0-alpha.57
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/README.md +4 -4
- package/dist/capabilities/index.d.mts +5 -0
- package/dist/cli.mjs +83 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,10 +56,10 @@ export default defineConfig({
|
|
|
56
56
|
`name` and `repo.name` are optional. When absent, Holocron fills them
|
|
57
57
|
in at load time:
|
|
58
58
|
|
|
59
|
-
| Field
|
|
60
|
-
|
|
61
|
-
| `name`
|
|
62
|
-
| `repo.name` | `git remote get-url origin` (parsed to `owner/repo`) | not set
|
|
59
|
+
| Field | Derived from | Fallback |
|
|
60
|
+
| ----------- | ---------------------------------------------------- | ------------------ |
|
|
61
|
+
| `name` | `package.json` → `name` field (scope stripped) | directory basename |
|
|
62
|
+
| `repo.name` | `git remote get-url origin` (parsed to `owner/repo`) | not set |
|
|
63
63
|
|
|
64
64
|
A minimal config — for repos with a `package.json` and a GitHub remote
|
|
65
65
|
— only needs `providers`:
|
|
@@ -137,6 +137,11 @@ interface Source extends ProviderIdentity {
|
|
|
137
137
|
* Optional — providers that don't support topics omit this.
|
|
138
138
|
*/
|
|
139
139
|
syncTopics?(topics: string[]): Promise<string>;
|
|
140
|
+
/**
|
|
141
|
+
* Set the repository description.
|
|
142
|
+
* Optional — providers that don't support setting descriptions omit this.
|
|
143
|
+
*/
|
|
144
|
+
syncDescription?(description: string): Promise<string>;
|
|
140
145
|
}
|
|
141
146
|
type CiRunStatus = "queued" | "in_progress" | "completed" | "cancelled" | "failure" | "success" | "skipped";
|
|
142
147
|
interface CiRun {
|
package/dist/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { Entry, findCredentials } from "@napi-rs/keyring";
|
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
import { createGitHubClient } from "@theholocron/github-client";
|
|
10
10
|
import { execFile, spawnSync } from "node:child_process";
|
|
11
|
-
import { access, readFile, stat } from "node:fs/promises";
|
|
11
|
+
import { access, readFile, stat, writeFile } from "node:fs/promises";
|
|
12
12
|
import { pathToFileURL } from "node:url";
|
|
13
13
|
import { promisify } from "node:util";
|
|
14
14
|
//#region src/capabilities/index.ts
|
|
@@ -4269,7 +4269,9 @@ function formatStep(step) {
|
|
|
4269
4269
|
const SYNC_STEPS = [
|
|
4270
4270
|
"labels",
|
|
4271
4271
|
"properties",
|
|
4272
|
-
"topics"
|
|
4272
|
+
"topics",
|
|
4273
|
+
"keywords",
|
|
4274
|
+
"description"
|
|
4273
4275
|
];
|
|
4274
4276
|
async function runSync(input) {
|
|
4275
4277
|
const print = input.print ?? ((line) => console.log(line));
|
|
@@ -4345,6 +4347,47 @@ async function runSync(input) {
|
|
|
4345
4347
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
4346
4348
|
}
|
|
4347
4349
|
}
|
|
4350
|
+
if (stepName === "keywords") {
|
|
4351
|
+
const topics = config.repo?.topics ?? [];
|
|
4352
|
+
if (topics.length === 0) {
|
|
4353
|
+
steps.push({
|
|
4354
|
+
capability: "source",
|
|
4355
|
+
step: "sync keywords",
|
|
4356
|
+
status: "skip",
|
|
4357
|
+
message: "no topics configured"
|
|
4358
|
+
});
|
|
4359
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
4360
|
+
} else {
|
|
4361
|
+
steps.push(await runSyncStep("source", "sync keywords", dryRun, async () => {
|
|
4362
|
+
return await writePackageJsonField(input.context.repoRoot, "keywords", topics) ? `${topics.length} keywords written` : `${topics.length} topics (no package.json)`;
|
|
4363
|
+
}));
|
|
4364
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
if (stepName === "description") {
|
|
4368
|
+
const description = config.description;
|
|
4369
|
+
if (!description) {
|
|
4370
|
+
steps.push({
|
|
4371
|
+
capability: "source",
|
|
4372
|
+
step: "sync description",
|
|
4373
|
+
status: "skip",
|
|
4374
|
+
message: "no description configured"
|
|
4375
|
+
});
|
|
4376
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
4377
|
+
} else {
|
|
4378
|
+
steps.push(await runSyncStep("source", "sync description", dryRun, async () => {
|
|
4379
|
+
const pkgWrote = await writePackageJsonField(input.context.repoRoot, "description", description);
|
|
4380
|
+
const readmeWrote = await updateReadmeDescription(input.context.repoRoot, description);
|
|
4381
|
+
if (source.syncDescription) await source.syncDescription(description);
|
|
4382
|
+
const parts = [];
|
|
4383
|
+
if (pkgWrote) parts.push("package.json");
|
|
4384
|
+
if (readmeWrote) parts.push("README.md");
|
|
4385
|
+
if (source.syncDescription) parts.push("GitHub");
|
|
4386
|
+
return parts.length > 0 ? parts.join(", ") + " updated" : "description synced";
|
|
4387
|
+
}));
|
|
4388
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4348
4391
|
}
|
|
4349
4392
|
if (requestedSteps) {
|
|
4350
4393
|
for (const name of requestedSteps) if (!SYNC_STEPS.includes(name)) {
|
|
@@ -4406,6 +4449,44 @@ function formatSyncStep(step) {
|
|
|
4406
4449
|
const detail = step.message ? ` (${step.message})` : "";
|
|
4407
4450
|
return ` ${icon} ${step.step}${detail}`;
|
|
4408
4451
|
}
|
|
4452
|
+
async function writePackageJsonField(repoRoot, field, value) {
|
|
4453
|
+
const pkgPath = join(repoRoot, "package.json");
|
|
4454
|
+
let content;
|
|
4455
|
+
try {
|
|
4456
|
+
content = await readFile(pkgPath, "utf8");
|
|
4457
|
+
} catch {
|
|
4458
|
+
return false;
|
|
4459
|
+
}
|
|
4460
|
+
const pkg = JSON.parse(content);
|
|
4461
|
+
pkg[field] = value;
|
|
4462
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
4463
|
+
return true;
|
|
4464
|
+
}
|
|
4465
|
+
const README_DESC_START = "<!-- holocron:description -->";
|
|
4466
|
+
const README_DESC_END = "<!-- /holocron:description -->";
|
|
4467
|
+
async function updateReadmeDescription(repoRoot, description) {
|
|
4468
|
+
const readmePath = join(repoRoot, "README.md");
|
|
4469
|
+
let content;
|
|
4470
|
+
try {
|
|
4471
|
+
content = await readFile(readmePath, "utf8");
|
|
4472
|
+
} catch {
|
|
4473
|
+
return false;
|
|
4474
|
+
}
|
|
4475
|
+
const lines = content.split("\n");
|
|
4476
|
+
const startIdx = lines.findIndex((l) => l.trim() === README_DESC_START);
|
|
4477
|
+
const endIdx = lines.findIndex((l) => l.trim() === README_DESC_END);
|
|
4478
|
+
if (startIdx !== -1) {
|
|
4479
|
+
if (endIdx === -1 || endIdx <= startIdx) return false;
|
|
4480
|
+
lines.splice(startIdx + 1, endIdx - startIdx - 1, description);
|
|
4481
|
+
await writeFile(readmePath, lines.join("\n"), "utf8");
|
|
4482
|
+
return true;
|
|
4483
|
+
}
|
|
4484
|
+
const h1Index = lines.findIndex((l) => /^# /.test(l));
|
|
4485
|
+
if (h1Index === -1) return false;
|
|
4486
|
+
lines.splice(h1Index + 1, 0, "", README_DESC_START, description, README_DESC_END);
|
|
4487
|
+
await writeFile(readmePath, lines.join("\n"), "utf8");
|
|
4488
|
+
return true;
|
|
4489
|
+
}
|
|
4409
4490
|
//#endregion
|
|
4410
4491
|
//#region src/load-config.ts
|
|
4411
4492
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.57",
|
|
4
4
|
"description": "The Holocron CLI — a pluggable, capability-based orchestrator for spinning up and operating software projects.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/cli#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|