create-githolon 0.96.0 → 0.97.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.
- package/index.mjs +41 -70
- package/package.json +12 -1
- package/template/README.md +3 -2
- package/template/docs/04-cloud.md +9 -14
- package/template/docs/05-superpowers.md +18 -15
- package/template/package.json +3 -3
package/index.mjs
CHANGED
|
@@ -1,106 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
// git repo. The holon itself is MINTED LOCALLY after compile (`githolon ledger
|
|
5
|
-
// init holon` — genesis + your law under YOUR identity, verified by the same
|
|
6
|
-
// wasm gate the cloud runs), so it is yours from the first byte. Plain node,
|
|
7
|
-
// zero dependencies; the heavy lifting (compile, codegen, the engine) lives in
|
|
8
|
-
// @githolon/dsl and githolon and arrives with the scaffold's `npm install`.
|
|
9
|
-
//
|
|
10
|
-
// Everything scaffolded here belongs to the user — see LICENSE.md ("You may /
|
|
11
|
-
// keep everything that's yours").
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
12
4
|
import { spawnSync } from "node:child_process";
|
|
13
5
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
14
6
|
import path from "node:path";
|
|
15
7
|
import { fileURLToPath } from "node:url";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
var HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
var TEMPLATE = path.join(HERE, "template");
|
|
10
|
+
var NOMOS_VERSION = (() => {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(path.join(HERE, "package.json"), "utf8")).version || "latest";
|
|
13
|
+
} catch {
|
|
14
|
+
return "latest";
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
17
|
+
var args = process.argv.slice(2);
|
|
20
18
|
if (args.includes("-h") || args.includes("--help")) {
|
|
21
19
|
process.stdout.write(
|
|
22
|
-
"create-githolon
|
|
23
|
-
"Usage:\n npm create githolon <dir> (or: npx create-githolon <dir>)\n\n" +
|
|
24
|
-
"Scaffolds the starter domain + compile config + live e2e into <dir>\n" +
|
|
25
|
-
"(default: my-holon-app) and inits a git repo. After `githolon compile`,\n" +
|
|
26
|
-
"`githolon ledger init holon` mints YOUR holon locally — verified by the\n" +
|
|
27
|
-
"same wasm gate the cloud runs, ready to birth by git push.\n\n" +
|
|
28
|
-
"Flags:\n --no-git skip git init\n",
|
|
20
|
+
"create-githolon \u2014 scaffold a Nomos domain package\n\nUsage:\n npm create githolon <dir> (or: npx create-githolon <dir>)\n\nScaffolds the starter domain + compile config + live e2e into <dir>\n(default: my-holon-app) and inits a git repo. After `githolon compile`,\n`githolon ledger init holon` mints YOUR holon locally \u2014 verified by the\nsame wasm gate the cloud runs, ready to birth by git push.\n\nFlags:\n --no-git skip git init\n"
|
|
29
21
|
);
|
|
30
22
|
process.exit(0);
|
|
31
23
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const appName = path
|
|
37
|
-
.basename(targetDir)
|
|
38
|
-
.toLowerCase()
|
|
39
|
-
.replace(/[^a-z0-9-_.]+/g, "-")
|
|
40
|
-
.replace(/^[-_.]+|[-_.]+$/g, "");
|
|
24
|
+
var noGit = args.includes("--no-git");
|
|
25
|
+
var dirArg = args.find((a) => !a.startsWith("-"));
|
|
26
|
+
var targetDir = path.resolve(process.cwd(), dirArg ?? "my-holon-app");
|
|
27
|
+
var appName = path.basename(targetDir).toLowerCase().replace(/[^a-z0-9-_.]+/g, "-").replace(/^[-_.]+|[-_.]+$/g, "");
|
|
41
28
|
if (!appName) {
|
|
42
29
|
console.error(`create-githolon: '${path.basename(targetDir)}' does not reduce to a valid package name`);
|
|
43
30
|
process.exit(1);
|
|
44
31
|
}
|
|
45
|
-
|
|
46
32
|
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
47
33
|
console.error(`create-githolon: refusing to scaffold into non-empty directory: ${targetDir}`);
|
|
48
34
|
process.exit(1);
|
|
49
35
|
}
|
|
50
|
-
|
|
51
36
|
mkdirSync(targetDir, { recursive: true });
|
|
52
37
|
cpSync(TEMPLATE, targetDir, { recursive: true });
|
|
53
|
-
|
|
54
|
-
// npm strips dotfiles from published packages — the template ships `gitignore`
|
|
55
|
-
// and the scaffold restores the dot.
|
|
56
38
|
renameSync(path.join(targetDir, "gitignore"), path.join(targetDir, ".gitignore"));
|
|
57
|
-
|
|
58
|
-
// Same dotfile-restore for the VS Code workspace recommendation: the template ships
|
|
59
|
-
// `vscode/` (no dot; a leading-dot dir does not reliably survive `npm publish`), and
|
|
60
|
-
// the scaffold restores `.vscode/` so opening the project prompts installing the
|
|
61
|
-
// Nomos Law extension (the author-time check gate as red squiggles while typing).
|
|
62
39
|
if (existsSync(path.join(targetDir, "vscode"))) {
|
|
63
40
|
renameSync(path.join(targetDir, "vscode"), path.join(targetDir, ".vscode"));
|
|
64
41
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// hash const are NAMED off the package name (`__APP_NAME__`): the artifact filename
|
|
68
|
-
// keeps the raw name (build/<app>.client.ts), the hash SYMBOL normalizes it to
|
|
69
|
-
// UPPER_SNAKE (the same transform `githolon compile` uses — see codegen_ts.ts). The
|
|
70
|
-
// client FACTORY (`todoClient`) is named off the DOMAIN key, which stays `todo`.
|
|
71
|
-
const appHash = `${appName.replace(/[-\s]+/g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase()}_DOMAIN_HASH`;
|
|
72
|
-
const subst = (s) => s.replaceAll("__APP_NAME__", appName).replaceAll("__APP_HASH__", appHash);
|
|
42
|
+
var appHash = `${appName.replace(/[-\s]+/g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase()}_DOMAIN_HASH`;
|
|
43
|
+
var subst = (s) => s.replaceAll("__APP_NAME__", appName).replaceAll("__APP_HASH__", appHash).replaceAll("__NOMOS_VERSION__", NOMOS_VERSION);
|
|
73
44
|
for (const f of ["package.json", "README.md", "nomos.package.mjs", "CLAUDE.md", "domains/todo.ts", "test/e2e.mts"]) {
|
|
74
45
|
const p = path.join(targetDir, f);
|
|
75
46
|
if (existsSync(p)) writeFileSync(p, subst(readFileSync(p, "utf8")), "utf8");
|
|
76
47
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// git init. Degrades gracefully: no git leaves a perfectly usable scaffold.
|
|
81
|
-
let gitInited = false;
|
|
48
|
+
var git = (cmd) => spawnSync("git", cmd, { cwd: targetDir, stdio: "ignore" }).status === 0;
|
|
49
|
+
var gitInited = false;
|
|
82
50
|
if (!noGit) {
|
|
83
51
|
gitInited = git(["init", "-b", "main"]) || git(["init"]);
|
|
84
52
|
if (gitInited) {
|
|
85
53
|
git(["add", "-A"]);
|
|
86
|
-
|
|
87
|
-
git(["commit", "-m", "scaffold: create-githolon starter"]) ||
|
|
88
|
-
git(["-c", "user.name=create-githolon", "-c", "user.email=create-githolon@nomos", "commit", "-m", "scaffold: create-githolon starter"]);
|
|
54
|
+
git(["commit", "-m", "scaffold: create-githolon starter"]) || git(["-c", "user.name=create-githolon", "-c", "user.email=create-githolon@nomos", "commit", "-m", "scaffold: create-githolon starter"]);
|
|
89
55
|
}
|
|
90
56
|
}
|
|
91
|
-
|
|
92
|
-
const rel = path.relative(process.cwd(), targetDir) || ".";
|
|
57
|
+
var rel = path.relative(process.cwd(), targetDir) || ".";
|
|
93
58
|
process.stdout.write(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
59
|
+
`
|
|
60
|
+
Scaffolded ${appName} into ${rel}/${gitInited ? " (git repo)" : ""}
|
|
61
|
+
|
|
62
|
+
Read docs/01-mental-model.md first \u2014 the docs travel with you, offline.
|
|
63
|
+
|
|
64
|
+
Next:
|
|
65
|
+
cd ${rel}
|
|
66
|
+
npm install
|
|
67
|
+
npx githolon compile # \u2192 build/: deployable package + manifests + typed client + a generated proof
|
|
68
|
+
npx githolon proof # run that proof live \u2014 GENERATED from your own law, no test rewriting
|
|
69
|
+
npx githolon login --agent # a verified identity, no browser
|
|
70
|
+
|
|
71
|
+
Your law compiles + proves OFFLINE today (compile + proof above \u2014 no cloud, no account).
|
|
72
|
+
To run it LIVE, deploy to a workspace you own: a workspace is born by its PARENT (a platform
|
|
73
|
+
you control births homes/children \u2014 birthHome/birthChild). There is no host-side create \u2014 the
|
|
74
|
+
flat 'ws create' lane is retired (orphanless holarchy). See docs/04-cloud.md for the birth-on-
|
|
75
|
+
parent flow; self-serve keyless birth-under-root is in flight.
|
|
76
|
+
`
|
|
106
77
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffold a Nomos domain package: the starter domain + compile config + live e2e. `npm create githolon my-app`.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -18,5 +18,16 @@
|
|
|
18
18
|
],
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsx build.ts",
|
|
24
|
+
"prepublishOnly": "tsx build.ts",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"esbuild": "^0.24.0",
|
|
29
|
+
"tsx": "^4.19.2",
|
|
30
|
+
"typescript": "^5.6.3",
|
|
31
|
+
"@types/node": "^25.9.2"
|
|
21
32
|
}
|
|
22
33
|
}
|
package/template/README.md
CHANGED
|
@@ -43,8 +43,9 @@ before serving anything. Authority lives in the git; no holon outranks another.
|
|
|
43
43
|
npx githolon ledger init holon # mint + assert locally: chain VALID, head <h>, under YOUR uid
|
|
44
44
|
npx githolon ledger verify holon # re-assert any time
|
|
45
45
|
cd holon
|
|
46
|
-
npx githolon
|
|
47
|
-
git
|
|
46
|
+
npx githolon ws create <ws> # birth the workspace (its parent's offer-effect); no secret minted
|
|
47
|
+
npx githolon git remote <ws> # wire the remote + credential helper for syncing
|
|
48
|
+
git push nomos refs/heads/session/<id> # sync your writes to the edge (main is not push-writable)
|
|
48
49
|
npx githolon deploy <ws> # idempotent (alreadyInstalled) + stages the read manifests so queries route
|
|
49
50
|
cd ..
|
|
50
51
|
```
|
|
@@ -19,29 +19,24 @@ the principal births will ride; sessions auto-refresh from
|
|
|
19
19
|
|
|
20
20
|
## Two deploy lanes, one destination
|
|
21
21
|
|
|
22
|
-
**
|
|
22
|
+
**Deploy your law** (the cloud births the workspace by its parent's offer-effect):
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npx githolon ws create <ws> # birth; NO secret is minted — ownership is a law fact in the chain
|
|
26
26
|
npx githolon deploy <ws> # POST build/<pkg>.deploy.json, authenticated as your principal
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Both end in the same place — all holons are equal in authority; the chain
|
|
37
|
-
proves itself either way ([05-superpowers.md](./05-superpowers.md) walks B).
|
|
29
|
+
A workspace is born ONLY by its PARENT authoring its birth (an offer-effect that records the
|
|
30
|
+
child in the parent's own chain). There is no "push a repo to create a workspace" — pushing
|
|
31
|
+
`refs/heads/main` to an unborn name is refused. You sync your work by pushing session branches
|
|
32
|
+
(`refs/heads/session/<client-id>`) to an EXISTING workspace; child workspaces you author offline
|
|
33
|
+
(e.g. an estate under a home in local custody) materialize on the cloud when you sync the parent.
|
|
38
34
|
|
|
39
35
|
## Ownership
|
|
40
36
|
|
|
41
|
-
There is no host secret. Ownership is a LAW FACT bound IN THE CHAIN —
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`main`); `/boot` returns no secret. DEPLOY write-authority is judged by the
|
|
37
|
+
There is no host secret. Ownership is a LAW FACT bound IN THE CHAIN — the principal the parent's
|
|
38
|
+
birth records as the owner (e.g. `HomeOwner.principal`). Born-ness derives from custody (the ledger
|
|
39
|
+
has a `main`); `/boot` returns no secret. DEPLOY write-authority is judged by the
|
|
45
40
|
KERNEL at the offer gate — you authenticate as a principal the law grants deploy
|
|
46
41
|
authority (`x-nomos-auth`, or transitionally `x-nomos-principal`), not a Bearer
|
|
47
42
|
secret. End-user apps carry no credential: reads and session pushes are open,
|
|
@@ -16,26 +16,29 @@ identity>` — and verifies the chain with the SAME byte-identical wasm gate the
|
|
|
16
16
|
cloud runs. This is not a mock or a dev server. It is a holon, born valid,
|
|
17
17
|
and you can assert that yourself, offline, before any network exists.
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Offline birth — the parent births the child, the cloud re-derives it on sync
|
|
20
|
+
|
|
21
|
+
A workspace is born ONLY by its PARENT authoring its birth. You don't push a repo to create a
|
|
22
|
+
workspace; you author the birth on a parent you hold, and the child materializes when the parent
|
|
23
|
+
syncs (or when you offer the birth to the parent's edge online). A home you hold in local custody
|
|
24
|
+
can birth an estate OFFLINE — no network — and the cloud materializes that estate, byte-identical,
|
|
25
|
+
the moment the home syncs:
|
|
20
26
|
|
|
21
27
|
```bash
|
|
22
28
|
cd holon
|
|
23
|
-
npx githolon git remote <ws>
|
|
24
|
-
git push nomos
|
|
29
|
+
npx githolon git remote <ws> # wire the remote + credential helper for THIS workspace
|
|
30
|
+
git push nomos refs/heads/session/<id> # sync your writes (incl. any births you authored) to the edge
|
|
25
31
|
```
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Why this matters: deployment is `git push`, the audit trail is `git log`, and
|
|
38
|
-
"does the cloud agree with my machine?" is a checkable equation, not a hope.
|
|
33
|
+
The cloud replay-verifies every synced intent — contiguity, each intent re-admitted against the
|
|
34
|
+
state folded from the chain prefix, every plan re-run and byte-compared — and re-derives your exact
|
|
35
|
+
local verdict: same head, same plans. A birth intent's consequence is the child, materialized in
|
|
36
|
+
cloud custody with an immutable `nomos-birth-record` on the parent (so the whole tree is walkable
|
|
37
|
+
from root — no orphans). Pushing `refs/heads/main` to an unborn name is REFUSED: the pushed bytes
|
|
38
|
+
buy nothing; a workspace comes to exist through its parent's law, not a push.
|
|
39
|
+
|
|
40
|
+
Why this matters: authorship is `git log`, "does the cloud agree with my machine?" is a checkable
|
|
41
|
+
equation, and every workspace's parentage is a verifiable ledger fact — no host says what exists.
|
|
39
42
|
|
|
40
43
|
## Byte-identical wasm everywhere
|
|
41
44
|
|
package/template/package.json
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"typecheck": "tsc --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@githolon/dsl": "^
|
|
15
|
-
"@githolon/client": "^
|
|
14
|
+
"@githolon/dsl": "^__NOMOS_VERSION__",
|
|
15
|
+
"@githolon/client": "^__NOMOS_VERSION__",
|
|
16
16
|
"zod": "^4.4.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"githolon": "^
|
|
19
|
+
"githolon": "^__NOMOS_VERSION__",
|
|
20
20
|
"@types/node": "^25.9.2",
|
|
21
21
|
"tsx": "^4.19.2",
|
|
22
22
|
"typescript": "^5.6.3"
|