genesis-compiler 1.1.0 → 1.2.0
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 +23 -3
- package/docs/prompt-integration.md +7 -1
- package/docs/stack-components.md +51 -16
- package/package.json +2 -1
- package/plugins/genesis/.codex-plugin/plugin.json +1 -1
- package/prompts/start.txt +30 -0
- package/prompts/work.txt +7 -0
- package/src/cli.js +1 -1
- package/src/index/assets.js +1 -0
- package/src/index/environment-files.js +19 -0
- package/src/index/process.js +13 -2
- package/src/index/prompt.js +110 -2
- package/src/index/stack-environment-files.js +83 -0
- package/src/index/stack-piece.js +6 -0
- package/src/index/stack-workspace-setup.js +15 -3
- package/src/index/stack.js +31 -1
- package/src/index/workspace-setup.js +28 -3
- package/src/index.js +5 -0
- package/stacks/pieces/jskit.md +5 -1
package/README.md
CHANGED
|
@@ -120,7 +120,9 @@ Hosts that create dependency-empty workspaces can call the public
|
|
|
120
120
|
`inspectWorkspaceSetup()` API. A concrete Stack piece may declare one exact,
|
|
121
121
|
ordered `## Workspace setup` recipe with labels, argument arrays expressed as
|
|
122
122
|
backticked tokens, abstract runtimes, and optional project-relative working
|
|
123
|
-
directories.
|
|
123
|
+
directories and readiness paths. A recipe whose declared project marker does
|
|
124
|
+
not exist remains unconfigured until a later inspection. The built-in `jskit`
|
|
125
|
+
piece waits for `package.json` and then declares `npm install`; generic
|
|
124
126
|
`nodejs` and `php` deliberately declare no installer. One project section in
|
|
125
127
|
`genesis/stack.md` replaces all component defaults, including with
|
|
126
128
|
`- Nothing.`. Without that override, exactly one component recipe is used;
|
|
@@ -128,6 +130,14 @@ multiple component recipes are reported as ambiguous and never merged.
|
|
|
128
130
|
Genesis does not run the recipe. Stack resources such as database variables do
|
|
129
131
|
not gate dependency installation.
|
|
130
132
|
|
|
133
|
+
Hosts that manage project environments can call `inspectEnvironment()`.
|
|
134
|
+
An optional readable `## Environment files` section declares safe
|
|
135
|
+
project-relative dotenv projections such as `.env`. A project declaration
|
|
136
|
+
replaces component defaults, including with `- Nothing.`; otherwise component
|
|
137
|
+
paths compose. Genesis reports resource declarations, value-free missing-input
|
|
138
|
+
diagnostics, and paths. It never returns values, writes files, stores secrets,
|
|
139
|
+
or decides which resolved values a host should materialize.
|
|
140
|
+
|
|
131
141
|
Hosts that provide previews can call the public `inspectLaunch()` API. An
|
|
132
142
|
optional readable `## Launch` section in `genesis/stack.md` declares labeled
|
|
133
143
|
targets, exact backticked argument tokens, relative working directories,
|
|
@@ -247,6 +257,7 @@ genesis prompt "Add filtering by note title"
|
|
|
247
257
|
|
|
248
258
|
```bash
|
|
249
259
|
genesis prompt # implementation work
|
|
260
|
+
genesis prompt --task start # open a new or existing project conversation
|
|
250
261
|
genesis prompt "Add filtering by note title"
|
|
251
262
|
genesis prompt --task deslop # explicit cleanup
|
|
252
263
|
genesis prompt --task deslop "Only review billing"
|
|
@@ -256,8 +267,12 @@ genesis prompt --task describe # create/refresh Blueprint a
|
|
|
256
267
|
genesis prompt --task review # compare intent, code, and explanation
|
|
257
268
|
```
|
|
258
269
|
|
|
259
|
-
The
|
|
270
|
+
The seven task types are deliberately explicit:
|
|
260
271
|
|
|
272
|
+
- `start` opens the project conversation. For a new project it asks what the
|
|
273
|
+
app is about, presents relevant choices from the live Stack catalog, and
|
|
274
|
+
waits for the person to select technology. For an existing project it asks
|
|
275
|
+
what the person wants to understand or change.
|
|
261
276
|
- `work` implements the Blueprint and optional request using current code,
|
|
262
277
|
Program, selected Stack context, and progressively loaded Agent Skills.
|
|
263
278
|
- `deslop` requests cleanup explicitly. Codex projects also receive a bounded
|
|
@@ -375,6 +390,7 @@ import {
|
|
|
375
390
|
getContext,
|
|
376
391
|
indexCodebase,
|
|
377
392
|
initialize,
|
|
393
|
+
inspectEnvironment,
|
|
378
394
|
inspectLaunch,
|
|
379
395
|
inspectWorkspaceSetup,
|
|
380
396
|
installCodex,
|
|
@@ -394,7 +410,11 @@ detailed Machine City and simple Program City projections.
|
|
|
394
410
|
`inspectWorkspaceSetup()` returns one normalized, shell-free workspace
|
|
395
411
|
preparation recipe—or an explicit unconfigured or ambiguous result—for a host
|
|
396
412
|
to execute with its own pinned runtimes, caches, process isolation, and
|
|
397
|
-
credentials.
|
|
413
|
+
credentials. A configured recipe may remain unconfigured while it waits for a
|
|
414
|
+
technology-declared project marker.
|
|
415
|
+
`inspectEnvironment()` returns normalized Stack resource declarations,
|
|
416
|
+
value-free missing-input diagnostics, and dotenv projection paths without
|
|
417
|
+
returning any environment value.
|
|
398
418
|
`inspectLaunch()` returns a validated, normalized Stack launch declaration for
|
|
399
419
|
a host to execute with its own runtime, port, process, readiness, preview
|
|
400
420
|
identity, secret, and browser policy.
|
|
@@ -15,7 +15,13 @@ const work = await generatePrompt({
|
|
|
15
15
|
await currentAgent.send(work.prompt);
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Tasks are `work`, `deslop`, `program`, `blueprint`, `describe`, and `review`.
|
|
18
|
+
Tasks are `start`, `work`, `deslop`, `program`, `blueprint`, `describe`, and `review`.
|
|
19
|
+
`start` is the host-independent first conversation: it classifies an initialized
|
|
20
|
+
project from current Genesis facts, asks what a new app is about, exposes the
|
|
21
|
+
live Stack catalog for an explicit user choice, or asks what the person wants
|
|
22
|
+
to do with an existing project. For an existing uninitialized codebase it owns
|
|
23
|
+
the adoption recommendation as part of the same portable prompt. A host such as Vibe64 sends this prompt on the
|
|
24
|
+
first chat turn; it does not recreate the questions or Stack catalog.
|
|
19
25
|
`describe` creates or refreshes the complete Blueprint and useful Program in one
|
|
20
26
|
agent task. The host owns the agent and its interaction. Genesis owns only
|
|
21
27
|
transparent prompt construction and deterministic project operations.
|
package/docs/stack-components.md
CHANGED
|
@@ -43,6 +43,10 @@ What this component contributes.
|
|
|
43
43
|
- Concise supplemental guidance used during implementation, explanation,
|
|
44
44
|
review, and cleanup.
|
|
45
45
|
|
|
46
|
+
## Environment files
|
|
47
|
+
|
|
48
|
+
- Dotenv `.env`
|
|
49
|
+
|
|
46
50
|
## Workspace setup
|
|
47
51
|
|
|
48
52
|
- Prepare `Install dependencies` with `example-runtime`: `example-install` `--locked`
|
|
@@ -99,13 +103,17 @@ through `genesis verify`.
|
|
|
99
103
|
fresh workspace. Each entry uses this readable, shell-free form:
|
|
100
104
|
|
|
101
105
|
```markdown
|
|
102
|
-
- Prepare `label` with `runtime` `another-runtime` in `optional/workdir
|
|
106
|
+
- Prepare `label` with `runtime` `another-runtime` in `optional/workdir` when `optional/marker` exists: `command` `argument`
|
|
103
107
|
```
|
|
104
108
|
|
|
105
109
|
Every command and argument is a separate backticked value and is returned as
|
|
106
110
|
an `argv` array. Runtime names are opaque technology ids. Omitted `in` means
|
|
107
111
|
the project root; otherwise the working directory must be canonical and
|
|
108
|
-
project-relative.
|
|
112
|
+
project-relative. The optional `when` path is also canonical and
|
|
113
|
+
project-relative. Until it exists, inspection reports the recipe as
|
|
114
|
+
`unconfigured`; this lets a host safely re-inspect after an agent creates a
|
|
115
|
+
technology substrate without knowing that technology's filenames. `- Nothing.`
|
|
116
|
+
is valid only as the entire section and lets a
|
|
109
117
|
project explicitly disable component setup. Genesis validates and reports the
|
|
110
118
|
recipe but never chooses a runtime, runs a command, installs a dependency, or
|
|
111
119
|
infers a package manager. Generic language pieces such as `nodejs` and `php`
|
|
@@ -123,6 +131,15 @@ Precedence is intentionally small and deterministic:
|
|
|
123
131
|
General Stack resources do not gate Workspace setup. In particular, missing
|
|
124
132
|
database variables do not prevent an otherwise independent dependency install.
|
|
125
133
|
|
|
134
|
+
`Environment files` optionally declares project-relative dotenv projections a
|
|
135
|
+
host may materialize from its own resolved project environment. Genesis
|
|
136
|
+
validates and returns only the format and path; it never reads environment
|
|
137
|
+
values, writes a file, stores a secret, or chooses which variables belong in a
|
|
138
|
+
projection. A project `## Environment files` section in `genesis/stack.md`
|
|
139
|
+
replaces component declarations, including with `- Nothing.`. Without a
|
|
140
|
+
project section, component declarations compose and identical paths collapse.
|
|
141
|
+
The declaration is useful independently of Launch and Workspace setup.
|
|
142
|
+
|
|
126
143
|
`Launch` is an optional readable declarative recipe for hosts that need to
|
|
127
144
|
offer a preview without guessing how a concrete technology starts. Repeat a
|
|
128
145
|
``### Target `id`: label`` block to declare several targets. Default, Workdir,
|
|
@@ -211,25 +228,40 @@ Replacement component description.
|
|
|
211
228
|
|
|
212
229
|
Each field is optional. `Override` replaces only that installed field; `Add`
|
|
213
230
|
then follows the installed or overridden value. These files cannot alter
|
|
214
|
-
dependencies, conflicts, Agent Skill source, indexers, resources,
|
|
215
|
-
Workspace setup, or Launch declarations.
|
|
231
|
+
dependencies, conflicts, Agent Skill source, indexers, resources, environment
|
|
232
|
+
files, commands, Workspace setup, or Launch declarations.
|
|
216
233
|
`genesis stack add` preserves the files and composes their effective prose after
|
|
217
234
|
dependency resolution.
|
|
218
235
|
|
|
219
236
|
A project may select component ids in `genesis/stack.md`, replace component
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
237
|
+
environment-file declarations, replace component Workspace setup defaults
|
|
238
|
+
with one project recipe, replace component Verify defaults with its own
|
|
239
|
+
`## Commands`, and replace every component Launch target with one project
|
|
240
|
+
`## Launch` contract. With no project Launch section, selected component
|
|
241
|
+
targets compose; duplicate ids or multiple defaults are rejected rather than
|
|
242
|
+
guessed. `genesis stack add` preserves existing project Environment files,
|
|
243
|
+
Workspace setup, and Launch sections. Adding another language, framework,
|
|
244
|
+
database, or integration is therefore an isolated Stack/indexer contribution
|
|
245
|
+
rather than a prompt or controller special case.
|
|
228
246
|
|
|
229
247
|
Description, Guidance, Agent Skill, and Deslop prose are instructions, not
|
|
230
248
|
verification evidence. Changes to them do not stale an exact successful
|
|
231
|
-
verification record; selected component ids, resources,
|
|
232
|
-
launch targets, and verification commands remain part of the
|
|
249
|
+
verification record; selected component ids, resources, environment files,
|
|
250
|
+
Workspace setup, launch targets, and verification commands remain part of the
|
|
251
|
+
Stack identity.
|
|
252
|
+
|
|
253
|
+
Hosts inspect normalized environment requirements independently:
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
import { inspectEnvironment } from 'genesis-compiler';
|
|
257
|
+
|
|
258
|
+
const projectEnvironment = await inspectEnvironment({ environment, projectRoot });
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The result has `status` (`ready`, `missing-inputs`, or `unconfigured`),
|
|
262
|
+
`stackHash`, selected `components`, declared `resources`, value-free
|
|
263
|
+
`diagnostics`, and normalized `files`. Each file contains `format`, `path`, and
|
|
264
|
+
`source`. Environment values never cross this API boundary.
|
|
233
265
|
|
|
234
266
|
Fresh-workspace hosts read setup from the package root:
|
|
235
267
|
|
|
@@ -242,8 +274,11 @@ const setup = await inspectWorkspaceSetup({ projectRoot, environment });
|
|
|
242
274
|
The setup result has `status` (`ready`, `blocked`, or `unconfigured`),
|
|
243
275
|
`stackHash`, an exact ready-recipe `recipeHash`, selected `components`, its `source`, distinct
|
|
244
276
|
`runtimeRequirements`, normalized ordered `steps`, and `diagnostics`. Each step
|
|
245
|
-
contains `label`, `argv`, `runtimeRequirements`,
|
|
246
|
-
component recipes conflict, not that
|
|
277
|
+
contains `label`, `argv`, `runtimeRequirements`, `workdir`, and an optional
|
|
278
|
+
`readyWhen` project path. `blocked` means component recipes conflict, not that
|
|
279
|
+
an unrelated Stack resource is absent. An absent `readyWhen` marker produces
|
|
280
|
+
the unconfigured diagnostic `STACK_WORKSPACE_SETUP_WAITING`, never a failed or
|
|
281
|
+
blocked result.
|
|
247
282
|
The optional environment object is accepted for a consistent host inspection
|
|
248
283
|
boundary, but Workspace setup v1 neither reads nor returns its values.
|
|
249
284
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genesis-compiler",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"prompts/program.txt",
|
|
34
34
|
"prompts/reconcile.txt",
|
|
35
35
|
"prompts/review.txt",
|
|
36
|
+
"prompts/start.txt",
|
|
36
37
|
"prompts/work.txt",
|
|
37
38
|
"plugins/genesis",
|
|
38
39
|
"skills",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Read and follow `.agents/skills/genesis-project/SKILL.md` completely.
|
|
2
|
+
|
|
3
|
+
This is the project-opening conversation. Do not begin implementation until
|
|
4
|
+
the product direction and any technology choice needed for the first useful
|
|
5
|
+
step are clear.
|
|
6
|
+
|
|
7
|
+
When `projectKind` is `new`, first ask what the app is about. Ask up to three
|
|
8
|
+
concise, high-impact questions at a time. Use `availableStackPieces` to present
|
|
9
|
+
only the relevant choices, explain them in product language, and let the user
|
|
10
|
+
decide. Never silently select a technology. After an explicit choice, use
|
|
11
|
+
`genesis stack add <piece...>`; do not edit `genesis/stack.md` by hand. Establish
|
|
12
|
+
the non-technical Blueprint from the user's answers before implementation.
|
|
13
|
+
If the product direction becomes clear while Stack is still empty, do not end
|
|
14
|
+
the turn after only writing or summarizing the Blueprint: present the relevant
|
|
15
|
+
Stack choices in that same turn and ask the user to select one explicitly.
|
|
16
|
+
|
|
17
|
+
When `projectKind` is `existing`, ask what the user wants to understand or
|
|
18
|
+
change. Use the current Blueprint, Stack, Program, and code index when present.
|
|
19
|
+
If the existing implementation is not yet explained, recommend the Genesis
|
|
20
|
+
description/adoption path instead of pretending its intent is known.
|
|
21
|
+
|
|
22
|
+
When `projectKind` is `existing-uninitialized`, ask what the user wants to
|
|
23
|
+
understand or change and strongly recommend `genesis adopt` before substantial
|
|
24
|
+
work. Explain that adoption preserves the implementation and prepares the
|
|
25
|
+
Blueprint and Program prompt. Do not run it without the user's approval. If the
|
|
26
|
+
user declines, continue normally and do not repeat the recommendation.
|
|
27
|
+
|
|
28
|
+
If the request already contains an unambiguous product description and an
|
|
29
|
+
explicit Stack choice, you may apply that choice and establish the Blueprint.
|
|
30
|
+
Otherwise this is a conversational turn: ask, listen, and preserve the source.
|
package/prompts/work.txt
CHANGED
|
@@ -19,3 +19,10 @@ constructing a parallel fallback.
|
|
|
19
19
|
|
|
20
20
|
Run focused checks when useful. Summarize files changed, checks actually run,
|
|
21
21
|
and anything still requiring attention.
|
|
22
|
+
|
|
23
|
+
If no Stack components are selected, continue the project-opening
|
|
24
|
+
conversation instead of guessing a technology. Use `genesis stack list` for
|
|
25
|
+
the current catalog, present relevant choices, wait for an explicit decision,
|
|
26
|
+
and add the selection with `genesis stack add <piece...>` before relying on
|
|
27
|
+
technology-specific setup or launch behavior. Do not end after only updating
|
|
28
|
+
or summarizing the Blueprint while the Stack remains empty.
|
package/src/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ const USAGE = `Usage:
|
|
|
31
31
|
genesis context <path...>
|
|
32
32
|
genesis index [function-or-path...]
|
|
33
33
|
genesis prompt [request...]
|
|
34
|
-
genesis prompt --task <work|deslop|program|blueprint|describe|review> [request...]
|
|
34
|
+
genesis prompt --task <start|work|deslop|program|blueprint|describe|review> [request...]
|
|
35
35
|
genesis verify
|
|
36
36
|
genesis check
|
|
37
37
|
|
package/src/index/assets.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
|
|
3
3
|
const root = new URL('../../', import.meta.url);
|
|
4
4
|
const assets = {
|
|
5
|
+
start: new URL('prompts/start.txt', root),
|
|
5
6
|
work: new URL('prompts/work.txt', root),
|
|
6
7
|
describe: new URL('prompts/describe.txt', root),
|
|
7
8
|
reconcile: new URL('prompts/reconcile.txt', root),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { gitContext } from './git.js';
|
|
2
|
+
import { readStack } from './stack.js';
|
|
3
|
+
import { missingStackResources } from './stack-preflight.js';
|
|
4
|
+
|
|
5
|
+
/** Inspect Stack environment requirements without returning or writing values. */
|
|
6
|
+
export async function inspectProjectEnvironment({ environment = process.env, projectRoot } = {}) {
|
|
7
|
+
const root = (await gitContext(projectRoot)).repositoryRoot;
|
|
8
|
+
const stack = await readStack(root);
|
|
9
|
+
const diagnostics = missingStackResources({ environment, resources: stack.resources });
|
|
10
|
+
const configured = stack.environmentFiles.length > 0 || stack.resources.length > 0;
|
|
11
|
+
return {
|
|
12
|
+
status: !configured ? 'unconfigured' : diagnostics.length > 0 ? 'missing-inputs' : 'ready',
|
|
13
|
+
stackHash: stack.identityHash,
|
|
14
|
+
components: stack.components.map(({ id }) => id),
|
|
15
|
+
files: stack.environmentFiles,
|
|
16
|
+
resources: stack.resources,
|
|
17
|
+
diagnostics,
|
|
18
|
+
};
|
|
19
|
+
}
|
package/src/index/process.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { promisify } from 'node:util';
|
|
3
2
|
|
|
4
3
|
import { GenesisError } from './errors.js';
|
|
5
4
|
|
|
6
5
|
const MAX_DIAGNOSTIC_OUTPUT = 16_384;
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
function executeFile(command, args, options) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const child = execFile(command, args, options, (error, stdout, stderr) => {
|
|
10
|
+
if (error) {
|
|
11
|
+
reject(error);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
resolve({ stdout, stderr });
|
|
15
|
+
});
|
|
16
|
+
child.stdin?.end();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
function boundedDiagnosticOutput(buffer) {
|
|
10
21
|
const source = buffer.toString('utf8');
|
package/src/index/prompt.js
CHANGED
|
@@ -3,15 +3,19 @@ import { inspectProjectSkills, renderAgentSkillCatalog } from './agent-skills.js
|
|
|
3
3
|
import { buildProjectIndex, MACHINE_CITY_PATH, PROGRAM_CITY_PATH } from './code-index.js';
|
|
4
4
|
import { BLUEPRINT_SKELETON_SOURCE, readBlueprint } from './blueprint.js';
|
|
5
5
|
import { readStack, stackPromptContext } from './stack.js';
|
|
6
|
+
import { listBuiltinStackPieces } from './stack-catalog.js';
|
|
6
7
|
import { GenesisError } from './errors.js';
|
|
7
8
|
import { gitContext } from './git.js';
|
|
8
9
|
import { inspectProgram } from './program.js';
|
|
9
10
|
import { inspectVerification } from './project-state.js';
|
|
10
11
|
import { missingStackResources } from './stack-preflight.js';
|
|
11
12
|
import { stableJson } from './utils.js';
|
|
13
|
+
import { gitVisibleFileStates } from './project-files.js';
|
|
14
|
+
import { isProjectContentPath } from './paths.js';
|
|
12
15
|
|
|
13
|
-
const TASKS = new Set(['work', 'deslop', 'program', 'blueprint', 'describe', 'review']);
|
|
16
|
+
const TASKS = new Set(['start', 'work', 'deslop', 'program', 'blueprint', 'describe', 'review']);
|
|
14
17
|
const DEFAULT_REQUEST = {
|
|
18
|
+
start: 'Start a conversation about this project.',
|
|
15
19
|
work: 'Implement the product intent expressed by the current Blueprint.',
|
|
16
20
|
deslop: 'Simplify the current Git-visible work without making unrelated changes.',
|
|
17
21
|
program: 'Refresh the complete useful Program for the code that exists now.',
|
|
@@ -19,6 +23,15 @@ const DEFAULT_REQUEST = {
|
|
|
19
23
|
review: 'Review the complete useful relationship between Blueprint, code, Program, and tests.',
|
|
20
24
|
};
|
|
21
25
|
|
|
26
|
+
function stackCatalogContext(pieces = []) {
|
|
27
|
+
return pieces.map(({ id, description, requires, conflicts }) => ({
|
|
28
|
+
id,
|
|
29
|
+
description,
|
|
30
|
+
requires,
|
|
31
|
+
conflicts,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
function requestText(value, task) {
|
|
23
36
|
const request = String(value ?? '').trim();
|
|
24
37
|
if (Buffer.byteLength(request, 'utf8') > 64 * 1024) {
|
|
@@ -142,6 +155,71 @@ async function generateExplanationPrompt({ instructions, program, request, root,
|
|
|
142
155
|
};
|
|
143
156
|
}
|
|
144
157
|
|
|
158
|
+
async function generateStartPrompt({ instructions, program, request, root }) {
|
|
159
|
+
let blueprint;
|
|
160
|
+
try {
|
|
161
|
+
blueprint = await readBlueprint(root, { required: true });
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error?.code !== 'BLUEPRINT_REQUIRED') throw error;
|
|
164
|
+
const files = await gitVisibleFileStates(root, { includePath: isProjectContentPath });
|
|
165
|
+
const existing = [...files.values()].some((state) => state.exists);
|
|
166
|
+
if (!existing) throw error;
|
|
167
|
+
return {
|
|
168
|
+
status: 'ready',
|
|
169
|
+
task: 'start',
|
|
170
|
+
prompt: renderPrompt({
|
|
171
|
+
instructions,
|
|
172
|
+
request,
|
|
173
|
+
context: {
|
|
174
|
+
task: 'start',
|
|
175
|
+
projectRoot: root,
|
|
176
|
+
projectKind: 'existing-uninitialized',
|
|
177
|
+
genesis: { initialized: false },
|
|
178
|
+
program: programContext(program),
|
|
179
|
+
},
|
|
180
|
+
}),
|
|
181
|
+
warnings: program.diagnostic ? [program.diagnostic] : [],
|
|
182
|
+
verificationCommands: [],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const [stack, index, catalog] = await Promise.all([
|
|
186
|
+
readStack(root),
|
|
187
|
+
buildProjectIndex({ projectRoot: root, write: false }),
|
|
188
|
+
listBuiltinStackPieces(),
|
|
189
|
+
]);
|
|
190
|
+
const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
|
|
191
|
+
const newProject = stack.components.length === 0 && index.fileCount === 0;
|
|
192
|
+
return {
|
|
193
|
+
status: 'ready',
|
|
194
|
+
task: 'start',
|
|
195
|
+
prompt: renderPrompt({
|
|
196
|
+
instructions,
|
|
197
|
+
request,
|
|
198
|
+
context: {
|
|
199
|
+
task: 'start',
|
|
200
|
+
projectRoot: root,
|
|
201
|
+
projectKind: newProject ? 'new' : 'existing',
|
|
202
|
+
blueprint: {
|
|
203
|
+
path: blueprint.path,
|
|
204
|
+
description: blueprint.description,
|
|
205
|
+
},
|
|
206
|
+
stack: stackPromptContext(stack),
|
|
207
|
+
availableStackPieces: stackCatalogContext(catalog),
|
|
208
|
+
program: programContext(program),
|
|
209
|
+
codeIndex: codeIndexContext(index),
|
|
210
|
+
},
|
|
211
|
+
guidance: stack.guidance,
|
|
212
|
+
skills: renderAgentSkillCatalog(projectSkills.skills),
|
|
213
|
+
}),
|
|
214
|
+
warnings: [
|
|
215
|
+
...(program.diagnostic ? [program.diagnostic] : []),
|
|
216
|
+
...projectSkills.diagnostics,
|
|
217
|
+
...index.diagnostics,
|
|
218
|
+
],
|
|
219
|
+
verificationCommands: [],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
145
223
|
export async function generateProjectPrompt({
|
|
146
224
|
environment = process.env,
|
|
147
225
|
projectRoot,
|
|
@@ -181,6 +259,36 @@ export async function generateProjectPrompt({
|
|
|
181
259
|
}
|
|
182
260
|
|
|
183
261
|
const program = await observeProgram(root);
|
|
262
|
+
if (task === 'start') {
|
|
263
|
+
return generateStartPrompt({
|
|
264
|
+
instructions,
|
|
265
|
+
program,
|
|
266
|
+
request: userRequest,
|
|
267
|
+
root,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
let stack;
|
|
271
|
+
if (task === 'work') {
|
|
272
|
+
try {
|
|
273
|
+
stack = await readStack(root);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (error?.code !== 'STACK_REQUIRED') throw error;
|
|
276
|
+
return generateStartPrompt({
|
|
277
|
+
instructions: await readInstalledAsset('start'),
|
|
278
|
+
program,
|
|
279
|
+
request: userRequest,
|
|
280
|
+
root,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (stack.components.length === 0) {
|
|
284
|
+
return generateStartPrompt({
|
|
285
|
+
instructions: await readInstalledAsset('start'),
|
|
286
|
+
program,
|
|
287
|
+
request: userRequest,
|
|
288
|
+
root,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
184
292
|
if (['describe', 'program'].includes(task)) {
|
|
185
293
|
return generateExplanationPrompt({
|
|
186
294
|
instructions,
|
|
@@ -192,7 +300,7 @@ export async function generateProjectPrompt({
|
|
|
192
300
|
}
|
|
193
301
|
|
|
194
302
|
const blueprint = await readBlueprint(root, { required: true, requireDescription: true });
|
|
195
|
-
|
|
303
|
+
stack ||= await readStack(root);
|
|
196
304
|
const [index, projectSkills] = await Promise.all([
|
|
197
305
|
buildProjectIndex({ projectRoot: root, write: false }),
|
|
198
306
|
inspectProjectSkills({ projectRoot: root, stack }),
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
2
|
+
import { isCanonicalProjectWorkdir } from './stack-process.js';
|
|
3
|
+
|
|
4
|
+
const DOTENV_LINE = /^- Dotenv `([^`\r\n]+)`[ \t]*$/u;
|
|
5
|
+
|
|
6
|
+
function invalid(environmentPath, message, line, details = {}) {
|
|
7
|
+
throw new GenesisError('STACK_ENVIRONMENT_FILES_INVALID', message, {
|
|
8
|
+
path: environmentPath,
|
|
9
|
+
...(line === undefined ? {} : { line }),
|
|
10
|
+
...details,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function environmentFilePath(value, environmentPath, line) {
|
|
15
|
+
if (value === '.' || !isCanonicalProjectWorkdir(value)) {
|
|
16
|
+
invalid(
|
|
17
|
+
environmentPath,
|
|
18
|
+
'Environment file paths must be canonical project-relative file paths.',
|
|
19
|
+
line,
|
|
20
|
+
{ observed: value },
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseStackEnvironmentFileLines(lines, {
|
|
27
|
+
path: environmentPath = 'stack environment files',
|
|
28
|
+
} = {}) {
|
|
29
|
+
if (lines === undefined) return null;
|
|
30
|
+
const entries = lines.map((line) => line.trim()).filter(Boolean);
|
|
31
|
+
if (entries.length === 1 && entries[0] === '- Nothing.') return [];
|
|
32
|
+
const files = [];
|
|
33
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
34
|
+
const source = lines[index].trim();
|
|
35
|
+
if (!source) continue;
|
|
36
|
+
const entry = source.match(DOTENV_LINE);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
invalid(
|
|
39
|
+
environmentPath,
|
|
40
|
+
'Every Environment files entry must use `- Dotenv `project-relative-path``.',
|
|
41
|
+
index + 1,
|
|
42
|
+
{ observed: source },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
files.push({
|
|
46
|
+
format: 'dotenv',
|
|
47
|
+
path: environmentFilePath(entry[1], environmentPath, index + 1),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (files.length === 0) {
|
|
51
|
+
invalid(environmentPath, '## Environment files needs at least one Dotenv entry.');
|
|
52
|
+
}
|
|
53
|
+
const paths = files.map((file) => file.path);
|
|
54
|
+
if (new Set(paths).size !== paths.length) {
|
|
55
|
+
invalid(environmentPath, '## Environment files contains a duplicate path.');
|
|
56
|
+
}
|
|
57
|
+
return files;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function composeStackEnvironmentFiles(components, projectFiles) {
|
|
61
|
+
if (projectFiles !== null) {
|
|
62
|
+
return projectFiles.map((file) => ({ ...file, source: 'project' }));
|
|
63
|
+
}
|
|
64
|
+
const files = new Map();
|
|
65
|
+
for (const component of components) {
|
|
66
|
+
for (const file of component.environmentFiles) {
|
|
67
|
+
const existing = files.get(file.path);
|
|
68
|
+
if (existing && existing.format !== file.format) {
|
|
69
|
+
invalid(
|
|
70
|
+
`component:${component.id}`,
|
|
71
|
+
`Stack components declare incompatible environment file formats for ${file.path}.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!existing) {
|
|
75
|
+
files.set(file.path, {
|
|
76
|
+
...file,
|
|
77
|
+
source: `component:${component.id}`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return [...files.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
83
|
+
}
|
package/src/index/stack-piece.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GenesisError } from './errors.js';
|
|
2
2
|
import { parseStackCommandLines } from './stack-command.js';
|
|
3
|
+
import { parseStackEnvironmentFileLines } from './stack-environment-files.js';
|
|
3
4
|
import { parseStackLaunchLines } from './stack-launch.js';
|
|
4
5
|
import { parseStackWorkspaceSetupLines } from './stack-workspace-setup.js';
|
|
5
6
|
import { normalizeSource } from './utils.js';
|
|
@@ -215,6 +216,10 @@ export function parseStackPieceSource(value, {
|
|
|
215
216
|
const deslop = textSection(all, 'Deslop');
|
|
216
217
|
if (!description) invalid(piecePath, 'Stack piece needs Description.');
|
|
217
218
|
const commands = parseStackCommandLines(all.get('Commands') || [], { path: piecePath });
|
|
219
|
+
const environmentFiles = parseStackEnvironmentFileLines(
|
|
220
|
+
all.has('Environment files') ? all.get('Environment files') : undefined,
|
|
221
|
+
{ path: piecePath },
|
|
222
|
+
);
|
|
218
223
|
const launch = parseStackLaunchLines(all.has('Launch') ? all.get('Launch') : undefined, {
|
|
219
224
|
path: piecePath,
|
|
220
225
|
});
|
|
@@ -232,6 +237,7 @@ export function parseStackPieceSource(value, {
|
|
|
232
237
|
skill: skill(all, piecePath),
|
|
233
238
|
resources: resources(all, piecePath),
|
|
234
239
|
commands: commands.map(({ line: _line, ...command }) => command),
|
|
240
|
+
environmentFiles: environmentFiles || [],
|
|
235
241
|
launchTargets: launch?.targets || [],
|
|
236
242
|
workspaceSetupSteps: workspaceSetup || [],
|
|
237
243
|
deslop,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
const PREPARE_LINE = /^- Prepare `([^`\r\n]+)` with (.+?):[ \t]+(.+)$/u;
|
|
9
9
|
const RUNTIME_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
10
|
+
const READY_WHEN_SUFFIX = /^(.*?)[ \t]+when `([^`\r\n]+)` exists$/u;
|
|
10
11
|
const WORKDIR_SUFFIX = /^(.*?)[ \t]+in `([^`\r\n]+)`$/u;
|
|
11
12
|
|
|
12
13
|
function invalid(setupPath, message, line, details = {}) {
|
|
@@ -58,7 +59,7 @@ export function parseStackWorkspaceSetupLines(lines, {
|
|
|
58
59
|
if (!entry) {
|
|
59
60
|
invalid(
|
|
60
61
|
setupPath,
|
|
61
|
-
'Every Workspace setup entry must use `- Prepare `label` with `runtime` [in `workdir`]: `command` `argument`...`.',
|
|
62
|
+
'Every Workspace setup entry must use `- Prepare `label` with `runtime` [in `workdir`] [when `path` exists]: `command` `argument`...`.',
|
|
62
63
|
line,
|
|
63
64
|
{ observed: source },
|
|
64
65
|
);
|
|
@@ -67,8 +68,18 @@ export function parseStackWorkspaceSetupLines(lines, {
|
|
|
67
68
|
if (!label || /[\0\r\n]/u.test(label)) {
|
|
68
69
|
invalid(setupPath, 'Workspace setup labels must be non-empty single lines.', line);
|
|
69
70
|
}
|
|
70
|
-
const
|
|
71
|
-
const
|
|
71
|
+
const readyEntry = entry[2].match(READY_WHEN_SUFFIX);
|
|
72
|
+
const setupSource = readyEntry ? readyEntry[1] : entry[2];
|
|
73
|
+
const readyWhen = readyEntry ? readyEntry[2] : '';
|
|
74
|
+
if (readyWhen && (readyWhen === '.' || !isCanonicalProjectWorkdir(readyWhen))) {
|
|
75
|
+
invalid(
|
|
76
|
+
setupPath,
|
|
77
|
+
'Workspace setup readiness paths must be canonical project-relative paths.',
|
|
78
|
+
line,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const workdirEntry = setupSource.match(WORKDIR_SUFFIX);
|
|
82
|
+
const runtimeSource = workdirEntry ? workdirEntry[1] : setupSource;
|
|
72
83
|
const workdir = workdirEntry ? workdirEntry[2] : '.';
|
|
73
84
|
if (!isCanonicalProjectWorkdir(workdir)) {
|
|
74
85
|
invalid(setupPath, 'Workspace setup workdir must be a canonical project-relative directory.', line);
|
|
@@ -86,6 +97,7 @@ export function parseStackWorkspaceSetupLines(lines, {
|
|
|
86
97
|
argv,
|
|
87
98
|
runtimeRequirements: parseRuntimeRequirements(runtimeSource, setupPath, line),
|
|
88
99
|
workdir,
|
|
100
|
+
...(readyWhen ? { readyWhen } : {}),
|
|
89
101
|
});
|
|
90
102
|
}
|
|
91
103
|
if (steps.length === 0) {
|
package/src/index/stack.js
CHANGED
|
@@ -6,6 +6,10 @@ import { GenesisError } from './errors.js';
|
|
|
6
6
|
import { readBuiltinStackCatalog } from './stack-catalog.js';
|
|
7
7
|
import { parseStackCommandLines } from './stack-command.js';
|
|
8
8
|
import { resolveStackPieces } from './stack-composition.js';
|
|
9
|
+
import {
|
|
10
|
+
composeStackEnvironmentFiles,
|
|
11
|
+
parseStackEnvironmentFileLines,
|
|
12
|
+
} from './stack-environment-files.js';
|
|
9
13
|
import { composeStackLaunchTargets, parseStackLaunchLines } from './stack-launch.js';
|
|
10
14
|
import {
|
|
11
15
|
composeStackWorkspaceSetup,
|
|
@@ -21,7 +25,13 @@ import { sha256, stableJson, writeFileAtomic } from './utils.js';
|
|
|
21
25
|
|
|
22
26
|
const COMPONENT_LINE = /^- `([a-z0-9]+(?:-[a-z0-9]+)*)`$/u;
|
|
23
27
|
export const EMPTY_STACK_SOURCE = '# Stack\n\n## Components\n';
|
|
24
|
-
const STACK_SECTIONS = new Set([
|
|
28
|
+
const STACK_SECTIONS = new Set([
|
|
29
|
+
'Components',
|
|
30
|
+
'Environment files',
|
|
31
|
+
'Workspace setup',
|
|
32
|
+
'Commands',
|
|
33
|
+
'Launch',
|
|
34
|
+
]);
|
|
25
35
|
const STACK_CUSTOMIZATION_ROOT = 'genesis/stack';
|
|
26
36
|
|
|
27
37
|
function parseSections(source) {
|
|
@@ -87,6 +97,7 @@ function withoutOuterBlankLines(lines = []) {
|
|
|
87
97
|
function renderStack({
|
|
88
98
|
commandLines = [],
|
|
89
99
|
componentIds: componentIdsValue,
|
|
100
|
+
environmentFileLines = null,
|
|
90
101
|
launchLines = null,
|
|
91
102
|
workspaceSetupLines = null,
|
|
92
103
|
}) {
|
|
@@ -95,6 +106,9 @@ function renderStack({
|
|
|
95
106
|
'',
|
|
96
107
|
'## Components',
|
|
97
108
|
...componentIdsValue.map((id) => `- \`${id}\``),
|
|
109
|
+
...(environmentFileLines === null
|
|
110
|
+
? []
|
|
111
|
+
: ['', '## Environment files', '', ...withoutOuterBlankLines(environmentFileLines)]),
|
|
98
112
|
...(workspaceSetupLines === null
|
|
99
113
|
? []
|
|
100
114
|
: ['', '## Workspace setup', '', ...withoutOuterBlankLines(workspaceSetupLines)]),
|
|
@@ -129,6 +143,13 @@ export async function addStackPieces({ pieces, projectRoot }) {
|
|
|
129
143
|
)).map(({ id }) => id);
|
|
130
144
|
const commandLines = parseStackCommandLines(sections.get('Commands') || [], { path: STACK_PATH })
|
|
131
145
|
.map(({ label, argv }) => `- Verify \`${label}\`: ${argv.map((value) => `\`${value}\``).join(' ')}`);
|
|
146
|
+
const environmentFileLines = sections.has('Environment files')
|
|
147
|
+
? sections.get('Environment files')
|
|
148
|
+
: null;
|
|
149
|
+
parseStackEnvironmentFileLines(
|
|
150
|
+
environmentFileLines === null ? undefined : environmentFileLines,
|
|
151
|
+
{ path: STACK_PATH },
|
|
152
|
+
);
|
|
132
153
|
const launchLines = sections.has('Launch') ? sections.get('Launch') : null;
|
|
133
154
|
parseStackLaunchLines(launchLines === null ? undefined : launchLines, { path: STACK_PATH });
|
|
134
155
|
const workspaceSetupLines = sections.has('Workspace setup')
|
|
@@ -141,6 +162,7 @@ export async function addStackPieces({ pieces, projectRoot }) {
|
|
|
141
162
|
const rendered = renderStack({
|
|
142
163
|
commandLines,
|
|
143
164
|
componentIds: selected,
|
|
165
|
+
environmentFileLines,
|
|
144
166
|
launchLines,
|
|
145
167
|
workspaceSetupLines,
|
|
146
168
|
});
|
|
@@ -228,6 +250,11 @@ export async function readStack(projectRoot) {
|
|
|
228
250
|
const componentCommands = components.flatMap((piece) => piece.commands);
|
|
229
251
|
const commands = distinctCommands(projectCommands.length > 0 ? projectCommands : componentCommands);
|
|
230
252
|
const resources = resourceDeclarations(components);
|
|
253
|
+
const projectEnvironmentFiles = parseStackEnvironmentFileLines(
|
|
254
|
+
sections.has('Environment files') ? sections.get('Environment files') : undefined,
|
|
255
|
+
{ path: STACK_PATH },
|
|
256
|
+
);
|
|
257
|
+
const environmentFiles = composeStackEnvironmentFiles(components, projectEnvironmentFiles);
|
|
231
258
|
const projectLaunch = parseStackLaunchLines(
|
|
232
259
|
sections.has('Launch') ? sections.get('Launch') : undefined,
|
|
233
260
|
{ path: STACK_PATH },
|
|
@@ -243,12 +270,14 @@ export async function readStack(projectRoot) {
|
|
|
243
270
|
identityHash: sha256(stableJson({
|
|
244
271
|
components: components.map(({ id }) => id),
|
|
245
272
|
commands: commands.map(({ label, argv }) => ({ label, argv })),
|
|
273
|
+
environmentFiles,
|
|
246
274
|
launchTargets,
|
|
247
275
|
resources,
|
|
248
276
|
workspaceSetup,
|
|
249
277
|
})),
|
|
250
278
|
components,
|
|
251
279
|
commands,
|
|
280
|
+
environmentFiles,
|
|
252
281
|
launchTargets,
|
|
253
282
|
workspaceSetup,
|
|
254
283
|
resources,
|
|
@@ -266,6 +295,7 @@ export function stackPromptContext(stack) {
|
|
|
266
295
|
requires: piece.requires,
|
|
267
296
|
})),
|
|
268
297
|
verifyCommands: stack.commands.map(({ label, argv }) => ({ label, argv })),
|
|
298
|
+
environmentFiles: stack.environmentFiles,
|
|
269
299
|
launchTargets: stack.launchTargets,
|
|
270
300
|
workspaceSetup: stack.workspaceSetup,
|
|
271
301
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
1
4
|
import { gitContext } from './git.js';
|
|
2
5
|
import { readStack } from './stack.js';
|
|
3
6
|
import { sha256, stableJson, uniqueSorted } from './utils.js';
|
|
@@ -8,10 +11,32 @@ export async function inspectProjectWorkspaceSetup({
|
|
|
8
11
|
} = {}) {
|
|
9
12
|
const root = (await gitContext(projectRoot)).repositoryRoot;
|
|
10
13
|
const stack = await readStack(root);
|
|
11
|
-
const diagnostics = stack.workspaceSetup.diagnostics;
|
|
14
|
+
const diagnostics = [...stack.workspaceSetup.diagnostics];
|
|
15
|
+
if (diagnostics.length === 0) {
|
|
16
|
+
const waitingFor = [];
|
|
17
|
+
for (const step of stack.workspaceSetup.steps) {
|
|
18
|
+
if (!step.readyWhen) continue;
|
|
19
|
+
try {
|
|
20
|
+
await access(path.join(root, step.readyWhen));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (!['ENOENT', 'ENOTDIR'].includes(error?.code)) throw error;
|
|
23
|
+
waitingFor.push(step.readyWhen);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (waitingFor.length > 0) {
|
|
27
|
+
const paths = uniqueSorted(waitingFor);
|
|
28
|
+
diagnostics.push({
|
|
29
|
+
code: 'STACK_WORKSPACE_SETUP_WAITING',
|
|
30
|
+
message: `Workspace setup is waiting for project ${paths.length === 1 ? 'path' : 'paths'}: ${paths.join(', ')}.`,
|
|
31
|
+
details: { paths },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const waiting = diagnostics.some(({ code }) => code === 'STACK_WORKSPACE_SETUP_WAITING');
|
|
36
|
+
const blocked = diagnostics.some(({ code }) => code !== 'STACK_WORKSPACE_SETUP_WAITING');
|
|
12
37
|
let status = 'unconfigured';
|
|
13
|
-
if (
|
|
14
|
-
else if (stack.workspaceSetup.steps.length > 0) status = 'ready';
|
|
38
|
+
if (blocked) status = 'blocked';
|
|
39
|
+
else if (!waiting && stack.workspaceSetup.steps.length > 0) status = 'ready';
|
|
15
40
|
const recipeHash = status === 'ready'
|
|
16
41
|
? sha256(stableJson({ version: 1, steps: stack.workspaceSetup.steps }))
|
|
17
42
|
: '';
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { contextForProjectPaths } from './index/context.js';
|
|
|
6
6
|
import { generateProjectPrompt } from './index/prompt.js';
|
|
7
7
|
import { initializeProject } from './index/init.js';
|
|
8
8
|
import { installCodexPlugin } from './index/codex-plugin.js';
|
|
9
|
+
import { inspectProjectEnvironment } from './index/environment-files.js';
|
|
9
10
|
import { inspectProjectLaunch } from './index/launch.js';
|
|
10
11
|
import { listBuiltinStackPieces } from './index/stack-catalog.js';
|
|
11
12
|
import { addStackPieces } from './index/stack.js';
|
|
@@ -72,6 +73,10 @@ export function inspectLaunch(options) {
|
|
|
72
73
|
return inspectProjectLaunch(options);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
export function inspectEnvironment(options) {
|
|
77
|
+
return inspectProjectEnvironment(options);
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
export function inspectWorkspaceSetup(options) {
|
|
76
81
|
return inspectProjectWorkspaceSetup(options);
|
|
77
82
|
}
|
package/stacks/pieces/jskit.md
CHANGED
|
@@ -29,7 +29,11 @@ generated files, and runtime composition.
|
|
|
29
29
|
|
|
30
30
|
## Workspace setup
|
|
31
31
|
|
|
32
|
-
- Prepare `Install dependencies` with `nodejs
|
|
32
|
+
- Prepare `Install dependencies` with `nodejs` when `package.json` exists: `npm` `install`
|
|
33
|
+
|
|
34
|
+
## Environment files
|
|
35
|
+
|
|
36
|
+
- Dotenv `.env`
|
|
33
37
|
|
|
34
38
|
## Launch
|
|
35
39
|
|