genesis-compiler 1.2.1 → 1.2.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.
- package/README.md +52 -17
- package/docs/assurance-model.md +15 -0
- package/docs/stack-components.md +113 -14
- package/package.json +2 -2
- package/plugins/genesis/.codex-plugin/plugin.json +1 -1
- package/prompts/start.txt +5 -0
- package/skills/genesis-project/SKILL.md +6 -3
- package/src/cli.js +24 -1
- package/src/index/agent-skills.js +25 -4
- package/src/index/check.js +5 -1
- package/src/index/city-presentation.js +83 -0
- package/src/index/code-index.js +24 -3
- package/src/index/deployment.js +31 -0
- package/src/index/environment-files.js +10 -3
- package/src/index/init.js +1 -1
- package/src/index/launch.js +6 -1
- package/src/index/prompt.js +5 -1
- package/src/index/stack-catalog.js +3 -0
- package/src/index/stack-city-presentation.js +178 -0
- package/src/index/stack-deployment.js +204 -0
- package/src/index/stack-environment-defaults.js +79 -0
- package/src/index/stack-launch.js +37 -0
- package/src/index/stack-piece.js +19 -0
- package/src/index/stack-workspace-setup.js +7 -3
- package/src/index/stack.js +35 -0
- package/src/index/utils.js +1 -1
- package/src/index/verification.js +31 -1
- package/src/index/workspace-setup.js +101 -10
- package/src/index.js +13 -1
- package/stacks/pieces/jskit-mysql.md +5 -1
- package/stacks/pieces/jskit-postgresql.md +5 -1
- package/stacks/pieces/jskit.md +30 -9
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ interaction.
|
|
|
13
13
|
```text
|
|
14
14
|
genesis/
|
|
15
15
|
blueprint.md non-technical product intent
|
|
16
|
-
stack.md selected technology guidance, checks, setup, and
|
|
16
|
+
stack.md selected technology guidance, checks, setup, launch, and deployment data
|
|
17
17
|
stack/ optional per-component Description, Guidance, and Deslop customization
|
|
18
18
|
program/ concise explanations grouped into conceptual subsystems
|
|
19
19
|
.agents/skills/
|
|
@@ -116,8 +116,13 @@ Genesis never overwrites an unmanaged skill and preserves locally modified
|
|
|
116
116
|
managed skills. Run `genesis init` after manually editing `genesis/stack.md`,
|
|
117
117
|
or use `genesis stack add`, to synchronize selected skills.
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
Run `genesis prepare` to execute the selected Stack's exact workspace recipe
|
|
120
|
+
directly with the caller's environment and installed toolchain. It executes
|
|
121
|
+
argument arrays without a shell, honors declared project-relative working
|
|
122
|
+
directories and conditional paths, stops on the first failure, and never
|
|
123
|
+
returns environment values. Hosts that need their own runtime, permission, or
|
|
124
|
+
process policy can instead call the public `inspectWorkspaceSetup()` API and
|
|
125
|
+
execute the same normalized recipe themselves. A concrete Stack piece may declare one exact,
|
|
121
126
|
ordered `## Workspace setup` recipe with labels, argument arrays expressed as
|
|
122
127
|
backticked tokens, abstract runtimes, and optional project-relative working
|
|
123
128
|
directories and readiness paths. A recipe whose declared project marker does
|
|
@@ -127,29 +132,56 @@ piece waits for `package.json` and then declares `npm install`; generic
|
|
|
127
132
|
`genesis/stack.md` replaces all component defaults, including with
|
|
128
133
|
`- Nothing.`. Without that override, exactly one component recipe is used;
|
|
129
134
|
multiple component recipes are reported as ambiguous and never merged.
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
The standalone command does not provision declared resources: callers provide
|
|
136
|
+
their own database, credentials, compilers, and other infrastructure. Stack
|
|
137
|
+
resources such as database variables do not gate dependency installation.
|
|
132
138
|
|
|
133
139
|
Hosts that manage project environments can call `inspectEnvironment()`.
|
|
140
|
+
Concrete Stack integrations may declare public, non-secret Environment
|
|
141
|
+
defaults such as the database driver selected by that integration. Explicit
|
|
142
|
+
host or user values take precedence. The built-in JSKIT MySQL and PostgreSQL
|
|
143
|
+
pieces respectively declare `DB_CLIENT=mysql2` and `DB_CLIENT=pg`, so users
|
|
144
|
+
provide connection values rather than restating a deterministic technology
|
|
145
|
+
choice.
|
|
134
146
|
An optional readable `## Environment files` section declares safe
|
|
135
147
|
project-relative dotenv projections such as `.env`. A project declaration
|
|
136
148
|
replaces component defaults, including with `- Nothing.`; otherwise component
|
|
137
|
-
paths compose. Genesis reports resource declarations,
|
|
138
|
-
diagnostics, and paths. It never returns
|
|
139
|
-
|
|
149
|
+
paths compose. Genesis reports public defaults, resource declarations,
|
|
150
|
+
value-free missing-input diagnostics, and paths. It never returns supplied
|
|
151
|
+
environment values, writes files, stores secrets, or decides which resolved
|
|
152
|
+
host values should be materialized.
|
|
140
153
|
|
|
141
154
|
Hosts that provide previews can call the public `inspectLaunch()` API. An
|
|
142
155
|
optional readable `## Launch` section in `genesis/stack.md` declares labeled
|
|
143
156
|
targets, exact backticked argument tokens, relative working directories,
|
|
144
157
|
abstract runtime requirements, `{host}`/`{port}` substitutions, and an optional
|
|
145
158
|
application-owned Preview identity block. Concrete components may provide a
|
|
146
|
-
default: `jskit` declares
|
|
147
|
-
`php` do not guess. Genesis starts nothing itself. It returns preview identity
|
|
159
|
+
default: `jskit` declares its app-owned `npm run develop` command; generic
|
|
160
|
+
`nodejs` and `php` do not guess. Genesis starts nothing itself. It returns preview identity
|
|
148
161
|
command metadata and environment variable names, never their values. The host
|
|
149
|
-
continues to own pinned runtimes, ports, processes, readiness
|
|
150
|
-
execution and secrets, browser binaries, Git, and
|
|
162
|
+
continues to own pinned runtimes, ports, processes, readiness probing and
|
|
163
|
+
timeouts, identity-command execution and secrets, browser binaries, Git, and
|
|
164
|
+
credentials. Genesis owns only the exact HTTP readiness predicate declared by
|
|
165
|
+
the selected Stack. See
|
|
151
166
|
[`docs/stack-components.md`](docs/stack-components.md) for the v1 contract.
|
|
152
167
|
|
|
168
|
+
Hosts that publish applications can call the separate public
|
|
169
|
+
`inspectDeployment()` API. An optional readable `## Deployment` section
|
|
170
|
+
declares an ordered production recipe: project-relative workdir, abstract
|
|
171
|
+
runtimes, exact Prepare/Build/Migrate/Serve argv, and one HTTP readiness
|
|
172
|
+
predicate. A concrete component may provide the recipe; the built-in `jskit`
|
|
173
|
+
piece declares production dependency installation, build, database preparation,
|
|
174
|
+
`npm start`, and `/api/health`. Generic language pieces do not guess. A project
|
|
175
|
+
section replaces component defaults wholesale, and `- Nothing.` explicitly
|
|
176
|
+
disables publishing.
|
|
177
|
+
|
|
178
|
+
Genesis only validates and reports this recipe and the Stack's abstract resource
|
|
179
|
+
requirements. It does not provision a database, resolve credentials, copy a
|
|
180
|
+
release, run commands, start a service, reserve a port, route traffic, issue TLS,
|
|
181
|
+
or roll back. A deployment host owns all of those operational responsibilities,
|
|
182
|
+
including the mapping from `mysql` or `postgresql` resources to its managed
|
|
183
|
+
services.
|
|
184
|
+
|
|
153
185
|
At session start, Codex receives only a short explanation of how Genesis is
|
|
154
186
|
organized. After it locates the source involved in a request, it can load the
|
|
155
187
|
specific explanatory and technology context with:
|
|
@@ -332,8 +364,11 @@ Run the Stack's declared checks explicitly:
|
|
|
332
364
|
genesis verify
|
|
333
365
|
```
|
|
334
366
|
|
|
335
|
-
Genesis first
|
|
336
|
-
|
|
367
|
+
Genesis first waits for any technology-declared Workspace setup marker. A
|
|
368
|
+
missing marker is `unconfigured`, not a failed command. Once the workspace
|
|
369
|
+
exists, Genesis applies public Stack defaults beneath explicit environment
|
|
370
|
+
values, evaluates generic resource declarations, and runs each declared
|
|
371
|
+
command without a shell. A complete successful run writes only
|
|
337
372
|
`.genesis/verification.json`, recording:
|
|
338
373
|
|
|
339
374
|
- the exact Git-visible code hash;
|
|
@@ -413,10 +448,10 @@ to execute with its own pinned runtimes, caches, process isolation, and
|
|
|
413
448
|
credentials. A configured recipe may remain unconfigured while it waits for a
|
|
414
449
|
technology-declared project marker.
|
|
415
450
|
`inspectEnvironment()` returns normalized Stack resource declarations,
|
|
416
|
-
value-free missing-input diagnostics, and dotenv
|
|
417
|
-
returning any environment value.
|
|
451
|
+
public non-secret defaults, value-free missing-input diagnostics, and dotenv
|
|
452
|
+
projection paths without returning any supplied environment value.
|
|
418
453
|
`inspectLaunch()` returns a validated, normalized Stack launch declaration for
|
|
419
|
-
a host to execute with its own runtime, port, process, readiness, preview
|
|
454
|
+
a host to execute with its own runtime, port, process, readiness probe, preview
|
|
420
455
|
identity, secret, and browser policy.
|
|
421
456
|
|
|
422
457
|
A host such as Vibe64 can send the generated prompt to its existing agent:
|
package/docs/assurance-model.md
CHANGED
|
@@ -24,3 +24,18 @@ semantically current, or that passing checks prove the whole product correct.
|
|
|
24
24
|
|
|
25
25
|
Git remains the review and recovery boundary. The host running the generated
|
|
26
26
|
prompt owns the agent and its interaction.
|
|
27
|
+
|
|
28
|
+
## Filesystem ownership
|
|
29
|
+
|
|
30
|
+
Genesis does not copy package-file ownership or broad source modes into a
|
|
31
|
+
project. It creates ordinary project files with mode `0666`, directories and
|
|
32
|
+
executable skill files with mode `0777`, and lets the invoking process umask
|
|
33
|
+
and the destination directory's inherited ACL establish the effective mode and
|
|
34
|
+
group. Atomic replacements are created beside their destination so they inherit
|
|
35
|
+
the same filesystem policy before becoming visible. Private hook continuation
|
|
36
|
+
state remains explicitly `0600`.
|
|
37
|
+
|
|
38
|
+
This keeps ordinary single-user projects conventional while allowing a managed
|
|
39
|
+
host to require shared-group writes and no access for other users. The host is
|
|
40
|
+
responsible for establishing and verifying that umask, setgid directory, ACL,
|
|
41
|
+
and cross-identity contract before Genesis or an agent starts.
|
package/docs/stack-components.md
CHANGED
|
@@ -24,6 +24,17 @@ What this component contributes.
|
|
|
24
24
|
|
|
25
25
|
- `javascript`
|
|
26
26
|
|
|
27
|
+
## City regions
|
|
28
|
+
|
|
29
|
+
- Ignore `**/test/**`
|
|
30
|
+
- Ignore `**/tests/**`
|
|
31
|
+
- Ignore `**/__tests__/**`
|
|
32
|
+
- Ignore `**/*.test.*`
|
|
33
|
+
- Ignore `**/*.spec.*`
|
|
34
|
+
- Match `packages` as `Packages`: `packages/**`
|
|
35
|
+
- Match `source` as `Source`: `src/**`
|
|
36
|
+
- Fallback `everything-else` as `Everything else`
|
|
37
|
+
|
|
27
38
|
## Resources
|
|
28
39
|
|
|
29
40
|
```json genesis-resource
|
|
@@ -37,6 +48,10 @@ What this component contributes.
|
|
|
37
48
|
}
|
|
38
49
|
```
|
|
39
50
|
|
|
51
|
+
## Environment defaults
|
|
52
|
+
|
|
53
|
+
- Default `EXAMPLE_DRIVER`: `example`
|
|
54
|
+
|
|
40
55
|
## Guidance
|
|
41
56
|
|
|
42
57
|
### Technology-specific behavior
|
|
@@ -69,6 +84,7 @@ What this component contributes.
|
|
|
69
84
|
- Workdir: `.`
|
|
70
85
|
- Preferred port: `3000`
|
|
71
86
|
- URL path: `/`
|
|
87
|
+
- Ready when: `GET` `/api/health` returns `200`
|
|
72
88
|
- Runtimes: `nodejs`
|
|
73
89
|
- Prepare `Build`: `npm` `run` `build`
|
|
74
90
|
- Serve `Start`: `npm` `run` `server` `--` `--host` `{host}` `--port` `{port}`
|
|
@@ -93,7 +109,9 @@ from Genesis or from the declared npm package. Its `SKILL.md`, `references/`,
|
|
|
93
109
|
Genesis expanding every manual into every prompt. `Resources` declares generic
|
|
94
110
|
alternative environment-name sets reported by prompt generation and checked
|
|
95
111
|
before verification. `allowEmpty` may name a required variable whose empty
|
|
96
|
-
string is valid. `
|
|
112
|
+
string is valid. `Environment defaults` declares public non-secret constants
|
|
113
|
+
owned by a concrete integration; explicit host values take precedence.
|
|
114
|
+
`Guidance` is supplemental prose used by work, review, Program,
|
|
97
115
|
description, and cleanup prompts, as well as path-focused context. `Deslop`
|
|
98
116
|
adds cleanup-only prose to explicit cleanup and the automatic Codex cleanup
|
|
99
117
|
continuation. With no selected component, only the `genesis-deslop` skill
|
|
@@ -132,6 +150,16 @@ Precedence is intentionally small and deterministic:
|
|
|
132
150
|
General Stack resources do not gate Workspace setup. In particular, missing
|
|
133
151
|
database variables do not prevent an otherwise independent dependency install.
|
|
134
152
|
|
|
153
|
+
Each `Environment defaults` entry is one `Default` bullet with a backticked
|
|
154
|
+
environment name and value. Selected
|
|
155
|
+
component defaults compose by name; identical declarations collapse and
|
|
156
|
+
different values fail rather than silently choosing one. Defaults participate
|
|
157
|
+
in Stack identity, resource preflight, prompt context, Launch inspection, and
|
|
158
|
+
verification execution. The JSKIT database integrations use this contract for
|
|
159
|
+
their deterministic `DB_CLIENT` driver ids; generic database pieces do not.
|
|
160
|
+
Secrets and deployment-specific connection values remain host inputs and must
|
|
161
|
+
never be declared as defaults.
|
|
162
|
+
|
|
135
163
|
`Environment files` optionally declares project-relative dotenv projections a
|
|
136
164
|
host may materialize from its own resolved project environment. Genesis
|
|
137
165
|
validates and returns only the format and path; it never reads environment
|
|
@@ -145,8 +173,9 @@ The declaration is useful independently of Launch and Workspace setup.
|
|
|
145
173
|
offer a preview without guessing how a concrete technology starts. Repeat a
|
|
146
174
|
``### Target `id`: label`` block to declare several targets. Default, Workdir,
|
|
147
175
|
Preferred port, URL path, and Runtimes are optional; their normalized defaults
|
|
148
|
-
are false, `.`, null, `/`, and no runtime requirements. Every
|
|
149
|
-
|
|
176
|
+
are false, `.`, null, `/`, and no runtime requirements. Every target declares
|
|
177
|
+
one HTTP readiness predicate with a project path and an exact successful status
|
|
178
|
+
from 200 through 399. Every Prepare or Serve command and argument is a separate backticked value returned as an `argv`
|
|
150
179
|
array, never a shell program. A target has exactly one final Serve step; any
|
|
151
180
|
earlier steps must be Prepare steps. Workdir is canonical and project-relative,
|
|
152
181
|
Preferred port is only a hint from 1024 through 65535, and URL path is an
|
|
@@ -155,7 +184,8 @@ application path rather than a URL. The only substitutions are `{host}` and
|
|
|
155
184
|
Launch section to disable component targets.
|
|
156
185
|
|
|
157
186
|
Genesis validates and reports Launch data but does not select a runtime,
|
|
158
|
-
reserve a port, start a process,
|
|
187
|
+
reserve a port, start a process, probe readiness, or control a browser. The host
|
|
188
|
+
executes the declared predicate and owns its timeout and retry policy.
|
|
159
189
|
|
|
160
190
|
The optional `#### Preview identity` block is specific to hosts that implement
|
|
161
191
|
the declared `vibe64.preview-identity.command.v1` protocol. Its executable is a
|
|
@@ -176,7 +206,41 @@ the ids it supports to its own pinned runtime packs; for example, it may map
|
|
|
176
206
|
dependencies such as `composer`, or treats an unknown id as something similar.
|
|
177
207
|
Declare each independently required runtime. Generic language pieces such as
|
|
178
208
|
`nodejs` and `php` provide no Launch recipe. A concrete piece such as `jskit`
|
|
179
|
-
may declare
|
|
209
|
+
may declare its app-owned `npm run develop` command; Genesis does not invent
|
|
210
|
+
missing targets or supervise framework-specific subprocesses.
|
|
211
|
+
|
|
212
|
+
`Deployment` is a separate optional readable production recipe. It must not be
|
|
213
|
+
inferred from Launch because preview preparation and production publication have
|
|
214
|
+
different safety, dependency, migration, artifact, and rollback concerns. Its
|
|
215
|
+
complete form is:
|
|
216
|
+
|
|
217
|
+
```markdown
|
|
218
|
+
## Deployment
|
|
219
|
+
|
|
220
|
+
- Workdir: `.`
|
|
221
|
+
- Runtimes: `nodejs`
|
|
222
|
+
- Ready when: `GET` `/api/health` returns `200`
|
|
223
|
+
- Prepare `Install production dependencies`: `npm` `install` `--omit=dev`
|
|
224
|
+
- Build `Build`: `npm` `run` `build`
|
|
225
|
+
- Migrate `Prepare database`: `npm` `run` `db:prepare`
|
|
226
|
+
- Serve `Start`: `npm` `start`
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Every command and argument is a separate backticked value returned as an argv
|
|
230
|
+
array; shell source is rejected. Workdir is canonical and project-relative.
|
|
231
|
+
Prepare, Build, and Migrate steps are optional and retain their declared order.
|
|
232
|
+
There is exactly one final Serve step and one exact HTTP readiness predicate
|
|
233
|
+
whose successful status is from 200 through 399. Runtime ids remain abstract.
|
|
234
|
+
|
|
235
|
+
A project `## Deployment` section replaces every component recipe as one unit,
|
|
236
|
+
including with `- Nothing.`. Without a project section, exactly one selected
|
|
237
|
+
component recipe may apply; several are blocked as ambiguous and never merged.
|
|
238
|
+
Genesis returns the Stack's existing resource declarations with the recipe but
|
|
239
|
+
does not check production values, provision services, execute commands, package
|
|
240
|
+
artifacts, start processes, probe health, route traffic, manage TLS, or retain
|
|
241
|
+
releases. Those are host responsibilities. This keeps the same project portable:
|
|
242
|
+
Genesis alone explains the production requirements, while a host such as Vibe64
|
|
243
|
+
Online can map `mysql` or `postgresql` to a pinned managed service.
|
|
180
244
|
|
|
181
245
|
`Indexers` selects installed structural adapters for the derived Machine City.
|
|
182
246
|
The shared ast-grep engine owns traversal, bounds, diagnostics, JSON, and
|
|
@@ -186,6 +250,25 @@ may contribute indexers, and duplicates are collapsed. Index output is
|
|
|
186
250
|
navigation data, not proof that every dynamic callable or framework edge was
|
|
187
251
|
discovered.
|
|
188
252
|
|
|
253
|
+
`City regions` is an optional complete presentation contract for Machine City.
|
|
254
|
+
Leading `Ignore` entries are canonical project-relative glob patterns using
|
|
255
|
+
only `*` within a path segment and `**` across segments. Genesis excludes their
|
|
256
|
+
matching files before it emits Machine City buildings or functions. This keeps
|
|
257
|
+
test/spec source out of the product City without requiring a host-side filter.
|
|
258
|
+
Each subsequent ordered `Match` entry declares one canonical project-relative
|
|
259
|
+
`path/**` prefix, stable id, and human title. Exactly one final `Fallback` entry
|
|
260
|
+
owns every remaining indexed path. Ignore patterns, ids, and prefixes must be
|
|
261
|
+
unique, prefixes must not overlap, and no more than one selected component may
|
|
262
|
+
declare the contract. Genesis resolves every indexed file and structural
|
|
263
|
+
district to a region and a campus while generating
|
|
264
|
+
`.genesis/machine-city.json`; a City renderer consumes those emitted facts and
|
|
265
|
+
must not reclassify or filter paths. Under a matched prefix, the first directory
|
|
266
|
+
is a campus, while files directly below the prefix belong to the prefix campus.
|
|
267
|
+
Under the fallback, each top-level directory is a campus and project-root files
|
|
268
|
+
belong to the project-root campus. The built-in JSKIT piece excludes exact
|
|
269
|
+
`test`, `tests`, and `__tests__` path segments plus `*.test.*` and `*.spec.*`
|
|
270
|
+
files, then declares `Packages`, `Source`, and `Everything else`.
|
|
271
|
+
|
|
189
272
|
Do not create a Genesis-owned generic skill merely because a component is
|
|
190
273
|
named `nodejs`, `php`, or another common technology. Official, user, and host
|
|
191
274
|
skills with those names must coexist normally. Use `## Skill` only for an
|
|
@@ -230,7 +313,7 @@ Replacement component description.
|
|
|
230
313
|
Each field is optional. `Override` replaces only that installed field; `Add`
|
|
231
314
|
then follows the installed or overridden value. These files cannot alter
|
|
232
315
|
dependencies, conflicts, Agent Skill source, indexers, resources, environment
|
|
233
|
-
files, commands, Workspace setup, or Launch declarations.
|
|
316
|
+
defaults, environment files, commands, Workspace setup, or Launch declarations.
|
|
234
317
|
`genesis stack add` preserves the files and composes their effective prose after
|
|
235
318
|
dependency resolution.
|
|
236
319
|
|
|
@@ -248,8 +331,8 @@ rather than a prompt or controller special case.
|
|
|
248
331
|
Description, Guidance, Agent Skill, and Deslop prose are instructions, not
|
|
249
332
|
verification evidence. Changes to them do not stale an exact successful
|
|
250
333
|
verification record; selected component ids, resources, environment files,
|
|
251
|
-
Workspace setup, launch targets, and verification
|
|
252
|
-
Stack identity.
|
|
334
|
+
environment defaults, Workspace setup, launch targets, and verification
|
|
335
|
+
commands remain part of the Stack identity.
|
|
253
336
|
|
|
254
337
|
Hosts inspect normalized environment requirements independently:
|
|
255
338
|
|
|
@@ -260,9 +343,10 @@ const projectEnvironment = await inspectEnvironment({ environment, projectRoot }
|
|
|
260
343
|
```
|
|
261
344
|
|
|
262
345
|
The result has `status` (`ready`, `missing-inputs`, or `unconfigured`),
|
|
263
|
-
`stackHash`, selected `components`,
|
|
264
|
-
`diagnostics`, and normalized `files`. Each
|
|
265
|
-
`
|
|
346
|
+
`stackHash`, selected `components`, public non-secret `environmentDefaults`,
|
|
347
|
+
declared `resources`, value-free `diagnostics`, and normalized `files`. Each default has
|
|
348
|
+
`name`, `value`, and component `sources`; each file contains `format`, `path`,
|
|
349
|
+
and `source`. Supplied environment values never cross this API boundary.
|
|
266
350
|
|
|
267
351
|
Fresh-workspace hosts read setup from the package root:
|
|
268
352
|
|
|
@@ -283,6 +367,20 @@ blocked result.
|
|
|
283
367
|
The optional environment object is accepted for a consistent host inspection
|
|
284
368
|
boundary, but Workspace setup v1 neither reads nor returns its values.
|
|
285
369
|
|
|
370
|
+
Standalone users may execute the same normalized recipe with their own
|
|
371
|
+
environment and installed toolchain:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
genesis prepare
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`genesis prepare` runs each declared argv directly without a shell, in order,
|
|
378
|
+
and stops at the first failure. It honors project-relative workdirs and
|
|
379
|
+
conditional paths. It does not provision databases, compilers, credentials,
|
|
380
|
+
runtimes, or other resources. Managed hosts remain free to consume
|
|
381
|
+
`inspectWorkspaceSetup()` and enforce their own execution, identity, runtime,
|
|
382
|
+
and permission policies.
|
|
383
|
+
|
|
286
384
|
Preview hosts read the normalized Launch contract independently:
|
|
287
385
|
|
|
288
386
|
```js
|
|
@@ -292,11 +390,12 @@ const launch = await inspectLaunch({ projectRoot, environment });
|
|
|
292
390
|
```
|
|
293
391
|
|
|
294
392
|
The result has `status` (`ready`, `blocked`, or `unconfigured`), `stackHash`,
|
|
295
|
-
selected `components`,
|
|
393
|
+
selected `components`, public `environmentDefaults`, distinct
|
|
394
|
+
`runtimeRequirements`, declared `resources`,
|
|
296
395
|
normalized `targets`, and resource `diagnostics`. Each target reports its
|
|
297
396
|
`source`, `available`, and `disabledReason`, plus its normalized
|
|
298
|
-
`previewIdentity` declaration when present.
|
|
299
|
-
but never returned. Here, `available` means only that the Stack's declared
|
|
397
|
+
`previewIdentity` declaration when present. Supplied environment values are
|
|
398
|
+
inspected but never returned. Here, `available` means only that the Stack's declared
|
|
300
399
|
resource inputs are present: the host must still reject runtime requirements it
|
|
301
400
|
cannot map. `unconfigured` means there are no targets, `blocked` means declared
|
|
302
401
|
resource inputs are missing, and `ready` means neither condition applies. The
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genesis-compiler",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with optional Codex hooks.",
|
|
6
6
|
"repository": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@ast-grep/lang-ruby": "^0.0.7",
|
|
57
57
|
"@ast-grep/lang-rust": "^0.0.7",
|
|
58
58
|
"@ast-grep/napi": "^0.45.1",
|
|
59
|
-
"@jskit-ai/agent-docs": "0.1.
|
|
59
|
+
"@jskit-ai/agent-docs": "0.1.132",
|
|
60
60
|
"yaml": "^2.9.0"
|
|
61
61
|
}
|
|
62
62
|
}
|
package/prompts/start.txt
CHANGED
|
@@ -13,6 +13,11 @@ the non-technical Blueprint from the user's answers before implementation.
|
|
|
13
13
|
If the product direction becomes clear while Stack is still empty, do not end
|
|
14
14
|
the turn after only writing or summarizing the Blueprint: present the relevant
|
|
15
15
|
Stack choices in that same turn and ask the user to select one explicitly.
|
|
16
|
+
This opening turn owns product direction and Stack selection, not final
|
|
17
|
+
verification. Do not run `genesis verify` before implementation has created
|
|
18
|
+
the selected technology's workspace substrate. If structural validation is
|
|
19
|
+
useful, run `genesis check`; treat missing workspace markers as unconfigured,
|
|
20
|
+
not as an implementation failure.
|
|
16
21
|
|
|
17
22
|
When `projectKind` is `existing`, ask what the user wants to understand or
|
|
18
23
|
change. Use the current Blueprint, Stack, Program, and code index when present.
|
|
@@ -48,6 +48,9 @@ Neither is authority or proof; both may be regenerated with `genesis index`.
|
|
|
48
48
|
|
|
49
49
|
## Verify and report
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
After the selected technology's workspace substrate exists, use `genesis
|
|
52
|
+
verify` for the Stack's declared final checks. An unconfigured result means the
|
|
53
|
+
declared workspace or checks do not exist yet; it is not a failing check. Report
|
|
54
|
+
files changed, checks actually run, and anything still requiring attention.
|
|
55
|
+
Never claim that an unrun check passed or that passing checks prove the whole
|
|
56
|
+
product.
|
package/src/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
initialize,
|
|
12
12
|
installCodex,
|
|
13
13
|
listStackPieces,
|
|
14
|
+
prepareWorkspace,
|
|
14
15
|
verify,
|
|
15
16
|
} from './index.js';
|
|
16
17
|
import {
|
|
@@ -30,6 +31,7 @@ const USAGE = `Usage:
|
|
|
30
31
|
genesis stack add <piece...>
|
|
31
32
|
genesis context <path...>
|
|
32
33
|
genesis index [function-or-path...]
|
|
34
|
+
genesis prepare
|
|
33
35
|
genesis prompt [request...]
|
|
34
36
|
genesis prompt --task <start|work|deslop|program|blueprint|describe|review> [request...]
|
|
35
37
|
genesis verify
|
|
@@ -46,7 +48,7 @@ prompt to the agent you already use. Review all edits through the ordinary Git
|
|
|
46
48
|
diff, then run genesis verify for the Stack's concrete checks.
|
|
47
49
|
`;
|
|
48
50
|
|
|
49
|
-
const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'hook', 'index', 'init', 'prompt', 'stack', 'verify']);
|
|
51
|
+
const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'hook', 'index', 'init', 'prepare', 'prompt', 'stack', 'verify']);
|
|
50
52
|
|
|
51
53
|
function parseCommand(argv) {
|
|
52
54
|
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h') || argv[0] === 'help') {
|
|
@@ -165,6 +167,19 @@ function writeResult(command, result) {
|
|
|
165
167
|
line(process.stdout, `index: ${result.status}`);
|
|
166
168
|
return;
|
|
167
169
|
}
|
|
170
|
+
if (command === 'prepare') {
|
|
171
|
+
if (result.summary) line(process.stdout, result.summary);
|
|
172
|
+
namedItems('Commands', result.commands?.map(({ label, argv }) => `${label}: ${argv.join(' ')}`));
|
|
173
|
+
for (const item of result.diagnostics || []) {
|
|
174
|
+
line(process.stdout, `${item.code}: ${item.message}`);
|
|
175
|
+
for (const field of ['stdout', 'stderr']) {
|
|
176
|
+
const output = item.details?.[field]?.trim();
|
|
177
|
+
if (output) line(process.stdout, output);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
line(process.stdout, `prepare: ${result.status}`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
168
183
|
if (command === 'hook') {
|
|
169
184
|
if (['discover', 'session'].includes(result.kind) && result.output) line(process.stdout, result.output);
|
|
170
185
|
else if (result.kind === 'stop') line(process.stdout, JSON.stringify(result.output));
|
|
@@ -226,6 +241,14 @@ async function execute({ command, operands, options }) {
|
|
|
226
241
|
}
|
|
227
242
|
if (command === 'context') return getContext({ paths: operands, projectRoot });
|
|
228
243
|
if (command === 'index') return indexCodebase({ projectRoot, queries: operands });
|
|
244
|
+
if (command === 'prepare') {
|
|
245
|
+
return prepareWorkspace({
|
|
246
|
+
projectRoot,
|
|
247
|
+
onEvent: (event) => {
|
|
248
|
+
if (!options.json) line(process.stderr, event.message);
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
229
252
|
if (command === 'hook') {
|
|
230
253
|
if (operands[0] === 'discover') {
|
|
231
254
|
return { kind: 'discover', ...await codexAdoptionRecommendation({ projectRoot }) };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import {
|
|
4
|
-
cp,
|
|
5
4
|
lstat,
|
|
6
5
|
mkdir,
|
|
7
6
|
readFile,
|
|
8
7
|
readdir,
|
|
9
8
|
rename,
|
|
10
9
|
rm,
|
|
10
|
+
writeFile,
|
|
11
11
|
} from 'node:fs/promises';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
@@ -92,7 +92,7 @@ async function skillTree(directory, relative = '') {
|
|
|
92
92
|
files.push({
|
|
93
93
|
path: child,
|
|
94
94
|
hash: sha256(content),
|
|
95
|
-
mode: (info.mode &
|
|
95
|
+
mode: (info.mode & 0o100) === 0 ? 0o100644 : 0o100755,
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
return files;
|
|
@@ -251,7 +251,7 @@ async function replaceSkill(source, target) {
|
|
|
251
251
|
await mkdir(path.dirname(target), { recursive: true });
|
|
252
252
|
let replaced = false;
|
|
253
253
|
try {
|
|
254
|
-
await
|
|
254
|
+
await copySkillTree(source, temporary);
|
|
255
255
|
try {
|
|
256
256
|
await rename(target, backup);
|
|
257
257
|
replaced = true;
|
|
@@ -271,6 +271,23 @@ async function replaceSkill(source, target) {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
async function copySkillTree(source, target) {
|
|
275
|
+
const info = await lstat(source);
|
|
276
|
+
if (info.isSymbolicLink() || (!info.isDirectory() && !info.isFile())) {
|
|
277
|
+
invalidSkill(`Agent Skill contains a non-ordinary entry: ${source}.`);
|
|
278
|
+
}
|
|
279
|
+
if (info.isDirectory()) {
|
|
280
|
+
await mkdir(target, { mode: 0o777 });
|
|
281
|
+
const entries = await readdir(source, { withFileTypes: true });
|
|
282
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
283
|
+
await copySkillTree(path.join(source, entry.name), path.join(target, entry.name));
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const mode = (info.mode & 0o100) === 0 ? 0o666 : 0o777;
|
|
288
|
+
await writeFile(target, await readFile(source), { flag: 'wx', mode });
|
|
289
|
+
}
|
|
290
|
+
|
|
274
291
|
function targetFiles(skill) {
|
|
275
292
|
return skill.files.map(({ path: file }) => `${SKILLS_ROOT}/${skill.name}/${file}`);
|
|
276
293
|
}
|
|
@@ -304,6 +321,10 @@ export async function syncProjectSkills({ projectRoot, stack } = {}) {
|
|
|
304
321
|
continue;
|
|
305
322
|
}
|
|
306
323
|
if (installed.hash !== managed.hash) {
|
|
324
|
+
if (installed.hash === skill.hash) {
|
|
325
|
+
manifest.value.skills[skill.name] = { hash: skill.hash, source: skill.source };
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
307
328
|
diagnostics.push({
|
|
308
329
|
code: 'AGENT_SKILL_CUSTOMIZED',
|
|
309
330
|
message: `Preserved locally modified Agent Skill ${skill.name}.`,
|
|
@@ -388,7 +409,7 @@ export async function inspectProjectSkills({ projectRoot, stack } = {}) {
|
|
|
388
409
|
message: `Project Agent Skill ${expected.name} is externally managed; Genesis preserved it.`,
|
|
389
410
|
details: { name: expected.name, path: `${SKILLS_ROOT}/${expected.name}/SKILL.md` },
|
|
390
411
|
});
|
|
391
|
-
} else if (installed.hash !== managed.hash) {
|
|
412
|
+
} else if (installed.hash !== managed.hash && installed.hash !== expected.hash) {
|
|
392
413
|
customized = true;
|
|
393
414
|
diagnostics.push({
|
|
394
415
|
code: 'AGENT_SKILL_CUSTOMIZED',
|
package/src/index/check.js
CHANGED
|
@@ -6,6 +6,7 @@ import { gitContext } from './git.js';
|
|
|
6
6
|
import { inspectProgram } from './program.js';
|
|
7
7
|
import { inspectVerification } from './project-state.js';
|
|
8
8
|
import { missingStackResources } from './stack-preflight.js';
|
|
9
|
+
import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
|
|
9
10
|
|
|
10
11
|
function invalidResult(area, error) {
|
|
11
12
|
return {
|
|
@@ -55,7 +56,10 @@ export async function checkProject({ environment = process.env, projectRoot } =
|
|
|
55
56
|
} catch (error) {
|
|
56
57
|
skills = { status: 'invalid', diagnostics: [asDiagnostic(error)], skills: [] };
|
|
57
58
|
}
|
|
58
|
-
const missingResources = missingStackResources({
|
|
59
|
+
const missingResources = missingStackResources({
|
|
60
|
+
environment: withStackEnvironmentDefaults(environment, stack.environmentDefaults),
|
|
61
|
+
resources: stack.resources,
|
|
62
|
+
});
|
|
59
63
|
const verification = await inspectVerification({ projectRoot: root, stack });
|
|
60
64
|
const diagnostics = [
|
|
61
65
|
...(program.diagnostic ? [program.diagnostic] : []),
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
function presentationRegionForPath(filePath, regions) {
|
|
4
|
+
return regions.find((region) => (
|
|
5
|
+
!region.fallback
|
|
6
|
+
&& (filePath === region.pathPrefix || filePath.startsWith(`${region.pathPrefix}/`))
|
|
7
|
+
)) || regions.find(({ fallback }) => fallback) || null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parentDirectory(filePath) {
|
|
11
|
+
const parent = path.posix.dirname(filePath);
|
|
12
|
+
return parent === '.' ? '' : parent;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function presentationCampusPath(filePath, region, { file = false } = {}) {
|
|
16
|
+
const ownershipPath = file ? parentDirectory(filePath) : filePath;
|
|
17
|
+
if (!region || region.fallback) {
|
|
18
|
+
return ownershipPath.split('/').filter(Boolean)[0] || '';
|
|
19
|
+
}
|
|
20
|
+
if (ownershipPath === region.pathPrefix) return region.pathPrefix;
|
|
21
|
+
const relative = ownershipPath.slice(region.pathPrefix.length + 1);
|
|
22
|
+
const child = relative.split('/').filter(Boolean)[0];
|
|
23
|
+
return child ? `${region.pathPrefix}/${child}` : region.pathPrefix;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function campusId(regionId, campusPath) {
|
|
27
|
+
return `presentation-campus:${regionId}:${campusPath || '.'}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Resolves Stack-declared City regions and campus roots into Machine City facts. */
|
|
31
|
+
export function buildCityPresentation({ districts = [], files = [], regions = [] } = {}) {
|
|
32
|
+
if (regions.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
campuses: [],
|
|
35
|
+
placementForPath: () => ({ presentationCampusId: null, presentationRegionId: null }),
|
|
36
|
+
regions: [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const districtsByPath = new Map(districts.map((district) => [district.path, district]));
|
|
40
|
+
const placements = new Map();
|
|
41
|
+
const mutableCampuses = new Map();
|
|
42
|
+
const mutableRegions = new Map(regions.map((region) => [region.id, {
|
|
43
|
+
...region,
|
|
44
|
+
buildingCount: 0,
|
|
45
|
+
}]));
|
|
46
|
+
|
|
47
|
+
function resolvePlacement(filePath = '', { file = false } = {}) {
|
|
48
|
+
if (placements.has(filePath)) return placements.get(filePath);
|
|
49
|
+
const region = presentationRegionForPath(filePath, regions);
|
|
50
|
+
const campusPath = presentationCampusPath(filePath, region, { file });
|
|
51
|
+
const placement = {
|
|
52
|
+
presentationCampusId: campusId(region.id, campusPath),
|
|
53
|
+
presentationRegionId: region.id,
|
|
54
|
+
};
|
|
55
|
+
placements.set(filePath, placement);
|
|
56
|
+
if (!mutableCampuses.has(placement.presentationCampusId)) {
|
|
57
|
+
mutableCampuses.set(placement.presentationCampusId, {
|
|
58
|
+
id: placement.presentationCampusId,
|
|
59
|
+
regionId: region.id,
|
|
60
|
+
path: campusPath,
|
|
61
|
+
title: campusPath ? path.posix.basename(campusPath) : 'Project root',
|
|
62
|
+
districtId: districtsByPath.get(campusPath)?.id || null,
|
|
63
|
+
buildingCount: 0,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return placement;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
const placement = resolvePlacement(file.path, { file: true });
|
|
71
|
+
mutableRegions.get(placement.presentationRegionId).buildingCount += 1;
|
|
72
|
+
mutableCampuses.get(placement.presentationCampusId).buildingCount += 1;
|
|
73
|
+
}
|
|
74
|
+
for (const district of districts) resolvePlacement(district.path);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
campuses: [...mutableCampuses.values()]
|
|
78
|
+
.filter(({ buildingCount }) => buildingCount > 0)
|
|
79
|
+
.sort((left, right) => left.id.localeCompare(right.id)),
|
|
80
|
+
placementForPath: (filePath = '') => placements.get(filePath) || resolvePlacement(filePath),
|
|
81
|
+
regions: [...mutableRegions.values()],
|
|
82
|
+
};
|
|
83
|
+
}
|