genesis-compiler 1.2.12 → 1.2.13
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 +34 -90
- package/docs/prompt-integration.md +5 -5
- package/docs/stack-components.md +119 -393
- package/package.json +1 -2
- package/plugins/genesis/.codex-plugin/plugin.json +1 -1
- package/prompts/adopt.txt +14 -23
- package/prompts/start.txt +10 -12
- package/prompts/work.txt +8 -9
- package/src/cli.js +27 -55
- package/src/index/check.js +13 -21
- package/src/index/context.js +3 -3
- package/src/index/contracts.js +1 -3
- package/src/index/project-state.js +2 -2
- package/src/index/prompt.js +1 -1
- package/src/index/stack-catalog.js +1 -2
- package/src/index/stack-piece.js +25 -21
- package/src/index/stack-section-inspection.js +33 -0
- package/src/index/stack-section.js +82 -0
- package/src/index/stack-verification.js +54 -0
- package/src/index/stack.js +50 -84
- package/src/index/verification.js +2 -25
- package/src/index.js +3 -20
- package/docs/preview-identity-command.md +0 -113
- package/src/index/deployment.js +0 -34
- package/src/index/launch.js +0 -42
- package/src/index/stack-command.js +0 -65
- package/src/index/stack-deployment.js +0 -259
- package/src/index/stack-launch.js +0 -464
- package/src/index/stack-workspace-setup.js +0 -133
- package/src/index/workspace-setup.js +0 -156
package/prompts/adopt.txt
CHANGED
|
@@ -28,20 +28,11 @@ relevant existing-application migration reference. Apply all SELECTED STACK
|
|
|
28
28
|
ADOPTION GUIDANCE. Component defaults describe current foundations and are
|
|
29
29
|
hypotheses until they match this source. They do not upgrade an older project.
|
|
30
30
|
|
|
31
|
-
Build one evidence-backed import inventory covering
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
environment names, without reading or returning secret values;
|
|
37
|
-
- database engine, migration ownership and ledger, preparation command,
|
|
38
|
-
idempotent seed behavior, and fresh versus existing-database safety;
|
|
39
|
-
- every long-running target's exact argv, host/port placeholders, URL path,
|
|
40
|
-
readiness predicate, and shutdown ownership;
|
|
41
|
-
- application-owned Preview identity helper, protocol, supported selectors,
|
|
42
|
-
environment names, runtime, and timeout when authenticated preview exists;
|
|
43
|
-
- concrete verification commands, browser-test assumptions, and deployment
|
|
44
|
-
preparation/build/migration/serve/readiness declarations.
|
|
31
|
+
Build one evidence-backed import inventory covering the selected technologies,
|
|
32
|
+
public environment/resource declarations, concrete Verification commands, and
|
|
33
|
+
every consumer-owned Stack section supported by direct source evidence. Use the
|
|
34
|
+
selected technology skill and the section's declared schema for any
|
|
35
|
+
consumer-specific detail; do not invent a generic operational grammar.
|
|
45
36
|
|
|
46
37
|
Translate observed contracts into exact project-owned Stack sections whenever
|
|
47
38
|
the selected components' defaults differ. A project section replaces the
|
|
@@ -51,18 +42,18 @@ Genesis; never add a permanent legacy reader or shim. Do not delete an old
|
|
|
51
42
|
manifest until all of its live facts have been accounted for.
|
|
52
43
|
|
|
53
44
|
The resulting project contract may therefore own complete `## Resources`,
|
|
54
|
-
`## Environment defaults`, `## Environment files`, `##
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
`## Environment defaults`, `## Environment files`, `## Verification`, and any
|
|
46
|
+
evidence-backed consumer-owned opaque sections. Treat these as one inventory:
|
|
47
|
+
preserve every observed contract without asking Genesis to infer or validate a
|
|
48
|
+
consumer's private semantics.
|
|
58
49
|
|
|
59
50
|
Create the non-technical Blueprint and useful subsystem-oriented Program from
|
|
60
51
|
current source and tests. Do not cite archive internals or Git object storage as
|
|
61
52
|
implementation source; Program sources must be current Git-visible files.
|
|
62
53
|
|
|
63
|
-
Run `genesis check` only for structural validation. Missing environment
|
|
64
|
-
|
|
65
|
-
import findings, not permission to invent or execute them. Finish with a
|
|
66
|
-
inventory of what was imported, what remains unconfigured or blocked, and
|
|
67
|
-
would require a separately approved implementation port. Never claim the
|
|
54
|
+
Run `genesis check` only for structural validation. Missing environment inputs,
|
|
55
|
+
Verification commands, or evidence required by a consumer-owned contract are
|
|
56
|
+
honest import findings, not permission to invent or execute them. Finish with a
|
|
57
|
+
clear inventory of what was imported, what remains unconfigured or blocked, and
|
|
58
|
+
what would require a separately approved implementation port. Never claim the
|
|
68
59
|
project is ready merely because Stack components were selected.
|
package/prompts/start.txt
CHANGED
|
@@ -14,10 +14,9 @@ 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
16
|
This opening turn owns product direction and Stack selection, not final
|
|
17
|
-
verification. Do not run `genesis verify` before implementation
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
not as an implementation failure.
|
|
17
|
+
verification. Do not run `genesis verify` before implementation exists. If
|
|
18
|
+
structural validation is useful, run `genesis check`; consumer-owned operations
|
|
19
|
+
remain governed by their own tools and must not be inferred here.
|
|
21
20
|
|
|
22
21
|
When `projectKind` is `existing`, ask what the user wants to understand or
|
|
23
22
|
change. Use the current Blueprint, Stack, Program, and code index when present.
|
|
@@ -25,14 +24,13 @@ If the existing implementation is not yet explained, recommend the Genesis
|
|
|
25
24
|
description/adoption path instead of pretending its intent is known.
|
|
26
25
|
When an existing implementation has no selected Stack yet, inspect its actual
|
|
27
26
|
package scripts, build files, setup commands, server entrypoint, and readiness
|
|
28
|
-
route before recording the user's technology choice.
|
|
29
|
-
|
|
30
|
-
the component does not port an older implementation to
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
user asking for a port.
|
|
27
|
+
route before recording the user's technology choice. Consumer-owned sections
|
|
28
|
+
declared by a component describe that component's current operational
|
|
29
|
+
foundation; selecting the component does not port an older implementation to
|
|
30
|
+
it. If the existing facts differ, add exact project-owned replacements using
|
|
31
|
+
the consumer's current schema and the code that exists. Never report an
|
|
32
|
+
inherited operation as usable merely because the component was selected, and
|
|
33
|
+
never add compatibility implementation without the user asking for a port.
|
|
36
34
|
|
|
37
35
|
When `projectKind` is `existing-uninitialized`, ask what the user wants to
|
|
38
36
|
understand or change and strongly recommend preparing the existing project for
|
package/prompts/work.txt
CHANGED
|
@@ -8,14 +8,13 @@ Before adding a helper or public operation, query `genesis index <name-or-path>`
|
|
|
8
8
|
and reuse an existing function when it already owns the behavior.
|
|
9
9
|
|
|
10
10
|
This is the implementation turn. Do not edit the Blueprint, Program, or
|
|
11
|
-
`.genesis/`; the separate reconciliation turn owns explanatory changes. Keep
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
constructing a parallel fallback.
|
|
11
|
+
`.genesis/`; the separate reconciliation turn owns explanatory changes. Keep
|
|
12
|
+
applicable consumer-owned Stack sections aligned when the implementation changes
|
|
13
|
+
facts governed by those contracts. Follow the selected technology skill and the
|
|
14
|
+
section's own schema; Genesis only transports the section and does not interpret
|
|
15
|
+
or execute it. Never put an environment value or secret in Stack. Never invent
|
|
16
|
+
missing external-resource values. Preserve useful work and report a genuinely
|
|
17
|
+
missing resource instead of constructing a parallel fallback.
|
|
19
18
|
|
|
20
19
|
Run focused checks when useful. Summarize files changed, checks actually run,
|
|
21
20
|
and anything still requiring attention.
|
|
@@ -24,5 +23,5 @@ If no Stack components are selected, continue the project-opening
|
|
|
24
23
|
conversation instead of guessing a technology. Use `genesis stack list` for
|
|
25
24
|
the current catalog, present relevant choices, wait for an explicit decision,
|
|
26
25
|
and add the selection with `genesis stack add <piece...>` before relying on
|
|
27
|
-
technology-specific
|
|
26
|
+
technology-specific operations. Do not end after only updating
|
|
28
27
|
or summarizing the Blueprint while the Stack remains empty.
|
package/src/cli.js
CHANGED
|
@@ -9,13 +9,10 @@ import {
|
|
|
9
9
|
getContext,
|
|
10
10
|
indexCodebase,
|
|
11
11
|
initialize,
|
|
12
|
-
inspectDeployment,
|
|
13
12
|
inspectEnvironment,
|
|
14
|
-
|
|
15
|
-
inspectWorkspaceSetup,
|
|
13
|
+
inspectStackSection,
|
|
16
14
|
installCodex,
|
|
17
15
|
listStackPieces,
|
|
18
|
-
prepareWorkspace,
|
|
19
16
|
verify,
|
|
20
17
|
} from './index.js';
|
|
21
18
|
import {
|
|
@@ -35,8 +32,8 @@ const USAGE = `Usage:
|
|
|
35
32
|
genesis stack add <piece...>
|
|
36
33
|
genesis context <path...>
|
|
37
34
|
genesis index [function-or-path...]
|
|
38
|
-
genesis inspect
|
|
39
|
-
genesis
|
|
35
|
+
genesis inspect environment
|
|
36
|
+
genesis inspect section <name>
|
|
40
37
|
genesis prompt [request...]
|
|
41
38
|
genesis prompt --task <start|adopt|work|deslop|program|blueprint|describe|review> [request...]
|
|
42
39
|
genesis verify
|
|
@@ -54,7 +51,7 @@ prompt to the agent you already use. Review all edits through the ordinary Git
|
|
|
54
51
|
diff, then run genesis verify for the Stack's concrete checks.
|
|
55
52
|
`;
|
|
56
53
|
|
|
57
|
-
const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'hook', 'index', 'init', 'inspect', '
|
|
54
|
+
const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'hook', 'index', 'init', 'inspect', 'prompt', 'stack', 'verify']);
|
|
58
55
|
|
|
59
56
|
function parseCommand(argv) {
|
|
60
57
|
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h') || argv[0] === 'help') {
|
|
@@ -105,10 +102,12 @@ function parseCommand(argv) {
|
|
|
105
102
|
} else if (command === 'context' && operands.length === 0) {
|
|
106
103
|
fail('CONTEXT_PATH_REQUIRED', 'Command context requires at least one project path.');
|
|
107
104
|
} else if (command === 'inspect') {
|
|
108
|
-
|
|
105
|
+
const ordinaryInspection = operands.length === 1 && operands[0] === 'environment';
|
|
106
|
+
const sectionInspection = operands.length >= 2 && operands[0] === 'section';
|
|
107
|
+
if (!ordinaryInspection && !sectionInspection) {
|
|
109
108
|
fail(
|
|
110
109
|
'CLI_INSPECT_TARGET_REQUIRED',
|
|
111
|
-
'Command inspect requires
|
|
110
|
+
'Command inspect requires environment or section <name>.',
|
|
112
111
|
);
|
|
113
112
|
}
|
|
114
113
|
} else if (command === 'hook' && (operands.length !== 1 || !['begin', 'discover', 'end', 'session', 'stop'].includes(operands[0]))) {
|
|
@@ -134,10 +133,8 @@ function writeCheck(result) {
|
|
|
134
133
|
line(process.stdout, `Stack: ${result.stack}`);
|
|
135
134
|
line(process.stdout, `Agent Skills: ${result.skills}`);
|
|
136
135
|
line(process.stdout, `Program: ${result.program}`);
|
|
137
|
-
line(process.stdout, `Workspace setup: ${result.workspaceSetup}`);
|
|
138
136
|
line(process.stdout, `Environment: ${result.environment}`);
|
|
139
|
-
line(process.stdout, `
|
|
140
|
-
line(process.stdout, `Deployment: ${result.deployment}`);
|
|
137
|
+
line(process.stdout, `Stack extensions: ${result.extensions}`);
|
|
141
138
|
line(process.stdout, `Resource inputs: ${result.resources}`);
|
|
142
139
|
line(process.stdout, `Verification: ${result.verification}`);
|
|
143
140
|
namedItems('Program files', result.programFiles);
|
|
@@ -152,21 +149,14 @@ function writeInspection(result) {
|
|
|
152
149
|
line(process.stdout, `${title}: ${result.status}`);
|
|
153
150
|
namedItems('Components', result.components);
|
|
154
151
|
namedItems('Runtimes', result.runtimeRequirements);
|
|
155
|
-
if (result.inspection === '
|
|
156
|
-
namedItems('Preparation steps', result.steps.map(({ label, argv, workdir }) => (
|
|
157
|
-
`${label} in ${workdir}: ${argv.join(' ')}`
|
|
158
|
-
)));
|
|
159
|
-
} else if (result.inspection === 'environment') {
|
|
152
|
+
if (result.inspection === 'environment') {
|
|
160
153
|
namedItems('Public defaults', result.environmentDefaults.map(({ name, value }) => `${name}=${value}`));
|
|
161
154
|
namedItems('Environment files', result.files.map(({ format, path: filePath }) => `${format}: ${filePath}`));
|
|
162
|
-
} else if (result.inspection === '
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)));
|
|
166
|
-
} else {
|
|
167
|
-
namedItems('Deployment steps', result.steps.map(({ label, argv }) => `${label}: ${argv.join(' ')}`));
|
|
155
|
+
} else if (result.inspection === 'section') {
|
|
156
|
+
if (result.source) line(process.stdout, `Source: ${result.source}`);
|
|
157
|
+
if (result.content) process.stdout.write(`${result.content}\n`);
|
|
168
158
|
}
|
|
169
|
-
namedItems('Resources', result.resources
|
|
159
|
+
namedItems('Resources', result.resources?.map(({ component, resource }) => (
|
|
170
160
|
`${resource.id} (${resource.kind}, ${component})`
|
|
171
161
|
)));
|
|
172
162
|
for (const item of result.diagnostics || []) line(process.stdout, `${item.code}: ${item.message}`);
|
|
@@ -215,19 +205,6 @@ function writeResult(command, result) {
|
|
|
215
205
|
writeInspection(result);
|
|
216
206
|
return;
|
|
217
207
|
}
|
|
218
|
-
if (command === 'prepare') {
|
|
219
|
-
if (result.summary) line(process.stdout, result.summary);
|
|
220
|
-
namedItems('Commands', result.commands?.map(({ label, argv }) => `${label}: ${argv.join(' ')}`));
|
|
221
|
-
for (const item of result.diagnostics || []) {
|
|
222
|
-
line(process.stdout, `${item.code}: ${item.message}`);
|
|
223
|
-
for (const field of ['stdout', 'stderr']) {
|
|
224
|
-
const output = item.details?.[field]?.trim();
|
|
225
|
-
if (output) line(process.stdout, output);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
line(process.stdout, `prepare: ${result.status}`);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
208
|
if (command === 'hook') {
|
|
232
209
|
if (['discover', 'session'].includes(result.kind) && result.output) line(process.stdout, result.output);
|
|
233
210
|
else if (result.kind === 'stop') line(process.stdout, JSON.stringify(result.output));
|
|
@@ -243,7 +220,7 @@ function writeResult(command, result) {
|
|
|
243
220
|
}
|
|
244
221
|
if (result.summary) line(process.stdout, result.summary);
|
|
245
222
|
namedItems('Changed files', result.changedFiles);
|
|
246
|
-
namedItems('
|
|
223
|
+
namedItems('Verification commands', result.commands?.map(({ label, argv }) => `${label}: ${argv.join(' ')}`));
|
|
247
224
|
for (const item of result.diagnostics || []) {
|
|
248
225
|
line(process.stdout, `${item.code}: ${item.message}`);
|
|
249
226
|
for (const field of ['stdout', 'stderr']) {
|
|
@@ -294,27 +271,22 @@ async function execute({ command, operands, options }, { signal } = {}) {
|
|
|
294
271
|
if (command === 'context') return getContext({ paths: operands, projectRoot, stackPackages });
|
|
295
272
|
if (command === 'index') return indexCodebase({ projectRoot, queries: operands, stackPackages });
|
|
296
273
|
if (command === 'inspect') {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
274
|
+
if (operands[0] === 'section') {
|
|
275
|
+
return {
|
|
276
|
+
inspection: 'section',
|
|
277
|
+
...await inspectStackSection({
|
|
278
|
+
name: operands.slice(1).join(' '),
|
|
279
|
+
projectRoot,
|
|
280
|
+
stackPackages,
|
|
281
|
+
}),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const inspections = { environment: inspectEnvironment };
|
|
303
285
|
return {
|
|
304
286
|
inspection: operands[0],
|
|
305
287
|
...await inspections[operands[0]]({ projectRoot, stackPackages }),
|
|
306
288
|
};
|
|
307
289
|
}
|
|
308
|
-
if (command === 'prepare') {
|
|
309
|
-
return prepareWorkspace({
|
|
310
|
-
projectRoot,
|
|
311
|
-
signal,
|
|
312
|
-
stackPackages,
|
|
313
|
-
onEvent: (event) => {
|
|
314
|
-
if (!options.json) line(process.stderr, event.message);
|
|
315
|
-
},
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
290
|
if (command === 'hook') {
|
|
319
291
|
if (operands[0] === 'discover') {
|
|
320
292
|
return { kind: 'discover', ...await codexAdoptionRecommendation({ projectRoot }) };
|
|
@@ -356,7 +328,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
356
328
|
return 0;
|
|
357
329
|
}
|
|
358
330
|
options = parsed.options;
|
|
359
|
-
const finiteCommand =
|
|
331
|
+
const finiteCommand = parsed.command === 'verify';
|
|
360
332
|
if (finiteCommand) {
|
|
361
333
|
process.once('SIGINT', abort);
|
|
362
334
|
process.once('SIGTERM', abort);
|
package/src/index/check.js
CHANGED
|
@@ -8,9 +8,6 @@ import { inspectVerification } from './project-state.js';
|
|
|
8
8
|
import { missingStackResources } from './stack-preflight.js';
|
|
9
9
|
import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
|
|
10
10
|
import { inspectProjectEnvironment } from './environment-files.js';
|
|
11
|
-
import { inspectProjectLaunch } from './launch.js';
|
|
12
|
-
import { inspectProjectDeployment } from './deployment.js';
|
|
13
|
-
import { inspectWorkspaceSetupForStack } from './workspace-setup.js';
|
|
14
11
|
|
|
15
12
|
function invalidResult(area, error) {
|
|
16
13
|
return {
|
|
@@ -19,10 +16,8 @@ function invalidResult(area, error) {
|
|
|
19
16
|
stack: area === 'stack' ? 'invalid' : 'unknown',
|
|
20
17
|
skills: area === 'skills' ? 'invalid' : 'unknown',
|
|
21
18
|
program: 'unknown',
|
|
22
|
-
workspaceSetup: 'unknown',
|
|
23
19
|
environment: 'unknown',
|
|
24
|
-
|
|
25
|
-
deployment: 'unknown',
|
|
20
|
+
extensions: 'unknown',
|
|
26
21
|
resources: 'unknown',
|
|
27
22
|
verification: 'unknown',
|
|
28
23
|
programFiles: [],
|
|
@@ -73,18 +68,17 @@ export async function checkProject({
|
|
|
73
68
|
resources: stack.resources,
|
|
74
69
|
});
|
|
75
70
|
const verification = await inspectVerification({ projectRoot: root, stack });
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
const projectEnvironment = await inspectProjectEnvironment({
|
|
72
|
+
environment,
|
|
73
|
+
projectRoot: root,
|
|
74
|
+
stackPackages,
|
|
75
|
+
});
|
|
76
|
+
const extensionDiagnostics = stack.extensions.flatMap(({ diagnostics }) => diagnostics);
|
|
82
77
|
const diagnostics = [
|
|
83
78
|
...(program.diagnostic ? [program.diagnostic] : []),
|
|
84
79
|
...skills.diagnostics,
|
|
85
80
|
...missingResources,
|
|
86
|
-
...
|
|
87
|
-
...deployment.diagnostics,
|
|
81
|
+
...extensionDiagnostics,
|
|
88
82
|
...(verification.status === 'invalid' ? [{
|
|
89
83
|
code: 'VERIFICATION_EVIDENCE_INVALID',
|
|
90
84
|
message: 'Saved verification evidence is malformed.',
|
|
@@ -99,8 +93,7 @@ export async function checkProject({
|
|
|
99
93
|
if (missingResources.length > 0) {
|
|
100
94
|
guidance.push(`${missingResources.map(({ message }) => message).join(' ')} Prompt generation remains available.`);
|
|
101
95
|
}
|
|
102
|
-
if (
|
|
103
|
-
if (deployment.status === 'blocked') guidance.push('Repair the project Deployment declaration.');
|
|
96
|
+
if (extensionDiagnostics.length > 0) guidance.push('Resolve the competing consumer-owned Stack sections.');
|
|
104
97
|
if (['missing', 'stale'].includes(verification.status)) guidance.push('Run genesis verify to refresh concrete evidence.');
|
|
105
98
|
if (verification.status === 'unconfigured') guidance.push('Add project verification commands to genesis/stack.md.');
|
|
106
99
|
guidance.push('Use genesis prompt --task review for a semantic, evidence-based comparison.');
|
|
@@ -108,8 +101,7 @@ export async function checkProject({
|
|
|
108
101
|
const needsAttention = program.status === 'missing'
|
|
109
102
|
|| skills.status === 'missing'
|
|
110
103
|
|| missingResources.length > 0
|
|
111
|
-
||
|
|
112
|
-
|| deployment.status === 'blocked'
|
|
104
|
+
|| extensionDiagnostics.length > 0
|
|
113
105
|
|| ['missing', 'stale', 'unconfigured'].includes(verification.status);
|
|
114
106
|
|
|
115
107
|
return {
|
|
@@ -120,10 +112,10 @@ export async function checkProject({
|
|
|
120
112
|
stack: 'valid',
|
|
121
113
|
skills: skills.status,
|
|
122
114
|
program: program.status,
|
|
123
|
-
workspaceSetup: workspaceSetup.status,
|
|
124
115
|
environment: projectEnvironment.status,
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
extensions: extensionDiagnostics.length > 0
|
|
117
|
+
? 'blocked'
|
|
118
|
+
: stack.extensions.length > 0 ? 'ready' : 'unconfigured',
|
|
127
119
|
resources: missingResources.length > 0 ? 'missing' : 'inputs-present',
|
|
128
120
|
verification: verification.status,
|
|
129
121
|
programFiles: program.files,
|
package/src/index/context.js
CHANGED
|
@@ -115,9 +115,9 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
|
|
|
115
115
|
'',
|
|
116
116
|
'## Verification',
|
|
117
117
|
'',
|
|
118
|
-
...(stack.
|
|
118
|
+
...(stack.verificationCommands.length === 0
|
|
119
119
|
? ['No verification commands are configured.']
|
|
120
|
-
: stack.
|
|
120
|
+
: stack.verificationCommands.map(({ label, argv }) => `- ${label}: ${argv.map((value) => `\`${value}\``).join(' ')}`)),
|
|
121
121
|
'',
|
|
122
122
|
].join('\n');
|
|
123
123
|
return {
|
|
@@ -125,7 +125,7 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
|
|
|
125
125
|
paths: targets,
|
|
126
126
|
modules: moduleSources.map(({ source: _source, ...module }) => module),
|
|
127
127
|
components: stack.components.map(({ id }) => id),
|
|
128
|
-
verificationCommands: stack.
|
|
128
|
+
verificationCommands: stack.verificationCommands.map(({ label, argv }) => ({ label, argv })),
|
|
129
129
|
warnings: [
|
|
130
130
|
...(program.diagnostic ? [program.diagnostic] : []),
|
|
131
131
|
...projectSkills.diagnostics,
|
package/src/index/contracts.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export const GENESIS_CONTRACTS = Object.freeze({
|
|
2
|
-
deployment: 'genesis.deployment.v2',
|
|
3
2
|
derivedArtifacts: 'genesis.derived-artifacts.v1',
|
|
4
3
|
environment: 'genesis.environment.v1',
|
|
5
|
-
|
|
4
|
+
stackSection: 'genesis.stack-section.v1',
|
|
6
5
|
verification: 'genesis.verification.v1',
|
|
7
|
-
workspaceSetup: 'genesis.workspace-setup.v1',
|
|
8
6
|
});
|
|
@@ -9,7 +9,7 @@ const SCHEMA_VERSION = 1;
|
|
|
9
9
|
const HASH = /^sha256:[0-9a-f]{64}$/u;
|
|
10
10
|
|
|
11
11
|
function commandRecords(stack) {
|
|
12
|
-
return stack.
|
|
12
|
+
return stack.verificationCommands.map(({ label, argv }) => ({ label, argv }));
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async function codebaseHash(projectRoot) {
|
|
@@ -52,7 +52,7 @@ async function readEvidence(projectRoot) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export async function inspectVerification({ projectRoot, stack }) {
|
|
55
|
-
if (stack.
|
|
55
|
+
if (stack.verificationCommands.length === 0) return { status: 'unconfigured', evidence: null };
|
|
56
56
|
const saved = await readEvidence(projectRoot);
|
|
57
57
|
if (saved.status !== 'recorded') return saved;
|
|
58
58
|
const expected = {
|
package/src/index/prompt.js
CHANGED
|
@@ -390,7 +390,7 @@ export async function generateProjectPrompt({
|
|
|
390
390
|
resourceInputs: missing.length > 0
|
|
391
391
|
? { status: 'missing', diagnostics: missing }
|
|
392
392
|
: { status: 'present' },
|
|
393
|
-
verificationCommands: stack.
|
|
393
|
+
verificationCommands: stack.verificationCommands.map(({ label, argv }) => ({ label, argv })),
|
|
394
394
|
agentSkills: { status: projectSkills.status },
|
|
395
395
|
codeIndex: codeIndexContext(index),
|
|
396
396
|
};
|
|
@@ -152,8 +152,7 @@ export async function listStackCatalogPieces(options = {}) {
|
|
|
152
152
|
cityRegions: piece.cityRegions,
|
|
153
153
|
indexers: piece.indexers,
|
|
154
154
|
environmentDefaults: piece.environmentDefaults,
|
|
155
|
-
|
|
156
|
-
workspaceSetup: piece.workspaceSetupSteps,
|
|
155
|
+
extensions: piece.extensions.map(({ name }) => name),
|
|
157
156
|
skill: piece.skill === null ? null : piece.skill.path,
|
|
158
157
|
stackPackage: piece.stackPackage,
|
|
159
158
|
resources: piece.resources.map(({ id }) => id),
|
package/src/index/stack-piece.js
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { GenesisError } from './errors.js';
|
|
2
|
-
import { parseStackCommandLines } from './stack-command.js';
|
|
3
2
|
import { parseStackCityPresentationLines } from './stack-city-presentation.js';
|
|
4
|
-
import { parseStackDeploymentLines } from './stack-deployment.js';
|
|
5
3
|
import { parseStackEnvironmentDefaultLines } from './stack-environment-defaults.js';
|
|
6
4
|
import { parseStackEnvironmentFileLines } from './stack-environment-files.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { opaqueStackSections } from './stack-section.js';
|
|
6
|
+
import { parseStackVerificationLines } from './stack-verification.js';
|
|
9
7
|
import { normalizeSource } from './utils.js';
|
|
10
8
|
|
|
11
9
|
const STACK_PIECE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
12
10
|
const STACK_CUSTOMIZATION_TITLE = /^# Stack customization: ([a-z0-9]+(?:-[a-z0-9]+)*)\s*$/u;
|
|
13
11
|
const STACK_CUSTOMIZATION_FIELDS = new Set(['Description', 'Guidance', 'Adoption', 'Deslop']);
|
|
12
|
+
const STACK_PIECE_SECTIONS = new Set([
|
|
13
|
+
'Adoption',
|
|
14
|
+
'City regions',
|
|
15
|
+
'Conflicts',
|
|
16
|
+
'Description',
|
|
17
|
+
'Deslop',
|
|
18
|
+
'Environment defaults',
|
|
19
|
+
'Environment files',
|
|
20
|
+
'Guidance',
|
|
21
|
+
'Indexers',
|
|
22
|
+
'Requires',
|
|
23
|
+
'Resources',
|
|
24
|
+
'Skill',
|
|
25
|
+
'Verification',
|
|
26
|
+
]);
|
|
14
27
|
|
|
15
28
|
export function normalizeStackPieceId(value, {
|
|
16
29
|
code = 'STACK_PIECE_INVALID',
|
|
@@ -214,7 +227,8 @@ function resources(all, piecePath) {
|
|
|
214
227
|
|
|
215
228
|
/**
|
|
216
229
|
* Stack pieces are guidance, not a second programming language. Genesis reads
|
|
217
|
-
* only
|
|
230
|
+
* only its own small grammar and preserves every other section as opaque data
|
|
231
|
+
* for the consumer that named it.
|
|
218
232
|
*/
|
|
219
233
|
export function parseStackPieceSource(value, {
|
|
220
234
|
expectedId,
|
|
@@ -231,7 +245,10 @@ export function parseStackPieceSource(value, {
|
|
|
231
245
|
const adoption = textSection(all, 'Adoption');
|
|
232
246
|
const deslop = textSection(all, 'Deslop');
|
|
233
247
|
if (!description) invalid(piecePath, 'Stack piece needs Description.');
|
|
234
|
-
const
|
|
248
|
+
const verificationCommands = parseStackVerificationLines(
|
|
249
|
+
all.get('Verification') || [],
|
|
250
|
+
{ path: piecePath },
|
|
251
|
+
);
|
|
235
252
|
const cityPresentation = parseStackCityPresentationLines(
|
|
236
253
|
all.has('City regions') ? all.get('City regions') : undefined,
|
|
237
254
|
{ path: piecePath },
|
|
@@ -244,17 +261,6 @@ export function parseStackPieceSource(value, {
|
|
|
244
261
|
all.has('Environment files') ? all.get('Environment files') : undefined,
|
|
245
262
|
{ path: piecePath },
|
|
246
263
|
);
|
|
247
|
-
const launch = parseStackLaunchLines(all.has('Launch') ? all.get('Launch') : undefined, {
|
|
248
|
-
path: piecePath,
|
|
249
|
-
});
|
|
250
|
-
const deployment = parseStackDeploymentLines(
|
|
251
|
-
all.has('Deployment') ? all.get('Deployment') : undefined,
|
|
252
|
-
{ path: piecePath },
|
|
253
|
-
);
|
|
254
|
-
const workspaceSetup = parseStackWorkspaceSetupLines(
|
|
255
|
-
all.has('Workspace setup') ? all.get('Workspace setup') : undefined,
|
|
256
|
-
{ path: piecePath },
|
|
257
|
-
);
|
|
258
264
|
return {
|
|
259
265
|
id,
|
|
260
266
|
description,
|
|
@@ -265,14 +271,12 @@ export function parseStackPieceSource(value, {
|
|
|
265
271
|
indexers: idList(all, 'Indexers', piecePath),
|
|
266
272
|
skill: skill(all, piecePath),
|
|
267
273
|
resources: resources(all, piecePath),
|
|
268
|
-
|
|
274
|
+
verificationCommands: verificationCommands.map(({ line: _line, ...command }) => command),
|
|
269
275
|
cityExclusions: cityPresentation.exclusions,
|
|
270
276
|
cityRegions: cityPresentation.regions,
|
|
271
277
|
environmentDefaults,
|
|
272
278
|
environmentFiles: environmentFiles || [],
|
|
273
|
-
|
|
274
|
-
deployment,
|
|
275
|
-
workspaceSetupSteps: workspaceSetup || [],
|
|
279
|
+
extensions: opaqueStackSections(all, STACK_PIECE_SECTIONS),
|
|
276
280
|
deslop,
|
|
277
281
|
};
|
|
278
282
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GENESIS_CONTRACTS } from './contracts.js';
|
|
2
|
+
import { gitContext } from './git.js';
|
|
3
|
+
import { readStack } from './stack.js';
|
|
4
|
+
import { normalizeStackSectionName } from './stack-section.js';
|
|
5
|
+
import { sha256, stableJson } from './utils.js';
|
|
6
|
+
|
|
7
|
+
/** Inspect one consumer-owned Stack section without interpreting its contents. */
|
|
8
|
+
export async function inspectProjectStackSection({
|
|
9
|
+
name,
|
|
10
|
+
projectRoot,
|
|
11
|
+
stackPackages = [],
|
|
12
|
+
} = {}) {
|
|
13
|
+
const sectionName = normalizeStackSectionName(name);
|
|
14
|
+
const root = (await gitContext(projectRoot)).repositoryRoot;
|
|
15
|
+
const stack = await readStack(root, { stackPackages });
|
|
16
|
+
const section = stack.extensions.find((candidate) => candidate.name === sectionName);
|
|
17
|
+
const diagnostics = [...(section?.diagnostics || [])];
|
|
18
|
+
const status = diagnostics.length > 0 ? 'blocked' : section ? 'ready' : 'unconfigured';
|
|
19
|
+
const lines = section ? [...section.lines] : [];
|
|
20
|
+
return {
|
|
21
|
+
contract: GENESIS_CONTRACTS.stackSection,
|
|
22
|
+
status,
|
|
23
|
+
stackHash: stack.identityHash,
|
|
24
|
+
sectionHash: status === 'ready' ? sha256(stableJson({ name: sectionName, lines })) : '',
|
|
25
|
+
components: stack.components.map(({ id }) => id),
|
|
26
|
+
name: sectionName,
|
|
27
|
+
source: section?.source || null,
|
|
28
|
+
content: lines.join('\n'),
|
|
29
|
+
lines,
|
|
30
|
+
diagnostics,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
2
|
+
|
|
3
|
+
export function trimStackSectionLines(lines = []) {
|
|
4
|
+
const result = [...lines];
|
|
5
|
+
while (result[0]?.trim() === '') result.shift();
|
|
6
|
+
while (result.at(-1)?.trim() === '') result.pop();
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function normalizeStackSectionName(value, {
|
|
11
|
+
code = 'STACK_SECTION_INVALID',
|
|
12
|
+
field = 'section',
|
|
13
|
+
} = {}) {
|
|
14
|
+
const name = String(value ?? '').trim();
|
|
15
|
+
if (!name || /[\0\r\n]/u.test(name) || /^#/u.test(name)) {
|
|
16
|
+
throw new GenesisError(code, 'Stack section names must be non-empty single lines.', {
|
|
17
|
+
[field]: value,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return name;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function opaqueStackSections(sections, knownNames) {
|
|
24
|
+
return [...sections.entries()]
|
|
25
|
+
.filter(([name]) => !knownNames.has(name))
|
|
26
|
+
.map(([name, lines]) => ({
|
|
27
|
+
name: normalizeStackSectionName(name),
|
|
28
|
+
lines: trimStackSectionLines(lines),
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compose consumer-owned Stack sections without interpreting their contents.
|
|
34
|
+
* A project declaration replaces component defaults; otherwise exactly one
|
|
35
|
+
* selected component may provide a section with a given name.
|
|
36
|
+
*/
|
|
37
|
+
export function composeStackSections(components = [], projectSections = []) {
|
|
38
|
+
const projectByName = new Map(projectSections.map((section) => [section.name, section]));
|
|
39
|
+
const componentByName = new Map();
|
|
40
|
+
for (const component of components) {
|
|
41
|
+
for (const section of component.extensions || []) {
|
|
42
|
+
const declarations = componentByName.get(section.name) || [];
|
|
43
|
+
declarations.push({
|
|
44
|
+
...section,
|
|
45
|
+
source: `component:${component.id}`,
|
|
46
|
+
});
|
|
47
|
+
componentByName.set(section.name, declarations);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const names = [...new Set([...projectByName.keys(), ...componentByName.keys()])].sort();
|
|
51
|
+
return names.map((name) => {
|
|
52
|
+
const project = projectByName.get(name);
|
|
53
|
+
if (project) {
|
|
54
|
+
return {
|
|
55
|
+
name,
|
|
56
|
+
lines: [...project.lines],
|
|
57
|
+
source: 'project',
|
|
58
|
+
diagnostics: [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const declarations = componentByName.get(name) || [];
|
|
62
|
+
if (declarations.length === 1) {
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
lines: [...declarations[0].lines],
|
|
66
|
+
source: declarations[0].source,
|
|
67
|
+
diagnostics: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const sources = declarations.map(({ source }) => source).sort();
|
|
71
|
+
return {
|
|
72
|
+
name,
|
|
73
|
+
lines: [],
|
|
74
|
+
source: null,
|
|
75
|
+
diagnostics: [{
|
|
76
|
+
code: 'STACK_SECTION_AMBIGUOUS',
|
|
77
|
+
message: `Selected Stack components provide competing \`${name}\` sections: ${sources.join(', ')}. Add one project \`## ${name}\` section to select the exact declaration.`,
|
|
78
|
+
details: { name, sources },
|
|
79
|
+
}],
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|