genesis-compiler 1.2.19 → 1.2.20
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/package.json +1 -1
- package/plugins/genesis/.codex-plugin/plugin.json +1 -1
- package/prompts/deslop.txt +3 -1
- package/prompts/reconcile.txt +3 -2
- package/src/cli.js +1 -0
- package/src/index/codex-hooks.js +218 -72
package/package.json
CHANGED
package/prompts/deslop.txt
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
For every repository listed in the task, read and follow that repository's
|
|
2
|
+
`.agents/skills/genesis-deslop/SKILL.md` completely. Review and clean every
|
|
3
|
+
listed repository before responding.
|
|
2
4
|
|
|
3
5
|
This is a dedicated behavior-preserving cleanup task, separate from
|
|
4
6
|
implementation. Review the requested scope, or otherwise the ordinary Git diff
|
package/prompts/reconcile.txt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
For every repository listed in the task, read and follow that repository's
|
|
2
|
+
`.agents/skills/genesis-program/SKILL.md` completely, operating in focused
|
|
3
|
+
post-change reconciliation mode.
|
|
3
4
|
|
|
4
5
|
Use the preceding implementation turn, listed changed paths, actual Git diff or
|
|
5
6
|
history, and relevant tests. Update Blueprint only for intentional observable
|
package/src/cli.js
CHANGED
|
@@ -257,6 +257,7 @@ function writeResult(command, result) {
|
|
|
257
257
|
}
|
|
258
258
|
if (command === 'hook') {
|
|
259
259
|
if (['discover', 'session'].includes(result.kind) && result.output) line(process.stdout, result.output);
|
|
260
|
+
else if (result.kind === 'authorize') line(process.stdout, `authorize: ${result.status}`);
|
|
260
261
|
else if (result.kind === 'stop') line(process.stdout, JSON.stringify(result.output));
|
|
261
262
|
return;
|
|
262
263
|
}
|
package/src/index/codex-hooks.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
|
|
4
5
|
import { readInstalledAsset } from './assets.js';
|
|
@@ -13,6 +14,7 @@ import { normalizeRelative, pathState, sha256, stableJson, writeFileAtomic } fro
|
|
|
13
14
|
|
|
14
15
|
const HOOKS_PATH = '.codex/hooks.json';
|
|
15
16
|
const HOOK_STATE_SCHEMA_VERSION = 2;
|
|
17
|
+
const HOOK_LOCATOR_SCHEMA_VERSION = 1;
|
|
16
18
|
function hookCommand(action) {
|
|
17
19
|
const local = `const {runCli}=await import('genesis-compiler/cli');process.exitCode=await runCli(['hook','${action}'])`;
|
|
18
20
|
return `if command -v genesis >/dev/null 2>&1; then genesis hook ${action}; else node --input-type=module --eval "${local}"; fi`;
|
|
@@ -157,7 +159,8 @@ export async function codexSessionContext({ projectRoot } = {}) {
|
|
|
157
159
|
'- Before creating a helper or public operation, run `genesis index <name-or-path>` and reuse an existing function when it already owns the behavior.',
|
|
158
160
|
'- `.genesis/machine-city.json` is the detailed code/function map; `.genesis/program-city.json` is the simpler subsystem/operation map.',
|
|
159
161
|
'- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
|
|
160
|
-
'- Before
|
|
162
|
+
'- Before the first project-file edit in a user-requested implementation turn, silently run `genesis hook authorize` once in every Git repository you will edit. This is internal Genesis bookkeeping, not a request for user permission: do not ask the user, announce successful authorization, or include it in progress or final summaries. Mention it only if the command fails.',
|
|
163
|
+
'- Authorization is turn-scoped. Run it again for a later code-changing turn after a user follow-up or interruption, but not again within the same active turn after it succeeds. The command must report `authorize: authorized` or `authorize: unchanged`; otherwise stop before editing. Never authorize answer, explanation, review, diagnosis-only, reconciliation, or Deslop turns.',
|
|
161
164
|
'- Apply the engineering approach below to implementation and cleanup; the Stop hook owns optional Stack Post-change work, Blueprint/Program reconciliation, and Deslop turns.',
|
|
162
165
|
'- After an authorized code-changing turn, Genesis may request separate post-change, explanation, and cleanup turns.',
|
|
163
166
|
`Engineering profile: ${engineering.profile?.id || 'invalid; run \`genesis check\`'}.`,
|
|
@@ -236,6 +239,12 @@ async function hookStatePath(projectRoot, input) {
|
|
|
236
239
|
return path.resolve(projectRoot, gitPath);
|
|
237
240
|
}
|
|
238
241
|
|
|
242
|
+
function hookLocatorPath(input) {
|
|
243
|
+
const key = sha256(hookId(input, 'session_id')).slice('sha256:'.length);
|
|
244
|
+
const user = typeof process.getuid === 'function' ? process.getuid() : 'user';
|
|
245
|
+
return path.join(tmpdir(), `genesis-codex-hook-${user}-${key}.json`);
|
|
246
|
+
}
|
|
247
|
+
|
|
239
248
|
function writeTurnState(statePath, state) {
|
|
240
249
|
return writeFileAtomic(statePath, stableJson(state), { mode: 0o600 });
|
|
241
250
|
}
|
|
@@ -247,14 +256,25 @@ async function refreshProjectIndex(projectRoot) {
|
|
|
247
256
|
export async function recordCodexTurn({ input, projectRoot } = {}) {
|
|
248
257
|
const root = (await gitContext(projectRoot || input?.cwd)).repositoryRoot;
|
|
249
258
|
const statePath = await hookStatePath(root, input);
|
|
259
|
+
const turnId = hookId(input, 'turn_id');
|
|
260
|
+
const existing = await readTurnState(statePath);
|
|
261
|
+
if (existing?.schemaVersion === HOOK_STATE_SCHEMA_VERSION && existing.turnId === turnId) {
|
|
262
|
+
await writeHookLocator(input, root);
|
|
263
|
+
return { status: 'preserved' };
|
|
264
|
+
}
|
|
265
|
+
const repositories = registeredRepositories(existing || {}, root);
|
|
250
266
|
const state = {
|
|
251
267
|
schemaVersion: HOOK_STATE_SCHEMA_VERSION,
|
|
252
|
-
turnId
|
|
268
|
+
turnId,
|
|
253
269
|
phase: 'implementation',
|
|
254
270
|
implementationAuthorized: false,
|
|
255
|
-
|
|
271
|
+
repositories,
|
|
272
|
+
snapshot: repositories.length > 0 && existing?.snapshot
|
|
273
|
+
? existing.snapshot
|
|
274
|
+
: await repositorySnapshot(root),
|
|
256
275
|
};
|
|
257
276
|
await writeTurnState(statePath, state);
|
|
277
|
+
await writeHookLocator(input, root);
|
|
258
278
|
return { status: 'recorded' };
|
|
259
279
|
}
|
|
260
280
|
|
|
@@ -265,6 +285,41 @@ async function readTurnState(statePath) {
|
|
|
265
285
|
}
|
|
266
286
|
}
|
|
267
287
|
|
|
288
|
+
async function readHookLocator(input) {
|
|
289
|
+
try { return JSON.parse(await readFile(hookLocatorPath(input), 'utf8')); } catch (error) {
|
|
290
|
+
if (['ENOENT', 'ENOTDIR'].includes(error?.code)) return null;
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function writeHookLocator(input, coordinatorRoot) {
|
|
296
|
+
return writeFileAtomic(hookLocatorPath(input), stableJson({
|
|
297
|
+
schemaVersion: HOOK_LOCATOR_SCHEMA_VERSION,
|
|
298
|
+
coordinatorRoot,
|
|
299
|
+
}), { mode: 0o600 });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function removeTurnState(statePath, input) {
|
|
303
|
+
await Promise.all([
|
|
304
|
+
rm(statePath, { force: true }),
|
|
305
|
+
rm(hookLocatorPath(input), { force: true }),
|
|
306
|
+
]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function registeredRepositories(state, coordinatorRoot) {
|
|
310
|
+
if (Array.isArray(state.repositories) && state.repositories.length > 0) {
|
|
311
|
+
return state.repositories;
|
|
312
|
+
}
|
|
313
|
+
if (state.implementationAuthorized === true && state.snapshot) {
|
|
314
|
+
return [{
|
|
315
|
+
projectRoot: coordinatorRoot,
|
|
316
|
+
snapshot: state.snapshot,
|
|
317
|
+
changedPaths: normalizedPaths(state.changedPaths || []).sort(),
|
|
318
|
+
}];
|
|
319
|
+
}
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
|
|
268
323
|
export async function authorizeCodexTurn({
|
|
269
324
|
projectRoot = process.cwd(),
|
|
270
325
|
sessionId = process.env.CODEX_SESSION_ID,
|
|
@@ -275,15 +330,52 @@ export async function authorizeCodexTurn({
|
|
|
275
330
|
'genesis hook authorize requires the current Codex session.',
|
|
276
331
|
);
|
|
277
332
|
}
|
|
333
|
+
const input = { session_id: sessionId };
|
|
278
334
|
const root = (await gitContext(projectRoot)).repositoryRoot;
|
|
279
|
-
const
|
|
335
|
+
const locator = await readHookLocator(input);
|
|
336
|
+
if (locator && (
|
|
337
|
+
locator.schemaVersion !== HOOK_LOCATOR_SCHEMA_VERSION
|
|
338
|
+
|| typeof locator.coordinatorRoot !== 'string'
|
|
339
|
+
|| !locator.coordinatorRoot
|
|
340
|
+
)) {
|
|
341
|
+
throw new GenesisError(
|
|
342
|
+
'CODEX_HOOK_COORDINATOR_INVALID',
|
|
343
|
+
'The current Codex turn has invalid Genesis coordination state.',
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
const coordinatorRoot = locator
|
|
347
|
+
? (await gitContext(locator.coordinatorRoot)).repositoryRoot
|
|
348
|
+
: root;
|
|
349
|
+
const statePath = await hookStatePath(coordinatorRoot, input);
|
|
280
350
|
const state = await readTurnState(statePath);
|
|
281
351
|
if (!state || state.schemaVersion !== HOOK_STATE_SCHEMA_VERSION || state.phase !== 'implementation') {
|
|
282
|
-
|
|
352
|
+
throw new GenesisError(
|
|
353
|
+
'CODEX_HOOK_TURN_UNAVAILABLE',
|
|
354
|
+
'No active Genesis implementation turn is available. Start from the session repository and authorize before editing.',
|
|
355
|
+
);
|
|
283
356
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
357
|
+
const repositories = registeredRepositories(state, coordinatorRoot);
|
|
358
|
+
const registered = repositories.some((repository) => repository.projectRoot === root);
|
|
359
|
+
if (!registered) {
|
|
360
|
+
repositories.push({
|
|
361
|
+
projectRoot: root,
|
|
362
|
+
snapshot: root === coordinatorRoot && state.snapshot
|
|
363
|
+
? state.snapshot
|
|
364
|
+
: await repositorySnapshot(root),
|
|
365
|
+
changedPaths: [],
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
await writeTurnState(statePath, {
|
|
369
|
+
...state,
|
|
370
|
+
implementationAuthorized: true,
|
|
371
|
+
repositories,
|
|
372
|
+
});
|
|
373
|
+
await writeHookLocator(input, coordinatorRoot);
|
|
374
|
+
return {
|
|
375
|
+
status: registered && state.implementationAuthorized === true
|
|
376
|
+
? 'unchanged'
|
|
377
|
+
: 'authorized',
|
|
378
|
+
};
|
|
287
379
|
}
|
|
288
380
|
|
|
289
381
|
async function committedPaths(projectRoot, before, after) {
|
|
@@ -329,72 +421,117 @@ function changedPathLines(changedPaths) {
|
|
|
329
421
|
];
|
|
330
422
|
}
|
|
331
423
|
|
|
332
|
-
|
|
333
|
-
|
|
424
|
+
function changedRepositoryLines(repositories) {
|
|
425
|
+
return repositories.flatMap(({ projectRoot, changedPaths }) => [
|
|
426
|
+
`## Repository: \`${projectRoot}\``,
|
|
427
|
+
'',
|
|
428
|
+
...changedPathLines(changedPaths),
|
|
429
|
+
'',
|
|
430
|
+
]);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async function changedRepositories(repositories) {
|
|
434
|
+
return (await Promise.all(repositories.map(async (repository) => {
|
|
435
|
+
const currentPaths = await changedProjectPaths(
|
|
436
|
+
repository.projectRoot,
|
|
437
|
+
repository.snapshot,
|
|
438
|
+
await repositorySnapshot(repository.projectRoot),
|
|
439
|
+
);
|
|
440
|
+
const changedPaths = normalizedPaths([
|
|
441
|
+
...(repository.changedPaths || []),
|
|
442
|
+
...currentPaths,
|
|
443
|
+
]).sort();
|
|
444
|
+
return changedPaths.length > 0 ? { ...repository, changedPaths } : null;
|
|
445
|
+
}))).filter(Boolean);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function repositoryContexts(repositories) {
|
|
449
|
+
return Promise.all(repositories.map(async (repository) => {
|
|
450
|
+
const [stack, engineering] = await Promise.all([
|
|
451
|
+
optionalStack(repository.projectRoot),
|
|
452
|
+
optionalEngineering(repository.projectRoot),
|
|
453
|
+
]);
|
|
454
|
+
return { ...repository, stack, engineering };
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function repositoryGuidanceLines({ projectRoot, stack, engineering }, {
|
|
459
|
+
deslop = false,
|
|
460
|
+
postChange = false,
|
|
461
|
+
} = {}) {
|
|
462
|
+
return [
|
|
463
|
+
`## Repository: \`${projectRoot}\``,
|
|
464
|
+
'',
|
|
465
|
+
'ENGINEERING APPROACH',
|
|
466
|
+
'',
|
|
467
|
+
engineering.guidance,
|
|
468
|
+
...(stack?.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
|
|
469
|
+
...(postChange && stack?.postChange
|
|
470
|
+
? ['', 'COMPOSED STACK POST-CHANGE WORK', '', stack.postChange]
|
|
471
|
+
: []),
|
|
472
|
+
...(deslop && stack?.deslop
|
|
473
|
+
? ['', 'SELECTED STACK CLEANUP GUIDANCE', '', stack.deslop]
|
|
474
|
+
: []),
|
|
475
|
+
'',
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function reconciliationContinuation(repositories, knownContexts) {
|
|
480
|
+
const [instructions, contexts] = await Promise.all([
|
|
334
481
|
readInstalledAsset('reconcile'),
|
|
335
|
-
|
|
336
|
-
optionalEngineering(projectRoot),
|
|
482
|
+
knownContexts || repositoryContexts(repositories),
|
|
337
483
|
]);
|
|
338
484
|
return [
|
|
339
485
|
'This is the automatic Genesis explanation turn for the preceding implementation turn.',
|
|
340
|
-
'Use the same conversation context and the Git-visible changes below. Do not perform Deslop yet.',
|
|
486
|
+
'Use the same conversation context and the Git-visible changes below in every listed repository. Do not perform Deslop yet.',
|
|
341
487
|
'',
|
|
342
|
-
'CHANGED PROJECT PATHS',
|
|
488
|
+
'REPOSITORIES AND CHANGED PROJECT PATHS',
|
|
343
489
|
'',
|
|
344
|
-
...
|
|
490
|
+
...changedRepositoryLines(repositories),
|
|
345
491
|
'',
|
|
346
492
|
instructions.trim(),
|
|
347
493
|
'',
|
|
348
|
-
'
|
|
494
|
+
'REPOSITORY GUIDANCE',
|
|
349
495
|
'',
|
|
350
|
-
|
|
351
|
-
...(stack?.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
|
|
496
|
+
...contexts.flatMap((context) => repositoryGuidanceLines(context)),
|
|
352
497
|
].join('\n');
|
|
353
498
|
}
|
|
354
499
|
|
|
355
|
-
function postChangeContinuation(
|
|
500
|
+
function postChangeContinuation(repositories, contexts) {
|
|
356
501
|
return [
|
|
357
502
|
'This is the one automatic Stack Post-change turn for the preceding implementation.',
|
|
358
|
-
'Use the same conversation context and the Git-visible changes below.',
|
|
503
|
+
'Use the same conversation context and the Git-visible changes below in every listed repository.',
|
|
359
504
|
'Perform only the composed Post-change work. Do not broaden the task, reconcile Genesis explanations, or perform Deslop yet.',
|
|
360
505
|
'Do not repeat or schedule another Post-change turn. If none of the configured work applies, make no changes and respond minimally.',
|
|
361
506
|
'',
|
|
362
|
-
'CHANGED PROJECT PATHS',
|
|
507
|
+
'REPOSITORIES AND CHANGED PROJECT PATHS',
|
|
363
508
|
'',
|
|
364
|
-
...
|
|
365
|
-
'',
|
|
366
|
-
'COMPOSED STACK POST-CHANGE WORK',
|
|
509
|
+
...changedRepositoryLines(repositories),
|
|
367
510
|
'',
|
|
368
|
-
|
|
511
|
+
'REPOSITORY GUIDANCE AND POST-CHANGE WORK',
|
|
369
512
|
'',
|
|
370
|
-
|
|
371
|
-
'',
|
|
372
|
-
engineering.guidance,
|
|
373
|
-
...(stack.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
|
|
513
|
+
...contexts.flatMap((context) => repositoryGuidanceLines(context, { postChange: true })),
|
|
374
514
|
].join('\n');
|
|
375
515
|
}
|
|
376
516
|
|
|
377
|
-
async function deslopContinuation(
|
|
378
|
-
const [instructions,
|
|
517
|
+
async function deslopContinuation(repositories) {
|
|
518
|
+
const [instructions, contexts] = await Promise.all([
|
|
379
519
|
readInstalledAsset('deslop'),
|
|
380
|
-
|
|
381
|
-
optionalEngineering(projectRoot),
|
|
520
|
+
repositoryContexts(repositories),
|
|
382
521
|
]);
|
|
383
522
|
return [
|
|
384
523
|
'This is the final automatic Genesis Deslop turn for the preceding implementation.',
|
|
385
|
-
'Review only that work and the paths listed below. Do not broaden the task.',
|
|
524
|
+
'Review every listed repository and only that work and the paths listed below. Do not broaden the task.',
|
|
386
525
|
'',
|
|
387
|
-
'CHANGED PROJECT PATHS',
|
|
526
|
+
'REPOSITORIES AND CHANGED PROJECT PATHS',
|
|
388
527
|
'',
|
|
389
|
-
...
|
|
528
|
+
...changedRepositoryLines(repositories),
|
|
390
529
|
'',
|
|
391
530
|
instructions.trim(),
|
|
392
531
|
'',
|
|
393
|
-
'
|
|
532
|
+
'REPOSITORY GUIDANCE',
|
|
394
533
|
'',
|
|
395
|
-
|
|
396
|
-
...(stack?.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
|
|
397
|
-
...(stack?.deslop ? ['', 'SELECTED STACK CLEANUP GUIDANCE', '', stack.deslop] : []),
|
|
534
|
+
...contexts.flatMap((context) => repositoryGuidanceLines(context, { deslop: true })),
|
|
398
535
|
'',
|
|
399
536
|
'PROGRAM CITATION MAINTENANCE',
|
|
400
537
|
'',
|
|
@@ -405,6 +542,10 @@ async function deslopContinuation(projectRoot, changedPaths) {
|
|
|
405
542
|
].join('\n');
|
|
406
543
|
}
|
|
407
544
|
|
|
545
|
+
async function refreshProjectIndexes(repositories) {
|
|
546
|
+
await Promise.all(repositories.map(({ projectRoot }) => refreshProjectIndex(projectRoot)));
|
|
547
|
+
}
|
|
548
|
+
|
|
408
549
|
export async function completeCodexTurn({ input, projectRoot } = {}) {
|
|
409
550
|
const root = (await gitContext(projectRoot || input?.cwd)).repositoryRoot;
|
|
410
551
|
const statePath = await hookStatePath(root, input);
|
|
@@ -412,93 +553,98 @@ export async function completeCodexTurn({ input, projectRoot } = {}) {
|
|
|
412
553
|
if (!state || state.schemaVersion !== HOOK_STATE_SCHEMA_VERSION) return {};
|
|
413
554
|
|
|
414
555
|
if (state.implementationAuthorized !== true) {
|
|
415
|
-
await
|
|
556
|
+
const repositories = await changedRepositories(registeredRepositories(state, root));
|
|
557
|
+
if (repositories.length === 0) {
|
|
558
|
+
await removeTurnState(statePath, input);
|
|
559
|
+
return {};
|
|
560
|
+
}
|
|
561
|
+
await writeTurnState(statePath, {
|
|
562
|
+
...state,
|
|
563
|
+
phase: 'implementation',
|
|
564
|
+
repositories,
|
|
565
|
+
});
|
|
416
566
|
return {};
|
|
417
567
|
}
|
|
418
568
|
|
|
419
569
|
if (state.phase === 'implementation') {
|
|
420
570
|
if (input?.stop_hook_active === true || state.turnId !== hookId(input, 'turn_id')) {
|
|
421
|
-
await
|
|
571
|
+
await removeTurnState(statePath, input);
|
|
422
572
|
return {};
|
|
423
573
|
}
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
426
|
-
await
|
|
574
|
+
const repositories = await changedRepositories(registeredRepositories(state, root));
|
|
575
|
+
if (repositories.length === 0) {
|
|
576
|
+
await removeTurnState(statePath, input);
|
|
427
577
|
return {};
|
|
428
578
|
}
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
optionalEngineering(root),
|
|
432
|
-
]);
|
|
433
|
-
const postChange = Boolean(stack?.postChange?.trim());
|
|
579
|
+
const contexts = await repositoryContexts(repositories);
|
|
580
|
+
const postChange = contexts.some(({ stack }) => Boolean(stack?.postChange?.trim()));
|
|
434
581
|
await writeTurnState(statePath, {
|
|
435
582
|
...state,
|
|
436
583
|
phase: postChange ? 'post-change' : 'reconcile',
|
|
437
|
-
|
|
584
|
+
repositories,
|
|
438
585
|
});
|
|
439
586
|
return {
|
|
440
587
|
decision: 'block',
|
|
441
588
|
reason: postChange
|
|
442
|
-
? postChangeContinuation(
|
|
443
|
-
: await reconciliationContinuation(
|
|
589
|
+
? postChangeContinuation(repositories, contexts)
|
|
590
|
+
: await reconciliationContinuation(repositories, contexts),
|
|
444
591
|
};
|
|
445
592
|
}
|
|
446
593
|
|
|
447
594
|
if (input?.stop_hook_active !== true) {
|
|
448
|
-
await
|
|
595
|
+
await removeTurnState(statePath, input);
|
|
449
596
|
return {};
|
|
450
597
|
}
|
|
451
598
|
|
|
452
599
|
if (state.phase === 'post-change') {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
await rm(statePath, { force: true });
|
|
600
|
+
const repositories = await changedRepositories(registeredRepositories(state, root));
|
|
601
|
+
if (repositories.length === 0) {
|
|
602
|
+
await removeTurnState(statePath, input);
|
|
457
603
|
return {};
|
|
458
604
|
}
|
|
459
605
|
await writeTurnState(statePath, {
|
|
460
606
|
...state,
|
|
461
607
|
phase: 'reconcile',
|
|
462
|
-
|
|
608
|
+
repositories,
|
|
463
609
|
});
|
|
464
610
|
return {
|
|
465
611
|
decision: 'block',
|
|
466
|
-
reason: await reconciliationContinuation(
|
|
612
|
+
reason: await reconciliationContinuation(repositories),
|
|
467
613
|
};
|
|
468
614
|
}
|
|
469
615
|
|
|
470
616
|
if (state.phase === 'reconcile') {
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
if (
|
|
474
|
-
await
|
|
475
|
-
await
|
|
617
|
+
const registered = registeredRepositories(state, root);
|
|
618
|
+
const repositories = await changedRepositories(registered);
|
|
619
|
+
if (repositories.length === 0) {
|
|
620
|
+
await refreshProjectIndexes(registered);
|
|
621
|
+
await removeTurnState(statePath, input);
|
|
476
622
|
return {};
|
|
477
623
|
}
|
|
478
|
-
await
|
|
624
|
+
await refreshProjectIndexes(repositories);
|
|
479
625
|
await writeTurnState(statePath, {
|
|
480
626
|
...state,
|
|
481
627
|
phase: 'deslop',
|
|
482
|
-
|
|
628
|
+
repositories,
|
|
483
629
|
});
|
|
484
630
|
return {
|
|
485
631
|
decision: 'block',
|
|
486
|
-
reason: await deslopContinuation(
|
|
632
|
+
reason: await deslopContinuation(repositories),
|
|
487
633
|
};
|
|
488
634
|
}
|
|
489
635
|
|
|
490
636
|
if (state.phase === 'deslop') {
|
|
491
|
-
await
|
|
492
|
-
await
|
|
637
|
+
await refreshProjectIndexes(registeredRepositories(state, root));
|
|
638
|
+
await removeTurnState(statePath, input);
|
|
493
639
|
return {};
|
|
494
640
|
}
|
|
495
641
|
|
|
496
|
-
await
|
|
642
|
+
await removeTurnState(statePath, input);
|
|
497
643
|
return {};
|
|
498
644
|
}
|
|
499
645
|
|
|
500
646
|
export async function discardCodexTurn({ input, projectRoot } = {}) {
|
|
501
647
|
const root = (await gitContext(projectRoot || input?.cwd)).repositoryRoot;
|
|
502
|
-
await
|
|
648
|
+
await removeTurnState(await hookStatePath(root, input), input);
|
|
503
649
|
return { status: 'discarded' };
|
|
504
650
|
}
|