@theholocron/cli 2.0.0-alpha.68 → 2.0.0-alpha.69
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 +25 -0
- package/dist/capabilities/index.d.mts +12 -1
- package/dist/cli.mjs +219 -3
- package/dist/index.d.mts +26 -2
- package/dist/index.mjs +3 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -64,6 +64,31 @@ in at load time:
|
|
|
64
64
|
A minimal config — for repos with a `package.json` and a GitHub remote
|
|
65
65
|
— only needs `providers`:
|
|
66
66
|
|
|
67
|
+
### repo options
|
|
68
|
+
|
|
69
|
+
Additional `repo` fields recognised by `holocron setup`:
|
|
70
|
+
|
|
71
|
+
| Field | Type | Description |
|
|
72
|
+
| ------------------ | ----------------------------------------- | ----------- |
|
|
73
|
+
| `repo.teams` | `Array<string \| { slug, permission }>` | GitHub teams granted repo access. String shorthand defaults to `push` (Write). `holocron setup` also writes `.github/CODEOWNERS` for teams with `push`/`maintain`/`admin`. |
|
|
74
|
+
| `repo.topics` | `string[]` | GitHub topics set on the repository. |
|
|
75
|
+
| `repo.protection` | `"balanced" \| "strict" \| "none"` | Branch-protection preset applied by `holocron setup`. |
|
|
76
|
+
| `repo.properties` | `RepoProperties` | Org-level custom property values synced to the GitHub dashboard. |
|
|
77
|
+
|
|
78
|
+
### Skills installer
|
|
79
|
+
|
|
80
|
+
`holocron setup` can install shared skills from `@theholocron/skills` into the local repo:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export default defineConfig({
|
|
84
|
+
agent: "claude", // "claude" | "codex" | "gemini"
|
|
85
|
+
skills: ["git-safety", "pr-workflow"], // skill names from @theholocron/skills
|
|
86
|
+
providers: { source: "github" },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Skills are copied to `.agents/skills/<name>/` and symlinked at the agent's expected path (e.g. `.claude/skills/<name>`). All installed paths are added to a managed block in `.gitignore` automatically.
|
|
91
|
+
|
|
67
92
|
<!-- prettier-ignore -->
|
|
68
93
|
```jsonc
|
|
69
94
|
{ "providers": { "source": "github" } }
|
|
@@ -71,6 +71,12 @@ interface LabelDef {
|
|
|
71
71
|
readonly color: string;
|
|
72
72
|
readonly description: string;
|
|
73
73
|
}
|
|
74
|
+
type TeamPermission = "pull" | "triage" | "push" | "maintain" | "admin";
|
|
75
|
+
/** Shorthand `"slug"` defaults to `push` permission. */
|
|
76
|
+
type TeamEntry = string | {
|
|
77
|
+
slug: string;
|
|
78
|
+
permission: TeamPermission;
|
|
79
|
+
};
|
|
74
80
|
interface Source extends ProviderIdentity {
|
|
75
81
|
readonly key: "source";
|
|
76
82
|
/** Auth sanity-check. Throws ProviderApiError on auth failure. */
|
|
@@ -137,6 +143,11 @@ interface Source extends ProviderIdentity {
|
|
|
137
143
|
* Optional — providers that don't support topics omit this.
|
|
138
144
|
*/
|
|
139
145
|
syncTopics?(topics: string[]): Promise<string>;
|
|
146
|
+
/**
|
|
147
|
+
* Sync GitHub team repository access. String shorthand defaults to `push`.
|
|
148
|
+
* Optional — providers without a team concept omit this.
|
|
149
|
+
*/
|
|
150
|
+
syncTeams?(teams: TeamEntry[]): Promise<string>;
|
|
140
151
|
/**
|
|
141
152
|
* Set the repository description.
|
|
142
153
|
* Optional — providers that don't support setting descriptions omit this.
|
|
@@ -554,4 +565,4 @@ type CardinalityFor<K extends CapabilityKey> = (typeof CARDINALITY)[K];
|
|
|
554
565
|
type ResolvedCapability<K extends CapabilityKey> = CardinalityFor<K> extends "many" ? CapabilityImpls[K][] : CapabilityImpls[K];
|
|
555
566
|
declare function isMulti<K extends CapabilityKey>(key: K): CardinalityFor<K> extends "many" ? true : false;
|
|
556
567
|
//#endregion
|
|
557
|
-
export { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti };
|
|
568
|
+
export { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import path, { basename, dirname, join } from "node:path";
|
|
4
|
+
import path, { basename, dirname, join, relative } from "node:path";
|
|
4
5
|
import yargs from "yargs";
|
|
5
6
|
import { hideBin } from "yargs/helpers";
|
|
6
7
|
import { createInterface } from "node:readline";
|
|
@@ -12,7 +13,7 @@ import chalk from "chalk";
|
|
|
12
13
|
import { createHash } from "node:crypto";
|
|
13
14
|
import { createGitHubClient } from "@theholocron/github-client";
|
|
14
15
|
import { execFile, execFileSync, spawnSync } from "node:child_process";
|
|
15
|
-
import { access, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
16
|
+
import { access, copyFile, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
16
17
|
import { pathToFileURL } from "node:url";
|
|
17
18
|
import { promisify } from "node:util";
|
|
18
19
|
//#region src/capabilities/index.ts
|
|
@@ -149,7 +150,9 @@ function resolveConfig(raw) {
|
|
|
149
150
|
workflows: raw.workflows,
|
|
150
151
|
providers,
|
|
151
152
|
apps: raw.apps ?? [],
|
|
152
|
-
doctor: raw.doctor ?? {}
|
|
153
|
+
doctor: raw.doctor ?? {},
|
|
154
|
+
agent: raw.agent,
|
|
155
|
+
skills: raw.skills
|
|
153
156
|
};
|
|
154
157
|
}
|
|
155
158
|
//#endregion
|
|
@@ -3298,6 +3301,36 @@ async function runSetup(input) {
|
|
|
3298
3301
|
steps.push(await runStep("source", "sync topics", dryRun, () => source.syncTopics(topics)));
|
|
3299
3302
|
print(formatStep(steps[steps.length - 1]));
|
|
3300
3303
|
}
|
|
3304
|
+
const teams = repo?.teams ?? [];
|
|
3305
|
+
if (teams.length > 0) if (source.syncTeams) {
|
|
3306
|
+
steps.push(await runStep("source", "sync teams", dryRun, () => source.syncTeams(teams)));
|
|
3307
|
+
print(formatStep(steps[steps.length - 1]));
|
|
3308
|
+
const repoCoord = input.context.repo ?? repo?.name ?? "";
|
|
3309
|
+
const org = repoCoord.includes("/") ? repoCoord.split("/")[0] : "";
|
|
3310
|
+
const writeableTeams = teams.map((t) => typeof t === "string" ? {
|
|
3311
|
+
slug: t,
|
|
3312
|
+
permission: "push"
|
|
3313
|
+
} : t).filter((t) => [
|
|
3314
|
+
"push",
|
|
3315
|
+
"maintain",
|
|
3316
|
+
"admin"
|
|
3317
|
+
].includes(t.permission));
|
|
3318
|
+
if (org && writeableTeams.length > 0) {
|
|
3319
|
+
steps.push(await runStep("source", "write .github/CODEOWNERS", dryRun, async () => {
|
|
3320
|
+
const content = writeableTeams.map((t) => `* @${org}/${t.slug}`).join("\n") + "\n";
|
|
3321
|
+
await source.writeRepoFile(".github/CODEOWNERS", content);
|
|
3322
|
+
}));
|
|
3323
|
+
print(formatStep(steps[steps.length - 1]));
|
|
3324
|
+
}
|
|
3325
|
+
} else {
|
|
3326
|
+
steps.push({
|
|
3327
|
+
capability: "source",
|
|
3328
|
+
step: "sync teams",
|
|
3329
|
+
status: "skip",
|
|
3330
|
+
message: "provider does not implement syncTeams"
|
|
3331
|
+
});
|
|
3332
|
+
print(formatStep(steps[steps.length - 1]));
|
|
3333
|
+
}
|
|
3301
3334
|
}
|
|
3302
3335
|
if (loader.has("environments")) {
|
|
3303
3336
|
const envs = loader.get("environments");
|
|
@@ -3382,6 +3415,23 @@ async function runSetup(input) {
|
|
|
3382
3415
|
print(formatStep(steps[steps.length - 1]));
|
|
3383
3416
|
}
|
|
3384
3417
|
}
|
|
3418
|
+
if (config.skills && config.skills.length > 0 && config.agent) {
|
|
3419
|
+
print(style.step("skills"));
|
|
3420
|
+
if (!(config.agent in AGENT_SYMLINK_PATHS)) steps.push({
|
|
3421
|
+
capability: "skills",
|
|
3422
|
+
step: "install skills",
|
|
3423
|
+
status: "skip",
|
|
3424
|
+
message: `agent "${config.agent}" has no known skill install path`
|
|
3425
|
+
});
|
|
3426
|
+
else steps.push(await runStep("skills", "install skills", dryRun, async () => {
|
|
3427
|
+
return await installSkills({
|
|
3428
|
+
agent: config.agent,
|
|
3429
|
+
skills: config.skills,
|
|
3430
|
+
repoRoot: input.context.repoRoot
|
|
3431
|
+
});
|
|
3432
|
+
}));
|
|
3433
|
+
print(formatStep(steps[steps.length - 1]));
|
|
3434
|
+
}
|
|
3385
3435
|
const summary = steps.reduce((acc, s) => {
|
|
3386
3436
|
if (s.status === "ok") acc.ok += 1;
|
|
3387
3437
|
else if (s.status === "fail") acc.fail += 1;
|
|
@@ -3419,6 +3469,94 @@ async function runSetup(input) {
|
|
|
3419
3469
|
summary
|
|
3420
3470
|
};
|
|
3421
3471
|
}
|
|
3472
|
+
const AGENTS_SKILLS_ROOT = ".agents/skills";
|
|
3473
|
+
/** Relative path of the agent-specific symlink. undefined = unsupported agent. */
|
|
3474
|
+
const AGENT_SYMLINK_PATHS = { claude: (name) => `.claude/skills/${name}` };
|
|
3475
|
+
const GITIGNORE_BLOCK_START = "# managed by holocron setup — skills";
|
|
3476
|
+
const GITIGNORE_BLOCK_END = "# end managed by holocron setup — skills";
|
|
3477
|
+
async function installSkills({ agent, skills, repoRoot }) {
|
|
3478
|
+
const symlinkFn = AGENT_SYMLINK_PATHS[agent];
|
|
3479
|
+
if (!symlinkFn) return `agent "${agent}" has no known skill install path — skipping`;
|
|
3480
|
+
const require = createRequire(pathToFileURL(join(repoRoot, "package.json")));
|
|
3481
|
+
let skillsRoot;
|
|
3482
|
+
try {
|
|
3483
|
+
skillsRoot = dirname(require.resolve("@theholocron/skills/package.json"));
|
|
3484
|
+
} catch {
|
|
3485
|
+
throw new Error("@theholocron/skills not found — run: pnpm add -D @theholocron/skills");
|
|
3486
|
+
}
|
|
3487
|
+
const gitignorePath = join(repoRoot, ".gitignore");
|
|
3488
|
+
const existingContent = await readFile(gitignorePath, "utf8").catch(() => "");
|
|
3489
|
+
const previouslyInstalled = parsePreviousSkills(existingContent, symlinkFn);
|
|
3490
|
+
const currentSet = new Set(skills);
|
|
3491
|
+
const stale = previouslyInstalled.filter((n) => !currentSet.has(n));
|
|
3492
|
+
for (const name of stale) {
|
|
3493
|
+
await rm(join(repoRoot, symlinkFn(name)), { force: true }).catch(() => void 0);
|
|
3494
|
+
await rm(join(repoRoot, AGENTS_SKILLS_ROOT, name), {
|
|
3495
|
+
recursive: true,
|
|
3496
|
+
force: true
|
|
3497
|
+
}).catch(() => void 0);
|
|
3498
|
+
}
|
|
3499
|
+
const installed = [];
|
|
3500
|
+
const missing = [];
|
|
3501
|
+
for (const name of skills) {
|
|
3502
|
+
const srcDir = join(skillsRoot, "skills", name);
|
|
3503
|
+
try {
|
|
3504
|
+
await stat(srcDir);
|
|
3505
|
+
} catch {
|
|
3506
|
+
missing.push(name);
|
|
3507
|
+
continue;
|
|
3508
|
+
}
|
|
3509
|
+
const agentsDir = join(repoRoot, AGENTS_SKILLS_ROOT, name);
|
|
3510
|
+
await copyDirRecursive(srcDir, agentsDir);
|
|
3511
|
+
const symlinkPath = join(repoRoot, symlinkFn(name));
|
|
3512
|
+
await mkdir(dirname(symlinkPath), { recursive: true });
|
|
3513
|
+
try {
|
|
3514
|
+
await unlink(symlinkPath);
|
|
3515
|
+
} catch {}
|
|
3516
|
+
await symlink(relative(dirname(symlinkPath), agentsDir).replace(/\\/g, "/"), symlinkPath);
|
|
3517
|
+
installed.push(name);
|
|
3518
|
+
}
|
|
3519
|
+
if (installed.length > 0 || stale.length > 0 || missing.length > 0) await updateSkillsGitignore(gitignorePath, existingContent, [...installed, ...missing], symlinkFn);
|
|
3520
|
+
const parts = [`installed ${installed.length}`];
|
|
3521
|
+
if (stale.length > 0) parts.push(`pruned: ${stale.join(", ")}`);
|
|
3522
|
+
if (missing.length > 0) parts.push(`unknown: ${missing.join(", ")}`);
|
|
3523
|
+
return parts.join("; ");
|
|
3524
|
+
}
|
|
3525
|
+
/** Extract skill names from the previous gitignore block so stale dirs can be pruned. */
|
|
3526
|
+
function parsePreviousSkills(gitignoreContent, symlinkFn) {
|
|
3527
|
+
if (!gitignoreContent.includes(GITIGNORE_BLOCK_START)) return [];
|
|
3528
|
+
const startIdx = gitignoreContent.indexOf(GITIGNORE_BLOCK_START);
|
|
3529
|
+
const endIdx = gitignoreContent.indexOf(GITIGNORE_BLOCK_END, startIdx);
|
|
3530
|
+
const block = endIdx !== -1 ? gitignoreContent.slice(startIdx, endIdx) : gitignoreContent.slice(startIdx);
|
|
3531
|
+
const placeholder = "__placeholder__";
|
|
3532
|
+
const symlinkPrefix = `/${symlinkFn(placeholder)}`.replace(placeholder, "");
|
|
3533
|
+
return block.split("\n").filter((line) => line.startsWith(symlinkPrefix)).map((line) => line.slice(symlinkPrefix.length));
|
|
3534
|
+
}
|
|
3535
|
+
async function copyDirRecursive(src, dest) {
|
|
3536
|
+
await mkdir(dest, { recursive: true });
|
|
3537
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
3538
|
+
for (const entry of entries) {
|
|
3539
|
+
const srcPath = join(src, entry.name);
|
|
3540
|
+
const destPath = join(dest, entry.name);
|
|
3541
|
+
if (entry.isDirectory()) await copyDirRecursive(srcPath, destPath);
|
|
3542
|
+
else await copyFile(srcPath, destPath);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
async function updateSkillsGitignore(gitignorePath, existingContent, skills, symlinkFn) {
|
|
3546
|
+
const block = [
|
|
3547
|
+
GITIGNORE_BLOCK_START,
|
|
3548
|
+
...[`/${AGENTS_SKILLS_ROOT}/`, ...skills.map((n) => `/${symlinkFn(n)}`)],
|
|
3549
|
+
GITIGNORE_BLOCK_END
|
|
3550
|
+
].join("\n");
|
|
3551
|
+
let content;
|
|
3552
|
+
if (existingContent.includes(GITIGNORE_BLOCK_START)) {
|
|
3553
|
+
const start = existingContent.indexOf(GITIGNORE_BLOCK_START);
|
|
3554
|
+
const end = existingContent.indexOf(GITIGNORE_BLOCK_END, start);
|
|
3555
|
+
const afterBlock = end !== -1 ? existingContent.slice(end + 40) : "\n";
|
|
3556
|
+
content = existingContent.slice(0, start) + block + afterBlock;
|
|
3557
|
+
} else content = (existingContent.trimEnd() ? existingContent.trimEnd() + "\n\n" : "") + block + "\n";
|
|
3558
|
+
await writeFile(gitignorePath, content, "utf8");
|
|
3559
|
+
}
|
|
3422
3560
|
async function runStep(capability, step, dryRun, body) {
|
|
3423
3561
|
if (dryRun) return {
|
|
3424
3562
|
capability,
|
|
@@ -3469,10 +3607,36 @@ function formatStep(step) {
|
|
|
3469
3607
|
return ` ${style.dim(`· ${label}`)}`;
|
|
3470
3608
|
}
|
|
3471
3609
|
//#endregion
|
|
3610
|
+
//#region src/commands/skills.ts
|
|
3611
|
+
async function runSkillsInstall(input) {
|
|
3612
|
+
const print = input.print ?? ((line) => console.log(line));
|
|
3613
|
+
const config = input.loaded.resolved;
|
|
3614
|
+
if (!config.agent || !config.skills?.length) {
|
|
3615
|
+
print("Nothing to install — set `agent` and `skills` in holocron.config.ts");
|
|
3616
|
+
return;
|
|
3617
|
+
}
|
|
3618
|
+
if (input.context.dryRun) {
|
|
3619
|
+
print(`Would install ${config.skills.length} skill(s) for agent: ${config.agent}`);
|
|
3620
|
+
for (const name of config.skills) print(` → would install: ${name}`);
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3623
|
+
print(`Installing ${config.skills.length} skill(s) for agent: ${config.agent}`);
|
|
3624
|
+
try {
|
|
3625
|
+
print(` → ${await installSkills({
|
|
3626
|
+
agent: config.agent,
|
|
3627
|
+
skills: config.skills,
|
|
3628
|
+
repoRoot: input.context.repoRoot
|
|
3629
|
+
})}`);
|
|
3630
|
+
} catch (err) {
|
|
3631
|
+
print(` ✗ ${err instanceof Error ? err.message : String(err)}`);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
//#endregion
|
|
3472
3635
|
//#region src/commands/sync.ts
|
|
3473
3636
|
const SYNC_STEPS = [
|
|
3474
3637
|
"labels",
|
|
3475
3638
|
"properties",
|
|
3639
|
+
"teams",
|
|
3476
3640
|
"topics",
|
|
3477
3641
|
"keywords",
|
|
3478
3642
|
"description"
|
|
@@ -3535,6 +3699,46 @@ async function runSync(input) {
|
|
|
3535
3699
|
});
|
|
3536
3700
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
3537
3701
|
}
|
|
3702
|
+
if (stepName === "teams") {
|
|
3703
|
+
const teams = config.repo?.teams ?? [];
|
|
3704
|
+
if (teams.length === 0) {
|
|
3705
|
+
steps.push({
|
|
3706
|
+
capability: "source",
|
|
3707
|
+
step: "sync teams",
|
|
3708
|
+
status: "skip",
|
|
3709
|
+
message: "no teams configured"
|
|
3710
|
+
});
|
|
3711
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
3712
|
+
} else if (source.syncTeams) {
|
|
3713
|
+
steps.push(await runSyncStep("source", "sync teams", dryRun, () => source.syncTeams(teams)));
|
|
3714
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
3715
|
+
const repoCoord = input.context.repo ?? config.repo?.name ?? "";
|
|
3716
|
+
const org = repoCoord.includes("/") ? repoCoord.split("/")[0] : "";
|
|
3717
|
+
const writeableTeams = teams.map((t) => typeof t === "string" ? {
|
|
3718
|
+
slug: t,
|
|
3719
|
+
permission: "push"
|
|
3720
|
+
} : t).filter((t) => [
|
|
3721
|
+
"push",
|
|
3722
|
+
"maintain",
|
|
3723
|
+
"admin"
|
|
3724
|
+
].includes(t.permission));
|
|
3725
|
+
if (org && writeableTeams.length > 0) {
|
|
3726
|
+
steps.push(await runSyncStep("source", "write .github/CODEOWNERS", dryRun, async () => {
|
|
3727
|
+
const content = writeableTeams.map((t) => `* @${org}/${t.slug}`).join("\n") + "\n";
|
|
3728
|
+
await source.writeRepoFile(".github/CODEOWNERS", content);
|
|
3729
|
+
}));
|
|
3730
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
3731
|
+
}
|
|
3732
|
+
} else {
|
|
3733
|
+
steps.push({
|
|
3734
|
+
capability: "source",
|
|
3735
|
+
step: "sync teams",
|
|
3736
|
+
status: "skip",
|
|
3737
|
+
message: "provider does not implement syncTeams"
|
|
3738
|
+
});
|
|
3739
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3538
3742
|
if (stepName === "topics") {
|
|
3539
3743
|
const topics = config.repo?.topics ?? [];
|
|
3540
3744
|
if (topics.length === 0) {
|
|
@@ -3862,6 +4066,18 @@ await yargs(hideBin(process.argv)).scriptName("holocron").usage("$0 <command> [o
|
|
|
3862
4066
|
...argv.token ? { cliToken: argv.token } : {}
|
|
3863
4067
|
}
|
|
3864
4068
|
})).summary.fail > 0) process.exitCode = 1;
|
|
4069
|
+
}).command("skills <action>", "Manage agent skills from the @theholocron/skills registry", (y) => y.positional("action", {
|
|
4070
|
+
type: "string",
|
|
4071
|
+
choices: ["install"],
|
|
4072
|
+
describe: "install — copy skills from @theholocron/skills into .agents/ with agent symlinks"
|
|
4073
|
+
}), async (argv) => {
|
|
4074
|
+
if (argv.action === "install") await runSkillsInstall({
|
|
4075
|
+
loaded: await loadConfig(argv.cwd),
|
|
4076
|
+
context: {
|
|
4077
|
+
repoRoot: argv.cwd,
|
|
4078
|
+
dryRun: argv.dryRun
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
3865
4081
|
}).command("secret set <name> [value]", "Set a single secret via the configured `secrets` capability", (y) => y.positional("name", {
|
|
3866
4082
|
type: "string",
|
|
3867
4083
|
demandOption: true,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti } from "./capabilities/index.mjs";
|
|
1
|
+
import { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti } from "./capabilities/index.mjs";
|
|
2
2
|
import { AuthError, RequestOptions, ResolveTokenConfig as ResolveTokenConfig$1, ResolveTokenInput, RestClient, RestClientConfig, createRestClient } from "@theholocron/http-client";
|
|
3
3
|
|
|
4
4
|
//#region src/auth-resolver.d.ts
|
|
@@ -47,6 +47,12 @@ interface RepoConfig {
|
|
|
47
47
|
protection?: RepoProtection;
|
|
48
48
|
/** CI check context names required on the default branch (only used when `protection` is "strict"). */
|
|
49
49
|
requiredChecks?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* GitHub teams granted repository access. Synced by `holocron setup`, which
|
|
52
|
+
* also writes `.github/CODEOWNERS` for teams with write-or-higher permission.
|
|
53
|
+
* String shorthand defaults to `push` (Write).
|
|
54
|
+
*/
|
|
55
|
+
teams?: TeamEntry[];
|
|
50
56
|
/** GitHub topics set on the repository. */
|
|
51
57
|
topics?: string[];
|
|
52
58
|
/** GitHub custom properties synced to the org dashboard. */
|
|
@@ -93,6 +99,22 @@ interface HolocronConfig {
|
|
|
93
99
|
providers: RawProvidersConfig;
|
|
94
100
|
apps?: AppConfig[];
|
|
95
101
|
doctor?: DoctorConfig;
|
|
102
|
+
/**
|
|
103
|
+
* Agent runtime that determines where skills are installed by `holocron setup`.
|
|
104
|
+
* Skills are installed to `.agents/skills/<name>/` (canonical) with a
|
|
105
|
+
* relative symlink at the agent-specific path:
|
|
106
|
+
* - `"claude"` → `.claude/skills/<name>` → `../../.agents/skills/<name>`
|
|
107
|
+
* - `"codex"` | `"gemini"` → logged as unsupported; skipped gracefully.
|
|
108
|
+
*/
|
|
109
|
+
agent?: "claude" | "codex" | "gemini";
|
|
110
|
+
/**
|
|
111
|
+
* Skill names from `@theholocron/skills` to install during `holocron setup`.
|
|
112
|
+
* Installed paths are gitignored and managed by setup — do not commit them.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ["git-safety", "pr-workflow", "commit-standards"]
|
|
116
|
+
*/
|
|
117
|
+
skills?: string[];
|
|
96
118
|
}
|
|
97
119
|
interface ResolvedTuple {
|
|
98
120
|
provider: string;
|
|
@@ -119,6 +141,8 @@ interface ResolvedHolocronConfig {
|
|
|
119
141
|
providers: ResolvedProvidersConfig;
|
|
120
142
|
apps: AppConfig[];
|
|
121
143
|
doctor: DoctorConfig;
|
|
144
|
+
agent?: "claude" | "codex" | "gemini";
|
|
145
|
+
skills?: string[];
|
|
122
146
|
}
|
|
123
147
|
declare class ConfigError extends Error {
|
|
124
148
|
name: string;
|
|
@@ -195,4 +219,4 @@ interface LoadedConfig {
|
|
|
195
219
|
*/
|
|
196
220
|
declare function loadConfig(cwd: string): Promise<LoadedConfig>;
|
|
197
221
|
//#endregion
|
|
198
|
-
export { Analytics, AppConfig, Auth, AuthDescription, AuthError, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, EnsureResult, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, LoadedConfig, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoConfig, RepoProperties, RepoProtection, RepoRef, RepoSettings, type RequestOptions, ResolveTokenConfig, type ResolveTokenInput, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, type RestClient, type RestClientConfig, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
|
222
|
+
export { Analytics, AppConfig, Auth, AuthDescription, AuthError, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, EnsureResult, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, LoadedConfig, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoConfig, RepoProperties, RepoProtection, RepoRef, RepoSettings, type RequestOptions, ResolveTokenConfig, type ResolveTokenInput, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, type RestClient, type RestClientConfig, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
package/dist/index.mjs
CHANGED
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.69",
|
|
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",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@napi-rs/keyring": "^1.3.0",
|
|
37
|
-
"@theholocron/github-client": "^
|
|
38
|
-
"@theholocron/http-client": "^
|
|
37
|
+
"@theholocron/github-client": "^1.1.0",
|
|
38
|
+
"@theholocron/http-client": "^1.1.0",
|
|
39
39
|
"chalk": "^5.4.1",
|
|
40
40
|
"ora": "^8.2.0",
|
|
41
41
|
"tsx": "^4.22.4",
|