@g3nomaly/mods 0.0.0-stage → 0.1.1

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 (87) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +95 -2
  3. package/dist/cli/commands/add.d.ts +24 -0
  4. package/dist/cli/commands/add.d.ts.map +1 -0
  5. package/dist/cli/commands/add.js +29 -0
  6. package/dist/cli/commands/add.js.map +1 -0
  7. package/dist/cli/commands/init.d.ts +21 -0
  8. package/dist/cli/commands/init.d.ts.map +1 -0
  9. package/dist/cli/commands/init.js +27 -0
  10. package/dist/cli/commands/init.js.map +1 -0
  11. package/dist/cli/commands/ls.d.ts +22 -0
  12. package/dist/cli/commands/ls.d.ts.map +1 -0
  13. package/dist/cli/commands/ls.js +25 -0
  14. package/dist/cli/commands/ls.js.map +1 -0
  15. package/dist/cli/commands/remove.d.ts +21 -0
  16. package/dist/cli/commands/remove.d.ts.map +1 -0
  17. package/dist/cli/commands/remove.js +37 -0
  18. package/dist/cli/commands/remove.js.map +1 -0
  19. package/dist/cli/commands/source.d.ts +60 -0
  20. package/dist/cli/commands/source.d.ts.map +1 -0
  21. package/dist/cli/commands/source.js +49 -0
  22. package/dist/cli/commands/source.js.map +1 -0
  23. package/dist/cli/commands/update.d.ts +21 -0
  24. package/dist/cli/commands/update.d.ts.map +1 -0
  25. package/dist/cli/commands/update.js +17 -0
  26. package/dist/cli/commands/update.js.map +1 -0
  27. package/dist/cli/index.d.ts +3 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +100 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/cli/project.d.ts +72 -0
  32. package/dist/cli/project.d.ts.map +1 -0
  33. package/dist/cli/project.js +72 -0
  34. package/dist/cli/project.js.map +1 -0
  35. package/dist/cli/viewer.d.ts +12 -0
  36. package/dist/cli/viewer.d.ts.map +1 -0
  37. package/dist/cli/viewer.js +33 -0
  38. package/dist/cli/viewer.js.map +1 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +5 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/registry/index.d.ts +7 -0
  44. package/dist/registry/index.d.ts.map +1 -0
  45. package/dist/registry/index.js +7 -0
  46. package/dist/registry/index.js.map +1 -0
  47. package/dist/registry/locate.d.ts +21 -0
  48. package/dist/registry/locate.d.ts.map +1 -0
  49. package/dist/registry/locate.js +42 -0
  50. package/dist/registry/locate.js.map +1 -0
  51. package/dist/registry/manifest.d.ts +8 -0
  52. package/dist/registry/manifest.d.ts.map +1 -0
  53. package/dist/registry/manifest.js +13 -0
  54. package/dist/registry/manifest.js.map +1 -0
  55. package/dist/registry/readModDir.d.ts +20 -0
  56. package/dist/registry/readModDir.d.ts.map +1 -0
  57. package/dist/registry/readModDir.js +84 -0
  58. package/dist/registry/readModDir.js.map +1 -0
  59. package/dist/registry/readSource.d.ts +9 -0
  60. package/dist/registry/readSource.d.ts.map +1 -0
  61. package/dist/registry/readSource.js +27 -0
  62. package/dist/registry/readSource.js.map +1 -0
  63. package/dist/registry/types.d.ts +69 -0
  64. package/dist/registry/types.d.ts.map +1 -0
  65. package/dist/registry/types.js +20 -0
  66. package/dist/registry/types.js.map +1 -0
  67. package/dist/sources/builtins.d.ts +4 -0
  68. package/dist/sources/builtins.d.ts.map +1 -0
  69. package/dist/sources/builtins.js +5 -0
  70. package/dist/sources/builtins.js.map +1 -0
  71. package/dist/sources/index.d.ts +2 -0
  72. package/dist/sources/index.d.ts.map +1 -0
  73. package/dist/sources/index.js +3 -0
  74. package/dist/sources/index.js.map +1 -0
  75. package/dist/viewer/extension.d.ts +28 -0
  76. package/dist/viewer/extension.d.ts.map +1 -0
  77. package/dist/viewer/extension.js +49 -0
  78. package/dist/viewer/extension.js.map +1 -0
  79. package/dist/viewer/index.d.ts +5 -0
  80. package/dist/viewer/index.d.ts.map +1 -0
  81. package/dist/viewer/index.js +4 -0
  82. package/dist/viewer/index.js.map +1 -0
  83. package/dist/viewer/settings.d.ts +37 -0
  84. package/dist/viewer/settings.d.ts.map +1 -0
  85. package/dist/viewer/settings.js +51 -0
  86. package/dist/viewer/settings.js.map +1 -0
  87. package/package.json +37 -4
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bun
2
+ // packages/mods/src/cli/index.ts
3
+ import { Command } from "commander";
4
+ import { readPackageMeta, warnIfOutdated } from "@g3nomaly/core/utils";
5
+ import { ViewerLocationSchema } from "../viewer";
6
+ import { addCommand } from "./commands/add";
7
+ import { initCommand } from "./commands/init";
8
+ import { lsCommand } from "./commands/ls";
9
+ import { removeCommand } from "./commands/remove";
10
+ import { sourceAddCommand, sourceLsCommand, sourceRemoveCommand } from "./commands/source";
11
+ import { updateCommand } from "./commands/update";
12
+ /**
13
+ * @brief Maps the `--user` flag to a scope, so every command can work on `~/.claude/`.
14
+ *
15
+ * @param opts - Parsed options holding the `--user` flag.
16
+ * @returns `user` when the flag is set, `project` otherwise.
17
+ */
18
+ function scopeOf(opts) {
19
+ return opts.user === true ? "user" : "project";
20
+ }
21
+ const program = new Command()
22
+ .name("g3n-mods")
23
+ .description("Install Claude Code mods from registered sources");
24
+ program
25
+ .command("init")
26
+ .description("Create mods.json and set up VS Code to show mods")
27
+ .option("-u, --user", "Work on ~/.claude/ for every project")
28
+ .option("--location <where>", "Where Claude opens in VS Code: panel (an editor tab) or sidebar")
29
+ .action((opts) => {
30
+ const location = opts.location === undefined ? undefined : ViewerLocationSchema.parse(opts.location);
31
+ initCommand({ scope: scopeOf(opts), location });
32
+ });
33
+ program
34
+ .command("ls")
35
+ .description("List the mods the registered sources provide")
36
+ .option("-u, --user", "Work on ~/.claude/ for every project")
37
+ .option("-i, --installed", "List installed mods instead")
38
+ .option("-s, --source <name>", "Only this source")
39
+ .option("--json", "Output as JSON")
40
+ .action(async (opts) => {
41
+ await lsCommand({ scope: scopeOf(opts), installed: opts.installed, source: opts.source, json: opts.json });
42
+ });
43
+ program
44
+ .command("add [mods...]")
45
+ .description("Install mods, or every mod of a source")
46
+ .option("-u, --user", "Work on ~/.claude/ for every project")
47
+ .option("-s, --source <name>", "Only read this source")
48
+ .option("-f, --force", "Overwrite locally modified files")
49
+ .action(async (mods, opts) => {
50
+ await addCommand(mods, { scope: scopeOf(opts), source: opts.source, force: opts.force });
51
+ });
52
+ program
53
+ .command("update [mods...]")
54
+ .description("Re-install installed mods from their sources")
55
+ .option("-u, --user", "Work on ~/.claude/ for every project")
56
+ .option("-f, --force", "Overwrite locally modified files without prompting")
57
+ .option("--dry-run", "Report what would change without writing")
58
+ .action(async (mods, opts) => {
59
+ await updateCommand(mods, { scope: scopeOf(opts), force: opts.force, dryRun: opts.dryRun });
60
+ });
61
+ program
62
+ .command("remove [mods...]")
63
+ .description("Remove installed mods")
64
+ .option("-u, --user", "Work on ~/.claude/ for every project")
65
+ .option("-f, --force", "Remove even if files have been modified")
66
+ .action(async (mods, opts) => {
67
+ await removeCommand(mods, { scope: scopeOf(opts), force: opts.force });
68
+ });
69
+ const source = program
70
+ .command("source")
71
+ .description("Register, list and unregister mod sources");
72
+ source
73
+ .command("add <source>")
74
+ .description("Register a git URL, owner/repo, npm package (npm:<pkg>, @scope/pkg) or local folder")
75
+ .option("-u, --user", "Work on ~/.claude/ for every project")
76
+ .option("-n, --name <name>", "Name to register it under")
77
+ .option("-r, --ref <ref>", "Branch or tag of a git source")
78
+ .action(async (input, opts) => {
79
+ await sourceAddCommand(input, { scope: scopeOf(opts), name: opts.name, ref: opts.ref });
80
+ });
81
+ source
82
+ .command("ls")
83
+ .description("List built-in and registered sources")
84
+ .option("-u, --user", "Work on ~/.claude/ for every project")
85
+ .option("--json", "Output as JSON")
86
+ .action((opts) => {
87
+ sourceLsCommand({ scope: scopeOf(opts), json: opts.json });
88
+ });
89
+ source
90
+ .command("remove <name>")
91
+ .description("Unregister a source")
92
+ .option("-u, --user", "Work on ~/.claude/ for every project")
93
+ .option("-f, --force", "Unregister even while installed mods come from it")
94
+ .action((name, opts) => {
95
+ sourceRemoveCommand(name, { scope: scopeOf(opts), force: opts.force });
96
+ });
97
+ const self = readPackageMeta(import.meta.url);
98
+ await warnIfOutdated(self.name, self.version, import.meta.url);
99
+ await program.parseAsync();
100
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { resolveProjectRoot } from "@g3nomaly/core/config";
3
+ import type { InstallCommandContext, ManifestTarget } from "@g3nomaly/core/cli";
4
+ import type { SourceRegistry } from "@g3nomaly/core/installer";
5
+ import type { RegisteredSource } from "@g3nomaly/core/sources";
6
+ import type { ModMeta, ModRegistry } from "../registry/types";
7
+ /** `project` installs into the repository's `.claude/`, `user` into `~/.claude/` for every project. */
8
+ export declare const ScopeSchema: z.ZodEnum<{
9
+ project: "project";
10
+ user: "user";
11
+ }>;
12
+ /** Install scope, one of the values of `ScopeSchema`. */
13
+ export type Scope = z.infer<typeof ScopeSchema>;
14
+ /**
15
+ * Install scope opened for one command: where mods go, the `mods.json` that
16
+ * records them, and the root that sources resolve against. Created by
17
+ * `openModProject`.
18
+ *
19
+ * @property scope - Scope the project was opened for.
20
+ * @property projectRoot - Absolute root for local source paths and the `node_modules` lookup: the git project, or home for `user` outside one.
21
+ * @property claudeDir - Absolute `.claude/` directory of the scope.
22
+ * @property target - The one install destination with the `mods.json` that backs it.
23
+ */
24
+ export interface ModProject {
25
+ scope: Scope;
26
+ projectRoot: string;
27
+ claudeDir: string;
28
+ target: ManifestTarget;
29
+ }
30
+ /**
31
+ * Overrides for the project lookup, used by tests.
32
+ *
33
+ * @property resolveProjectRoot - Replaces git discovery of the project root.
34
+ * @property home - Home folder for the `user` scope, instead of `os.homedir()`.
35
+ */
36
+ export interface ProjectDeps {
37
+ resolveProjectRoot?: typeof resolveProjectRoot;
38
+ home?: string;
39
+ }
40
+ /**
41
+ * @brief Opens the scope's `.claude/` directory and its `mods.json`. Exits with an
42
+ * error when the project scope is asked for outside a git repository.
43
+ *
44
+ * @param scope - Scope to open.
45
+ * @param deps - Overrides for the project lookup, used by tests.
46
+ * @returns The opened project.
47
+ */
48
+ export declare function openModProject(scope: Scope, deps?: ProjectDeps): ModProject;
49
+ /**
50
+ * @brief Lists the sources of a scope: built-in ones first, then those registered in its `mods.json`.
51
+ *
52
+ * @param project - Scope to list sources for.
53
+ */
54
+ export declare function modSources(project: ModProject): RegisteredSource[];
55
+ /** What the core install and source commands print and check for mods. */
56
+ export declare const MOD_CONTEXT: InstallCommandContext<ModMeta>;
57
+ /**
58
+ * @brief Reads the mods installed in the scope.
59
+ *
60
+ * @param project - Scope to read.
61
+ */
62
+ export declare function installedMods(project: ModProject): ModRegistry;
63
+ /**
64
+ * @brief Reads every mod the scope's sources provide, or only those of `only`, and
65
+ * prints each warning. The caller disposes the result when done.
66
+ *
67
+ * @param project - Scope to read sources for.
68
+ * @param only - Source name to read alone; exits when no such source is registered.
69
+ * @returns Registry of the mods read.
70
+ */
71
+ export declare function openModRegistry(project: ModProject, only?: string): Promise<SourceRegistry<ModMeta>>;
72
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/cli/project.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG9D,uGAAuG;AACvG,eAAO,MAAM,WAAW;;;EAA8B,CAAC;AAEvD,yDAAyD;AACzD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAKhD;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAQ,KAAK,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAI,MAAM,CAAC;IACpB,MAAM,EAAO,cAAc,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,kBAAkB,CAAC,EAAE,OAAO,kBAAkB,CAAC;IAC/C,IAAI,CAAC,EAAgB,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,WAAgB,GAAG,UAAU,CAiB/E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAElE;AAED,0EAA0E;AAC1E,eAAO,MAAM,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAgE,CAAC;AAExH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,WAAW,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAI1G"}
@@ -0,0 +1,72 @@
1
+ // packages/mods/src/cli/project.ts
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { resolveProjectRoot } from "@g3nomaly/core/config";
6
+ import { lineError, narrowSources, orExit, reportSourceRegistry } from "@g3nomaly/core/cli";
7
+ import { installedEntries, loadInstallManifest, loadSourceRegistry, manifestSources } from "@g3nomaly/core/installer";
8
+ import { modsManifestPath } from "../registry/manifest";
9
+ import { readModSource } from "../registry/readSource";
10
+ import { ModMetaSchema } from "../registry/types";
11
+ import { BUILTIN_SOURCES } from "../sources/builtins";
12
+ /** `project` installs into the repository's `.claude/`, `user` into `~/.claude/` for every project. */
13
+ export const ScopeSchema = z.enum(["project", "user"]);
14
+ /** Folder under `.claude/` that Claude Code auto-loads plugins from, as `<name>@skills-dir`. */
15
+ const INSTALL_DIR = "skills";
16
+ /**
17
+ * @brief Opens the scope's `.claude/` directory and its `mods.json`. Exits with an
18
+ * error when the project scope is asked for outside a git repository.
19
+ *
20
+ * @param scope - Scope to open.
21
+ * @param deps - Overrides for the project lookup, used by tests.
22
+ * @returns The opened project.
23
+ */
24
+ export function openModProject(scope, deps = {}) {
25
+ const home = deps.home ?? os.homedir();
26
+ const found = (deps.resolveProjectRoot ?? resolveProjectRoot)();
27
+ if (scope === "project" && found === null) {
28
+ console.error(lineError("No project found — run g3n-mods inside a git repository, or pass --user to install for every project."));
29
+ process.exit(1);
30
+ }
31
+ const projectRoot = found ?? home;
32
+ const claudeDir = path.join(scope === "user" ? home : projectRoot, ".claude");
33
+ const manifest = orExit(() => loadInstallManifest(modsManifestPath(claudeDir), ModMetaSchema));
34
+ return {
35
+ scope,
36
+ projectRoot,
37
+ claudeDir,
38
+ target: { label: scope, installDir: path.join(claudeDir, INSTALL_DIR), store: manifest.store, manifest },
39
+ };
40
+ }
41
+ /**
42
+ * @brief Lists the sources of a scope: built-in ones first, then those registered in its `mods.json`.
43
+ *
44
+ * @param project - Scope to list sources for.
45
+ */
46
+ export function modSources(project) {
47
+ return manifestSources([project.target.manifest], BUILTIN_SOURCES);
48
+ }
49
+ /** What the core install and source commands print and check for mods. */
50
+ export const MOD_CONTEXT = { bin: "g3n-mods", noun: "mod", entrySchema: ModMetaSchema };
51
+ /**
52
+ * @brief Reads the mods installed in the scope.
53
+ *
54
+ * @param project - Scope to read.
55
+ */
56
+ export function installedMods(project) {
57
+ return installedEntries(project.target.manifest, ModMetaSchema);
58
+ }
59
+ /**
60
+ * @brief Reads every mod the scope's sources provide, or only those of `only`, and
61
+ * prints each warning. The caller disposes the result when done.
62
+ *
63
+ * @param project - Scope to read sources for.
64
+ * @param only - Source name to read alone; exits when no such source is registered.
65
+ * @returns Registry of the mods read.
66
+ */
67
+ export async function openModRegistry(project, only) {
68
+ const registry = await loadSourceRegistry(narrowSources(modSources(project), only, "g3n-mods"), project.projectRoot, readModSource);
69
+ reportSourceRegistry(registry);
70
+ return registry;
71
+ }
72
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/cli/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ViewerLocation } from "../viewer";
2
+ import type { ModProject } from "./project";
3
+ /**
4
+ * @brief Sets up where mods are seen and prints what it found: the project's VS
5
+ * Code settings (project scope only) and the installed extension's version
6
+ * against the one the mod API needs.
7
+ *
8
+ * @param project - Scope to set up; VS Code settings are written only for `project`.
9
+ * @param location - Where Claude opens; when absent, a location already set is kept.
10
+ */
11
+ export declare function setUpViewer(project: ModProject, location?: ViewerLocation): void;
12
+ //# sourceMappingURL=viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../src/cli/viewer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAkBhF"}
@@ -0,0 +1,33 @@
1
+ // packages/mods/src/cli/viewer.ts
2
+ import { c, lineInfo, lineSuccess, lineWarn } from "@g3nomaly/core/cli";
3
+ import { MIN_ENGINE_VERSION, compareVersions, configureViewer, findClaudeExtension } from "../viewer";
4
+ /**
5
+ * @brief Sets up where mods are seen and prints what it found: the project's VS
6
+ * Code settings (project scope only) and the installed extension's version
7
+ * against the one the mod API needs.
8
+ *
9
+ * @param project - Scope to set up; VS Code settings are written only for `project`.
10
+ * @param location - Where Claude opens; when absent, a location already set is kept.
11
+ */
12
+ export function setUpViewer(project, location) {
13
+ if (project.scope === "project") {
14
+ const result = configureViewer(project.projectRoot, location);
15
+ if (result.kind === "written") {
16
+ console.log(lineSuccess(`VS Code opens Claude in its native UI ${c.dim(`(${c.file(result.file)})`)}`));
17
+ }
18
+ else if (result.kind === "unreadable") {
19
+ console.log(lineWarn(`${c.file(result.file)} is not plain JSON — add these settings by hand: ${JSON.stringify(result.settings)}`));
20
+ }
21
+ }
22
+ const extension = findClaudeExtension();
23
+ if (extension === null) {
24
+ console.log(lineWarn(`Claude Code for VS Code is not installed — mods draw in its native UI. Install it from the marketplace (anthropic.claude-code), ${MIN_ENGINE_VERSION} or newer.`));
25
+ }
26
+ else if (compareVersions(extension.version, MIN_ENGINE_VERSION) < 0) {
27
+ console.log(lineWarn(`Claude Code for VS Code is ${extension.version}; mods need ${MIN_ENGINE_VERSION} or newer. Update the extension.`));
28
+ }
29
+ else {
30
+ console.log(lineInfo(`Claude Code for VS Code ${extension.version} draws mod panes. Run ${c.cmd("Claude Code: Open in New Window")} to give them a window of their own.`));
31
+ }
32
+ }
33
+ //# sourceMappingURL=viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/cli/viewer.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAItG;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAmB,EAAE,QAAyB;IACxE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,yCAAyC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACzG,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACrI,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,mBAAmB,EAAE,CAAC;IACxC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,mIAAmI,kBAAkB,YAAY,CAAC,CAAC,CAAC;IAC3L,CAAC;SAAM,IAAI,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,8BAA8B,SAAS,CAAC,OAAO,eAAe,kBAAkB,kCAAkC,CAAC,CAAC,CAAC;IAC5I,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,2BAA2B,SAAS,CAAC,OAAO,yBAAyB,CAAC,CAAC,GAAG,CAAC,iCAAiC,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAC7K,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./registry";
2
+ export * from "./sources";
3
+ export * from "./viewer";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // packages/mods/src/index.ts
2
+ export * from "./registry";
3
+ export * from "./sources";
4
+ export * from "./viewer";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from "./types";
2
+ export { isModDir, readModDir, PLUGIN_MANIFEST, HOOKS_MANIFEST } from "./readModDir";
3
+ export { locateMods } from "./locate";
4
+ export type { ModScan } from "./locate";
5
+ export { readModSource } from "./readSource";
6
+ export { modsManifestPath } from "./manifest";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ // packages/mods/src/registry/index.ts
2
+ export * from "./types";
3
+ export { isModDir, readModDir, PLUGIN_MANIFEST, HOOKS_MANIFEST } from "./readModDir";
4
+ export { locateMods } from "./locate";
5
+ export { readModSource } from "./readSource";
6
+ export { modsManifestPath } from "./manifest";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mod folders found in one source checkout, and what was skipped.
3
+ *
4
+ * @property dirs - Absolute mod folders.
5
+ * @property warnings - Why each skipped plugin or folder was left out.
6
+ */
7
+ export interface ModScan {
8
+ dirs: string[];
9
+ warnings: string[];
10
+ }
11
+ /**
12
+ * @brief Finds the mods a source provides. A `marketplace.json` lists them by
13
+ * relative path; without one, the source root is the mod when it is one,
14
+ * else every mod folder directly under `mods/` or the root. Plugins that
15
+ * hold no hooks module are skipped with a warning.
16
+ *
17
+ * @param root - Absolute root of the source checkout.
18
+ * @returns Mod folders, and warnings for what was skipped.
19
+ */
20
+ export declare function locateMods(root: string): ModScan;
21
+ //# sourceMappingURL=locate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../../src/registry/locate.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAM,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgBhD"}
@@ -0,0 +1,42 @@
1
+ // packages/mods/src/registry/locate.ts
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { readMarketplace } from "@g3nomaly/core/installer";
5
+ import { isModDir } from "./readModDir";
6
+ const MODS_DIR = "mods";
7
+ /** Lists the direct child folders of `dir`, leaving out hidden ones and `node_modules`. */
8
+ function childDirs(dir) {
9
+ if (!fs.existsSync(dir))
10
+ return [];
11
+ return fs.readdirSync(dir, { withFileTypes: true })
12
+ .filter((d) => d.isDirectory() && !d.name.startsWith(".") && d.name !== "node_modules")
13
+ .map((d) => path.join(dir, d.name));
14
+ }
15
+ /**
16
+ * @brief Finds the mods a source provides. A `marketplace.json` lists them by
17
+ * relative path; without one, the source root is the mod when it is one,
18
+ * else every mod folder directly under `mods/` or the root. Plugins that
19
+ * hold no hooks module are skipped with a warning.
20
+ *
21
+ * @param root - Absolute root of the source checkout.
22
+ * @returns Mod folders, and warnings for what was skipped.
23
+ */
24
+ export function locateMods(root) {
25
+ const marketplace = readMarketplace(root);
26
+ if (marketplace !== null) {
27
+ const warnings = [...marketplace.warnings];
28
+ const dirs = [];
29
+ for (const plugin of marketplace.plugins) {
30
+ if (isModDir(plugin.dir))
31
+ dirs.push(plugin.dir);
32
+ else
33
+ warnings.push(`${plugin.name}: a plugin without a hooks module, not a mod, skipped`);
34
+ }
35
+ return { dirs: dirs.sort(), warnings };
36
+ }
37
+ if (isModDir(root))
38
+ return { dirs: [root], warnings: [] };
39
+ const dirs = [...childDirs(path.join(root, MODS_DIR)), ...childDirs(root)].filter(isModDir);
40
+ return { dirs: [...new Set(dirs)].sort(), warnings: [] };
41
+ }
42
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/registry/locate.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAaxB,2FAA2F;AAC3F,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAChD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC;SACtF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAC3C,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,uDAAuD,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAE1D,MAAM,IAAI,GAAG,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5F,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @brief Builds the path of `mods.json` in a Claude directory.
3
+ *
4
+ * @param claudeDir - Absolute `.claude` directory of the scope, e.g. `/home/me/app/.claude`
5
+ * @returns Absolute path of the file.
6
+ */
7
+ export declare function modsManifestPath(claudeDir: string): string;
8
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/registry/manifest.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,13 @@
1
+ // packages/mods/src/registry/manifest.ts
2
+ import path from "node:path";
3
+ const MANIFEST_FILE = "mods.json";
4
+ /**
5
+ * @brief Builds the path of `mods.json` in a Claude directory.
6
+ *
7
+ * @param claudeDir - Absolute `.claude` directory of the scope, e.g. `/home/me/app/.claude`
8
+ * @returns Absolute path of the file.
9
+ */
10
+ export function modsManifestPath(claudeDir) {
11
+ return path.join(claudeDir, MANIFEST_FILE);
12
+ }
13
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/registry/manifest.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,WAAW,CAAC;AAElC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { ReadContext } from "@g3nomaly/core/installer";
2
+ import type { ModMeta } from "./types";
3
+ export declare const PLUGIN_MANIFEST: string;
4
+ export declare const HOOKS_MANIFEST: string;
5
+ /** Tells whether `dir` holds the plugin and hooks manifests a mod needs; `readModDir` checks the rest. */
6
+ export declare function isModDir(dir: string): boolean;
7
+ /**
8
+ * @brief Reads one mod folder into its registry entry. The key is the folder name,
9
+ * which must equal the `name` in its `plugin.json`, the name Claude Code
10
+ * loads it under; every installable file becomes a `files[]` entry at
11
+ * `<key>/<file>`.
12
+ *
13
+ * @param modDir - Absolute mod folder inside a source checkout
14
+ * @param context - Origin to record, the source as group, and the version to fall back on
15
+ * @throws when the folder is not a mod, its manifests are invalid, its name
16
+ * differs from its folder, its hooks module is not TypeScript, or it holds
17
+ * any JavaScript file
18
+ */
19
+ export declare function readModDir(modDir: string, context: ReadContext): Promise<[string, ModMeta]>;
20
+ //# sourceMappingURL=readModDir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readModDir.d.ts","sourceRoot":"","sources":["../../src/registry/readModDir.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAwB,OAAO,EAAE,MAAM,SAAS,CAAC;AAE7D,eAAO,MAAM,eAAe,QAA6C,CAAC;AAC1E,eAAO,MAAM,cAAc,QAAoC,CAAC;AAsBhE,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7C;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA8BjG"}
@@ -0,0 +1,84 @@
1
+ // packages/mods/src/registry/readModDir.ts
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { hashFile } from "@g3nomaly/core/hash";
6
+ import { listFiles } from "@g3nomaly/core/installer";
7
+ export const PLUGIN_MANIFEST = path.join(".claude-plugin", "plugin.json");
8
+ export const HOOKS_MANIFEST = path.join("hooks", "hooks.json");
9
+ /** Folders and files a mod folder may hold that are never installed: tests, the engine's type output, build state. */
10
+ const SKIPPED_DIRS = ["tests", path.join(".claude-plugin", "types")];
11
+ const SKIPPED_FILES = /(\.test\.tsx?$)|(^tsconfig\.json$)|(^\.DS_Store$)/;
12
+ const PluginManifestSchema = z.object({
13
+ name: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "use lowercase letters, digits and `-`"),
14
+ version: z.string().min(1).optional(),
15
+ description: z.string().optional(),
16
+ });
17
+ /** JavaScript in any form; a mod holding one is refused. */
18
+ const JAVASCRIPT_FILE = /\.(?:js|mjs|cjs|jsx)$/i;
19
+ /** Hooks modules are TypeScript only. */
20
+ const TYPESCRIPT_MODULE = /\.(?:ts|tsx|mts|cts)$/i;
21
+ const HooksManifestSchema = z.object({
22
+ modules: z.array(z.string().regex(TYPESCRIPT_MODULE, "must be a TypeScript file (.ts, .tsx, .mts, .cts)")).min(1),
23
+ });
24
+ /** Tells whether `dir` holds the plugin and hooks manifests a mod needs; `readModDir` checks the rest. */
25
+ export function isModDir(dir) {
26
+ return fs.existsSync(path.join(dir, PLUGIN_MANIFEST)) && fs.existsSync(path.join(dir, HOOKS_MANIFEST));
27
+ }
28
+ /**
29
+ * @brief Classifies a file by the top folder it sits in inside the mod.
30
+ *
31
+ * @param relativeToMod - File path relative to the mod folder.
32
+ * @returns Kind of the file; `asset` for anything outside the known folders.
33
+ */
34
+ function fileType(relativeToMod) {
35
+ const [top] = relativeToMod.split(path.sep);
36
+ if (top === ".claude-plugin")
37
+ return "manifest";
38
+ if (top === "hooks")
39
+ return "hooks";
40
+ if (top === "types")
41
+ return "types";
42
+ return "asset";
43
+ }
44
+ /**
45
+ * @brief Reads one mod folder into its registry entry. The key is the folder name,
46
+ * which must equal the `name` in its `plugin.json`, the name Claude Code
47
+ * loads it under; every installable file becomes a `files[]` entry at
48
+ * `<key>/<file>`.
49
+ *
50
+ * @param modDir - Absolute mod folder inside a source checkout
51
+ * @param context - Origin to record, the source as group, and the version to fall back on
52
+ * @throws when the folder is not a mod, its manifests are invalid, its name
53
+ * differs from its folder, its hooks module is not TypeScript, or it holds
54
+ * any JavaScript file
55
+ */
56
+ export async function readModDir(modDir, context) {
57
+ if (!isModDir(modDir)) {
58
+ throw new Error(`${modDir} is not a mod: it needs ${PLUGIN_MANIFEST} and ${HOOKS_MANIFEST}`);
59
+ }
60
+ const manifest = PluginManifestSchema.parse(JSON.parse(fs.readFileSync(path.join(modDir, PLUGIN_MANIFEST), "utf8")));
61
+ HooksManifestSchema.parse(JSON.parse(fs.readFileSync(path.join(modDir, HOOKS_MANIFEST), "utf8")));
62
+ const key = path.basename(modDir);
63
+ if (manifest.name !== key) {
64
+ throw new Error(`plugin.json names it "${manifest.name}", but its folder is "${key}"; they must match`);
65
+ }
66
+ const rels = listFiles(modDir, { skipDirs: SKIPPED_DIRS, skipFiles: SKIPPED_FILES });
67
+ const javascript = rels.filter((rel) => JAVASCRIPT_FILE.test(rel));
68
+ if (javascript.length > 0) {
69
+ throw new Error(`holds JavaScript (${javascript.join(", ")}); mods are TypeScript only`);
70
+ }
71
+ const files = [];
72
+ for (const rel of rels) {
73
+ files.push({ path: path.join(key, rel), type: fileType(rel), hash: await hashFile(path.join(modDir, rel)) });
74
+ }
75
+ return [key, {
76
+ name: manifest.name,
77
+ description: manifest.description,
78
+ version: manifest.version ?? context.version,
79
+ group: context.group,
80
+ origin: context.origin,
81
+ files,
82
+ }];
83
+ }
84
+ //# sourceMappingURL=readModDir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readModDir.js","sourceRoot":"","sources":["../../src/registry/readModDir.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAIrD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEhE,sHAAsH;AACtH,MAAM,YAAY,GAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;AACtE,MAAM,aAAa,GAAG,mDAAmD,CAAC;AAE1E,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAS,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE,uCAAuC,CAAC;IAC9F,OAAO,EAAM,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC;AAEH,4DAA4D;AAC5D,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD,yCAAyC;AACzC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAEnD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,EAAE,mDAAmD,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAClH,CAAC,CAAC;AAEH,0GAA0G;AAC1G,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;AACzG,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,aAAqB;IACrC,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,gBAAgB;QAAE,OAAO,UAAU,CAAC;IAChD,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACpC,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACpC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,OAAoB;IACnE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,2BAA2B,eAAe,QAAQ,cAAc,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrH,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAClG,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,QAAQ,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,IAAI,yBAAyB,GAAG,oBAAoB,CAAC,CAAC;IAC1G,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IACrF,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/G,CAAC;IAED,OAAO,CAAC,GAAG,EAAE;YACX,IAAI,EAAS,QAAQ,CAAC,IAAI;YAC1B,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,OAAO,EAAM,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO;YAChD,KAAK,EAAQ,OAAO,CAAC,KAAK;YAC1B,MAAM,EAAO,OAAO,CAAC,MAAM;YAC3B,KAAK;SACN,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ReadSource } from "@g3nomaly/core/installer";
2
+ import type { ModMeta } from "./types";
3
+ /**
4
+ * @brief Reads every mod a checked-out source provides, with the source as group.
5
+ * An unreadable mod is skipped with a warning. Handed to
6
+ * `loadSourceRegistry` by every mods command.
7
+ */
8
+ export declare const readModSource: ReadSource<ModMeta>;
9
+ //# sourceMappingURL=readSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSource.d.ts","sourceRoot":"","sources":["../../src/registry/readSource.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,UAAU,CAAC,OAAO,CAgB7C,CAAC"}