genesis-compiler 1.5.2 → 1.7.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 CHANGED
@@ -28,7 +28,8 @@ genesis/
28
28
  engineering.md selected engineering profile and project-specific requirements
29
29
  stack.md selected components, resources, verification, and project-owned operation contracts
30
30
  stack/ optional per-component Description, Guidance, Adoption, Post-change, and Deslop customization
31
- program/ concise explanations grouped into conceptual subsystems
31
+ subsystems.md authored responsibilities, Program membership, and data ownership/use
32
+ program/ concise public-operation explanations
32
33
  .agents/skills/
33
34
  genesis-* Genesis workflow skills
34
35
  <technology>/ authoritative skills installed by selected Stack pieces
@@ -897,6 +898,10 @@ import {
897
898
  installCodex,
898
899
  listEngineeringProfiles,
899
900
  listStackPieces,
901
+ listParsers,
902
+ installParsers,
903
+ verifyParsers,
904
+ parserEnvironment,
900
905
  migrate,
901
906
  projectSessionContext,
902
907
  projectTurnContext,
@@ -910,6 +915,9 @@ import {
910
915
 
911
916
  `initialize()` installs the project files, Genesis workflow skills, selected
912
917
  Stack skills, and local Codex hooks.
918
+ Extra language parsers install on first use, outside the project's dependencies.
919
+ The CLI can also prepare and verify parsers for an offline host; see
920
+ [language parser management](docs/parsers.md).
913
921
  `migrate()` updates recognized older Genesis project-file formats and returns
914
922
  the resulting structural check for hosts such as Vibe64.
915
923
  `adoptProject()` also returns the initial `adopt` prompt for an existing
@@ -977,3 +985,60 @@ project operations, and records exact verification evidence. Code, tests, Git
977
985
  review, and the user's chosen agent remain responsible for implementation. It
978
986
  does not claim universal semantic convergence, exhaustive coverage, or
979
987
  whole-product correctness.
988
+
989
+ ## Authored subsystems (version zero)
990
+
991
+ `genesis/subsystems.md` is the single authored map connecting architectural
992
+ responsibilities to Program operations and data. Program directories are only
993
+ navigation; Genesis never infers subsystem membership from them. Blueprint
994
+ remains non-technical product intent. Source, migrations and runtime behavior
995
+ remain evidence; the map does not define application behavior or database DDL.
996
+
997
+ ```markdown
998
+ # Subsystems
999
+
1000
+ ## `forms` Forms
1001
+
1002
+ Owns reusable form authoring, rendering, and answer validation.
1003
+
1004
+ ### Program
1005
+ - `genesis/program/forms/render.md`
1006
+
1007
+ ### Data owned
1008
+ - Table `database` `default` `form_definitions`
1009
+
1010
+ ### Data used
1011
+ - Nothing.
1012
+ ```
1013
+
1014
+ Each subsystem has a unique lowercase hyphenated id, title, responsibility
1015
+ prose, and exactly the three shown sections. Each Program module is assigned
1016
+ exactly once, by its complete project-relative path. Each table has at most one
1017
+ owner. Data used names a table owned by another declared subsystem. Empty
1018
+ sections explicitly say `- Nothing.`; an empty initialized map contains
1019
+ `# Subsystems` followed by `- Nothing.`. Data-only and code-only subsystems are
1020
+ valid. No subsystem hierarchy is inferred from paths.
1021
+
1022
+ Table references contain three distinct values: declared Stack resource **id**,
1023
+ schema, and table. `default` selects that resource's default schema; other schema
1024
+ names are literal. Never put host database names or environment values here.
1025
+ Genesis validates declarations and resource references without opening a database.
1026
+ Hosts resolve table references against their inspected schema and show missing
1027
+ references and unassigned tables explicitly; the map is not a complete schema
1028
+ inventory. Table/column descriptions and relationships remain database metadata.
1029
+
1030
+ `inspectSubsystems({ projectRoot })` and `genesis inspect subsystems --json`
1031
+ return `genesis.subsystems.v0`, normalized entries, status and a deterministic
1032
+ identity. Program inspection additionally validates membership and referenced
1033
+ modules. Context includes the relevant subsystem through its Program Sources;
1034
+ `context .` includes all subsystems. Program City uses authored titles and table
1035
+ associations, including data-only districts, and changes its hash when the map
1036
+ changes. Opening context stays concise; detailed associations load on demand.
1037
+
1038
+ Adoption, Program refresh, work prompts, session guidance and installed workflow
1039
+ skills maintain the map alongside Program in the same turn. Review checks it
1040
+ read-only; Blueprint-only work leaves it alone. Ordinary private helper edits
1041
+ need no map change. Existing projects do not need a bulk update: missing or empty maps preserve
1042
+ Program access and instruct the agent to author the map from source before its
1043
+ next implementation task. Until then, operations have no declared subsystem;
1044
+ there is no folder-based compatibility reader or automatic ownership inference. Initialization creates an empty map without replacing one.
@@ -0,0 +1,46 @@
1
+ # Language parsers
2
+
3
+ Genesis installs extra language parsers automatically the first time it indexes
4
+ matching source. JavaScript, TypeScript and TSX use its built-in core parser.
5
+ No project dependency or lockfile is changed.
6
+
7
+ ```sh
8
+ genesis parsers list
9
+ genesis parsers install python cpp
10
+ genesis parsers install --all
11
+ genesis parsers verify --all
12
+ ```
13
+
14
+ These commands work outside a Git repository and support `--json`. Downloads
15
+ and verification progress go to stderr. The language names come from `parsers
16
+ list`; C and C++ are separate parsers (`c` and `cpp`), and shell uses `bash`.
17
+
18
+ Use `--directory /absolute/path` or `GENESIS_PARSER_ROOT` to choose a cache.
19
+ Otherwise Genesis uses `$XDG_CACHE_HOME/genesis/parsers`, falling back to
20
+ `~/.cache/genesis/parsers`. Installations are pinned by language, package version,
21
+ OS and CPU. npm must be available for installation. Genesis uses the public npm
22
+ registry, disables package lifecycle scripts, verifies each native parser, and
23
+ retains only its current-platform binary and loader. Prebuilt native support is
24
+ required; Genesis does not compile a missing parser on the user's machine.
25
+
26
+ For a prepared host or release artifact:
27
+
28
+ ```sh
29
+ genesis parsers install --all --directory /absolute/release/parsers
30
+ genesis parsers verify --all --directory /absolute/release/parsers
31
+ export GENESIS_PARSER_ROOT=/absolute/release/parsers
32
+ export GENESIS_PARSER_AUTO_INSTALL=0
33
+ ```
34
+
35
+ The cache can be relocated on the same OS/CPU. Verify it again at its destination
36
+ before activation. Missing or broken parsers fail verification; indexing reports
37
+ an actionable diagnostic. A damaged installed version is preserved for inspection:
38
+ remove only that reported version directory deliberately, then rerun install.
39
+ Successful installations are reusable offline and require no runtime writes.
40
+
41
+ The library exports `listParsers`, `installParsers`, `verifyParsers`, and
42
+ `parserEnvironment`. The first three accept `directory`; install/verify accept
43
+ `languages` or `all` and an abort `signal`. Install accepts `onProgress`.
44
+ `parserEnvironment({ directory, autoInstall, environment })` returns the two
45
+ normalized environment values for a host and its commands. Preparing or verifying
46
+ all parsers uses short-lived children; native indexing runs in a finite child and releases parser memory on exit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.5.2",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with project agent guidance.",
6
6
  "repository": {
@@ -43,25 +43,18 @@
43
43
  "plugins/opencode",
44
44
  "profiles",
45
45
  "skills",
46
- "src"
46
+ "src",
47
+ "docs/parsers.md"
47
48
  ],
48
49
  "scripts": {
49
50
  "test": "node --test test/*.test.js"
50
51
  },
51
52
  "dependencies": {
52
- "@ast-grep/lang-bash": "^0.0.8",
53
- "@ast-grep/lang-c": "^0.0.6",
54
- "@ast-grep/lang-cpp": "^0.0.6",
55
- "@ast-grep/lang-csharp": "^0.0.6",
56
- "@ast-grep/lang-go": "^0.0.6",
57
- "@ast-grep/lang-java": "^0.0.7",
58
- "@ast-grep/lang-kotlin": "^0.0.7",
59
- "@ast-grep/lang-php": "^0.0.7",
60
- "@ast-grep/lang-python": "^0.0.6",
61
- "@ast-grep/lang-ruby": "^0.0.7",
62
- "@ast-grep/lang-rust": "^0.0.7",
63
53
  "@ast-grep/napi": "^0.45.1",
64
54
  "mdast-util-from-markdown": "^2.0.3",
65
55
  "yaml": "^2.9.0"
56
+ },
57
+ "devDependencies": {
58
+ "@ast-grep/lang-python": "0.0.6"
66
59
  }
67
60
  }
package/prompts/adopt.txt CHANGED
@@ -60,3 +60,18 @@ execute them. Finish with a clear inventory of what was imported, what remains
60
60
  unconfigured or blocked, and what would require a separately approved
61
61
  implementation port. Never claim the project is ready merely because Stack
62
62
  components were selected.
63
+
64
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
65
+ adds, removes, or changes a subsystem responsibility, Program membership, or
66
+ data ownership/use. Read the map before changing related code. Reuse existing
67
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
68
+ module exactly once; folders do not determine membership. Keep table references
69
+ grounded in schema/migrations; Genesis does not inspect databases. Private
70
+ helper changes need no map edit when these associations remain unchanged.
71
+
72
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
73
+ application, create it from the actual source, schema, and existing Program
74
+ before implementation. This is ordinary explanatory adoption, not a blocker or
75
+ a reason to ask permission again. Preserve the existing Program and source;
76
+ inspect the relevant responsibilities and complete the smallest truthful map.
77
+ Initialization can create the empty file, but only the agent authors its meaning.
@@ -12,6 +12,21 @@ If the codebase is too large for one careful pass, use subagents as you judge
12
12
  appropriate, reconcile their findings, and remain responsible for one coherent
13
13
  result. Identify anything not inspected or genuinely uncertain.
14
14
 
15
- Edit only `genesis/blueprint.md` and explanatory Markdown below
15
+ Edit only `genesis/blueprint.md`, `genesis/subsystems.md`, and explanatory Markdown below
16
16
  `genesis/program/`. Summarize every explanatory file created, updated, or
17
17
  removed.
18
+
19
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
20
+ adds, removes, or changes a subsystem responsibility, Program membership, or
21
+ data ownership/use. Read the map before changing related code. Reuse existing
22
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
23
+ module exactly once; folders do not determine membership. Keep table references
24
+ grounded in schema/migrations; Genesis does not inspect databases. Private
25
+ helper changes need no map edit when these associations remain unchanged.
26
+
27
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
28
+ application, create it from the actual source, schema, and existing Program
29
+ before implementation. This is ordinary explanatory adoption, not a blocker or
30
+ a reason to ask permission again. Preserve the existing Program and source;
31
+ inspect the relevant responsibilities and complete the smallest truthful map.
32
+ Initialization can create the empty file, but only the agent authors its meaning.
@@ -4,9 +4,24 @@ in complete Program-refresh mode.
4
4
  Explain the code that currently exists using the smallest useful
5
5
  subsystem-oriented Program. Read implementation, tests, Blueprint, and current
6
6
  Program material. Use Machine City as a navigation aid, then verify relevant
7
- source directly. Edit only explanatory Markdown below `genesis/program/`.
7
+ source directly. Edit only `genesis/subsystems.md` and explanatory Markdown below `genesis/program/`.
8
8
  Delete stale or duplicate modules and do not edit code, tests, configuration,
9
9
  Blueprint, Stack, or `.genesis/`.
10
10
 
11
11
  When finished, summarize Program files created, updated, or removed and report
12
12
  genuine ambiguity.
13
+
14
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
15
+ adds, removes, or changes a subsystem responsibility, Program membership, or
16
+ data ownership/use. Read the map before changing related code. Reuse existing
17
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
18
+ module exactly once; folders do not determine membership. Keep table references
19
+ grounded in schema/migrations; Genesis does not inspect databases. Private
20
+ helper changes need no map edit when these associations remain unchanged.
21
+
22
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
23
+ application, create it from the actual source, schema, and existing Program
24
+ before implementation. This is ordinary explanatory adoption, not a blocker or
25
+ a reason to ask permission again. Preserve the existing Program and source;
26
+ inspect the relevant responsibilities and complete the smallest truthful map.
27
+ Initialization can create the empty file, but only the agent authors its meaning.
@@ -10,3 +10,5 @@ evidence from inference and state questions that require human intent.
10
10
 
11
11
  Do not edit any file. Return a concise report ordered by impact with exact file
12
12
  references, the evidence inspected, and the smallest sensible correction.
13
+
14
+ Review `genesis/subsystems.md` responsibilities, Program membership, and data ownership/use against source and schema. Report missing, stale, or conflicting associations without editing them.
package/prompts/work.txt CHANGED
@@ -38,3 +38,18 @@ explicit decision, and add the selection with the Genesis
38
38
  `stack add <piece...>` operation before relying on technology-specific
39
39
  operations. Do not end after only updating
40
40
  or summarizing the Blueprint while the Stack remains empty.
41
+
42
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
43
+ adds, removes, or changes a subsystem responsibility, Program membership, or
44
+ data ownership/use. Read the map before changing related code. Reuse existing
45
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
46
+ module exactly once; folders do not determine membership. Keep table references
47
+ grounded in schema/migrations; Genesis does not inspect databases. Private
48
+ helper changes need no map edit when these associations remain unchanged.
49
+
50
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
51
+ application, create it from the actual source, schema, and existing Program
52
+ before implementation. This is ordinary explanatory adoption, not a blocker or
53
+ a reason to ask permission again. Preserve the existing Program and source;
54
+ inspect the relevant responsibilities and complete the smallest truthful map.
55
+ Initialization can create the empty file, but only the agent authors its meaning.
@@ -21,7 +21,8 @@ private design choice, or ambiguity into product intent.
21
21
 
22
22
  ## Program
23
23
 
24
- Organize Program beneath conceptual subsystem directories:
24
+ Organize Program in readable directories; membership is declared only in
25
+ `genesis/subsystems.md`, independently of directory names:
25
26
 
26
27
  ```text
27
28
  genesis/program/billing/invoices.md
@@ -64,3 +65,58 @@ The caller determines whether this is initial description, complete Program
64
65
  refresh, focused post-change reconciliation, Blueprint-only work, or read-only
65
66
  review. Respect the caller's edit boundary. Report ambiguity rather than
66
67
  inventing intent.
68
+
69
+ ## Subsystems
70
+
71
+ Maintain `genesis/subsystems.md` alongside Program in description and refresh
72
+ work. Blueprint-only work does not change the map; review remains read-only.
73
+ The map is the authored association authority, but responsibilities and table
74
+ references remain fallible explanations of application source and schema.
75
+
76
+ Use this exact version-zero grammar:
77
+
78
+ ```markdown
79
+ # Subsystems
80
+
81
+ ## `forms` Forms
82
+
83
+ Owns reusable form rendering, validation, and submission capture.
84
+
85
+ ### Program
86
+ - `genesis/program/forms/render.md`
87
+
88
+ ### Data owned
89
+ - Table `database` `default` `form_definitions`
90
+
91
+ ### Data used
92
+ - Nothing.
93
+ ```
94
+
95
+ Use one stable lowercase hyphenated id and a human title per subsystem. Each
96
+ entry needs responsibility prose and exactly the three sections shown. Empty
97
+ sections say `- Nothing.`; a new empty map contains only `# Subsystems` and
98
+ `- Nothing.`. Every Program module belongs to exactly one subsystem. No folder
99
+ inference, globs, duplicated operations, or duplicated table owners.
100
+
101
+ A table identity is three separate exact backticked values: the declared Stack
102
+ resource id, schema, and table. `default` means the resource's default schema;
103
+ use an explicit schema where applicable. Never record a host database name,
104
+ credential, SQL expression, or environment value. Data used references a table
105
+ owned by another declared subsystem. A subsystem may own data without Program
106
+ operations, or operations without data. Read the real schema/migrations before
107
+ assigning ownership; report uncertainty instead of inventing associations.
108
+
109
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
110
+ adds, removes, or changes a subsystem responsibility, Program membership, or
111
+ data ownership/use. Read the map before changing related code. Reuse existing
112
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
113
+ module exactly once; folders do not determine membership. Keep table references
114
+ grounded in schema/migrations; Genesis does not inspect databases. Private
115
+ helper changes need no map edit when these associations remain unchanged.
116
+
117
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
118
+ application, create it from the actual source, schema, and existing Program
119
+ before implementation. This is ordinary explanatory adoption, not a blocker or
120
+ a reason to ask permission again. Preserve the existing Program and source;
121
+ inspect the relevant responsibilities and complete the smallest truthful map.
122
+ Initialization can create the empty file, but only the agent authors its meaning.
@@ -152,3 +152,20 @@ required inputs and resources, declared project operations, and focused evidence
152
152
  with what actually exists. Report files changed, checks actually run,
153
153
  anything not proven, and anything still requiring attention. Never claim that
154
154
  an unrun check passed or that passing checks prove the whole product.
155
+
156
+ ## Subsystem maintenance
157
+
158
+ Maintain `genesis/subsystems.md` in the same implementation turn when a change
159
+ adds, removes, or changes a subsystem responsibility, Program membership, or
160
+ data ownership/use. Read the map before changing related code. Reuse existing
161
+ subsystems unless a distinct responsibility is evidenced. Declare each Program
162
+ module exactly once; folders do not determine membership. Keep table references
163
+ grounded in schema/migrations; Genesis does not inspect databases. Private
164
+ helper changes need no map edit when these associations remain unchanged.
165
+
166
+ If `genesis/subsystems.md` is missing, or is empty in an existing explained
167
+ application, create it from the actual source, schema, and existing Program
168
+ before implementation. This is ordinary explanatory adoption, not a blocker or
169
+ a reason to ask permission again. Preserve the existing Program and source;
170
+ inspect the relevant responsibilities and complete the smallest truthful map.
171
+ Initialization can create the empty file, but only the agent authors its meaning.
package/src/cli.js CHANGED
@@ -14,12 +14,16 @@ import {
14
14
  inspectEngineering,
15
15
  inspectEnvironment,
16
16
  inspectStackSection,
17
+ inspectSubsystems,
17
18
  inspectProject,
18
19
  inspectSkills,
19
20
  installCodex,
20
21
  listEngineeringProfiles,
21
22
  listStackPieces,
22
23
  listTemplates,
24
+ listParsers,
25
+ installParsers,
26
+ verifyParsers,
23
27
  migrate,
24
28
  setCollaboration,
25
29
  setEngineeringProfile,
@@ -44,6 +48,7 @@ import {
44
48
  import { projectSessionContext, projectTurnContext } from './index/session-context.js';
45
49
  import { installedFirstPartyStackPackages } from './index/stack-catalog.js';
46
50
  import { readStack } from './index/stack.js';
51
+ import { withParserCancellation } from './index/code-indexers/parsers.js';
47
52
 
48
53
  const USAGE = `Usage:
49
54
  genesis init
@@ -61,7 +66,11 @@ const USAGE = `Usage:
61
66
  genesis templates apply <catalogue:technology/variant>
62
67
  genesis context <path...>
63
68
  genesis index [function-or-path...]
69
+ genesis parsers list
70
+ genesis parsers install <language...> | --all
71
+ genesis parsers verify <language...> | --all
64
72
  genesis migrate
73
+ genesis inspect subsystems
65
74
  genesis inspect environment
66
75
  genesis inspect project
67
76
  genesis inspect skills
@@ -76,6 +85,8 @@ Options:
76
85
  --stack-package <name> Add an installed external Stack package (repeatable)
77
86
  --template-source <namespace=repository[#branch]> Add a template catalogue (repeatable)
78
87
  --task <task> Select the prompt task (default: work)
88
+ --directory <path> Parser cache directory (parsers commands only)
89
+ --all Select every parser (parsers install/verify only)
79
90
  --json Emit one machine-readable result
80
91
  -h, --help Show this help
81
92
 
@@ -84,7 +95,7 @@ prompt to the agent you already use. Review all edits through the ordinary Git
84
95
  diff, then run genesis verify for the Stack's concrete checks.
85
96
  `;
86
97
 
87
- const COMMANDS = new Set(['adopt', 'check', 'codex', 'collaboration', 'context', 'engineering', 'hook', 'index', 'init', 'inspect', 'migrate', 'prompt', 'skills', 'stack', 'templates', 'verify']);
98
+ const COMMANDS = new Set(['adopt', 'check', 'codex', 'collaboration', 'context', 'engineering', 'hook', 'index', 'init', 'inspect', 'migrate', 'parsers', 'prompt', 'skills', 'stack', 'templates', 'verify']);
88
99
 
89
100
  function parseCommand(argv) {
90
101
  if (argv.length === 0 || argv.includes('--help') || argv.includes('-h') || argv[0] === 'help') {
@@ -100,6 +111,8 @@ function parseCommand(argv) {
100
111
  strict: true,
101
112
  options: {
102
113
  json: { type: 'boolean', default: false },
114
+ all: { type: 'boolean', default: false },
115
+ directory: { type: 'string' },
103
116
  'project-root': { type: 'string' },
104
117
  'stack-package': { type: 'string', multiple: true, default: [] },
105
118
  'template-source': { type: 'string', multiple: true, default: [] },
@@ -111,6 +124,8 @@ function parseCommand(argv) {
111
124
  }
112
125
  const options = {
113
126
  json: parsed.values.json,
127
+ all: parsed.values.all,
128
+ directory: parsed.values.directory,
114
129
  projectRoot: parsed.values['project-root'],
115
130
  stackPackages: parsed.values['stack-package'],
116
131
  templateSources: parsed.values['template-source'].map((source) => {
@@ -123,13 +138,23 @@ function parseCommand(argv) {
123
138
  task: parsed.values.task,
124
139
  };
125
140
  const operands = parsed.positionals;
141
+ if (command !== 'parsers' && (options.all || options.directory !== undefined)) {
142
+ fail('CLI_OPTION_NOT_APPLICABLE', 'Options --all and --directory apply only to parsers commands.');
143
+ }
126
144
  if (options.templateSources.length && command !== 'templates') {
127
145
  fail('CLI_OPTION_NOT_APPLICABLE', `Option --template-source is not applicable to ${command}.`);
128
146
  }
129
147
  if (options.task !== undefined && command !== 'prompt') {
130
148
  fail('CLI_OPTION_NOT_APPLICABLE', `Option --task is not applicable to ${command}.`);
131
149
  }
132
- if (command === 'templates') {
150
+ if (command === 'parsers') {
151
+ const [action, ...languages] = operands;
152
+ if (!['list', 'install', 'verify'].includes(action)) fail('CLI_PARSER_ACTION_REQUIRED', 'Use parsers list, install, or verify.');
153
+ if (action === 'list' && (languages.length || options.all)) fail('CLI_EXTRA_ARGUMENT', 'parsers list accepts no languages or --all.');
154
+ if (action !== 'list' && (options.all ? languages.length : !languages.length)) {
155
+ fail('PARSER_SELECTION_INVALID', 'Choose parser names or --all.');
156
+ }
157
+ } else if (command === 'templates') {
133
158
  if (!(operands[0] === 'list' && operands.length === 1) && !(operands[0] === 'apply' && operands.length === 2)) {
134
159
  fail('CLI_TEMPLATE_ACTION_REQUIRED', 'Use templates list or templates apply <catalogue:technology/variant>.');
135
160
  }
@@ -182,12 +207,12 @@ function parseCommand(argv) {
182
207
  } else if (command === 'context' && operands.length === 0) {
183
208
  fail('CONTEXT_PATH_REQUIRED', 'Command context requires at least one project path.');
184
209
  } else if (command === 'inspect') {
185
- const ordinaryInspection = operands.length === 1 && ['environment', 'project', 'skills'].includes(operands[0]);
210
+ const ordinaryInspection = operands.length === 1 && ['environment', 'project', 'skills', 'subsystems'].includes(operands[0]);
186
211
  const sectionInspection = operands.length >= 2 && operands[0] === 'section';
187
212
  if (!ordinaryInspection && !sectionInspection) {
188
213
  fail(
189
214
  'CLI_INSPECT_TARGET_REQUIRED',
190
- 'Command inspect requires project, environment, skills, or section <name>.',
215
+ 'Command inspect requires project, environment, skills, subsystems, or section <name>.',
191
216
  );
192
217
  }
193
218
  } else if (command === 'hook' && (operands.length !== 1 || !['discover', 'session', 'turn'].includes(operands[0]))) {
@@ -272,7 +297,9 @@ function writeInspection(result) {
272
297
  line(process.stdout, `${title}: ${result.status}`);
273
298
  namedItems('Components', result.components);
274
299
  namedItems('Runtimes', result.runtimeRequirements);
275
- if (result.inspection === 'environment') {
300
+ if (result.inspection === 'subsystems') {
301
+ namedItems('Subsystems', result.subsystems.map(({ id, title }) => `${id}: ${title}`));
302
+ } else if (result.inspection === 'environment') {
276
303
  namedItems('Public defaults', result.environmentDefaults.map(({ name, value }) => `${name}=${value}`));
277
304
  namedItems('Environment files', result.files.map(({ format, path: filePath }) => `${format}: ${filePath}`));
278
305
  } else if (result.inspection === 'section') {
@@ -286,6 +313,12 @@ function writeInspection(result) {
286
313
  }
287
314
 
288
315
  function writeResult(command, result) {
316
+ if (command === 'parsers') {
317
+ line(process.stdout, `Parser directory: ${result.directory}`);
318
+ if (result.parsers) for (const parser of result.parsers) line(process.stdout, `${parser.language}: ${parser.status}${parser.message ? ` — ${parser.message}` : ''}`);
319
+ else line(process.stdout, `Parsers verified: ${result.languages.join(', ')}`);
320
+ return;
321
+ }
289
322
  if (command === 'inspect' && result.inspection === 'skills') {
290
323
  line(process.stdout, `Agent Skills: ${result.status}`);
291
324
  for (const skill of result.skills) line(process.stdout, ` - ${skill.name}: ${skill.status}`);
@@ -425,6 +458,13 @@ async function cliStackPackages(projectRoot, supplied) {
425
458
  }
426
459
 
427
460
  async function execute({ command, operands, options }, { signal } = {}) {
461
+ if (command === 'parsers') {
462
+ const parserOptions = {
463
+ languages: operands.slice(1), all: options.all, directory: options.directory, signal,
464
+ onProgress: (message) => line(process.stderr, message),
465
+ };
466
+ return { list: listParsers, install: installParsers, verify: verifyParsers }[operands[0]](parserOptions);
467
+ }
428
468
  const projectRoot = options.projectRoot || process.cwd();
429
469
  const stackPackages = await cliStackPackages(projectRoot, options.stackPackages || []);
430
470
  if (command === 'init') return initialize({ projectRoot, stackPackages });
@@ -511,7 +551,7 @@ async function execute({ command, operands, options }, { signal } = {}) {
511
551
  }),
512
552
  };
513
553
  }
514
- const inspections = { environment: inspectEnvironment, project: inspectProject, skills: inspectSkills };
554
+ const inspections = { subsystems: inspectSubsystems, environment: inspectEnvironment, project: inspectProject, skills: inspectSkills };
515
555
  return {
516
556
  inspection: operands[0],
517
557
  ...await inspections[operands[0]]({ projectRoot, stackPackages }),
@@ -578,7 +618,7 @@ async function optionalProjectFormat(projectRoot) {
578
618
  }
579
619
 
580
620
  function requiresCurrentProjectFormat(command, operands) {
581
- if (['check', 'codex', 'hook', 'migrate'].includes(command)) return false;
621
+ if (['check', 'codex', 'hook', 'migrate', 'parsers'].includes(command)) return false;
582
622
  if (command === 'engineering') {
583
623
  return operands[0] === 'set' || (operands[0] === 'show' && operands.length === 1);
584
624
  }
@@ -607,7 +647,7 @@ export async function runCli(argv = process.argv.slice(2)) {
607
647
  return 0;
608
648
  }
609
649
  options = parsed.options;
610
- const projectFormat = await optionalProjectFormat(options.projectRoot || process.cwd());
650
+ const projectFormat = parsed.command === 'parsers' ? null : await optionalProjectFormat(options.projectRoot || process.cwd());
611
651
  const formatDiagnostic = projectFormatDiagnostic(projectFormat);
612
652
  if (formatDiagnostic && !options.json) {
613
653
  line(process.stderr, `WARNING: ${formatDiagnostic.message}`);
@@ -615,13 +655,10 @@ export async function runCli(argv = process.argv.slice(2)) {
615
655
  if (formatDiagnostic && requiresCurrentProjectFormat(parsed.command, parsed.operands)) {
616
656
  fail(formatDiagnostic.code, formatDiagnostic.message, formatDiagnostic.details);
617
657
  }
618
- const finiteCommand = parsed.command === 'verify';
619
- if (finiteCommand) {
620
- process.once('SIGINT', abort);
621
- process.once('SIGTERM', abort);
622
- }
658
+ process.once('SIGINT', abort);
659
+ process.once('SIGTERM', abort);
623
660
  const result = withProjectFormatWarning(
624
- await execute(parsed, { signal: controller.signal }),
661
+ await withParserCancellation(controller.signal, () => execute(parsed, { signal: controller.signal })),
625
662
  projectFormat,
626
663
  formatDiagnostic,
627
664
  );
@@ -1,7 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
- import { astGrepCodeIndexers } from './code-indexers/ast-grep.js';
4
+ import { extractCodeIndexes } from './code-indexers/ast-grep.js';
5
5
  import { asDiagnostic } from './errors.js';
6
6
  import { buildCityPresentation } from './city-presentation.js';
7
7
  import { cityPathExcluded } from './stack-city-presentation.js';
@@ -28,8 +28,6 @@ export const GENESIS_DERIVED_ARTIFACTS = Object.freeze([
28
28
  }),
29
29
  ]);
30
30
 
31
- const INDEXERS = astGrepCodeIndexers;
32
-
33
31
  function directoryId(value) {
34
32
  return `directory:${value || '.'}`;
35
33
  }
@@ -43,7 +41,7 @@ function subsystemId(value) {
43
41
  }
44
42
 
45
43
  function operationId(module) {
46
- return `operation:${module.subsystem}/${module.name}`;
44
+ return `operation:${module.path}`;
47
45
  }
48
46
 
49
47
  function parentDirectory(value) {
@@ -70,18 +68,10 @@ function directoryRecords(paths) {
70
68
  }
71
69
 
72
70
  function subsystemRecords(subsystems) {
73
- const paths = new Set();
74
- for (const subsystem of subsystems) {
75
- const segments = subsystem.split('/');
76
- for (let count = 1; count <= segments.length; count += 1) {
77
- paths.add(segments.slice(0, count).join('/'));
78
- }
79
- }
80
- return [...paths].sort().map((subsystem) => ({
81
- id: subsystemId(subsystem),
82
- path: subsystem,
83
- title: path.posix.basename(subsystem).replace(/-/gu, ' '),
84
- parentId: subsystem.includes('/') ? subsystemId(parentDirectory(subsystem)) : null,
71
+ return subsystems.map((entry) => ({
72
+ id: subsystemId(entry.id), path: entry.id, title: entry.title,
73
+ description: entry.description, parentId: null,
74
+ dataOwned: entry.dataOwned, dataUsed: entry.dataUsed,
85
75
  }));
86
76
  }
87
77
 
@@ -195,7 +185,7 @@ async function programCity(projectRoot) {
195
185
  implementationMap: module.implementationMap,
196
186
  path: module.path,
197
187
  subsystem: module.subsystem,
198
- districtId: subsystemId(module.subsystem),
188
+ districtId: module.subsystem === null ? null : subsystemId(module.subsystem),
199
189
  sources: module.sources,
200
190
  sourceFileIds: module.sources.map(fileId),
201
191
  }));
@@ -203,9 +193,9 @@ async function programCity(projectRoot) {
203
193
  schema: 'genesis.program-city.v1',
204
194
  schemaVersion: 1,
205
195
  status: program.status,
206
- programHash: sha256(stableJson(operations)),
196
+ programHash: sha256(stableJson({ operations, subsystems: program.subsystemMap })),
207
197
  diagnostics: [],
208
- districts: subsystemRecords(program.subsystems),
198
+ districts: subsystemRecords(program.subsystemMap.subsystems),
209
199
  buildings: operations,
210
200
  links: operations.flatMap((operation) => operation.sourceFileIds.map((targetId) => ({
211
201
  kind: 'implemented-by',
@@ -270,22 +260,7 @@ export async function buildProjectIndex({
270
260
  ))
271
261
  .map(([filePath, state]) => ({ path: filePath, hash: state.hash, mode: state.mode }));
272
262
  const indexers = [...new Set(stack.components.flatMap((component) => component.indexers || []))].sort();
273
- const contributions = [];
274
- const diagnostics = [];
275
- for (const id of indexers) {
276
- const indexer = INDEXERS.get(id);
277
- if (!indexer) {
278
- diagnostics.push({ code: 'CODE_INDEXER_UNAVAILABLE', message: `No installed code indexer exists for ${id}.` });
279
- continue;
280
- }
281
- try {
282
- const contribution = await indexer.extract({ files, projectRoot: root });
283
- contributions.push({ ...contribution, extractor: id });
284
- diagnostics.push(...(contribution.diagnostics || []).map((diagnostic) => ({ ...diagnostic, extractor: id })));
285
- } catch (error) {
286
- diagnostics.push({ ...asDiagnostic(error), code: 'CODE_INDEXER_FAILED', extractor: id });
287
- }
288
- }
263
+ const { contributions, diagnostics } = await extractCodeIndexes({ indexers, files, projectRoot: root });
289
264
  const machine = machineCity({
290
265
  cityRegions: stack.cityRegions,
291
266
  components: stack.components.map(({ id }) => id),