genesis-compiler 1.2.6 → 1.2.8

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
@@ -51,6 +51,18 @@ completely or correctly explains the implementation.
51
51
 
52
52
  Genesis requires Node.js 22 or newer and Git.
53
53
 
54
+ Install the framework-neutral compiler and the optional first-party technology
55
+ catalog as separate packages:
56
+
57
+ ```bash
58
+ npm install --global genesis-compiler genesis-stack
59
+ ```
60
+
61
+ `genesis-compiler` has no dependency on `genesis-stack` or on any framework.
62
+ The catalog is static Markdown and contains the complete curated set of
63
+ language, database, UI, and framework pieces. A host may supply another catalog
64
+ through the same contract.
65
+
54
66
  For an existing codebase, `adopt` is the direct entry point:
55
67
 
56
68
  ```bash
@@ -102,7 +114,8 @@ that, use Codex normally. No technology is assumed until you explicitly select
102
114
  Stack components:
103
115
 
104
116
  ```bash
105
- genesis stack add nodejs # or jskit jskit-mysql / jskit-postgresql
117
+ genesis stack add --stack-package genesis-stack nodejs
118
+ # or: genesis stack add --stack-package genesis-stack jskit jskit-mysql
106
119
  ```
107
120
 
108
121
  Genesis does not install substitute generic `nodejs`, `vue`, `php`, or similar
@@ -110,8 +123,9 @@ skills. Official, user, or host skills retain their normal names. A Stack piece
110
123
  may instead name one authoritative technology skill; Genesis copies that
111
124
  complete directory into `.agents/skills/`, including its `references/`,
112
125
  `scripts/`, `assets/`, and agent metadata. The agent loads those resources only
113
- when the skill requires them. For example, the JSKIT piece installs the JSKIT
114
- package's own `jskit` skill. A piece's optional `## Guidance` is concise
126
+ when the skill requires them. For example, the `genesis-stack` catalog's JSKIT
127
+ piece installs the JSKIT package's own `jskit` skill. A piece's optional
128
+ `## Guidance` is concise
115
129
  supplemental project-work guidance; it does not create or replace a generic
116
130
  technology skill.
117
131
 
@@ -130,8 +144,8 @@ execute the same normalized recipe themselves. A concrete Stack piece may declar
130
144
  ordered `## Workspace setup` recipe with labels, argument arrays expressed as
131
145
  backticked tokens, abstract runtimes, and optional project-relative working
132
146
  directories and readiness paths. A recipe whose declared project marker does
133
- not exist remains unconfigured until a later inspection. The built-in `jskit`
134
- piece waits for `package.json` and then declares `npm install`; generic
147
+ not exist remains unconfigured until a later inspection. The catalog's
148
+ `jskit` piece waits for `package.json` and then declares `npm install`; generic
135
149
  `nodejs` and `php` deliberately declare no installer. One project section in
136
150
  `genesis/stack.md` replaces all component defaults, including with
137
151
  `- Nothing.`. Without that override, exactly one component recipe is used;
@@ -143,7 +157,7 @@ resources such as database variables do not gate dependency installation.
143
157
  Hosts that manage project environments can call `inspectEnvironment()`.
144
158
  Concrete Stack integrations may declare public, non-secret Environment
145
159
  defaults such as the database driver selected by that integration. Explicit
146
- host or user values take precedence. The built-in JSKIT MySQL and PostgreSQL
160
+ host or user values take precedence. The catalog's JSKIT MySQL and PostgreSQL
147
161
  pieces respectively declare `DB_CLIENT=mysql2` and `DB_CLIENT=pg`, so users
148
162
  provide connection values rather than restating a deterministic technology
149
163
  choice.
@@ -164,7 +178,8 @@ optional readable `## Launch` section in `genesis/stack.md` declares labeled
164
178
  targets, exact backticked argument tokens, relative working directories,
165
179
  abstract runtime requirements, `{host}`/`{port}` substitutions, and an optional
166
180
  application-owned Preview identity block. Concrete components may provide a
167
- default: `jskit` declares its app-owned `npm run develop` command; generic
181
+ default: the catalog's `jskit` piece declares its app-owned `npm run develop`
182
+ command; generic
168
183
  `nodejs` and `php` do not guess. Genesis starts nothing itself. It returns preview identity
169
184
  command metadata and environment variable names, never their values. The host
170
185
  continues to own pinned runtimes, ports, processes, readiness probing and
@@ -177,7 +192,7 @@ Hosts that publish applications can call the separate public
177
192
  `inspectDeployment()` API. An optional readable `## Deployment` section
178
193
  declares an ordered production recipe: project-relative workdir, abstract
179
194
  runtimes, exact Prepare/Build/Migrate/Serve argv, and one HTTP readiness
180
- predicate. A concrete component may provide the recipe; the built-in `jskit`
195
+ predicate. A concrete component may provide the recipe; the catalog's `jskit`
181
196
  piece declares production dependency installation, build, database preparation,
182
197
  `npm start`, and `/api/health`. Generic language pieces do not guess. A project
183
198
  section replaces component defaults wholesale, and `- Nothing.` explicitly
@@ -355,17 +370,23 @@ object.
355
370
 
356
371
  ## Stack
357
372
 
358
- List and select the built-in Stack components:
373
+ List and select pieces from the installed optional catalog:
359
374
 
360
375
  ```bash
361
- genesis stack list
362
- genesis stack add jskit jskit-mysql # or jskit-postgresql
376
+ genesis stack list --stack-package genesis-stack
377
+ genesis stack add --stack-package genesis-stack jskit jskit-mysql
363
378
  ```
364
379
 
365
- `genesis/stack.md` records component ids and optional project verification
366
- commands. Selecting no components is valid. Built-in components include the
367
- eleven common language families listed above, plus Vue, MySQL, PostgreSQL,
368
- JSKIT, and JSKIT database integrations. Components may contribute:
380
+ The explicit package flag bootstraps an unconfigured project. Once a Stack
381
+ selection records `genesis-stack` in `genesis/stack.md`, ordinary commands use
382
+ that recorded package directly; `genesis stack list` needs no host flag.
383
+
384
+ `genesis/stack.md` records the owning Stack package, component ids, and optional
385
+ project verification commands. After the first selection, ordinary commands
386
+ resolve the recorded package without another flag. Selecting no components is
387
+ valid. `genesis-stack` contains the eleven common language families listed
388
+ above, plus Vue, MySQL, PostgreSQL, JSKIT, and JSKIT database integrations.
389
+ Components may contribute:
369
390
 
370
391
  - a concise description and supplemental Guidance used across relevant tasks;
371
392
  - existing-project Adoption requirements used only while importing a codebase;
@@ -381,8 +402,8 @@ cleanup tasks; `Adoption` adds import-only requirements; `Deslop` adds
381
402
  cleanup-only rules. None shadows an official generic technology skill or
382
403
  assumes that an upstream skill contains every rule Genesis needs.
383
404
 
384
- Genesis core contains no database, framework, language, or platform-specific
385
- controller behavior. See [Stack components](docs/stack-components.md).
405
+ Genesis core contains no Stack pieces and no database, framework, language, or
406
+ platform dependency. See [Stack components](docs/stack-components.md).
386
407
 
387
408
  ## Verification
388
409
 
@@ -35,7 +35,7 @@ Genesis keeps each instruction at one useful level:
35
35
  project's `.agents/skills/` directory.
36
36
  - `prompts/<task>.txt` is a task launcher. It selects the relevant
37
37
  workflow skill and states only that turn's edit boundary.
38
- - `stacks/pieces/<component>.md` supplies concise Description, supplemental
38
+ - a declared Stack package's `stacks/pieces/<component>.md` supplies concise Description, supplemental
39
39
  Guidance, Adoption requirements, Resources, Deslop overlays, structural Indexers, verification
40
40
  Commands, and an optional authoritative Agent Skill source.
41
41
  - `genesis/blueprint.md`, `genesis/program/`, and `genesis/stack.md` are the
@@ -1,8 +1,23 @@
1
1
  # Stack components
2
2
 
3
3
  Genesis core knows no framework, language, database, package manager, or
4
- environment-variable name. A built-in component is one Markdown file at
5
- `stacks/pieces/<id>.md`.
4
+ environment-variable name, and it ships no technology pieces. A Stack package
5
+ is an ordinary npm package with one static manifest pointer:
6
+
7
+ ```json
8
+ {
9
+ "name": "example-stack",
10
+ "genesis": { "stackPieces": "stacks/pieces" }
11
+ }
12
+ ```
13
+
14
+ Each piece is one Markdown file in that declared directory. Genesis reads the
15
+ manifest and Markdown only; it never imports or executes package code. Catalog
16
+ names are explicit, collisions fail, and the selected package is recorded in
17
+ the project's `## Stack packages` section. The first-party optional
18
+ `genesis-stack` package contains the complete curated catalog; Genesis core does
19
+ not depend on it. A framework contributes to that catalog rather than requiring
20
+ a compiler release or a separate catalog package.
6
21
 
7
22
  ````markdown
8
23
  # Stack piece: example
@@ -169,7 +184,7 @@ environment name and value. Selected
169
184
  component defaults compose by name; identical declarations collapse and
170
185
  different values fail rather than silently choosing one. Defaults participate
171
186
  in Stack identity, resource preflight, prompt context, Launch inspection, and
172
- verification execution. The JSKIT database integrations use this contract for
187
+ verification execution. The `genesis-stack` JSKIT database integrations use this contract for
173
188
  their deterministic `DB_CLIENT` driver ids; generic database pieces do not.
174
189
  Secrets and deployment-specific connection values remain host inputs and must
175
190
  never be declared as defaults.
@@ -288,7 +303,7 @@ district to a region and a campus while generating
288
303
  must not reclassify or filter paths. Under a matched prefix, the first directory
289
304
  is a campus, while files directly below the prefix belong to the prefix campus.
290
305
  Under the fallback, each top-level directory is a campus and project-root files
291
- belong to the project-root campus. The built-in JSKIT piece excludes exact
306
+ belong to the project-root campus. The `genesis-stack` JSKIT piece excludes exact
292
307
  `test`, `tests`, and `__tests__` path segments plus `*.test.*` and `*.spec.*`
293
308
  files, then declares `Packages`, `Source`, and `Everything else`.
294
309
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
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": {
@@ -39,8 +39,7 @@
39
39
  "prompts/work.txt",
40
40
  "plugins/genesis",
41
41
  "skills",
42
- "src",
43
- "stacks/pieces"
42
+ "src"
44
43
  ],
45
44
  "scripts": {
46
45
  "test": "node --test test/*.test.js"
@@ -58,7 +57,6 @@
58
57
  "@ast-grep/lang-ruby": "^0.0.7",
59
58
  "@ast-grep/lang-rust": "^0.0.7",
60
59
  "@ast-grep/napi": "^0.45.1",
61
- "@jskit-ai/agent-docs": "0.1.134",
62
60
  "yaml": "^2.9.0"
63
61
  }
64
62
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
package/src/cli.js CHANGED
@@ -44,6 +44,7 @@ const USAGE = `Usage:
44
44
 
45
45
  Options:
46
46
  --project-root <path> Set the project root (default: current directory)
47
+ --stack-package <name> Add an installed external Stack package (repeatable)
47
48
  --task <task> Select the prompt task (default: work)
48
49
  --json Emit one machine-readable result
49
50
  -h, --help Show this help
@@ -70,6 +71,7 @@ function parseCommand(argv) {
70
71
  options: {
71
72
  json: { type: 'boolean', default: false },
72
73
  'project-root': { type: 'string' },
74
+ 'stack-package': { type: 'string', multiple: true, default: [] },
73
75
  task: { type: 'string' },
74
76
  },
75
77
  });
@@ -79,6 +81,7 @@ function parseCommand(argv) {
79
81
  const options = {
80
82
  json: parsed.values.json,
81
83
  projectRoot: parsed.values['project-root'],
84
+ stackPackages: parsed.values['stack-package'],
82
85
  task: parsed.values.task,
83
86
  };
84
87
  const operands = parsed.positionals;
@@ -268,24 +271,28 @@ async function hookInput() {
268
271
 
269
272
  async function execute({ command, operands, options }, { signal } = {}) {
270
273
  const projectRoot = options.projectRoot || process.cwd();
271
- if (command === 'init') return initialize({ projectRoot });
274
+ const stackPackages = options.stackPackages || [];
275
+ if (command === 'init') return initialize({ projectRoot, stackPackages });
272
276
  if (command === 'adopt') {
273
- return adoptProject({ projectRoot, request: operands.join(' ') });
277
+ return adoptProject({ projectRoot, request: operands.join(' '), stackPackages });
274
278
  }
275
279
  if (command === 'codex') return installCodex();
276
280
  if (command === 'stack') {
277
- if (operands[0] === 'list') return { status: 'ok', pieces: await listStackPieces() };
278
- return addStack({ pieces: operands.slice(1), projectRoot });
281
+ if (operands[0] === 'list') {
282
+ return { status: 'ok', pieces: await listStackPieces({ projectRoot, stackPackages }) };
283
+ }
284
+ return addStack({ pieces: operands.slice(1), projectRoot, stackPackages });
279
285
  }
280
286
  if (command === 'prompt') {
281
287
  return generatePrompt({
282
288
  projectRoot,
283
289
  task: options.task || 'work',
284
290
  request: operands.join(' '),
291
+ stackPackages,
285
292
  });
286
293
  }
287
- if (command === 'context') return getContext({ paths: operands, projectRoot });
288
- if (command === 'index') return indexCodebase({ projectRoot, queries: operands });
294
+ if (command === 'context') return getContext({ paths: operands, projectRoot, stackPackages });
295
+ if (command === 'index') return indexCodebase({ projectRoot, queries: operands, stackPackages });
289
296
  if (command === 'inspect') {
290
297
  const inspections = {
291
298
  workspace: inspectWorkspaceSetup,
@@ -293,12 +300,16 @@ async function execute({ command, operands, options }, { signal } = {}) {
293
300
  launch: inspectLaunch,
294
301
  deployment: inspectDeployment,
295
302
  };
296
- return { inspection: operands[0], ...await inspections[operands[0]]({ projectRoot }) };
303
+ return {
304
+ inspection: operands[0],
305
+ ...await inspections[operands[0]]({ projectRoot, stackPackages }),
306
+ };
297
307
  }
298
308
  if (command === 'prepare') {
299
309
  return prepareWorkspace({
300
310
  projectRoot,
301
311
  signal,
312
+ stackPackages,
302
313
  onEvent: (event) => {
303
314
  if (!options.json) line(process.stderr, event.message);
304
315
  },
@@ -324,12 +335,13 @@ async function execute({ command, operands, options }, { signal } = {}) {
324
335
  return verify({
325
336
  projectRoot,
326
337
  signal,
338
+ stackPackages,
327
339
  onEvent: (event) => {
328
340
  if (!options.json) line(process.stderr, event.message);
329
341
  },
330
342
  });
331
343
  }
332
- if (command === 'check') return check({ projectRoot });
344
+ if (command === 'check') return check({ projectRoot, stackPackages });
333
345
  fail('CLI_UNKNOWN_COMMAND', `Unknown command: ${command}`);
334
346
  }
335
347
 
@@ -156,8 +156,14 @@ async function describeSkill(directory, source) {
156
156
  };
157
157
  }
158
158
 
159
- async function packageDirectory(packageName) {
160
- for (const modulesRoot of require.resolve.paths(packageName) || []) {
159
+ async function packageDirectory(packageName, resolveFrom = null) {
160
+ const roots = [
161
+ ...(resolveFrom
162
+ ? createRequire(path.join(resolveFrom, 'package.json')).resolve.paths(packageName) || []
163
+ : []),
164
+ ...(require.resolve.paths(packageName) || []),
165
+ ];
166
+ for (const modulesRoot of [...new Set(roots)]) {
161
167
  const manifest = path.join(modulesRoot, packageName, 'package.json');
162
168
  try {
163
169
  const value = JSON.parse(await readFile(manifest, 'utf8'));
@@ -180,7 +186,9 @@ async function packageDirectory(packageName) {
180
186
  }
181
187
 
182
188
  async function resolveLocator(locator) {
183
- const root = locator.package ? await packageDirectory(locator.package) : packageRoot;
189
+ const root = locator.package
190
+ ? await packageDirectory(locator.package, locator.resolveFrom)
191
+ : packageRoot;
184
192
  const directory = path.join(root, locator.path);
185
193
  const source = locator.source || (
186
194
  locator.package
@@ -32,7 +32,11 @@ function invalidResult(area, error) {
32
32
  };
33
33
  }
34
34
 
35
- export async function checkProject({ environment = process.env, projectRoot } = {}) {
35
+ export async function checkProject({
36
+ environment = process.env,
37
+ projectRoot,
38
+ stackPackages = [],
39
+ } = {}) {
36
40
  const root = (await gitContext(projectRoot)).repositoryRoot;
37
41
  try {
38
42
  await readBlueprint(root, { required: true, requireDescription: true });
@@ -42,7 +46,7 @@ export async function checkProject({ environment = process.env, projectRoot } =
42
46
 
43
47
  let stack;
44
48
  try {
45
- stack = await readStack(root);
49
+ stack = await readStack(root, { stackPackages });
46
50
  } catch (error) {
47
51
  return invalidResult('stack', error);
48
52
  }
@@ -71,9 +75,9 @@ export async function checkProject({ environment = process.env, projectRoot } =
71
75
  const verification = await inspectVerification({ projectRoot: root, stack });
72
76
  const [workspaceSetup, projectEnvironment, launch, deployment] = await Promise.all([
73
77
  inspectWorkspaceSetupForStack({ projectRoot: root, stack }),
74
- inspectProjectEnvironment({ environment, projectRoot: root }),
75
- inspectProjectLaunch({ environment, projectRoot: root }),
76
- inspectProjectDeployment({ projectRoot: root }),
78
+ inspectProjectEnvironment({ environment, projectRoot: root, stackPackages }),
79
+ inspectProjectLaunch({ environment, projectRoot: root, stackPackages }),
80
+ inspectProjectDeployment({ projectRoot: root, stackPackages }),
77
81
  ]);
78
82
  const diagnostics = [
79
83
  ...(program.diagnostic ? [program.diagnostic] : []),
@@ -238,9 +238,14 @@ function formatCount(value, singular) {
238
238
  return `${value} ${singular}${value === 1 ? '' : 's'}`;
239
239
  }
240
240
 
241
- export async function buildProjectIndex({ projectRoot, queries = [], write = true } = {}) {
241
+ export async function buildProjectIndex({
242
+ projectRoot,
243
+ queries = [],
244
+ stackPackages = [],
245
+ write = true,
246
+ } = {}) {
242
247
  const root = (await gitContext(projectRoot)).repositoryRoot;
243
- const stack = await readStack(root);
248
+ const stack = await readStack(root, { stackPackages });
244
249
  const states = await gitVisibleFileStates(root, { includePath: isProjectContentPath });
245
250
  const files = [...states]
246
251
  .filter(([filePath, state]) => (
@@ -49,7 +49,7 @@ function pathMatchesTarget(filePath, target) {
49
49
  return !target || filePath === target || filePath.startsWith(`${target}/`);
50
50
  }
51
51
 
52
- export async function contextForProjectPaths({ paths, projectRoot } = {}) {
52
+ export async function contextForProjectPaths({ paths, projectRoot, stackPackages = [] } = {}) {
53
53
  if (!Array.isArray(paths) || paths.length === 0) {
54
54
  throw new GenesisError('CONTEXT_PATH_REQUIRED', 'Genesis context requires at least one project path.');
55
55
  }
@@ -62,7 +62,7 @@ export async function contextForProjectPaths({ paths, projectRoot } = {}) {
62
62
  modules: [],
63
63
  diagnostic: asDiagnostic(error),
64
64
  })),
65
- readStack(root),
65
+ readStack(root, { stackPackages }),
66
66
  ]);
67
67
  const modules = program.modules.filter((module) => targets.some((target) => citesTarget(module, target)));
68
68
  const moduleSources = await Promise.all(modules.map(async (module) => ({
@@ -4,9 +4,9 @@ import { sha256, stableJson, uniqueSorted } from './utils.js';
4
4
  import { GENESIS_CONTRACTS } from './contracts.js';
5
5
 
6
6
  /** Read the Stack's production recipe without provisioning or publishing anything. */
7
- export async function inspectProjectDeployment({ projectRoot } = {}) {
7
+ export async function inspectProjectDeployment({ projectRoot, stackPackages = [] } = {}) {
8
8
  const root = (await gitContext(projectRoot)).repositoryRoot;
9
- const stack = await readStack(root);
9
+ const stack = await readStack(root, { stackPackages });
10
10
  const diagnostics = [...stack.deployment.diagnostics];
11
11
  let status = 'unconfigured';
12
12
  if (diagnostics.length > 0) status = 'blocked';
@@ -5,9 +5,13 @@ import { missingStackResources } from './stack-preflight.js';
5
5
  import { GENESIS_CONTRACTS } from './contracts.js';
6
6
 
7
7
  /** Inspect Stack environment requirements without returning supplied host values. */
8
- export async function inspectProjectEnvironment({ environment = process.env, projectRoot } = {}) {
8
+ export async function inspectProjectEnvironment({
9
+ environment = process.env,
10
+ projectRoot,
11
+ stackPackages = [],
12
+ } = {}) {
9
13
  const root = (await gitContext(projectRoot)).repositoryRoot;
10
- const stack = await readStack(root);
14
+ const stack = await readStack(root, { stackPackages });
11
15
  const diagnostics = missingStackResources({
12
16
  environment: withStackEnvironmentDefaults(environment, stack.environmentDefaults),
13
17
  resources: stack.resources,
package/src/index/init.js CHANGED
@@ -20,14 +20,14 @@ async function createIfMissing(projectRoot, relativePath, source) {
20
20
  }
21
21
  }
22
22
 
23
- export async function initializeProject({ projectRoot } = {}) {
23
+ export async function initializeProject({ projectRoot, stackPackages = [] } = {}) {
24
24
  const root = (await gitContext(projectRoot)).repositoryRoot;
25
25
  const created = (await Promise.all([
26
26
  createIfMissing(root, BLUEPRINT_PATH, BLUEPRINT_SKELETON_SOURCE),
27
27
  createIfMissing(root, STACK_PATH, EMPTY_STACK_SOURCE),
28
28
  ])).filter(Boolean);
29
29
  await mkdir(path.join(root, PROGRAM_ROOT), { recursive: true });
30
- const stack = await readStack(root);
30
+ const stack = await readStack(root, { stackPackages });
31
31
  const skills = await syncProjectSkills({ projectRoot: root, stack });
32
32
  const hooks = await installCodexHooks({ projectRoot: root });
33
33
  const changedFiles = [...created, ...hooks.changedFiles, ...skills.changedFiles].sort();
@@ -9,9 +9,10 @@ import { GENESIS_CONTRACTS } from './contracts.js';
9
9
  export async function inspectProjectLaunch({
10
10
  environment = process.env,
11
11
  projectRoot,
12
+ stackPackages = [],
12
13
  } = {}) {
13
14
  const root = (await gitContext(projectRoot)).repositoryRoot;
14
- const stack = await readStack(root);
15
+ const stack = await readStack(root, { stackPackages });
15
16
  const diagnostics = missingStackResources({
16
17
  environment: withStackEnvironmentDefaults(environment, stack.environmentDefaults),
17
18
  resources: stack.resources,
@@ -3,7 +3,7 @@ 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
+ import { listStackCatalogPieces } from './stack-catalog.js';
7
7
  import { GenesisError } from './errors.js';
8
8
  import { gitContext } from './git.js';
9
9
  import { inspectProgram } from './program.js';
@@ -120,12 +120,13 @@ function renderPrompt({
120
120
  ].join('\n');
121
121
  }
122
122
 
123
- async function generateAdoptionPrompt({ environment, instructions, program, request, root }) {
123
+ async function generateAdoptionPrompt({ environment, instructions, program, request, root, stackPackages }) {
124
124
  const blueprint = await readBlueprint(root, { required: true });
125
- const [stack, index, catalog] = await Promise.all([
126
- readStack(root),
127
- buildProjectIndex({ projectRoot: root, write: false }),
128
- listBuiltinStackPieces(),
125
+ const stack = await readStack(root, { stackPackages });
126
+ const availableStackPackages = [...new Set([...stack.stackPackages, ...stackPackages])];
127
+ const [index, catalog] = await Promise.all([
128
+ buildProjectIndex({ projectRoot: root, stackPackages, write: false }),
129
+ listStackCatalogPieces({ projectRoot: root, stackPackages: availableStackPackages }),
129
130
  ]);
130
131
  const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
131
132
  const missing = missingStackResources({
@@ -165,14 +166,14 @@ async function generateAdoptionPrompt({ environment, instructions, program, requ
165
166
  };
166
167
  }
167
168
 
168
- async function generateExplanationPrompt({ instructions, program, request, root, task }) {
169
+ async function generateExplanationPrompt({ instructions, program, request, root, stackPackages, task }) {
169
170
  const blueprint = await readBlueprint(root, {
170
171
  required: true,
171
172
  requireDescription: task === 'program',
172
173
  });
173
174
  const [stack, index] = await Promise.all([
174
- readStack(root),
175
- buildProjectIndex({ projectRoot: root, write: false }),
175
+ readStack(root, { stackPackages }),
176
+ buildProjectIndex({ projectRoot: root, stackPackages, write: false }),
176
177
  ]);
177
178
  const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
178
179
  const blueprintContext = task === 'describe'
@@ -204,7 +205,7 @@ async function generateExplanationPrompt({ instructions, program, request, root,
204
205
  };
205
206
  }
206
207
 
207
- async function generateStartPrompt({ instructions, program, request, root }) {
208
+ async function generateStartPrompt({ instructions, program, request, root, stackPackages }) {
208
209
  let blueprint;
209
210
  try {
210
211
  blueprint = await readBlueprint(root, { required: true });
@@ -231,10 +232,11 @@ async function generateStartPrompt({ instructions, program, request, root }) {
231
232
  verificationCommands: [],
232
233
  };
233
234
  }
234
- const [stack, index, catalog] = await Promise.all([
235
- readStack(root),
236
- buildProjectIndex({ projectRoot: root, write: false }),
237
- listBuiltinStackPieces(),
235
+ const stack = await readStack(root, { stackPackages });
236
+ const availableStackPackages = [...new Set([...stack.stackPackages, ...stackPackages])];
237
+ const [index, catalog] = await Promise.all([
238
+ buildProjectIndex({ projectRoot: root, stackPackages, write: false }),
239
+ listStackCatalogPieces({ projectRoot: root, stackPackages: availableStackPackages }),
238
240
  ]);
239
241
  const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
240
242
  const newProject = stack.components.length === 0 && index.fileCount === 0;
@@ -273,6 +275,7 @@ export async function generateProjectPrompt({
273
275
  environment = process.env,
274
276
  projectRoot,
275
277
  request = '',
278
+ stackPackages = [],
276
279
  task = 'work',
277
280
  } = {}) {
278
281
  if (!TASKS.has(task)) {
@@ -314,6 +317,7 @@ export async function generateProjectPrompt({
314
317
  program,
315
318
  request: userRequest,
316
319
  root,
320
+ stackPackages,
317
321
  });
318
322
  }
319
323
  if (task === 'adopt') {
@@ -323,12 +327,13 @@ export async function generateProjectPrompt({
323
327
  program,
324
328
  request: userRequest,
325
329
  root,
330
+ stackPackages,
326
331
  });
327
332
  }
328
333
  let stack;
329
334
  if (task === 'work') {
330
335
  try {
331
- stack = await readStack(root);
336
+ stack = await readStack(root, { stackPackages });
332
337
  } catch (error) {
333
338
  if (error?.code !== 'STACK_REQUIRED') throw error;
334
339
  return generateStartPrompt({
@@ -336,6 +341,7 @@ export async function generateProjectPrompt({
336
341
  program,
337
342
  request: userRequest,
338
343
  root,
344
+ stackPackages,
339
345
  });
340
346
  }
341
347
  if (stack.components.length === 0) {
@@ -344,6 +350,7 @@ export async function generateProjectPrompt({
344
350
  program,
345
351
  request: userRequest,
346
352
  root,
353
+ stackPackages,
347
354
  });
348
355
  }
349
356
  }
@@ -353,12 +360,13 @@ export async function generateProjectPrompt({
353
360
  program,
354
361
  request: userRequest,
355
362
  root,
363
+ stackPackages,
356
364
  task,
357
365
  });
358
366
  }
359
367
 
360
368
  const blueprint = await readBlueprint(root, { required: true, requireDescription: true });
361
- stack ||= await readStack(root);
369
+ stack ||= await readStack(root, { stackPackages });
362
370
  const [index, projectSkills] = await Promise.all([
363
371
  buildProjectIndex({ projectRoot: root, write: false }),
364
372
  inspectProjectSkills({ projectRoot: root, stack }),