@grimoire-rs/indexer 0.1.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.
Files changed (114) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +48 -0
  3. package/dist/cli/build.d.ts +6 -0
  4. package/dist/cli/build.d.ts.map +1 -0
  5. package/dist/cli/build.js +29 -0
  6. package/dist/cli/build.js.map +1 -0
  7. package/dist/cli/exit.d.ts +28 -0
  8. package/dist/cli/exit.d.ts.map +1 -0
  9. package/dist/cli/exit.js +33 -0
  10. package/dist/cli/exit.js.map +1 -0
  11. package/dist/cli/index.d.ts +3 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +8 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +40 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +317 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/cli/main.d.ts +2 -0
  20. package/dist/cli/main.d.ts.map +1 -0
  21. package/dist/cli/main.js +112 -0
  22. package/dist/cli/main.js.map +1 -0
  23. package/dist/cli/validate.d.ts +18 -0
  24. package/dist/cli/validate.d.ts.map +1 -0
  25. package/dist/cli/validate.js +139 -0
  26. package/dist/cli/validate.js.map +1 -0
  27. package/dist/config.d.ts +76 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +134 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/data/index.d.ts +40 -0
  32. package/dist/data/index.d.ts.map +1 -0
  33. package/dist/data/index.js +103 -0
  34. package/dist/data/index.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +4 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/integration.d.ts +2 -0
  40. package/dist/integration.d.ts.map +1 -0
  41. package/dist/integration.js +4 -0
  42. package/dist/integration.js.map +1 -0
  43. package/dist/renderer/astro/components/Catalog.d.ts +6 -0
  44. package/dist/renderer/astro/components/Catalog.d.ts.map +1 -0
  45. package/dist/renderer/astro/components/Catalog.js +73 -0
  46. package/dist/renderer/astro/components/Catalog.js.map +1 -0
  47. package/dist/renderer/astro/components/Catalog.tsx +256 -0
  48. package/dist/renderer/astro/components/CopyButton.astro +24 -0
  49. package/dist/renderer/astro/components/VersionMenu.astro +105 -0
  50. package/dist/renderer/astro/content.config.ts +30 -0
  51. package/dist/renderer/astro/layouts/Base.astro +623 -0
  52. package/dist/renderer/astro/lib/catalog.d.ts +7 -0
  53. package/dist/renderer/astro/lib/catalog.d.ts.map +1 -0
  54. package/dist/renderer/astro/lib/catalog.js +61 -0
  55. package/dist/renderer/astro/lib/catalog.js.map +1 -0
  56. package/dist/renderer/astro/lib/catalog.ts +69 -0
  57. package/dist/renderer/astro/lib/data.d.ts +3 -0
  58. package/dist/renderer/astro/lib/data.d.ts.map +1 -0
  59. package/dist/renderer/astro/lib/data.js +4 -0
  60. package/dist/renderer/astro/lib/data.js.map +1 -0
  61. package/dist/renderer/astro/lib/data.ts +12 -0
  62. package/dist/renderer/astro/pages/index.astro +69 -0
  63. package/dist/renderer/astro/pages/p/[...slug].astro +367 -0
  64. package/dist/renderer/index.d.ts +20 -0
  65. package/dist/renderer/index.d.ts.map +1 -0
  66. package/dist/renderer/index.js +192 -0
  67. package/dist/renderer/index.js.map +1 -0
  68. package/dist/renderer/public/favicon.svg +4 -0
  69. package/dist/renderer/types.d.ts +34 -0
  70. package/dist/renderer/types.d.ts.map +1 -0
  71. package/dist/renderer/types.js +4 -0
  72. package/dist/renderer/types.js.map +1 -0
  73. package/dist/validate/adapters/files.d.ts +17 -0
  74. package/dist/validate/adapters/files.d.ts.map +1 -0
  75. package/dist/validate/adapters/files.js +50 -0
  76. package/dist/validate/adapters/files.js.map +1 -0
  77. package/dist/validate/adapters/forge.d.ts +25 -0
  78. package/dist/validate/adapters/forge.d.ts.map +1 -0
  79. package/dist/validate/adapters/forge.js +100 -0
  80. package/dist/validate/adapters/forge.js.map +1 -0
  81. package/dist/validate/adapters/http.d.ts +24 -0
  82. package/dist/validate/adapters/http.d.ts.map +1 -0
  83. package/dist/validate/adapters/http.js +75 -0
  84. package/dist/validate/adapters/http.js.map +1 -0
  85. package/dist/validate/adapters/registry.d.ts +16 -0
  86. package/dist/validate/adapters/registry.d.ts.map +1 -0
  87. package/dist/validate/adapters/registry.js +91 -0
  88. package/dist/validate/adapters/registry.js.map +1 -0
  89. package/dist/validate/core/metadata.d.ts +67 -0
  90. package/dist/validate/core/metadata.d.ts.map +1 -0
  91. package/dist/validate/core/metadata.js +149 -0
  92. package/dist/validate/core/metadata.js.map +1 -0
  93. package/dist/validate/core/ownership.d.ts +43 -0
  94. package/dist/validate/core/ownership.d.ts.map +1 -0
  95. package/dist/validate/core/ownership.js +33 -0
  96. package/dist/validate/core/ownership.js.map +1 -0
  97. package/dist/validate/core/paths.d.ts +21 -0
  98. package/dist/validate/core/paths.d.ts.map +1 -0
  99. package/dist/validate/core/paths.js +34 -0
  100. package/dist/validate/core/paths.js.map +1 -0
  101. package/dist/validate/index.d.ts +40 -0
  102. package/dist/validate/index.d.ts.map +1 -0
  103. package/dist/validate/index.js +206 -0
  104. package/dist/validate/index.js.map +1 -0
  105. package/package.json +50 -0
  106. package/templates/README.md +48 -0
  107. package/templates/github-pages.yml +24 -0
  108. package/templates/github-validate.yml +36 -0
  109. package/templates/gitignore +9 -0
  110. package/templates/gitlab-ci.yml +13 -0
  111. package/templates/publish.toml +17 -0
  112. package/templates/reusable/gitlab-index-ci.yml +61 -0
  113. package/templates/reusable/index-pages.yml +80 -0
  114. package/templates/reusable/index-validate.yml +88 -0
@@ -0,0 +1,317 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+ // `grim-indexer init` — scaffold an index repo, following the UX of
4
+ // Microsoft's `yo code` (prompt, write, tell the user what to do next) but
5
+ // none of its runtime: the wizard is @clack/prompts, there is no Yeoman.
6
+ import { execFileSync } from "node:child_process";
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import * as prompts from "@clack/prompts";
11
+ import { CliError, EXIT } from "./exit.js";
12
+ /** Scaffold templates ship beside `dist/`, so this resolves identically from `src/cli/` and `dist/cli/`. */
13
+ const TEMPLATE_DIR = fileURLToPath(new URL("../../templates/", import.meta.url));
14
+ /** `{{name}}`, but never GitHub Actions' `${{ ... }}` — hence the `$` lookbehind. */
15
+ const PLACEHOLDER = /(?<!\$)\{\{\s*([A-Za-z_]\w*)\s*\}\}/g;
16
+ const NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
17
+ function slug(value) {
18
+ return value
19
+ .trim()
20
+ .toLowerCase()
21
+ .replace(/[^a-z0-9._-]+/g, "-")
22
+ .replace(/^-+|-+$/g, "");
23
+ }
24
+ function titleCase(name) {
25
+ return name
26
+ .split(/[-_.]+/)
27
+ .filter(Boolean)
28
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
29
+ .join(" ");
30
+ }
31
+ function readTemplate(rel) {
32
+ const file = path.join(TEMPLATE_DIR, rel);
33
+ try {
34
+ return fs.readFileSync(file, "utf8");
35
+ }
36
+ catch {
37
+ throw new CliError(`scaffold template ${rel} is missing from ${TEMPLATE_DIR} — the installed package is incomplete`, EXIT.unavailable);
38
+ }
39
+ }
40
+ function render(template, vars, rel) {
41
+ return template.replace(PLACEHOLDER, (match, key) => {
42
+ if (!(key in vars)) {
43
+ throw new CliError(`scaffold template ${rel} references unknown placeholder {{${key}}}`);
44
+ }
45
+ return vars[key];
46
+ });
47
+ }
48
+ /** Validate a URL the user typed. Returns the reason it is bad, or `null`. */
49
+ function badUrl(value) {
50
+ let url;
51
+ try {
52
+ url = new URL(value);
53
+ }
54
+ catch {
55
+ return "must be an absolute URL, e.g. https://index.example.com";
56
+ }
57
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
58
+ return "must be an http(s) URL";
59
+ }
60
+ return null;
61
+ }
62
+ function badName(value) {
63
+ if (!NAME_RE.test(value)) {
64
+ return "must be lowercase, start alphanumeric, and contain only a-z 0-9 . _ -";
65
+ }
66
+ return null;
67
+ }
68
+ /**
69
+ * Resolve flags into a complete answer set, prompting for whatever is
70
+ * missing. `--quick` never prompts, so CI and tests get a deterministic
71
+ * tree from flags alone.
72
+ */
73
+ async function resolveAnswers(dir, flags) {
74
+ const defaultName = slug(path.basename(path.resolve(dir))) || "my-index";
75
+ // Flags always win, and any flag value is validated whether or not a
76
+ // prompt would have caught it — `--quick` skips the prompt, not the check.
77
+ for (const [flag, value, check] of [
78
+ ["--name", flags.name, badName],
79
+ ["--registry", flags.registry, badName],
80
+ ["--base-url", flags.baseUrl, badUrl],
81
+ ]) {
82
+ if (value !== undefined) {
83
+ const reason = check(value);
84
+ if (reason)
85
+ throw new CliError(`${flag} ${JSON.stringify(value)}: ${reason}`, EXIT.data);
86
+ }
87
+ }
88
+ if (flags.quick) {
89
+ const name = flags.name ?? defaultName;
90
+ return {
91
+ name,
92
+ title: flags.title ?? titleCase(name),
93
+ baseUrl: flags.baseUrl ?? "http://localhost:4321",
94
+ registryAlias: flags.registry ?? name,
95
+ registryHost: flags.registryHost ?? "ghcr.io",
96
+ logo: flags.logo ?? "",
97
+ forge: flags.forge ?? "github",
98
+ git: flags.git ?? true,
99
+ withSkills: flags.withSkills ?? false,
100
+ };
101
+ }
102
+ prompts.intro("grim-indexer — new package index");
103
+ const name = flags.name ??
104
+ (await ask(prompts.text({
105
+ message: "Index name (used as the identifier)",
106
+ placeholder: defaultName,
107
+ defaultValue: defaultName,
108
+ validate: (value) => badName(value || defaultName) ?? undefined,
109
+ })));
110
+ const title = flags.title ??
111
+ (await ask(prompts.text({
112
+ message: "Display title",
113
+ placeholder: titleCase(name),
114
+ defaultValue: titleCase(name),
115
+ })));
116
+ const baseUrl = flags.baseUrl ??
117
+ (await ask(prompts.text({
118
+ message: "Base URL the index is served from",
119
+ placeholder: "https://index.example.com",
120
+ validate: (value) => badUrl(value ?? "") ?? undefined,
121
+ })));
122
+ const registryAlias = flags.registry ??
123
+ (await ask(prompts.text({
124
+ message: "Registry alias packages are published under",
125
+ placeholder: name,
126
+ defaultValue: name,
127
+ validate: (value) => badName(value || name) ?? undefined,
128
+ })));
129
+ const logo = flags.logo ??
130
+ (await ask(prompts.text({
131
+ message: "Brand logo (path or URL, blank for none)",
132
+ defaultValue: "",
133
+ })));
134
+ const forge = flags.forge ??
135
+ (await ask(prompts.select({
136
+ message: "CI to scaffold",
137
+ options: [
138
+ { value: "github", label: "GitHub Actions" },
139
+ { value: "gitlab", label: "GitLab CI" },
140
+ { value: "both", label: "Both" },
141
+ ],
142
+ initialValue: "github",
143
+ })));
144
+ const git = flags.git ??
145
+ (await ask(prompts.confirm({ message: "Initialize a git repository?", initialValue: true })));
146
+ return {
147
+ name,
148
+ title,
149
+ baseUrl,
150
+ registryAlias,
151
+ registryHost: flags.registryHost ?? "ghcr.io",
152
+ logo,
153
+ forge,
154
+ git,
155
+ withSkills: flags.withSkills ?? false,
156
+ };
157
+ }
158
+ /** Unwrap a clack prompt, turning Ctrl-C into a clean non-error abort. */
159
+ async function ask(pending) {
160
+ const value = await pending;
161
+ if (prompts.isCancel(value)) {
162
+ prompts.cancel("Cancelled — nothing written.");
163
+ throw new CliError("cancelled", EXIT.ok);
164
+ }
165
+ return value;
166
+ }
167
+ function json(value) {
168
+ return JSON.stringify(value, null, 2) + "\n";
169
+ }
170
+ /**
171
+ * `index.config.json`, in the shape `loadConfig` validates. Every
172
+ * key is optional there, so an unanswered prompt omits its key rather than
173
+ * writing an empty string the validator would reject.
174
+ */
175
+ function siteConfig(answers) {
176
+ return json({
177
+ site: answers.baseUrl,
178
+ brand: answers.title,
179
+ description: `${answers.title} — a Grimoire package index.`,
180
+ ...(answers.logo ? { favicon: answers.logo } : {}),
181
+ registry: { alias: answers.registryAlias, index: answers.baseUrl },
182
+ });
183
+ }
184
+ /**
185
+ * `index-policy.json` — the committed allowlist the contribution gate reads.
186
+ * `registryHosts` bounds which registries an entry may point at (empty or
187
+ * absent means any public DNS host); `reservedNamespaces` are refused before
188
+ * any network call; `trustedBots` names accounts allowed to announce for a
189
+ * namespace they do not own — prefer the object form, `{ login, id,
190
+ * namespaces }`, since a bare string pins no account id and scopes nothing.
191
+ */
192
+ function indexPolicy(answers) {
193
+ return json({
194
+ version: 1,
195
+ registryHosts: [answers.registryHost],
196
+ reservedNamespaces: ["grim", "grimoire", "index"],
197
+ trustedBots: [],
198
+ });
199
+ }
200
+ /** The complete scaffold as (destination path, content) pairs — nothing touches disk yet. */
201
+ function plan(answers, version) {
202
+ const vars = {
203
+ name: answers.name,
204
+ title: answers.title,
205
+ baseUrl: answers.baseUrl,
206
+ registryAlias: answers.registryAlias,
207
+ registryHost: answers.registryHost,
208
+ logo: answers.logo,
209
+ version,
210
+ // The reusable workflows are pinned by tag; Renovate's `github-actions`
211
+ // manager bumps this in every scaffolded repo.
212
+ ref: `v${version}`,
213
+ };
214
+ const from = (rel, dest) => ({
215
+ path: dest,
216
+ content: render(readTemplate(rel), vars, rel),
217
+ });
218
+ const files = [
219
+ { path: "index/.gitkeep", content: "" },
220
+ { path: "index.config.json", content: siteConfig(answers) },
221
+ { path: "index-policy.json", content: indexPolicy(answers) },
222
+ from("gitignore", ".gitignore"),
223
+ from("README.md", "README.md"),
224
+ ];
225
+ if (answers.forge === "github" || answers.forge === "both") {
226
+ files.push(from("github-pages.yml", ".github/workflows/pages.yml"));
227
+ files.push(from("github-validate.yml", ".github/workflows/validate.yml"));
228
+ }
229
+ if (answers.forge === "gitlab" || answers.forge === "both") {
230
+ files.push(from("gitlab-ci.yml", ".gitlab-ci.yml"));
231
+ }
232
+ if (answers.withSkills) {
233
+ files.push({ path: "skills/.gitkeep", content: "" });
234
+ files.push(from("publish.toml", "publish.toml"));
235
+ }
236
+ return files;
237
+ }
238
+ /**
239
+ * Write the scaffold. Re-running is safe: a file whose content already
240
+ * matches is reported `unchanged`, and one the user has edited is left
241
+ * alone as `skipped` unless `--force`.
242
+ */
243
+ function write(dir, files, force) {
244
+ const written = [];
245
+ for (const file of files) {
246
+ const abs = path.join(dir, file.path);
247
+ let outcome;
248
+ if (fs.existsSync(abs)) {
249
+ if (fs.readFileSync(abs, "utf8") === file.content) {
250
+ written.push({ path: file.path, outcome: "unchanged" });
251
+ continue;
252
+ }
253
+ if (!force) {
254
+ written.push({ path: file.path, outcome: "skipped" });
255
+ continue;
256
+ }
257
+ outcome = "overwritten";
258
+ }
259
+ else {
260
+ outcome = "created";
261
+ }
262
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
263
+ fs.writeFileSync(abs, file.content);
264
+ written.push({ path: file.path, outcome });
265
+ }
266
+ return written;
267
+ }
268
+ function initGit(dir) {
269
+ if (fs.existsSync(path.join(dir, ".git")))
270
+ return false;
271
+ try {
272
+ execFileSync("git", ["init", "-b", "main"], { cwd: dir, stdio: "ignore" });
273
+ return true;
274
+ }
275
+ catch {
276
+ console.error("warning: `git init` failed — initialize the repository yourself");
277
+ return false;
278
+ }
279
+ }
280
+ function nextSteps(result, answers) {
281
+ const rel = path.relative(process.cwd(), result.dir);
282
+ // A relative path that climbs out of the working directory is worse than
283
+ // the absolute one it was derived from.
284
+ const target = rel === "" ? "" : rel.startsWith("..") ? result.dir : rel;
285
+ const cd = target === "" ? "" : `cd ${target}\n`;
286
+ const lines = [
287
+ `${cd}npx @grimoire-rs/indexer build`,
288
+ "",
289
+ "Then:",
290
+ " 1. Add packages under index/<namespace>/<package>/metadata.json",
291
+ " 2. Push to your forge — CI builds and publishes the site",
292
+ ];
293
+ if (answers.baseUrl === "http://localhost:4321") {
294
+ lines.push(" 3. Set baseUrl in index.config.json to the real site URL");
295
+ }
296
+ return lines.join("\n");
297
+ }
298
+ export async function init(dir, flags, version) {
299
+ const target = path.resolve(dir);
300
+ const answers = await resolveAnswers(target, flags);
301
+ fs.mkdirSync(target, { recursive: true });
302
+ const files = write(target, plan(answers, version), flags.force ?? false);
303
+ const gitInitialized = answers.git ? initGit(target) : false;
304
+ const result = { dir: target, files, gitInitialized };
305
+ for (const file of files) {
306
+ console.log(` ${file.outcome.padEnd(12)}${file.path}`);
307
+ }
308
+ const skipped = files.filter((f) => f.outcome === "skipped");
309
+ if (skipped.length > 0) {
310
+ console.error(`\n${skipped.length} file(s) differ from the scaffold and were left alone. ` +
311
+ `Re-run with --force to overwrite them.`);
312
+ }
313
+ prompts.note(nextSteps(result, answers), "Next steps");
314
+ prompts.outro(`Index "${answers.name}" ready in ${target}`);
315
+ return EXIT.ok;
316
+ }
317
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function run(argv: string[]): Promise<number>;
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAuEA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0EzD"}
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+ // Argument parsing and the single place errors become exit codes. The bin
4
+ // entry (`index.ts`) does nothing but call `run` and set `process.exitCode`.
5
+ import fs from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import { Command, CommanderError, Option } from "commander";
8
+ import { build } from "./build.js";
9
+ import { CliError, EXIT } from "./exit.js";
10
+ import { init } from "./init.js";
11
+ import { validate } from "./validate.js";
12
+ /** Read from disk rather than imported, so `rootDir: src` stays intact. */
13
+ function packageVersion() {
14
+ const manifest = fileURLToPath(new URL("../../package.json", import.meta.url));
15
+ const parsed = JSON.parse(fs.readFileSync(manifest, "utf8"));
16
+ const version = parsed.version;
17
+ return typeof version === "string" ? version : "0.0.0";
18
+ }
19
+ /**
20
+ * Map a thrown value to an exit code, printing whatever the user needs to
21
+ * see. Commander has already written its own usage text by the time its
22
+ * errors land here.
23
+ */
24
+ function classify(err) {
25
+ if (err instanceof CommanderError) {
26
+ // `--help` and `--version` also arrive here, with exitCode 0.
27
+ return err.exitCode === 0 ? EXIT.ok : EXIT.usage;
28
+ }
29
+ if (err instanceof CliError) {
30
+ if (err.code !== EXIT.ok)
31
+ console.error(err.message);
32
+ return err.code;
33
+ }
34
+ if (err instanceof Error && "code" in err && err.code === "ERR_MODULE_NOT_FOUND") {
35
+ console.error(`${err.message}\nthis build of grim-indexer is incomplete — run \`npm run build\``);
36
+ return EXIT.unavailable;
37
+ }
38
+ // How the compiler, the config loader, and the renderer reject bad input
39
+ // — a data problem in the repo being built, not a bug in the tool. Matched
40
+ // by name so `build`'s modules stay dynamically imported.
41
+ if (err instanceof Error &&
42
+ ["IndexValidationError", "SiteConfigError", "RenderInputError"].includes(err.name)) {
43
+ console.error(err.message);
44
+ return EXIT.data;
45
+ }
46
+ console.error(err instanceof Error ? err.message : String(err));
47
+ return EXIT.failure;
48
+ }
49
+ export async function run(argv) {
50
+ const version = packageVersion();
51
+ let code = EXIT.ok;
52
+ const program = new Command()
53
+ .name("grim-indexer")
54
+ .description("Run your own grim package index")
55
+ .version(version)
56
+ .exitOverride();
57
+ program
58
+ .command("init")
59
+ .argument("[dir]", "directory to scaffold into", ".")
60
+ .description("scaffold an index repo")
61
+ .option("--quick", "skip every prompt and take defaults (for CI)")
62
+ .option("--name <name>", "index identifier")
63
+ .option("--title <title>", "display title")
64
+ .option("--base-url <url>", "URL the index is served from")
65
+ .option("--registry <alias>", "registry alias packages are published under")
66
+ .option("--registry-host <host>", "OCI registry host", "ghcr.io")
67
+ .option("--logo <path>", "brand logo path or URL")
68
+ .addOption(new Option("--forge <forge>", "CI to scaffold").choices(["github", "gitlab", "both"]))
69
+ .option("--no-git", "do not run `git init`")
70
+ .option("--with-skills", "scaffold the combined layout: this repo holds its skills and its index")
71
+ .option("--force", "overwrite files that differ from the scaffold")
72
+ .action(async (dir, opts, cmd) => {
73
+ code = await init(dir,
74
+ // `--no-git` defaults `git` to true, which would silently answer the
75
+ // interactive prompt; only an explicit flag counts as an answer.
76
+ { ...opts, git: cmd.getOptionValueSource("git") === "cli" ? opts.git : undefined }, version);
77
+ });
78
+ program
79
+ .command("build")
80
+ .argument("[root]", "index repo root", ".")
81
+ .description("compile index/** and render the site into dist/")
82
+ .option("--out-dir <dir>", "output directory", "dist")
83
+ .action(async (root, opts) => {
84
+ code = await build(root, { outDir: opts.outDir });
85
+ });
86
+ program
87
+ .command("validate")
88
+ .argument("[files...]", "changed index/** paths from the PR/MR diff")
89
+ .description("contribution gate: exit 0 = eligible for auto-merge")
90
+ .option("--root <dir>", "trusted base checkout — the committed state", ".")
91
+ // Required, but enforced in `validate` rather than by commander: the gate
92
+ // contract puts a missing `--pr-tree` in the 65 bucket, not commander's 64.
93
+ .option("--pr-tree <dir>", "checkout of the PR head, read as data only")
94
+ .option("--policy <file>", "path to index-policy.json")
95
+ .addOption(new Option("--forge <forge>", "forge hosting the contribution").choices([
96
+ "github",
97
+ "gitlab",
98
+ ]))
99
+ .option("--author-login <login>", "contribution author's forge login")
100
+ .option("--author-id <id>", "contribution author's numeric forge id")
101
+ .action(async (files, opts) => {
102
+ code = await validate(files, opts);
103
+ });
104
+ try {
105
+ await program.parseAsync(argv);
106
+ }
107
+ catch (err) {
108
+ return classify(err);
109
+ }
110
+ return code;
111
+ }
112
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type ExitCode } from "./exit.js";
2
+ export type ValidateForge = "github" | "gitlab";
3
+ export interface ValidateFlags {
4
+ root?: string;
5
+ prTree?: string;
6
+ policy?: string;
7
+ forge?: ValidateForge;
8
+ authorLogin?: string;
9
+ authorId?: string;
10
+ }
11
+ /**
12
+ * @param files Changed `index/**` paths as CI's `git diff --name-only`
13
+ * printed them — repo-relative, and passed through unchanged. An empty set
14
+ * is not "check everything": the gate has nothing to authorize and returns
15
+ * ineligible.
16
+ */
17
+ export declare function validate(files: string[], flags: ValidateFlags): Promise<ExitCode>;
18
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAeA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgFD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqDvF"}
@@ -0,0 +1,139 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+ // `grim-indexer validate` — the contribution gate CI runs on a PR/MR.
4
+ //
5
+ // The contract callers depend on is the exit code, and only that:
6
+ // 0 -> eligible for auto-merge
7
+ // non-zero -> manual review required
8
+ //
9
+ // This is also where CI plumbing lives: forge, PR author, and the committed
10
+ // registry-host allowlist are read off the environment and the policy file
11
+ // so the generated workflow stays a thin caller with no logic of its own.
12
+ import fs from "node:fs";
13
+ import path from "node:path";
14
+ import { CliError, EXIT } from "./exit.js";
15
+ function detectForge() {
16
+ if (process.env.GITHUB_ACTIONS)
17
+ return "github";
18
+ if (process.env.GITLAB_CI)
19
+ return "gitlab";
20
+ return undefined;
21
+ }
22
+ /**
23
+ * The PR/MR author, from whatever the forge exposes. GitHub carries it in
24
+ * the event payload; GitLab's merge-request pipeline runs as the author, so
25
+ * `GITLAB_USER_*` is the author.
26
+ */
27
+ function detectAuthor(forge) {
28
+ if (forge === "gitlab") {
29
+ const login = process.env.GITLAB_USER_LOGIN;
30
+ const id = process.env.GITLAB_USER_ID;
31
+ return login && id ? { login, id } : undefined;
32
+ }
33
+ const eventPath = process.env.GITHUB_EVENT_PATH;
34
+ if (!eventPath)
35
+ return undefined;
36
+ let event;
37
+ try {
38
+ event = JSON.parse(fs.readFileSync(eventPath, "utf8"));
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ const user = event
44
+ ?.pull_request?.user;
45
+ if (typeof user?.login !== "string" || (typeof user.id !== "number" && typeof user.id !== "string")) {
46
+ return undefined;
47
+ }
48
+ return { login: user.login, id: String(user.id) };
49
+ }
50
+ /**
51
+ * Read the committed allowlist. An absent default-location file is fine —
52
+ * the gate then bounds nothing. An explicitly requested one that is absent
53
+ * or malformed is a data error, never a silent pass.
54
+ */
55
+ function readPolicy(file, explicit) {
56
+ let text;
57
+ try {
58
+ text = fs.readFileSync(file, "utf8");
59
+ }
60
+ catch {
61
+ if (explicit)
62
+ throw new CliError(`${file}: cannot be read`, EXIT.data);
63
+ return {};
64
+ }
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(text);
68
+ }
69
+ catch (err) {
70
+ throw new CliError(`${file}: ${err.message}`, EXIT.data);
71
+ }
72
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
73
+ throw new CliError(`${file}: must contain a JSON object`, EXIT.data);
74
+ }
75
+ const policy = parsed;
76
+ for (const key of ["registryHosts", "reservedNamespaces"]) {
77
+ const value = policy[key];
78
+ if (value === undefined)
79
+ continue;
80
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
81
+ throw new CliError(`${file}: ${key} must be an array of strings`, EXIT.data);
82
+ }
83
+ }
84
+ if (policy.trustedBots !== undefined && !Array.isArray(policy.trustedBots)) {
85
+ throw new CliError(`${file}: trustedBots must be an array`, EXIT.data);
86
+ }
87
+ return policy;
88
+ }
89
+ /**
90
+ * @param files Changed `index/**` paths as CI's `git diff --name-only`
91
+ * printed them — repo-relative, and passed through unchanged. An empty set
92
+ * is not "check everything": the gate has nothing to authorize and returns
93
+ * ineligible.
94
+ */
95
+ export async function validate(files, flags) {
96
+ const root = path.resolve(flags.root ?? ".");
97
+ const forge = flags.forge ?? detectForge();
98
+ if (!forge) {
99
+ throw new CliError("cannot tell which forge this is; pass --forge github|gitlab", EXIT.usage);
100
+ }
101
+ const author = flags.authorLogin && flags.authorId
102
+ ? { login: flags.authorLogin, id: flags.authorId }
103
+ : detectAuthor(forge);
104
+ if (!author) {
105
+ throw new CliError("cannot determine the contribution author; pass --author-login and --author-id", EXIT.usage);
106
+ }
107
+ // Not defaulted to `root`. Passing the same dir for both silently disables
108
+ // the committed-owner-id continuity check — the one that stops a
109
+ // re-registered login from inheriting the previous owner's packages — and a
110
+ // gate that quietly drops a check is worse than one that refuses to run.
111
+ if (!flags.prTree) {
112
+ throw new CliError("--pr-tree is required: the directory holding the PR head", EXIT.data);
113
+ }
114
+ const policyFile = flags.policy
115
+ ? path.resolve(flags.policy)
116
+ : path.join(root, "index-policy.json");
117
+ const policy = readPolicy(policyFile, flags.policy !== undefined);
118
+ const { runValidate } = await import("../validate/index.js");
119
+ const { eligible, reasons } = await runValidate({
120
+ forge,
121
+ root,
122
+ prTree: path.resolve(flags.prTree),
123
+ changedFiles: files,
124
+ author,
125
+ ...(policy.registryHosts ? { registryHosts: policy.registryHosts } : {}),
126
+ ...(policy.reservedNamespaces ? { reservedNamespaces: policy.reservedNamespaces } : {}),
127
+ ...(policy.trustedBots ? { trustedBots: policy.trustedBots } : {}),
128
+ });
129
+ if (eligible) {
130
+ console.log("eligible for auto-merge");
131
+ return EXIT.ok;
132
+ }
133
+ console.error("manual review required:");
134
+ for (const reason of reasons) {
135
+ console.error(` - ${reason}`);
136
+ }
137
+ return EXIT.failure;
138
+ }
139
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"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"}