genesis-compiler 1.8.5 → 1.8.6

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 CHANGED
@@ -1005,6 +1005,13 @@ repository. It retains the same Stack validation and performs no writes or
1005
1005
  commands. Missing/invalid roots fail; this is never an automatic fallback from
1006
1006
  Git ownership or discovery errors.
1007
1007
 
1008
+ `inspectStackSections({ names, projectRoot, rootMode, stackPackages })` returns
1009
+ an array of those same section results in the requested order. Names must be
1010
+ distinct and the array must be nonempty. It discovers the root once and reads
1011
+ the Stack once, so related sections share one Stack identity. Each call reads
1012
+ current files; results are not cached across calls. Missing sections retain
1013
+ their ordinary `unconfigured` result and repository/directory rules are unchanged.
1014
+
1008
1015
  Hosts that already validate access to a shared worktree may wrap an operation
1009
1016
  in `withTrustedGitRepository(absoluteWorktreeRoot, () => inspectEnvironment({
1010
1017
  projectRoot: absoluteWorktreeRoot }))`. This explicitly trusts that one
@@ -99,6 +99,10 @@ The executable returns exactly one JSON result:
99
99
  means the host already delivers through a native system field. This result
100
100
  is valid only for session scope.
101
101
 
102
+ A validated complete host delivery is resolved before CLI project-format, Git
103
+ root, or Stack inspection. Unclaimed conversations and host contributions retain
104
+ the normal project diagnostics and composition. The resolver runs once per hook.
105
+
102
106
  Malformed results and configured-resolver failures fail the hook. An unavailable
103
107
  claimed owner must fail rather than returning `unclaimed`. Ownership belongs to
104
108
  the native conversation: these results do not change project hook files, so
@@ -184,6 +184,13 @@ discovery. Default library and CLI inspection continue to use the enclosing
184
184
  Git repository and its trust checks. Invalid modes or directory roots fail;
185
185
  there is no automatic fallback.
186
186
 
187
+ When a consumer needs several related declarations, use
188
+ `inspectStackSections({ names: ['First operation', 'Second operation'], projectRoot })`.
189
+ It returns an array of the same `genesis.stack-section.v1` results in the requested
190
+ order, using one repository discovery and one fresh Stack read. All results have
191
+ the same Stack hash. The names array must be nonempty and contain no duplicates.
192
+ The explicit directory mode is also supported; no cross-call cache is retained.
193
+
187
194
  ## Selection and identity
188
195
 
189
196
  `genesis stack add` records selected packages and component dependency closure,
@@ -265,3 +272,21 @@ previously effective declarations once and writing them into
265
272
  `genesis/stack.md`. Existing project sections win exactly as they did before.
266
273
  Current-format reads reject a selected component whose required contract was
267
274
  not materialized, so a tool upgrade cannot quietly substitute a new proposal.
275
+
276
+ `genesis migrate` does not materialize newly added catalog proposals when the
277
+ project already uses the current format. Reconcile those explicitly with the
278
+ existing `genesis stack add` operation for an already-selected component. For
279
+ example, if `nodejs` is selected and the missing heading is
280
+ `## Connected development`, run `genesis stack add nodejs` and review its
281
+ preparation prompt. This adds the Node `npm` default without choosing connected
282
+ repositories or installing dependencies. Existing complete project sections
283
+ remain authoritative; reconcile any proposed operation against the real source.
284
+ Use the project's pinned compiler invocation when it declares `genesis-compiler`:
285
+ `npm exec --no -- genesis stack add nodejs`.
286
+
287
+ Before rolling out a catalog that adds operation proposals, operators should
288
+ run read-only project inspection against representative existing projects with
289
+ the candidate compiler and catalog. Arrange explicit reconciliation of affected
290
+ projects before rollout. A format marker alone does not establish compatibility
291
+ with new catalog proposals. Do not repair project contracts through opening,
292
+ status reads or another lazy write path.
package/docs/templates.md CHANGED
@@ -14,6 +14,9 @@ application has been described or that an application has been created.
14
14
  `genesis inspect project --json` is a read-only opening inspection shared by
15
15
  the CLI, session guidance, and hosts such as Vibe64:
16
16
 
17
+ The diagnostics list reports each identical failure once, even when separate
18
+ inspectors encounter it. Distinct failures remain available for repair.
19
+
17
20
  | State | Evidence | Next step |
18
21
  | --- | --- | --- |
19
22
  | `new` | No content beyond known bootstrap files | Choose a template or create through conversation |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.8.5",
3
+ "version": "1.8.6",
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": {
package/src/cli.js CHANGED
@@ -471,7 +471,7 @@ async function cliStackPackages(projectRoot, supplied) {
471
471
  return installedFirstPartyStackPackages({ projectRoot });
472
472
  }
473
473
 
474
- async function execute({ command, operands, options }, { signal } = {}) {
474
+ async function execute({ command, operands, options }, { signal, hostContext = null } = {}) {
475
475
  if (command === 'parsers') {
476
476
  const parserOptions = {
477
477
  languages: operands.slice(1), all: options.all, directory: options.directory, signal,
@@ -576,23 +576,6 @@ async function execute({ command, operands, options }, { signal } = {}) {
576
576
  return { kind: 'discover', ...await codexAdoptionRecommendation({ projectRoot }) };
577
577
  }
578
578
  const scope = operands[0];
579
- if (scope === 'turn' && process.env.GENESIS_TURN_CONTEXT_ENABLED === '0') {
580
- return { kind: 'turn', status: 'ready', output: '' };
581
- }
582
- if (scope === 'session' && process.env[SESSION_CONTEXT_INSTALLED_ENV] === '1') {
583
- return { kind: 'session', status: 'ready', output: '' };
584
- }
585
- const nativeSession = await hookSessionInput();
586
- const hostContext = await resolveConfiguredHostContext({
587
- projectRoot,
588
- ...nativeSession,
589
- scope,
590
- });
591
- // Ownership is resolved for this native conversation, not for the project.
592
- // A host-owned delivery already contains everything its runtime requires.
593
- if (hostContext?.kind === 'delivery') {
594
- return { kind: 'session', status: 'ready', output: hostContext.text };
595
- }
596
579
  if (scope === 'turn') {
597
580
  return hostContext === null
598
581
  ? { kind: 'turn', status: 'ready', output: '' }
@@ -669,7 +652,25 @@ export async function runCli(argv = process.argv.slice(2)) {
669
652
  return 0;
670
653
  }
671
654
  options = parsed.options;
672
- const projectFormat = parsed.command === 'parsers' ? null : await optionalProjectFormat(options.projectRoot || process.cwd());
655
+ const projectRoot = options.projectRoot || process.cwd();
656
+ let hookResult = null;
657
+ let hostContext = null;
658
+ if (parsed.command === 'hook' && parsed.operands[0] !== 'discover') {
659
+ const scope = parsed.operands[0];
660
+ if ((scope === 'turn' && process.env.GENESIS_TURN_CONTEXT_ENABLED === '0') ||
661
+ (scope === 'session' && process.env[SESSION_CONTEXT_INSTALLED_ENV] === '1')) {
662
+ hookResult = { kind: scope, status: 'ready', output: '' };
663
+ } else {
664
+ const nativeSession = await hookSessionInput();
665
+ hostContext = await resolveConfiguredHostContext({ projectRoot, ...nativeSession, scope });
666
+ // A validated owner supplies the complete delivery; project composition is unused.
667
+ if (hostContext?.kind === 'delivery') {
668
+ hookResult = { kind: 'session', status: 'ready', output: hostContext.text };
669
+ }
670
+ }
671
+ }
672
+ const projectFormat = hookResult || parsed.command === 'parsers'
673
+ ? null : await optionalProjectFormat(projectRoot);
673
674
  const formatDiagnostic = projectFormatDiagnostic(projectFormat);
674
675
  if (formatDiagnostic && !options.json) {
675
676
  line(process.stderr, `WARNING: ${formatDiagnostic.message}`);
@@ -680,7 +681,7 @@ export async function runCli(argv = process.argv.slice(2)) {
680
681
  process.once('SIGINT', abort);
681
682
  process.once('SIGTERM', abort);
682
683
  const result = withProjectFormatWarning(
683
- await withParserCancellation(controller.signal, () => execute(parsed, { signal: controller.signal })),
684
+ hookResult ?? await withParserCancellation(controller.signal, () => execute(parsed, { signal: controller.signal, hostContext })),
684
685
  projectFormat,
685
686
  formatDiagnostic,
686
687
  );
@@ -1,5 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { isDeepStrictEqual } from 'node:util';
3
4
 
4
5
  import { readBlueprint } from './blueprint.js';
5
6
  import { readCollaboration } from './collaboration.js';
@@ -125,6 +126,9 @@ export async function inspectProject({ projectRoot, stackPackages = [] } = {}) {
125
126
  try { await read(root); } catch (error) { result.diagnostics.push(asDiagnostic(error)); }
126
127
  }
127
128
  try { await inspectProgram(root); } catch (error) { result.diagnostics.push(asDiagnostic(error)); }
129
+ // Program resource validation can encounter the same Stack failure already reported above.
130
+ result.diagnostics = result.diagnostics.filter((diagnostic, index, diagnostics) =>
131
+ !diagnostics.slice(0, index).some((previous) => isDeepStrictEqual(previous, diagnostic)));
128
132
  if (result.diagnostics.length) return result;
129
133
  if (content.kind !== 'existing') {
130
134
  return { ...result, state: 'new', templateEligible: true, nextAction: 'create' };
@@ -10,11 +10,25 @@ import { sha256, stableJson } from './utils.js';
10
10
  /** Inspect one consumer-owned Stack section without interpreting its contents. */
11
11
  export async function inspectProjectStackSection({
12
12
  name,
13
+ ...options
14
+ } = {}) {
15
+ return (await inspectProjectStackSections({ ...options, names: [name] }))[0];
16
+ }
17
+
18
+ /** Inspect several sections from one fresh Stack read, in the requested order. */
19
+ export async function inspectProjectStackSections({
20
+ names,
13
21
  projectRoot,
14
22
  rootMode = 'repository',
15
23
  stackPackages = [],
16
24
  } = {}) {
17
- const sectionName = normalizeStackSectionName(name);
25
+ if (!Array.isArray(names) || names.length === 0) {
26
+ throw new TypeError('Stack section inspection requires a nonempty names array.');
27
+ }
28
+ const sectionNames = names.map(normalizeStackSectionName);
29
+ if (new Set(sectionNames).size !== sectionNames.length) {
30
+ throw new TypeError('Stack section inspection requires distinct names.');
31
+ }
18
32
  let root;
19
33
  if (rootMode === 'repository') {
20
34
  root = (await gitContext(projectRoot)).repositoryRoot;
@@ -28,20 +42,22 @@ export async function inspectProjectStackSection({
28
42
  throw new TypeError('Stack section rootMode must be repository or directory.');
29
43
  }
30
44
  const stack = await readStack(root, { stackPackages });
31
- const section = stack.extensions.find((candidate) => candidate.name === sectionName);
32
- const diagnostics = [...(section?.diagnostics || [])];
33
- const status = diagnostics.length > 0 ? 'blocked' : section ? 'ready' : 'unconfigured';
34
- const lines = section ? [...section.lines] : [];
35
- return {
36
- contract: GENESIS_CONTRACTS.stackSection,
37
- status,
38
- stackHash: stack.identityHash,
39
- sectionHash: status === 'ready' ? sha256(stableJson({ name: sectionName, lines })) : '',
40
- components: stack.components.map(({ id }) => id),
41
- name: sectionName,
42
- source: section?.source || null,
43
- content: lines.join('\n'),
44
- lines,
45
- diagnostics,
46
- };
45
+ return sectionNames.map((sectionName) => {
46
+ const section = stack.extensions.find((candidate) => candidate.name === sectionName);
47
+ const diagnostics = [...(section?.diagnostics || [])];
48
+ const status = diagnostics.length > 0 ? 'blocked' : section ? 'ready' : 'unconfigured';
49
+ const lines = section ? [...section.lines] : [];
50
+ return {
51
+ contract: GENESIS_CONTRACTS.stackSection,
52
+ status,
53
+ stackHash: stack.identityHash,
54
+ sectionHash: status === 'ready' ? sha256(stableJson({ name: sectionName, lines })) : '',
55
+ components: stack.components.map(({ id }) => id),
56
+ name: sectionName,
57
+ source: section?.source || null,
58
+ content: lines.join('\n'),
59
+ lines,
60
+ diagnostics,
61
+ };
62
+ });
47
63
  }
package/src/index.js CHANGED
@@ -21,7 +21,7 @@ import { initializeProject } from './index/init.js';
21
21
  import { migrateProject } from './index/migration.js';
22
22
  import { installCodexPlugin } from './index/codex-plugin.js';
23
23
  import { inspectProjectEnvironment } from './index/environment-files.js';
24
- import { inspectProjectStackSection } from './index/stack-section-inspection.js';
24
+ import { inspectProjectStackSection, inspectProjectStackSections } from './index/stack-section-inspection.js';
25
25
  import { listStackCatalogPieces } from './index/stack-catalog.js';
26
26
  import { addStackPieces, readStack } from './index/stack.js';
27
27
  import { uniqueSorted } from './index/utils.js';
@@ -161,6 +161,10 @@ export function inspectStackSection(options) {
161
161
  return inspectProjectStackSection(options);
162
162
  }
163
163
 
164
+ export function inspectStackSections(options) {
165
+ return inspectProjectStackSections(options);
166
+ }
167
+
164
168
  export function listEngineeringProfiles() {
165
169
  return listEngineeringProfileCatalog();
166
170
  }