genesis-compiler 1.10.1 → 1.10.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/docs/templates.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# Project opening and templates
|
|
2
2
|
|
|
3
|
-
The official JSKIT choices are **Basic app**, **Database app**,
|
|
4
|
-
app**. Basic keeps the existing `jskit/public` ID and
|
|
5
|
-
uses `jskit/database` and the `database` branch,
|
|
6
|
-
Accounts keeps `jskit/accounts` and the `accounts`
|
|
7
|
-
|
|
3
|
+
The official JSKIT choices are **Basic app**, **Database app**, **Accounts
|
|
4
|
+
app** and **Accounts hub**. Basic keeps the existing `jskit/public` ID and
|
|
5
|
+
`public` branch. Database uses `jskit/database` and the `database` branch,
|
|
6
|
+
adding MySQL without login. Accounts keeps `jskit/accounts` and the `accounts`
|
|
7
|
+
branch. Accounts hub uses `jskit/accounts-hub` and the `accounts-hub` branch:
|
|
8
|
+
an OpenID Connect hub that gives an owner's other apps one sign-in. These are
|
|
9
|
+
complete starter projects with ready tests; importing source still runs no
|
|
10
|
+
setup commands.
|
|
11
|
+
|
|
12
|
+
Each framework component also has one official **Basic app**: `django/basic`,
|
|
13
|
+
`rails/basic`, `spring-boot/basic`, `express/basic` and `nextjs/basic`. Each is
|
|
14
|
+
the `basic` branch of its `vibe64-dev/seed-<technology>` repository and stores
|
|
15
|
+
notes from one form: SQLite for Django, Rails, Express and Next.js, and
|
|
16
|
+
MySQL/MariaDB for Spring Boot. Only the Rails starter has sign-in, with a seeded
|
|
17
|
+
development account. The catalogue lists JSKIT first, then the frameworks.
|
|
8
18
|
|
|
9
19
|
`genesis init` installs the portable Genesis documents, workflow skills, and
|
|
10
20
|
agent integration in an ordinary Git repository. It preserves existing source.
|
|
@@ -66,8 +76,10 @@ genesis templates apply official:jskit/public
|
|
|
66
76
|
|
|
67
77
|
The first-party JSKIT repository is
|
|
68
78
|
the [JSKIT seed repository](https://github.com/vibe64-dev/seed-jskit). `public` supplies
|
|
69
|
-
an adaptive public app; `accounts` adds local accounts, private Home and MySQL
|
|
70
|
-
|
|
79
|
+
an adaptive public app; `accounts` adds local accounts, private Home and MySQL;
|
|
80
|
+
`accounts-hub` is an OpenID Connect hub for one sign-in across apps.
|
|
81
|
+
The framework starters live in `seed-django`, `seed-rails`, `seed-spring-boot`,
|
|
82
|
+
`seed-express` and `seed-nextjs`, each on its `basic` branch. Each branch is a complete, current Genesis project with actual application
|
|
71
83
|
source, dependency lockfile, setup/run contracts and focused checks.
|
|
72
84
|
|
|
73
85
|
Applying fetches the explicitly named branch once, resolves its commit and reads
|
|
@@ -92,7 +104,11 @@ An empty bootstrap subsystem map yields to the starter's authored map, keeping
|
|
|
92
104
|
the imported Program connected to its source. A nonempty destination map remains
|
|
93
105
|
authored project context; incompatible declarations fail validation before copying.
|
|
94
106
|
Template Stack components/packages are combined with existing selections;
|
|
95
|
-
existing complete operation sections take precedence.
|
|
107
|
+
existing complete operation sections take precedence. When the starter's Stack
|
|
108
|
+
predates a section its selected components now propose, Genesis adopts the
|
|
109
|
+
current proposal, as `genesis migrate` would, so a new project never starts with
|
|
110
|
+
pending proposals. A section whose proposals compete still fails validation
|
|
111
|
+
before copying. No dependency command,
|
|
96
112
|
database command, agent, or Git commit runs during import. The result reports
|
|
97
113
|
the resolved source repository, branch and commit; no provenance ledger is
|
|
98
114
|
written to the project. Review the ordinary diff and commit it, or save it through the host.
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
-
import { access, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { access, chmod, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
@@ -137,6 +137,10 @@ async function installParser(definition, root, { signal, onProgress, environment
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
await verifyParser(definition, installation, signal);
|
|
140
|
+
// mkdtemp stages privately (0700); a shared parser root (for example one
|
|
141
|
+
// prepared by root for service users) needs the mode an ordinary directory
|
|
142
|
+
// beside it would have, or other users cannot load the parser.
|
|
143
|
+
await chmod(staging, (await stat(path.dirname(destination))).mode & 0o7777);
|
|
140
144
|
try {
|
|
141
145
|
await rename(staging, destination);
|
|
142
146
|
} catch (error) {
|
package/src/index/prompt.js
CHANGED
|
@@ -311,11 +311,20 @@ async function generateStartPrompt({
|
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
313
|
let blueprint;
|
|
314
|
+
let uninitializedNew = false;
|
|
314
315
|
try {
|
|
315
316
|
blueprint = await readBlueprint(root, { required: true });
|
|
316
317
|
} catch (error) {
|
|
317
318
|
if (error?.code !== 'BLUEPRINT_REQUIRED') throw error;
|
|
318
|
-
if (await classifyProjectKind({ projectRoot: root }) === 'new')
|
|
319
|
+
if (await classifyProjectKind({ projectRoot: root }) === 'new') {
|
|
320
|
+
// An empty repository that was never initialized still opens a new-project
|
|
321
|
+
// conversation; the agent initializes Genesis before writing intent.
|
|
322
|
+
if (inspection.projectFormat?.status !== 'uninitialized') throw error;
|
|
323
|
+
uninitializedNew = true;
|
|
324
|
+
blueprint = { path: 'genesis/blueprint.md', description: null };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (!blueprint) {
|
|
319
328
|
return {
|
|
320
329
|
status: 'ready',
|
|
321
330
|
task: 'start',
|
|
@@ -336,7 +345,10 @@ async function generateStartPrompt({
|
|
|
336
345
|
verificationCommands: [],
|
|
337
346
|
};
|
|
338
347
|
}
|
|
339
|
-
|
|
348
|
+
// Initialization writes the Stack; until then the selection is empty.
|
|
349
|
+
const stack = uninitializedNew
|
|
350
|
+
? { components: [], stackPackages: [], guidance: '' }
|
|
351
|
+
: await readStack(root, { stackPackages });
|
|
340
352
|
const projectKind = await classifyProjectKind({
|
|
341
353
|
projectRoot: root,
|
|
342
354
|
stackComponents: stack.components,
|
|
@@ -351,7 +363,9 @@ async function generateStartPrompt({
|
|
|
351
363
|
.then((profiles) => profiles.map(({ id, name, description }) => ({ id, name, description })))
|
|
352
364
|
: Promise.resolve([]),
|
|
353
365
|
inspectProjectSkills({ projectRoot: root, stack }),
|
|
354
|
-
startInstructions(instructions, projectKind)
|
|
366
|
+
startInstructions(instructions, projectKind).then((text) => (uninitializedNew
|
|
367
|
+
? `${text}\n\nGenesis is not initialized in this repository yet. Before writing the Blueprint\nor selecting Stack components, run the Genesis \`init\` operation. Applying a\nready-made template initializes the project itself.`
|
|
368
|
+
: text)),
|
|
355
369
|
]);
|
|
356
370
|
return {
|
|
357
371
|
status: 'ready',
|
|
@@ -367,6 +381,7 @@ async function generateStartPrompt({
|
|
|
367
381
|
path: blueprint.path,
|
|
368
382
|
description: blueprint.description,
|
|
369
383
|
},
|
|
384
|
+
...(uninitializedNew ? { genesis: { initialized: false } } : {}),
|
|
370
385
|
...sessionPrompt.context,
|
|
371
386
|
stack: startStackContext(stack),
|
|
372
387
|
...(projectKind === 'new'
|
|
@@ -11,19 +11,25 @@ export function missingStackResources({ environment = process.env, resources = [
|
|
|
11
11
|
throw new TypeError('Stack resource inspection requires an environment object and resource declarations.');
|
|
12
12
|
}
|
|
13
13
|
return resources.flatMap(({ component, resource }) => {
|
|
14
|
-
const
|
|
15
|
-
Object.entries(bindings)
|
|
16
|
-
present(environment, name, allowEmpty.includes(semantic))
|
|
17
|
-
|
|
14
|
+
const missing = resource.environmentAlternatives.map(({ bindings, allowEmpty }) => (
|
|
15
|
+
Object.entries(bindings)
|
|
16
|
+
.filter(([semantic, name]) => !present(environment, name, allowEmpty.includes(semantic)))
|
|
17
|
+
.map(([, name]) => name)
|
|
18
18
|
));
|
|
19
|
-
if (
|
|
19
|
+
if (missing.some((names) => names.length === 0)) return [];
|
|
20
20
|
const alternatives = resource.environmentAlternatives.map(({ bindings }) => (
|
|
21
21
|
Object.values(bindings).join(' + ')
|
|
22
22
|
));
|
|
23
|
+
// Name what is still absent: defaults may already supply most bindings.
|
|
24
|
+
const described = alternatives.map((alternative, index) => (
|
|
25
|
+
missing[index].length < alternative.split(' + ').length
|
|
26
|
+
? `${alternative} (missing ${missing[index].join(', ')})`
|
|
27
|
+
: alternative
|
|
28
|
+
));
|
|
23
29
|
return [{
|
|
24
30
|
code: 'STACK_RESOURCE_MISSING',
|
|
25
|
-
message: `Stack component ${component} requires ${resource.id}: ${
|
|
26
|
-
details: { component, resource: resource.id, alternatives },
|
|
31
|
+
message: `Stack component ${component} requires ${resource.id}: ${described.join(' OR ')}.`,
|
|
32
|
+
details: { component, resource: resource.id, alternatives, missing },
|
|
27
33
|
}];
|
|
28
34
|
});
|
|
29
35
|
}
|
|
@@ -11,7 +11,7 @@ import { GENESIS_BOOTSTRAP_PATHS, inspectProject } from './project-inspection.js
|
|
|
11
11
|
import { runGit, runGitText } from './process.js';
|
|
12
12
|
import { listTemplates, resolveTemplate } from './template-catalog.js';
|
|
13
13
|
import { readGitSnapshot } from './template-source.js';
|
|
14
|
-
import { readStack } from './stack.js';
|
|
14
|
+
import { materializeProjectStackContracts, readStack } from './stack.js';
|
|
15
15
|
import { inspectSubsystems } from './subsystems.js';
|
|
16
16
|
import { writeFileAtomic } from './utils.js';
|
|
17
17
|
import { claudeSkillLinkTarget } from './claude-hooks.js';
|
|
@@ -124,6 +124,10 @@ export async function applyTemplate({ projectRoot, templateId, stackPackages = [
|
|
|
124
124
|
await symlink(linkTarget, location, 'dir');
|
|
125
125
|
} else await writeFile(location, value);
|
|
126
126
|
}
|
|
127
|
+
// A new project starts complete: adopt the current proposals of the starter's
|
|
128
|
+
// components for any section its Stack predates, as `genesis migrate` would.
|
|
129
|
+
const { pendingSections } = await readStack(stagingRoot, { allowUnresolvedSections: true, stackPackages });
|
|
130
|
+
if (pendingSections.length > 0) await materializeProjectStackContracts({ projectRoot: stagingRoot, stackPackages });
|
|
127
131
|
await initializeProject({ projectRoot: stagingRoot, stackPackages });
|
|
128
132
|
await buildProjectIndex({ projectRoot: stagingRoot, stackPackages });
|
|
129
133
|
const prepared = await inspectProject({ projectRoot: stagingRoot, stackPackages });
|