@g3nomaly/core 0.3.2 → 0.3.3

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 (2) hide show
  1. package/README.md +122 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @g3nomaly/core
2
+
3
+ `@g3nomaly/core` is the shared runtime behind the g3n installers: [`@g3nomaly/ui`](https://www.npmjs.com/package/@g3nomaly/ui), [`@g3nomaly/mods`](https://www.npmjs.com/package/@g3nomaly/mods) and [`@g3nomaly/skills`](https://www.npmjs.com/package/@g3nomaly/skills). It reads sources from git, npm or a local folder, copies entries into a project, records them in an install manifest and runs the `add`, `update`, `remove`, `ls` and `source` commands those installers share. Use it to build an installer of your own, or to reuse one piece, such as file hashing or the `.g3n.config.json` loader.
4
+
5
+ ## Requirements
6
+
7
+ - [Bun](https://bun.sh). Core calls `Bun.file` and `Bun.Transpiler`, so it does not run on Node.
8
+ - A CLI or script. `hash` and `config` throw when loaded in a browser.
9
+
10
+ ```sh
11
+ bun add @g3nomaly/core
12
+ ```
13
+
14
+ ## What each import gives you
15
+
16
+ Import from a subpath. The package root exports nothing.
17
+
18
+ | Import | What it gives you |
19
+ |---|---|
20
+ | `@g3nomaly/core/hash` | SHA-256 of a file or a string, full or 12 characters |
21
+ | `@g3nomaly/core/registry` | A typed registry of entries on a TanStack Store, saved as `.ts` or `.json`, and the base entry schemas |
22
+ | `@g3nomaly/core/config` | Read and write `.g3n.config.json`, with defaults for every section |
23
+ | `@g3nomaly/core/security` | `getSafeObject`, which reads the object a `.ts` file exports without importing the file |
24
+ | `@g3nomaly/core/sources` | Parse `owner/repo`, `npm:pkg` or `./folder` into a source spec and check it out to a folder |
25
+ | `@g3nomaly/core/installer` | Install manifests, reading every source into one registry, resolving names and dependencies, copying files |
26
+ | `@g3nomaly/core/cli` | The shared commands, the prompts they ask, and the terminal theme |
27
+ | `@g3nomaly/core/utils` | `findGitRoot`, `findUp`, `readPackageMeta`, `warnIfOutdated`, `errorMessage` |
28
+
29
+ ## Hashes and registries
30
+
31
+ An installer hashes each file it copies, so a later `update` can tell which files you changed.
32
+
33
+ ```ts
34
+ import { hashFile } from "@g3nomaly/core/hash";
35
+ import { createRegistryStore, flush, jsonHandler, load, register } from "@g3nomaly/core/registry";
36
+
37
+ const registry = createRegistryStore({ filePath: "/home/me/app/.installed.json", handler: jsonHandler });
38
+ await load(registry); // a missing file leaves the store empty
39
+
40
+ register(registry, "button", {
41
+ name: "button",
42
+ files: [{ path: "button.tsx", type: "component", hash: await hashFile("/home/me/app/src/button.tsx") }],
43
+ });
44
+ flush(registry); // writes the entries back, sorted by key
45
+ ```
46
+
47
+ `registry.store` is the TanStack Store itself, so you can subscribe to it. Without a handler, `flush` writes a `.ts` file that exports `registry`. Packages add their own fields by extending `BaseRegistryEntrySchema` and `BaseFileEntrySchema`.
48
+
49
+ ## Project config
50
+
51
+ `.g3n.config.json` holds one section per package: `g3nui`, `g3nerate` and `skills`. `loadConfig` fills in every default. `saveConfig` writes only the sections already in the file or named in the update, so one package's `init` never writes another package's section.
52
+
53
+ ```ts
54
+ import { loadConfig, resolveProjectRoot, saveConfig } from "@g3nomaly/core/config";
55
+
56
+ const root = resolveProjectRoot() ?? process.cwd(); // nearest folder with .g3n.config.json or .git
57
+ const { skills } = await loadConfig(root);
58
+ await saveConfig(root, { skills: { settings: { targets: ["claude", "codex"] } } });
59
+ ```
60
+
61
+ ## Reading a `.ts` file safely
62
+
63
+ `getSafeObject(filePath, schema, timeoutMs = 1000)` takes the object literal a file exports, evaluates it in a worker and checks the result against the zod schema. It refuses source with `eval`, dynamic `import()` or a nested-quantifier regex, and a result holding functions or too many keys. It drops getters without running them.
64
+
65
+ ## Building an installer
66
+
67
+ An installer supplies three things: a zod schema for its entries, a function that reads the entries in one checked-out source, and the folder they install into.
68
+
69
+ ```ts
70
+ import path from "node:path";
71
+ import { z } from "zod";
72
+ import { BaseFileEntrySchema } from "@g3nomaly/core/registry";
73
+ import { listFiles, loadInstallManifest, loadSourceRegistry, manifestSources, OriginEntrySchema, type ReadSource } from "@g3nomaly/core/installer";
74
+ import { addFromSources, reportSourceRegistry } from "@g3nomaly/core/cli";
75
+
76
+ const PromptSchema = OriginEntrySchema.extend({
77
+ files: z.array(BaseFileEntrySchema.extend({ type: z.literal("prompt") })).min(1),
78
+ });
79
+ type Prompt = z.infer<typeof PromptSchema>;
80
+
81
+ // Each file under prompts/ in a source is one entry.
82
+ const readPrompts: ReadSource<Prompt> = async (checkout) => ({
83
+ entries: listFiles(path.join(checkout.dir, "prompts")).map((file): [string, Prompt] => {
84
+ const name = path.basename(file, ".md");
85
+ return [name, {
86
+ name,
87
+ origin: { source: checkout.name, dir: "prompts", commit: checkout.commit, version: checkout.version },
88
+ files: [{ path: file, type: "prompt" }],
89
+ }];
90
+ }),
91
+ warnings: [],
92
+ });
93
+
94
+ const root = "/home/me/app";
95
+ const manifest = loadInstallManifest(path.join(root, ".prompts.json"), PromptSchema);
96
+ const target = { label: "project", installDir: path.join(root, "prompts"), store: manifest.store, manifest };
97
+ const builtins = { "my-prompts": { kind: "git" as const, url: "https://github.com/me/prompts.git" } };
98
+
99
+ const registry = await loadSourceRegistry(manifestSources([manifest], builtins), root, readPrompts);
100
+ reportSourceRegistry(registry); // prints warnings and the sources that failed to load
101
+ try {
102
+ await addFromSources([target], ["review"], registry, {}, { bin: "my-prompts", noun: "prompt", entrySchema: PromptSchema });
103
+ } finally {
104
+ registry.dispose(); // removes temporary checkouts
105
+ }
106
+ ```
107
+
108
+ `addFromSources` resolves `review` or `review@my-prompts` across every source, installs its `registryDependencies`, asks before it overwrites a file you changed, and saves `.prompts.json`. `updateFromSources`, `removeFromTargets`, `addSourceEntries`, `lsSources`, `removeSourceEntries`, `lsSourceRegistry` and `lsEntries` cover the other commands. The CLI of [`@g3nomaly/mods`](https://www.npmjs.com/package/@g3nomaly/mods) is a working example, built from these calls.
109
+
110
+ ## Development
111
+
112
+ Run `bun install` at the repository root first, then every command from this directory.
113
+
114
+ ```sh
115
+ bun run build # dist/
116
+ bun run typecheck
117
+ bun test
118
+ ```
119
+
120
+ ## License
121
+
122
+ Elastic License 2.0. See `LICENSE`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@g3nomaly/core",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Shared runtime for @g3n packages — hashing, registry, security, config",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",