genesis-compiler 1.2.25 → 1.2.26

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
@@ -21,7 +21,8 @@ genesis/
21
21
  .agents/skills/
22
22
  genesis-* Genesis workflow skills
23
23
  <technology>/ authoritative skills installed by selected Stack pieces
24
- .codex/hooks.json optional project-local Codex lifecycle integration
24
+ .codex/hooks.json project-local Codex lifecycle integration
25
+ .opencode/plugins/ project-local OpenCode lifecycle integration
25
26
  .genesis/
26
27
  machine-city.json derived detailed source and function map
27
28
  program-city.json derived simple subsystem and operation map
@@ -108,12 +109,13 @@ empty or already-adopted project.
108
109
 
109
110
  `genesis init` creates an empty Blueprint, a focused engineering approach, an
110
111
  empty optional Stack, the current `genesis/version`, three project Agent Skills,
111
- and one project-local Codex guidance hook. The skills are ordinary
112
+ and project-local guidance adapters for Codex and OpenCode. The skills are ordinary
112
113
  [Agent Skills](https://agentskills.io): `genesis-project`, `genesis-program`,
113
114
  and `genesis-deslop`, each with standard `SKILL.md` and `agents/openai.yaml`
114
- metadata. Open `/hooks` once in Codex to review and trust the guidance hook. After
115
- that, use Codex normally. No technology is assumed until you explicitly select
116
- Stack components:
115
+ metadata. Open `/hooks` once in Codex to review and trust the guidance hook;
116
+ OpenCode discovers its local project plugin automatically. Both use the same
117
+ Genesis-owned operating guide for new sessions and after compaction. No
118
+ technology is assumed until you explicitly select Stack components:
117
119
 
118
120
  ```bash
119
121
  genesis stack add --stack-package genesis-stack nodejs
@@ -263,11 +265,12 @@ Python, Java, C#, C/C++, PHP, Go, Rust, Ruby, Kotlin, and Shell. A future techno
263
265
  adds its adapter through its Stack piece rather than teaching Genesis prompts
264
266
  to parse that language.
265
267
 
266
- The project Codex hook injects a short operating guide at startup, resume,
267
- clear, and compaction. It records no turn state, blocks no Stop, and requests no
268
- follow-up turn. Implementation prompts compose selected Stack `Post-change`
269
- guidance into the original implementation turn, and the project skill keeps
270
- intentional Blueprint and affected Program changes aligned there.
268
+ The project Codex hook and OpenCode plugin use the same short operating guide
269
+ for a new session and after compaction. They record no conversation message,
270
+ block no completion, and request no follow-up turn. Implementation prompts
271
+ compose selected Stack `Post-change` guidance into the original implementation
272
+ turn, and the project skill keeps intentional Blueprint and affected Program
273
+ changes aligned there.
271
274
 
272
275
  Deslop runs only when explicitly requested. With no stated scope it reviews the
273
276
  latest commit; “the last N commits” reviews the net surviving first-parent
@@ -77,10 +77,13 @@ does not perform adoption automatically. If the user accepts, Codex runs the
77
77
  command itself and follows its returned prompt in the same conversation. Other
78
78
  hosts can call `adoptProject()` and send its `prompt` directly.
79
79
 
80
- Codex can instead use the project-local hook installed by `genesis init`.
81
- That hook injects a short operating guide at startup, resume, clear, and
82
- compaction. It does not record user turns, authorize edits, intercept Stop, or
83
- request another model turn. Selected `Post-change` contributions are included
80
+ Codex and OpenCode can use the project-local adapters installed by `genesis
81
+ init`. Both receive the same short operating guide for a new session and after
82
+ compaction. Codex uses `SessionStart` for startup, clear, and compact events.
83
+ OpenCode renders once per session, keeps that value active in its stateless
84
+ model requests, and rerenders after its `session.compacted` event. Neither
85
+ adapter records a user message, authorizes edits, intercepts completion, or
86
+ requests another model turn. Selected `Post-change` contributions are included
84
87
  in the original implementation prompt, whose project skill also keeps
85
88
  intentional Blueprint and affected Program changes aligned.
86
89
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.2.25",
3
+ "version": "1.2.26",
4
4
  "type": "module",
5
- "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with optional Codex hooks.",
5
+ "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with project agent guidance.",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/mobily-enterprises/genesis-compiler.git"
@@ -36,6 +36,7 @@
36
36
  "prompts/start.txt",
37
37
  "prompts/work.txt",
38
38
  "plugins/genesis",
39
+ "plugins/opencode",
39
40
  "profiles",
40
41
  "skills",
41
42
  "src"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.2.25",
3
+ "version": "1.2.26",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -0,0 +1,57 @@
1
+ // Generated by Genesis. Run `genesis migrate` to refresh this project plugin.
2
+ import { execFile } from "node:child_process";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+
6
+ const execute = promisify(execFile);
7
+
8
+ async function renderProjectContext(projectRoot) {
9
+ const { stdout } = await execute(
10
+ "genesis",
11
+ ["hook", "session", "--project-root", projectRoot],
12
+ {
13
+ cwd: projectRoot,
14
+ maxBuffer: 256 * 1024,
15
+ timeout: 5_000
16
+ }
17
+ );
18
+ return stdout.trimEnd();
19
+ }
20
+
21
+ function eventSessionId(event = {}) {
22
+ return String(
23
+ event.properties?.sessionID || event.properties?.info?.id || ""
24
+ ).trim();
25
+ }
26
+
27
+ export const GenesisProjectGuidance = async ({ directory, worktree } = {}) => {
28
+ const projectRoot = path.resolve(worktree || directory || process.cwd());
29
+ const contexts = new Map();
30
+
31
+ function contextForSession(sessionId) {
32
+ let context = contexts.get(sessionId);
33
+ if (!context) {
34
+ context = renderProjectContext(projectRoot);
35
+ contexts.set(sessionId, context);
36
+ context.catch(() => {
37
+ if (contexts.get(sessionId) === context) contexts.delete(sessionId);
38
+ });
39
+ }
40
+ return context;
41
+ }
42
+
43
+ return {
44
+ event: async ({ event } = {}) => {
45
+ if (!["session.compacted", "session.deleted"].includes(event?.type)) return;
46
+ const sessionId = eventSessionId(event);
47
+ if (sessionId) contexts.delete(sessionId);
48
+ },
49
+ "experimental.chat.system.transform": async (input = {}, output = {}) => {
50
+ const sessionId = String(input.sessionID || input.sessionId || "").trim();
51
+ if (!sessionId) return;
52
+ const context = await contextForSession(sessionId);
53
+ const system = Array.isArray(output.system) ? output.system : [];
54
+ if (!system.includes(context)) output.system = [...system, context];
55
+ }
56
+ };
57
+ };
package/src/cli.js CHANGED
@@ -21,7 +21,6 @@ import {
21
21
  } from './index.js';
22
22
  import {
23
23
  codexAdoptionRecommendation,
24
- codexSessionContext,
25
24
  } from './index/codex-hooks.js';
26
25
  import { engineeringProfile, readEngineeringBaseline } from './index/engineering.js';
27
26
  import { asDiagnostic, fail } from './index/errors.js';
@@ -29,6 +28,7 @@ import {
29
28
  inspectProjectFormat,
30
29
  projectFormatDiagnostic,
31
30
  } from './index/project-format.js';
31
+ import { projectSessionContext } from './index/session-context.js';
32
32
 
33
33
  const USAGE = `Usage:
34
34
  genesis init
@@ -362,7 +362,7 @@ async function execute({ command, operands, options }, { signal } = {}) {
362
362
  if (operands[0] === 'discover') {
363
363
  return { kind: 'discover', ...await codexAdoptionRecommendation({ projectRoot }) };
364
364
  }
365
- return { kind: 'session', ...await codexSessionContext({ projectRoot }) };
365
+ return { kind: 'session', ...await projectSessionContext({ projectRoot }) };
366
366
  }
367
367
  if (command === 'verify') {
368
368
  return verify({
@@ -1,12 +1,10 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
- import { readEngineering, readEngineeringBaseline } from './engineering.js';
5
4
  import { GenesisError } from './errors.js';
6
5
  import { gitContext } from './git.js';
7
6
  import { isProjectContentPath } from './paths.js';
8
7
  import { runGit } from './process.js';
9
- import { readStack } from './stack.js';
10
8
  import { normalizeRelative, pathState, writeFileAtomic } from './utils.js';
11
9
 
12
10
  const HOOKS_PATH = '.codex/hooks.json';
@@ -20,7 +18,7 @@ function hookCommand(action) {
20
18
  const SESSION_HOOK = {
21
19
  event: 'SessionStart',
22
20
  group: {
23
- matcher: '^(startup|resume|clear|compact)$',
21
+ matcher: '^(startup|clear|compact)$',
24
22
  hooks: [{
25
23
  type: 'command',
26
24
  command: hookCommand('session'),
@@ -90,58 +88,6 @@ export async function installCodexHooks({ projectRoot } = {}) {
90
88
  return { status: 'updated', changedFiles: [HOOKS_PATH] };
91
89
  }
92
90
 
93
- async function optionalStack(projectRoot) {
94
- try { return await readStack(projectRoot); } catch { return null; }
95
- }
96
-
97
- async function optionalEngineering(projectRoot) {
98
- try {
99
- return await readEngineering(projectRoot);
100
- } catch {
101
- return {
102
- guidance: [
103
- '## Universal complexity gate',
104
- '',
105
- await readEngineeringBaseline(),
106
- '',
107
- 'The project engineering profile is invalid. Do not infer a replacement; run the Genesis `check` operation and ask the user before implementation if the selected approach matters.',
108
- ].join('\n'),
109
- profile: null,
110
- };
111
- }
112
- }
113
-
114
- export async function codexSessionContext({ projectRoot } = {}) {
115
- const root = (await gitContext(projectRoot)).repositoryRoot;
116
- const [stack, engineering] = await Promise.all([
117
- optionalStack(root),
118
- optionalEngineering(root),
119
- ]);
120
- const selected = stack?.components.map(({ id }) => id) || [];
121
- const stackStatus = stack
122
- ? (selected.length > 0 ? selected.join(', ') : 'none')
123
- : 'unavailable; run the Genesis `check` operation';
124
- return {
125
- status: 'ready',
126
- output: [
127
- 'This is a Genesis-enriched project.',
128
- '- Read `genesis/blueprint.md`, `genesis/engineering.md`, and `genesis/stack.md` for product intent, engineering approach, and selected technology.',
129
- '- Use the relevant project Agent Skills below `.agents/skills/`.',
130
- '- After locating source, run `genesis context <path...>`; before adding a helper or public operation, run `genesis index <name-or-path...>` and reuse an existing owner.',
131
- '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
132
- '- Keep Blueprint and affected Program explanations aligned with intentional observable product behavior in the same implementation turn. Private restructuring may need only source citations or no explanatory change.',
133
- '- Deslop only when explicitly requested. Genesis defines its behavior-preserving committed scope; selected Stack components may add technology-specific cleanup guidance.',
134
- '- This guidance refreshes after startup, resume, clear, and compaction. Continue the active request without restarting completed work.',
135
- `Engineering profile: ${engineering.profile?.id || 'invalid; run the Genesis `check` operation'}.`,
136
- `Selected Stack components: ${stackStatus}.`,
137
- '',
138
- 'ENGINEERING APPROACH',
139
- '',
140
- engineering.guidance,
141
- ].join('\n'),
142
- };
143
- }
144
-
145
91
  function zeroPaths(buffer) {
146
92
  return [...new Set(buffer.toString('utf8').split('\0').map(normalizeRelative).filter(Boolean))];
147
93
  }
package/src/index/init.js CHANGED
@@ -6,6 +6,7 @@ import { BLUEPRINT_SKELETON_SOURCE } from './blueprint.js';
6
6
  import { installCodexHooks } from './codex-hooks.js';
7
7
  import { ENGINEERING_SKELETON_SOURCE } from './engineering.js';
8
8
  import { gitContext } from './git.js';
9
+ import { installOpenCodePlugin } from './opencode-plugin.js';
9
10
  import {
10
11
  BLUEPRINT_PATH,
11
12
  ENGINEERING_PATH,
@@ -44,22 +45,32 @@ export async function initializeProject({ projectRoot, stackPackages = [] } = {}
44
45
  await mkdir(path.join(root, PROGRAM_ROOT), { recursive: true });
45
46
  const stack = await readStack(root, { stackPackages });
46
47
  const skills = await syncProjectSkills({ projectRoot: root, stack });
47
- const hooks = await installCodexHooks({ projectRoot: root });
48
+ const [hooks, openCodePlugin] = await Promise.all([
49
+ installCodexHooks({ projectRoot: root }),
50
+ installOpenCodePlugin({ projectRoot: root }),
51
+ ]);
48
52
  const version = projectFormat.status === 'uninitialized'
49
53
  ? await createIfMissing(root, PROJECT_VERSION_PATH, CURRENT_PROJECT_FORMAT_SOURCE)
50
54
  : null;
51
- const changedFiles = [...created, ...hooks.changedFiles, ...skills.changedFiles, version]
55
+ const changedFiles = [
56
+ ...created,
57
+ ...hooks.changedFiles,
58
+ ...openCodePlugin.changedFiles,
59
+ ...skills.changedFiles,
60
+ version,
61
+ ]
52
62
  .filter(Boolean)
53
63
  .sort();
54
64
  return {
55
65
  status: changedFiles.length > 0 ? 'updated' : 'unchanged',
56
66
  summary: changedFiles.length > 0
57
- ? 'Initialized Genesis, Agent Skills, and the project Codex guidance hook.'
58
- : 'Genesis, its Agent Skills, and the project Codex guidance hook are already initialized.',
67
+ ? 'Initialized Genesis, Agent Skills, and project guidance for Codex and OpenCode.'
68
+ : 'Genesis, its Agent Skills, and project guidance for Codex and OpenCode are already initialized.',
59
69
  changedFiles,
60
70
  diagnostics: skills.diagnostics,
61
71
  guidance: [
62
72
  'Open Codex and use /hooks to review and trust the project guidance hook.',
73
+ 'OpenCode loads the project guidance plugin automatically.',
63
74
  'Genesis workflow skills are available in .agents/skills/.',
64
75
  'Describe product intent in genesis/blueprint.md.',
65
76
  'Choose the project engineering approach with genesis engineering set <profile>.',
@@ -0,0 +1,28 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { gitContext } from './git.js';
5
+ import { writeFileAtomic } from './utils.js';
6
+
7
+ const OPENCODE_PLUGIN_PATH = '.opencode/plugins/genesis-project-guidance.js';
8
+ const OPENCODE_PLUGIN_SOURCE = new URL('../../plugins/opencode/project-guidance.js', import.meta.url);
9
+
10
+ async function existingSource(location) {
11
+ try {
12
+ return await readFile(location, 'utf8');
13
+ } catch (error) {
14
+ if (['ENOENT', 'ENOTDIR'].includes(error?.code)) return null;
15
+ throw error;
16
+ }
17
+ }
18
+
19
+ export async function installOpenCodePlugin({ projectRoot } = {}) {
20
+ const root = (await gitContext(projectRoot)).repositoryRoot;
21
+ const location = path.join(root, OPENCODE_PLUGIN_PATH);
22
+ const source = await readFile(OPENCODE_PLUGIN_SOURCE, 'utf8');
23
+ if (await existingSource(location) === source) {
24
+ return { status: 'unchanged', changedFiles: [] };
25
+ }
26
+ await writeFileAtomic(location, source);
27
+ return { status: 'updated', changedFiles: [OPENCODE_PLUGIN_PATH] };
28
+ }
@@ -0,0 +1,55 @@
1
+ import { readEngineering, readEngineeringBaseline } from './engineering.js';
2
+ import { gitContext } from './git.js';
3
+ import { readStack } from './stack.js';
4
+
5
+ async function optionalStack(projectRoot, stackPackages) {
6
+ try { return await readStack(projectRoot, { stackPackages }); } catch { return null; }
7
+ }
8
+
9
+ async function optionalEngineering(projectRoot) {
10
+ try {
11
+ return await readEngineering(projectRoot);
12
+ } catch {
13
+ return {
14
+ guidance: [
15
+ '## Universal complexity gate',
16
+ '',
17
+ await readEngineeringBaseline(),
18
+ '',
19
+ 'The project engineering profile is invalid. Do not infer a replacement; run the Genesis `check` operation and ask the user before implementation if the selected approach matters.',
20
+ ].join('\n'),
21
+ profile: null,
22
+ };
23
+ }
24
+ }
25
+
26
+ export async function projectSessionContext({ projectRoot, stackPackages = [] } = {}) {
27
+ const root = (await gitContext(projectRoot)).repositoryRoot;
28
+ const [stack, engineering] = await Promise.all([
29
+ optionalStack(root, stackPackages),
30
+ optionalEngineering(root),
31
+ ]);
32
+ const selected = stack?.components.map(({ id }) => id) || [];
33
+ const stackStatus = stack
34
+ ? (selected.length > 0 ? selected.join(', ') : 'none')
35
+ : 'unavailable; run the Genesis `check` operation';
36
+ return {
37
+ status: 'ready',
38
+ output: [
39
+ 'This is a Genesis-enriched project.',
40
+ '- Read `genesis/blueprint.md`, `genesis/engineering.md`, and `genesis/stack.md` for product intent, engineering approach, and selected technology.',
41
+ '- Use the relevant project Agent Skills below `.agents/skills/`.',
42
+ '- After locating source, run `genesis context <path...>`; before adding a helper or public operation, run `genesis index <name-or-path...>` and reuse an existing owner.',
43
+ '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
44
+ '- Keep Blueprint and affected Program explanations aligned with intentional observable product behavior in the same implementation turn. Private restructuring may need only source citations or no explanatory change.',
45
+ '- Deslop only when explicitly requested. Genesis defines its behavior-preserving committed scope; selected Stack components may add technology-specific cleanup guidance.',
46
+ '- This guidance is loaded for a new session and refreshed after compaction. Continue the active request without restarting completed work.',
47
+ `Engineering profile: ${engineering.profile?.id || 'invalid; run the Genesis `check` operation'}.`,
48
+ `Selected Stack components: ${stackStatus}.`,
49
+ '',
50
+ 'ENGINEERING APPROACH',
51
+ '',
52
+ engineering.guidance,
53
+ ].join('\n'),
54
+ };
55
+ }