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**, and **Accounts
4
- app**. Basic keeps the existing `jskit/public` ID and `public` branch. Database
5
- uses `jskit/database` and the `database` branch, adding MySQL without login.
6
- Accounts keeps `jskit/accounts` and the `accounts` branch. These are complete
7
- starter projects with ready tests; importing source still runs no setup commands.
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
- Each branch is a complete, current Genesis project with actual application
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. No dependency command,
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,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.1",
3
+ "version": "1.10.3",
4
4
  "type": "module",
5
5
  "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with project agent guidance.",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.1",
3
+ "version": "1.10.3",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -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) {
@@ -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') throw error;
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
- const stack = await readStack(root, { stackPackages });
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 satisfied = resource.environmentAlternatives.some(({ bindings, allowEmpty }) => (
15
- Object.entries(bindings).every(([semantic, name]) => (
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 (satisfied) return [];
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}: ${alternatives.join(' OR ')}.`,
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 });