genesis-compiler 1.7.4 → 1.7.5
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/docs/prompt-integration.md +9 -0
- package/package.json +1 -1
- package/src/cli.js +24 -3
- package/src/index/claude-hooks.js +6 -3
- package/src/index/hook-command.js +3 -3
|
@@ -198,6 +198,15 @@ init`. All receive the same short operating guide for a new session and after
|
|
|
198
198
|
compaction. The Codex session hook sets `additionalContextLimit: 0` so native
|
|
199
199
|
truncation cannot remove part of the bounded guide or host contribution.
|
|
200
200
|
Codex and Claude Code use `SessionStart` for startup, resume, clear, and compact events.
|
|
201
|
+
Claude's generated session hooks use `genesis hook session --claude-part <1-4>`.
|
|
202
|
+
Claude caps each hook output at 10,000 characters, so four hooks deliver the
|
|
203
|
+
unchanged guide in numbered parts below that limit. Empty parts emit nothing.
|
|
204
|
+
A guide above 36,000 characters delivers an explicit guidance-unavailable
|
|
205
|
+
warning instead of a partial guide. Claude does not enforce startup hook stop
|
|
206
|
+
requests; this warning instructs the model to report the problem before work,
|
|
207
|
+
and is not a runtime admission control. Turn guidance is already bounded at 512
|
|
208
|
+
bytes and uses the ordinary turn command. This is provider transport only;
|
|
209
|
+
composition and the host bridge remain shared.
|
|
201
210
|
Claude settings preserve unrelated hooks and configuration. The common operating
|
|
202
211
|
guide lists installed project skills and their canonical `.agents/skills/` paths;
|
|
203
212
|
an agent reads the relevant skill before following its workflow. Genesis exposes
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -88,6 +88,7 @@ Options:
|
|
|
88
88
|
--directory <path> Parser cache directory (parsers commands only)
|
|
89
89
|
--all Select every parser (parsers install/verify only)
|
|
90
90
|
--json Emit one machine-readable result
|
|
91
|
+
--claude-part <1-4> Emit one bounded Claude session-hook part
|
|
91
92
|
-h, --help Show this help
|
|
92
93
|
|
|
93
94
|
Genesis generates prompts; it never starts or manages an AI agent. Give the
|
|
@@ -111,6 +112,7 @@ function parseCommand(argv) {
|
|
|
111
112
|
strict: true,
|
|
112
113
|
options: {
|
|
113
114
|
json: { type: 'boolean', default: false },
|
|
115
|
+
'claude-part': { type: 'string' },
|
|
114
116
|
all: { type: 'boolean', default: false },
|
|
115
117
|
directory: { type: 'string' },
|
|
116
118
|
'project-root': { type: 'string' },
|
|
@@ -124,6 +126,7 @@ function parseCommand(argv) {
|
|
|
124
126
|
}
|
|
125
127
|
const options = {
|
|
126
128
|
json: parsed.values.json,
|
|
129
|
+
claudePart: parsed.values['claude-part'],
|
|
127
130
|
all: parsed.values.all,
|
|
128
131
|
directory: parsed.values.directory,
|
|
129
132
|
projectRoot: parsed.values['project-root'],
|
|
@@ -138,6 +141,9 @@ function parseCommand(argv) {
|
|
|
138
141
|
task: parsed.values.task,
|
|
139
142
|
};
|
|
140
143
|
const operands = parsed.positionals;
|
|
144
|
+
if (options.claudePart !== undefined && (command !== 'hook' || operands[0] !== 'session' || !/^[1-4]$/u.test(options.claudePart) || options.json)) {
|
|
145
|
+
fail('CLI_OPTION_NOT_APPLICABLE', 'Option --claude-part requires hook session, a part from 1 to 4, and no --json.');
|
|
146
|
+
}
|
|
141
147
|
if (command !== 'parsers' && (options.all || options.directory !== undefined)) {
|
|
142
148
|
fail('CLI_OPTION_NOT_APPLICABLE', 'Options --all and --directory apply only to parsers commands.');
|
|
143
149
|
}
|
|
@@ -665,9 +671,24 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
665
671
|
projectFormat,
|
|
666
672
|
formatDiagnostic,
|
|
667
673
|
);
|
|
668
|
-
if (options.
|
|
669
|
-
|
|
670
|
-
|
|
674
|
+
if (options.claudePart !== undefined) {
|
|
675
|
+
// Claude spills each hook output over 10,000 characters to a file.
|
|
676
|
+
// Four native hooks deliver the exact text in bounded parts instead.
|
|
677
|
+
if (result.output.length > 36000) {
|
|
678
|
+
if (options.claudePart === '1') line(process.stdout,
|
|
679
|
+
'GENESIS GUIDANCE UNAVAILABLE: The session guide exceeds the Claude hook capacity of 36,000 characters. The guide was not delivered. Tell the user before doing project work. Shorten the project guidance and restart the conversation to load it completely.');
|
|
680
|
+
return 0;
|
|
681
|
+
}
|
|
682
|
+
let start = (Number(options.claudePart) - 1) * 9000;
|
|
683
|
+
let end = start + 9000;
|
|
684
|
+
// Keep UTF-16 surrogate pairs together at the shared part boundaries.
|
|
685
|
+
if (/[\uDC00-\uDFFF]/u.test(result.output[start] || '')) start--;
|
|
686
|
+
if (/[\uDC00-\uDFFF]/u.test(result.output[end] || '')) end--;
|
|
687
|
+
const part = result.output.slice(start, end);
|
|
688
|
+
if (part) process.stdout.write(`Genesis session guidance, part ${options.claudePart} of ${Math.ceil(result.output.length / 9000)} (read in numeric order):\n${part}`);
|
|
689
|
+
} else if (options.json) line(process.stdout, JSON.stringify(result));
|
|
690
|
+
else writeResult(parsed.command, result);
|
|
691
|
+
if (!options.json) {
|
|
671
692
|
for (const warning of result.warnings || []) {
|
|
672
693
|
if (warning.code !== formatDiagnostic?.code) line(process.stderr, `WARNING: ${warning.code}: ${warning.message}`);
|
|
673
694
|
}
|
|
@@ -7,8 +7,11 @@ import { genesisHookCommand } from './hook-command.js';
|
|
|
7
7
|
import { writeFileAtomic } from './utils.js';
|
|
8
8
|
|
|
9
9
|
const SETTINGS_PATH = '.claude/settings.json';
|
|
10
|
-
const
|
|
10
|
+
const SESSION_COMMANDS = [1, 2, 3, 4].map((claudePart) => genesisHookCommand('session', { claudePart }));
|
|
11
11
|
const TURN_COMMAND = genesisHookCommand('turn');
|
|
12
|
+
// Refresh exact generated commands, including the previous plain-text output.
|
|
13
|
+
const OWNED_COMMANDS = new Set([...SESSION_COMMANDS, TURN_COMMAND,
|
|
14
|
+
genesisHookCommand('session')]);
|
|
12
15
|
|
|
13
16
|
async function renderedSettings(projectRoot) {
|
|
14
17
|
const root = (await gitContext(projectRoot)).repositoryRoot;
|
|
@@ -34,13 +37,13 @@ async function renderedSettings(projectRoot) {
|
|
|
34
37
|
}
|
|
35
38
|
value.hooks[event] = groups.flatMap((group) => {
|
|
36
39
|
if (!Array.isArray(group?.hooks)) return [group];
|
|
37
|
-
const hooks = group.hooks.filter((hook) => !
|
|
40
|
+
const hooks = group.hooks.filter((hook) => !OWNED_COMMANDS.has(hook?.command));
|
|
38
41
|
return hooks.length ? [{ ...group, hooks }] : [];
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
(value.hooks.SessionStart ||= []).push({
|
|
42
45
|
matcher: '^(startup|resume|clear|compact)$',
|
|
43
|
-
hooks:
|
|
46
|
+
hooks: SESSION_COMMANDS.map((command) => ({ type: 'command', command, timeout: 30 })),
|
|
44
47
|
});
|
|
45
48
|
(value.hooks.UserPromptSubmit ||= []).push({
|
|
46
49
|
hooks: [{ type: 'command', command: TURN_COMMAND, timeout: 30 }],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function genesisHookCommand(action) {
|
|
2
|
-
const local = `const {runCli}=await import('genesis-compiler/cli');process.exitCode=await runCli(['hook','${action}'])`;
|
|
3
|
-
return `if command -v genesis >/dev/null 2>&1; then genesis hook ${action}; else node --input-type=module --eval "${local}"; fi`;
|
|
1
|
+
export function genesisHookCommand(action, { claudePart } = {}) {
|
|
2
|
+
const local = `const {runCli}=await import('genesis-compiler/cli');process.exitCode=await runCli(['hook','${action}'${claudePart ? `,'--claude-part','${claudePart}'` : ''}])`;
|
|
3
|
+
return `if command -v genesis >/dev/null 2>&1; then genesis hook ${action}${claudePart ? ` --claude-part ${claudePart}` : ''}; else node --input-type=module --eval "${local}"; fi`;
|
|
4
4
|
}
|