@jskit-ai/jskit-cli 0.2.89 → 0.2.90
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 +4 -4
- package/src/server/commandHandlers/session.js +173 -142
- package/src/server/core/argParser.js +0 -4
- package/src/server/core/commandCatalog.js +47 -35
- package/src/server/sessionRuntime/constants.js +125 -348
- package/src/server/sessionRuntime/io.js +2 -2
- package/src/server/sessionRuntime/preconditions.js +39 -143
- package/src/server/sessionRuntime/promptRenderer.js +2 -15
- package/src/server/sessionRuntime/prompts/app_blueprint.md +2 -7
- package/src/server/sessionRuntime/prompts/{automated_checks.md → automated_checks_run.md} +2 -17
- package/src/server/sessionRuntime/prompts/{update_blueprint.md → blueprint_updated.md} +2 -11
- package/src/server/sessionRuntime/prompts/{deep_ui_check.md → deep_ui_check_run.md} +2 -19
- package/src/server/sessionRuntime/prompts/final_report_created.md +44 -0
- package/src/server/sessionRuntime/prompts/issue_created.md +26 -0
- package/src/server/sessionRuntime/prompts/issue_prompt_rendered.md +1 -0
- package/src/server/sessionRuntime/prompts/{plan_issue.md → make_plan.md} +8 -37
- package/src/server/sessionRuntime/prompts/{execute_plan.md → plan_executed.md} +4 -29
- package/src/server/sessionRuntime/prompts/{prepare_pr_merge.md → pr_merge_prepared.md} +3 -3
- package/src/server/sessionRuntime/prompts/{resolve_deslop_findings.md → review_changes_accepted_resolve.md} +2 -6
- package/src/server/sessionRuntime/prompts/{review_changes.md → review_prompt_rendered.md} +3 -28
- package/src/server/sessionRuntime/prompts/{user_check.md → user_check_completed.md} +1 -11
- package/src/server/sessionRuntime/responses.js +431 -292
- package/src/server/sessionRuntime.js +1201 -926
- package/src/server/sessionRuntime/prompts/issue_details.md +0 -49
- package/src/server/sessionRuntime/prompts/new_issue.md +0 -46
|
@@ -2,8 +2,10 @@ import { mkdir, readdir } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
CYCLE_STEP_IDS,
|
|
5
|
+
DEPENDENCIES_INSTALL_RESULT_FILE,
|
|
6
|
+
ISSUE_FILE_CODEX_HANDOFF,
|
|
7
|
+
ISSUE_DEFINITION_CODEX_HANDOFF,
|
|
5
8
|
JSKIT_CLI_SHELL_COMMAND,
|
|
6
|
-
PROMPT_DIRECTORY,
|
|
7
9
|
REVIEW_EXECUTION_CODEX_HANDOFF,
|
|
8
10
|
REVIEW_PASS_LIMIT,
|
|
9
11
|
SESSION_WORKFLOW_VERSION,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
readTextIfExists,
|
|
20
22
|
readTrimmedFile,
|
|
21
23
|
runGitInWorktree,
|
|
22
|
-
|
|
24
|
+
timestampForStepRecord,
|
|
23
25
|
writeTextFile
|
|
24
26
|
} from "./io.js";
|
|
25
27
|
import {
|
|
@@ -69,6 +71,11 @@ const ACCEPTED_CHANGES_NOOP_WARNING = createWarning({
|
|
|
69
71
|
message: "No accepted worktree changes were found; continuing without a new commit."
|
|
70
72
|
});
|
|
71
73
|
|
|
74
|
+
function issueNumberFromUrl(issueUrl = "") {
|
|
75
|
+
const match = /\/issues\/(\d+)(?:\b|$)/u.exec(String(issueUrl || ""));
|
|
76
|
+
return match ? match[1] : "";
|
|
77
|
+
}
|
|
78
|
+
|
|
72
79
|
function normalizeStepId(stepId) {
|
|
73
80
|
return normalizeText(stepId);
|
|
74
81
|
}
|
|
@@ -126,10 +133,6 @@ async function readActiveCycle(paths) {
|
|
|
126
133
|
return normalizeCycleNumber(cycle || DEFAULT_ACTIVE_CYCLE);
|
|
127
134
|
}
|
|
128
135
|
|
|
129
|
-
async function writeActiveCycle(paths, cycle) {
|
|
130
|
-
await writeTextFile(path.join(paths.sessionRoot, "active_cycle"), `${normalizeCycleNumber(cycle)}\n`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
136
|
function cycleStepsRoot(paths, cycle) {
|
|
134
137
|
return path.join(paths.sessionRoot, "steps", cycleDirectoryName(cycle));
|
|
135
138
|
}
|
|
@@ -138,18 +141,6 @@ function cycleRoot(paths, cycle) {
|
|
|
138
141
|
return path.join(paths.sessionRoot, "cycles", cycleDirectoryName(cycle));
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
function cyclePlanPath(paths, cycle) {
|
|
142
|
-
return path.join(cycleRoot(paths, cycle), "plan.md");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function cyclePlanPromptFileName(cycle) {
|
|
146
|
-
return `cycle_${normalizeCycleNumber(cycle)}_plan_request.md`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function cyclePlanExecutionPromptFileName(cycle) {
|
|
150
|
-
return `cycle_${normalizeCycleNumber(cycle)}_plan_execution.md`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
144
|
function normalizeReviewPassNumber(value = "") {
|
|
154
145
|
const normalized = normalizeText(value).replace(/^pass_/u, "");
|
|
155
146
|
if (!/^\d+$/u.test(normalized)) {
|
|
@@ -204,7 +195,7 @@ async function readReviewPassInfo(paths, pass) {
|
|
|
204
195
|
pass: normalizedPass,
|
|
205
196
|
label: reviewPassDirectoryName(normalizedPass),
|
|
206
197
|
status,
|
|
207
|
-
promptPath: prompt?.promptPath || path.join(root, "
|
|
198
|
+
promptPath: prompt?.promptPath || path.join(root, "review_prompt_rendered"),
|
|
208
199
|
acceptedAt: accepted?.acceptedAt || "",
|
|
209
200
|
changedFiles,
|
|
210
201
|
commit: "",
|
|
@@ -248,27 +239,6 @@ async function readReviewPromptForStep(paths, artifacts = {}) {
|
|
|
248
239
|
return "";
|
|
249
240
|
}
|
|
250
241
|
|
|
251
|
-
const PROMPT_ARTIFACT_BY_STEP_ID = Object.freeze({
|
|
252
|
-
issue_drafted: "issue_draft.md",
|
|
253
|
-
issue_details_gathered: "issue_details.md",
|
|
254
|
-
deep_ui_check_run: "deep_ui_check_run.md",
|
|
255
|
-
automated_checks_run: "automated_checks_run.md",
|
|
256
|
-
blueprint_updated: "update_blueprint.md",
|
|
257
|
-
pr_merge_prepared: "prepare_pr_merge.md",
|
|
258
|
-
user_check_completed: "user_check.md"
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
async function promptArtifactForStep(paths, stepId) {
|
|
262
|
-
const normalizedStepId = normalizeStepId(stepId);
|
|
263
|
-
if (normalizedStepId === "plan_made") {
|
|
264
|
-
return cyclePlanPromptFileName(await readActiveCycle(paths));
|
|
265
|
-
}
|
|
266
|
-
if (normalizedStepId === "plan_executed") {
|
|
267
|
-
return cyclePlanExecutionPromptFileName(await readActiveCycle(paths));
|
|
268
|
-
}
|
|
269
|
-
return PROMPT_ARTIFACT_BY_STEP_ID[normalizedStepId] || "";
|
|
270
|
-
}
|
|
271
|
-
|
|
272
242
|
async function readPromptForStep(paths, stepId, artifacts = {}) {
|
|
273
243
|
if (!stepCanExposeStoredPrompt(stepId)) {
|
|
274
244
|
return "";
|
|
@@ -276,13 +246,6 @@ async function readPromptForStep(paths, stepId, artifacts = {}) {
|
|
|
276
246
|
if (REVIEW_STEP_IDS.includes(normalizeStepId(stepId))) {
|
|
277
247
|
return readReviewPromptForStep(paths, artifacts);
|
|
278
248
|
}
|
|
279
|
-
const promptArtifact = await promptArtifactForStep(paths, stepId);
|
|
280
|
-
if (promptArtifact) {
|
|
281
|
-
const prompt = await readTextIfExists(path.join(paths.sessionRoot, "prompts", promptArtifact));
|
|
282
|
-
if (prompt) {
|
|
283
|
-
return prompt;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
249
|
return "";
|
|
287
250
|
}
|
|
288
251
|
|
|
@@ -299,11 +262,18 @@ async function readStepFileNames(stepsRoot) {
|
|
|
299
262
|
|
|
300
263
|
async function readCompletedSteps(paths) {
|
|
301
264
|
const stepsRoot = path.join(paths.sessionRoot, "steps");
|
|
302
|
-
const activeCycle = await readActiveCycle(paths);
|
|
303
265
|
const globalStepIds = normalizeKnownStepIds(
|
|
304
266
|
(await readStepFileNames(stepsRoot)).filter((stepId) => !isCycleStepId(stepId))
|
|
305
267
|
);
|
|
306
|
-
const cycleStepIds =
|
|
268
|
+
const cycleStepIds = [];
|
|
269
|
+
try {
|
|
270
|
+
const entries = await readdir(stepsRoot, { withFileTypes: true });
|
|
271
|
+
for (const entry of entries.filter((item) => item.isDirectory() && /^cycle_\d+$/u.test(item.name)).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
272
|
+
cycleStepIds.push(...await readStepFileNames(path.join(stepsRoot, entry.name)));
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
// Legacy sessions may not have cycle record directories.
|
|
276
|
+
}
|
|
307
277
|
return applyReviewPassCompletionOverlay(paths, normalizeKnownStepIds([...globalStepIds, ...cycleStepIds]));
|
|
308
278
|
}
|
|
309
279
|
|
|
@@ -315,7 +285,6 @@ async function applyReviewPassCompletionOverlay(paths, completedSteps = []) {
|
|
|
315
285
|
const reviewPasses = await readReviewPasses(paths);
|
|
316
286
|
const latestPass = reviewPasses.at(-1);
|
|
317
287
|
if (!latestPass) {
|
|
318
|
-
REVIEW_STEP_IDS.forEach((stepId) => completed.delete(stepId));
|
|
319
288
|
return normalizeKnownStepIds([...completed]);
|
|
320
289
|
}
|
|
321
290
|
const latestPassAccepted = latestPass.status === "accepted" || latestPass.status === "no_changes";
|
|
@@ -324,11 +293,6 @@ async function applyReviewPassCompletionOverlay(paths, completedSteps = []) {
|
|
|
324
293
|
REVIEW_STEP_IDS.forEach((stepId) => completed.delete(stepId));
|
|
325
294
|
return normalizeKnownStepIds([...completed]);
|
|
326
295
|
}
|
|
327
|
-
if (latestPass.status === "prompted") {
|
|
328
|
-
completed.add("review_prompt_rendered");
|
|
329
|
-
completed.delete("review_changes_accepted");
|
|
330
|
-
return normalizeKnownStepIds([...completed]);
|
|
331
|
-
}
|
|
332
296
|
if (latestPass.status === "accepted" || latestPass.status === "no_changes") {
|
|
333
297
|
REVIEW_STEP_IDS.forEach((stepId) => completed.add(stepId));
|
|
334
298
|
}
|
|
@@ -340,7 +304,7 @@ async function readCycleInfo(paths, cycle) {
|
|
|
340
304
|
const root = cycleRoot(paths, normalizedCycle);
|
|
341
305
|
const userCheckPassed = await readTextIfExists(path.join(cycleStepsRoot(paths, normalizedCycle), "user_check_completed"));
|
|
342
306
|
const userCheckFailed = await readTextIfExists(path.join(cycleStepsRoot(paths, normalizedCycle), "user_check_failed"));
|
|
343
|
-
const reworkRequestPath = path.join(root, "rework_request
|
|
307
|
+
const reworkRequestPath = path.join(root, "rework_request");
|
|
344
308
|
const reworkRequest = await readTextIfExists(reworkRequestPath);
|
|
345
309
|
return {
|
|
346
310
|
cycle: normalizedCycle,
|
|
@@ -349,7 +313,7 @@ async function readCycleInfo(paths, cycle) {
|
|
|
349
313
|
reworkRequestPath: reworkRequest ? reworkRequestPath : "",
|
|
350
314
|
status: userCheckPassed ? "passed" : userCheckFailed ? "failed" : "active",
|
|
351
315
|
userCheckResult: userCheckPassed ? "passed" : userCheckFailed ? "failed" : "",
|
|
352
|
-
|
|
316
|
+
userCheckRecord: (userCheckPassed || userCheckFailed).trim()
|
|
353
317
|
};
|
|
354
318
|
}
|
|
355
319
|
|
|
@@ -451,7 +415,7 @@ async function readWorktreeStatus(paths, worktreeReady) {
|
|
|
451
415
|
};
|
|
452
416
|
}
|
|
453
417
|
|
|
454
|
-
async function
|
|
418
|
+
async function readStepRecords(paths) {
|
|
455
419
|
const stepsRoot = path.join(paths.sessionRoot, "steps");
|
|
456
420
|
try {
|
|
457
421
|
const entries = await readdir(stepsRoot, { withFileTypes: true });
|
|
@@ -460,19 +424,19 @@ async function readReceiptSteps(paths) {
|
|
|
460
424
|
entries
|
|
461
425
|
.filter((entry) => entry.isFile())
|
|
462
426
|
.map((entry) => entry.name)
|
|
463
|
-
.forEach((
|
|
464
|
-
const stepId = normalizeStepId(
|
|
427
|
+
.forEach((recordName) => {
|
|
428
|
+
const stepId = normalizeStepId(recordName);
|
|
465
429
|
if (STEP_IDS.includes(stepId)) {
|
|
466
|
-
if (!knownStepRows.has(stepId) ||
|
|
430
|
+
if (!knownStepRows.has(stepId) || recordName === stepId) {
|
|
467
431
|
knownStepRows.set(stepId, {
|
|
468
|
-
|
|
432
|
+
recordName,
|
|
469
433
|
stepId
|
|
470
434
|
});
|
|
471
435
|
}
|
|
472
436
|
return;
|
|
473
437
|
}
|
|
474
438
|
unknownStepRows.push({
|
|
475
|
-
|
|
439
|
+
recordName,
|
|
476
440
|
stepId
|
|
477
441
|
});
|
|
478
442
|
});
|
|
@@ -493,14 +457,14 @@ async function readReceiptSteps(paths) {
|
|
|
493
457
|
return left.stepId.localeCompare(right.stepId);
|
|
494
458
|
});
|
|
495
459
|
|
|
496
|
-
const
|
|
460
|
+
const globalRecords = await Promise.all(stepRows.map(async ({ recordName, stepId }) => ({
|
|
497
461
|
cycle: "",
|
|
462
|
+
details: (await readTextIfExists(path.join(stepsRoot, recordName))).trim(),
|
|
498
463
|
label: STEP_LABEL_BY_ID[stepId] || stepId,
|
|
499
|
-
receipt: (await readTextIfExists(path.join(stepsRoot, receiptName))).trim(),
|
|
500
464
|
stepId
|
|
501
465
|
})));
|
|
502
466
|
|
|
503
|
-
const
|
|
467
|
+
const cycleRecords = [];
|
|
504
468
|
const cycleDirectories = entries
|
|
505
469
|
.filter((entry) => entry.isDirectory() && /^cycle_\d+$/u.test(entry.name))
|
|
506
470
|
.map((entry) => entry.name)
|
|
@@ -509,18 +473,18 @@ async function readReceiptSteps(paths) {
|
|
|
509
473
|
const cycle = normalizeCycleNumber(cycleDirectory);
|
|
510
474
|
const cycleRootPath = path.join(stepsRoot, cycleDirectory);
|
|
511
475
|
const cycleStepIds = await readStepFileNames(cycleRootPath);
|
|
512
|
-
for (const
|
|
513
|
-
const stepId = normalizeStepId(
|
|
514
|
-
|
|
476
|
+
for (const recordName of cycleStepIds) {
|
|
477
|
+
const stepId = normalizeStepId(recordName);
|
|
478
|
+
cycleRecords.push({
|
|
515
479
|
cycle,
|
|
480
|
+
details: (await readTextIfExists(path.join(cycleRootPath, recordName))).trim(),
|
|
516
481
|
label: STEP_LABEL_BY_ID[stepId] || stepId,
|
|
517
|
-
receipt: (await readTextIfExists(path.join(cycleRootPath, receiptName))).trim(),
|
|
518
482
|
stepId
|
|
519
483
|
});
|
|
520
484
|
}
|
|
521
485
|
}
|
|
522
486
|
|
|
523
|
-
return [...
|
|
487
|
+
return [...globalRecords, ...cycleRecords];
|
|
524
488
|
} catch {
|
|
525
489
|
return [];
|
|
526
490
|
}
|
|
@@ -584,33 +548,15 @@ async function publicCodexContract(codex = null) {
|
|
|
584
548
|
if (!codex || typeof codex !== "object" || Array.isArray(codex)) {
|
|
585
549
|
return null;
|
|
586
550
|
}
|
|
587
|
-
|
|
588
|
-
const resolvePrompt = clonedCodex.responseContract?.resolvePrompt;
|
|
589
|
-
const templateFile = normalizeText(resolvePrompt?.templateFile || "");
|
|
590
|
-
if (templateFile) {
|
|
591
|
-
const template = await readTextIfExists(path.join(PROMPT_DIRECTORY, templateFile));
|
|
592
|
-
clonedCodex.responseContract.resolvePrompt = {
|
|
593
|
-
...resolvePrompt,
|
|
594
|
-
template
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
return clonedCodex;
|
|
551
|
+
return cloneContractValue(codex);
|
|
598
552
|
}
|
|
599
553
|
|
|
600
554
|
function stepRepeatabilityContract(stepId) {
|
|
601
|
-
if (!CYCLE_STEP_IDS.includes(normalizeStepId(stepId))) {
|
|
602
|
-
return {
|
|
603
|
-
repeatable: false,
|
|
604
|
-
repeatableGroupId: "",
|
|
605
|
-
repeatableGroupLabel: "",
|
|
606
|
-
repeatableLabel: ""
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
555
|
return {
|
|
610
|
-
repeatable:
|
|
611
|
-
repeatableGroupId: "
|
|
612
|
-
repeatableGroupLabel: "
|
|
613
|
-
repeatableLabel: "
|
|
556
|
+
repeatable: false,
|
|
557
|
+
repeatableGroupId: "",
|
|
558
|
+
repeatableGroupLabel: "",
|
|
559
|
+
repeatableLabel: ""
|
|
614
560
|
};
|
|
615
561
|
}
|
|
616
562
|
|
|
@@ -645,17 +591,6 @@ function stepIsRetryableWhenBlocked(stepId) {
|
|
|
645
591
|
].includes(normalizeStepId(stepId));
|
|
646
592
|
}
|
|
647
593
|
|
|
648
|
-
function stepIsConditional(stepId) {
|
|
649
|
-
return [
|
|
650
|
-
"deep_ui_check_run"
|
|
651
|
-
].includes(normalizeStepId(stepId));
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
function activeCycleInfoFromArtifacts(artifacts = {}) {
|
|
655
|
-
const activeCycle = normalizeCycleNumber(artifacts.activeCycle || "");
|
|
656
|
-
return (artifacts.cycles || []).find((cycle) => cycle?.cycle === activeCycle) || null;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
594
|
function uiCheckPromptedForStep(artifacts = {}, stepId = "") {
|
|
660
595
|
const normalizedStepId = normalizeStepId(stepId);
|
|
661
596
|
return (artifacts.uiChecks || []).some((entry) => {
|
|
@@ -665,10 +600,8 @@ function uiCheckPromptedForStep(artifacts = {}, stepId = "") {
|
|
|
665
600
|
}
|
|
666
601
|
|
|
667
602
|
function skipReasonForStep(stepId, artifacts = {}) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return "uiImpact is none.";
|
|
671
|
-
}
|
|
603
|
+
void stepId;
|
|
604
|
+
void artifacts;
|
|
672
605
|
return "";
|
|
673
606
|
}
|
|
674
607
|
|
|
@@ -677,53 +610,27 @@ function buildCurrentStepAction(stepId, artifacts = {}) {
|
|
|
677
610
|
if (!step) {
|
|
678
611
|
return null;
|
|
679
612
|
}
|
|
680
|
-
const activeCycleInfo = activeCycleInfoFromArtifacts(artifacts);
|
|
681
613
|
const planExecutionPrompted = artifacts.planExecution?.prompted === true;
|
|
682
614
|
const planExecutionSubmitted = artifacts.planExecution?.submitted === true;
|
|
683
|
-
const
|
|
615
|
+
const issueDefinitionPrompted = step.id === "issue_prompt_rendered" && artifacts.issueDefinitionRequested === true;
|
|
616
|
+
const issueFilePromptAction = step.id === "issue_created";
|
|
617
|
+
const issueSubmissionAction = step.id === "issue_submitted";
|
|
618
|
+
const pullRequestFileAction = step.id === "final_report_created";
|
|
619
|
+
const prSubmissionAction = step.id === "pr_created";
|
|
684
620
|
const deepUiCheckPrompted = step.id === "deep_ui_check_run" && uiCheckPromptedForStep(artifacts, "deep_ui_check_run");
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
? step.codex.promptActionLabel || ""
|
|
690
|
-
: "";
|
|
691
|
-
const buttonLabel = promptPhaseButtonLabel || step.buttonLabel;
|
|
621
|
+
const automatedChecksPrompted = step.id === "automated_checks_run" && (artifacts.checks || []).some((entry) => {
|
|
622
|
+
return normalizeStepId(entry?.stepId || "") === "automated_checks_run" &&
|
|
623
|
+
normalizeText(entry?.status || "") === "prompted";
|
|
624
|
+
});
|
|
692
625
|
const alternateActions = [];
|
|
693
|
-
if (step.id === "user_check_completed") {
|
|
694
|
-
alternateActions.push({
|
|
695
|
-
id: "return_to_plan_made",
|
|
696
|
-
input: {
|
|
697
|
-
formatHint: "markdown",
|
|
698
|
-
label: "What needs to be reworked?",
|
|
699
|
-
multiline: true,
|
|
700
|
-
name: "reworkNotes",
|
|
701
|
-
required: true,
|
|
702
|
-
type: "text"
|
|
703
|
-
},
|
|
704
|
-
label: "Return to Plan made",
|
|
705
|
-
presentation: "exclusive",
|
|
706
|
-
requiredErrorCode: "user_check_failed",
|
|
707
|
-
submitOptions: {
|
|
708
|
-
userCheck: "failed"
|
|
709
|
-
},
|
|
710
|
-
targetStep: "plan_made"
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
626
|
if (step.id === "review_changes_accepted") {
|
|
714
627
|
alternateActions.push({
|
|
715
628
|
id: "request_another_review_pass",
|
|
716
|
-
helpText: "
|
|
629
|
+
helpText: "Run another explicit deslop prompt before continuing.",
|
|
717
630
|
input: {
|
|
718
|
-
|
|
719
|
-
label: "What important findings remain?",
|
|
720
|
-
multiline: true,
|
|
721
|
-
name: "reviewFindings",
|
|
722
|
-
placeholder: "List the specific findings Codex still needs to address.",
|
|
723
|
-
required: true,
|
|
724
|
-
type: "text"
|
|
631
|
+
type: "none"
|
|
725
632
|
},
|
|
726
|
-
label: "Run
|
|
633
|
+
label: "Run deslop",
|
|
727
634
|
presentation: "secondary",
|
|
728
635
|
submitOptions: {
|
|
729
636
|
reviewFindingsRemaining: true
|
|
@@ -731,73 +638,75 @@ function buildCurrentStepAction(stepId, artifacts = {}) {
|
|
|
731
638
|
targetStep: "review_prompt_rendered"
|
|
732
639
|
});
|
|
733
640
|
}
|
|
734
|
-
if (step.id === "pr_finalized") {
|
|
735
|
-
alternateActions.push({
|
|
736
|
-
id: "skip_merge",
|
|
737
|
-
helpText: "Leave the PR open and record the skipped-merge outcome; final cleanup removes the session worktree.",
|
|
738
|
-
input: {
|
|
739
|
-
type: "none"
|
|
740
|
-
},
|
|
741
|
-
label: "Skip merge",
|
|
742
|
-
presentation: "secondary",
|
|
743
|
-
submitOptions: {
|
|
744
|
-
closeWithoutMerge: true
|
|
745
|
-
},
|
|
746
|
-
targetStep: "pr_finalized"
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
if (step.id === "main_checkout_synced") {
|
|
750
|
-
alternateActions.push({
|
|
751
|
-
id: "skip_main_checkout_sync",
|
|
752
|
-
helpText: "Leave the main checkout untouched and continue with session cleanup.",
|
|
753
|
-
input: {
|
|
754
|
-
type: "none"
|
|
755
|
-
},
|
|
756
|
-
label: "Skip sync",
|
|
757
|
-
presentation: "secondary",
|
|
758
|
-
submitOptions: {
|
|
759
|
-
skipMainSync: true
|
|
760
|
-
},
|
|
761
|
-
targetStep: "main_checkout_synced"
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
641
|
const dynamicButtonLabel = (() => {
|
|
765
|
-
if (step.id === "
|
|
766
|
-
return "
|
|
642
|
+
if (step.id === "issue_created" && !artifacts.issueText) {
|
|
643
|
+
return "Create issue file";
|
|
767
644
|
}
|
|
768
|
-
if (step.id === "
|
|
769
|
-
return "
|
|
645
|
+
if (step.id === "issue_submitted") {
|
|
646
|
+
return "Create issue on GH";
|
|
770
647
|
}
|
|
771
|
-
if (step.id === "
|
|
772
|
-
return "
|
|
648
|
+
if (step.id === "pr_created") {
|
|
649
|
+
return "Create PR on GH";
|
|
773
650
|
}
|
|
774
|
-
if (step.id === "
|
|
775
|
-
return "
|
|
651
|
+
if (step.id === "pr_merge_prepared") {
|
|
652
|
+
return "Merge";
|
|
776
653
|
}
|
|
777
654
|
if (step.id === "main_checkout_synced" && artifacts.prOutcome?.outcome && artifacts.prOutcome.outcome !== "merged") {
|
|
778
|
-
return "
|
|
655
|
+
return "Sync main checkout";
|
|
779
656
|
}
|
|
780
|
-
return buttonLabel;
|
|
657
|
+
return step.buttonLabel;
|
|
781
658
|
})();
|
|
782
659
|
const dynamicDescription = (() => {
|
|
660
|
+
if (issueDefinitionPrompted) {
|
|
661
|
+
return "Codex has the issue-definition prompt. Continue after the issue is scoped clearly enough.";
|
|
662
|
+
}
|
|
663
|
+
if (issueFilePromptAction && artifacts.issueFileRequested) {
|
|
664
|
+
return "Codex has the issue-file prompt. Review issue.md and issue_title, then continue when ready.";
|
|
665
|
+
}
|
|
666
|
+
if (issueSubmissionAction && artifacts.issueUrl) {
|
|
667
|
+
return "The GitHub issue has been created. Continue when ready.";
|
|
668
|
+
}
|
|
669
|
+
if (pullRequestFileAction && artifacts.pullRequestFileRequested) {
|
|
670
|
+
return "Codex has the PR-file prompt. Review pull_request.md, then continue when ready.";
|
|
671
|
+
}
|
|
672
|
+
if (prSubmissionAction && artifacts.prUrl) {
|
|
673
|
+
return "The GitHub pull request has been created. Continue when ready.";
|
|
674
|
+
}
|
|
675
|
+
if (step.id === "pr_merge_prepared" && artifacts.prOutcome?.outcome === "merged") {
|
|
676
|
+
return "The pull request was merged. Use Next when ready.";
|
|
677
|
+
}
|
|
783
678
|
if (step.id === "plan_executed" && planExecutionPrompted && !planExecutionSubmitted) {
|
|
784
|
-
return "Codex has the execution prompt. Review the result, then use
|
|
679
|
+
return "Codex has the execution prompt. Review the result, then use Next when ready.";
|
|
785
680
|
}
|
|
786
681
|
if (step.id === "deep_ui_check_run" && deepUiCheckPrompted) {
|
|
787
|
-
return "Codex has the
|
|
682
|
+
return "Codex has the run deep UI check prompt. Review the result, then use Next when ready.";
|
|
788
683
|
}
|
|
789
|
-
if (step.id === "automated_checks_run" &&
|
|
790
|
-
return "Codex has the automated
|
|
791
|
-
}
|
|
792
|
-
if (step.id === "plan_made" && planReworkMode && hasActiveReworkRequest) {
|
|
793
|
-
return "Codex writes a revised implementation plan from the user's rework notes for this cycle.";
|
|
684
|
+
if (step.id === "automated_checks_run" && automatedChecksPrompted) {
|
|
685
|
+
return "Codex has the run automated checks prompt. Review the result, then use Next when ready.";
|
|
794
686
|
}
|
|
795
687
|
if (step.id === "main_checkout_synced" && artifacts.prOutcome?.outcome && artifacts.prOutcome.outcome !== "merged") {
|
|
796
|
-
return "
|
|
688
|
+
return "Main checkout sync is only available after a successful merge. Use Next to continue.";
|
|
689
|
+
}
|
|
690
|
+
if (step.id === "main_checkout_synced" && artifacts.mainCheckoutSync?.status === "synced") {
|
|
691
|
+
return "The main checkout has been synced. Use Next when ready.";
|
|
797
692
|
}
|
|
798
693
|
return step.description;
|
|
799
694
|
})();
|
|
800
695
|
const dynamicUtilityActions = (() => {
|
|
696
|
+
if (step.id === "review_prompt_rendered" || step.id === "review_changes_accepted") {
|
|
697
|
+
return [
|
|
698
|
+
{
|
|
699
|
+
id: "resolve_deslop",
|
|
700
|
+
helpText: "Send Codex the explicit resolve deslop prompt. Nothing advances automatically after it finishes.",
|
|
701
|
+
kind: "codex_prompt",
|
|
702
|
+
label: "Resolve deslop",
|
|
703
|
+
submitOptions: {
|
|
704
|
+
actionCommand: "resolve_deslop"
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
...(step.utilityActions || [])
|
|
708
|
+
];
|
|
709
|
+
}
|
|
801
710
|
return step.utilityActions || [];
|
|
802
711
|
})();
|
|
803
712
|
return {
|
|
@@ -807,14 +716,13 @@ function buildCurrentStepAction(stepId, artifacts = {}) {
|
|
|
807
716
|
displayGroupId: step.displayGroupId,
|
|
808
717
|
displayGroupLabel: step.displayGroupLabel,
|
|
809
718
|
index: STEP_IDS.indexOf(step.id),
|
|
810
|
-
input: cloneContractValue(step.input),
|
|
811
|
-
kind: step.kind,
|
|
719
|
+
input: cloneContractValue(issueDefinitionPrompted || issueFilePromptAction || pullRequestFileAction ? { type: "none" } : step.input),
|
|
720
|
+
kind: issueDefinitionPrompted || issueFilePromptAction || pullRequestFileAction ? "codex_prompt" : step.kind,
|
|
812
721
|
label: dynamicButtonLabel,
|
|
813
|
-
automation: cloneContractValue(step.automation || { mode: "manual" }),
|
|
722
|
+
automation: cloneContractValue(issueDefinitionPrompted || issueFilePromptAction || pullRequestFileAction ? { mode: "codex_prompt" } : step.automation || { mode: "manual" }),
|
|
814
723
|
...stepRepeatabilityContract(step.id),
|
|
815
|
-
requiredInput: cloneContractValue(step.input),
|
|
724
|
+
requiredInput: cloneContractValue(issueDefinitionPrompted || issueFilePromptAction || pullRequestFileAction ? { type: "none" } : step.input),
|
|
816
725
|
requiresExplicitRun: step.requiresExplicitRun === true,
|
|
817
|
-
conditional: stepIsConditional(step.id),
|
|
818
726
|
retryable: artifacts.status === SESSION_STATUS.BLOCKED && stepIsRetryableWhenBlocked(step.id),
|
|
819
727
|
skipReason: skipReasonForStep(step.id, artifacts),
|
|
820
728
|
stepId: step.id,
|
|
@@ -824,19 +732,17 @@ function buildCurrentStepAction(stepId, artifacts = {}) {
|
|
|
824
732
|
}
|
|
825
733
|
|
|
826
734
|
function rawCodexHandoff(stepId, artifacts = {}) {
|
|
735
|
+
if (normalizeStepId(stepId) === "issue_prompt_rendered" && artifacts.issueDefinitionRequested) {
|
|
736
|
+
return cloneContractValue(ISSUE_DEFINITION_CODEX_HANDOFF);
|
|
737
|
+
}
|
|
738
|
+
if (normalizeStepId(stepId) === "issue_created" && artifacts.issueFileRequested) {
|
|
739
|
+
return cloneContractValue(ISSUE_FILE_CODEX_HANDOFF);
|
|
740
|
+
}
|
|
827
741
|
if (normalizeStepId(stepId) === "review_changes_accepted" && latestReviewPassIsPrompted(artifacts)) {
|
|
828
742
|
return cloneContractValue(REVIEW_EXECUTION_CODEX_HANDOFF);
|
|
829
743
|
}
|
|
830
744
|
const step = STEP_DEFINITION_BY_ID[stepId];
|
|
831
|
-
|
|
832
|
-
if (
|
|
833
|
-
codex &&
|
|
834
|
-
normalizeStepId(stepId) === "plan_made" &&
|
|
835
|
-
normalizeCycleNumber(artifacts.activeCycle || "") !== "001"
|
|
836
|
-
) {
|
|
837
|
-
codex.promptIntroText = "Codex will create a revised implementation plan based on the rework notes.";
|
|
838
|
-
}
|
|
839
|
-
return codex;
|
|
745
|
+
return step?.codex ? cloneContractValue(step.codex) : null;
|
|
840
746
|
}
|
|
841
747
|
|
|
842
748
|
async function buildCodexHandoff(stepId, artifacts = {}) {
|
|
@@ -845,24 +751,30 @@ async function buildCodexHandoff(stepId, artifacts = {}) {
|
|
|
845
751
|
|
|
846
752
|
async function readSessionArtifacts(paths) {
|
|
847
753
|
const activeCycle = await readActiveCycle(paths);
|
|
754
|
+
const globalPlanExecutionRecordPath = path.join(paths.sessionRoot, "steps", "plan_executed");
|
|
755
|
+
const legacyPlanExecutionRecordPath = path.join(cycleStepsRoot(paths, activeCycle), "plan_executed");
|
|
848
756
|
const [
|
|
849
757
|
status,
|
|
850
758
|
rawCurrentStep,
|
|
851
759
|
issueUrl,
|
|
760
|
+
issueNumber,
|
|
852
761
|
prUrl,
|
|
853
762
|
issueText,
|
|
854
763
|
issueTitle,
|
|
855
|
-
planText,
|
|
856
|
-
issueDetails,
|
|
857
|
-
agentDecisions,
|
|
858
764
|
finalReportText,
|
|
765
|
+
pullRequestText,
|
|
859
766
|
githubCommentsText,
|
|
860
767
|
codexThreadId,
|
|
861
768
|
workflowVersion,
|
|
862
769
|
baseBranch,
|
|
863
770
|
baseCommit,
|
|
864
|
-
|
|
865
|
-
|
|
771
|
+
planExecutionRecord,
|
|
772
|
+
issueDefinitionRequested,
|
|
773
|
+
issueFileRequested,
|
|
774
|
+
pullRequestFileRequested,
|
|
775
|
+
makePlanRequested,
|
|
776
|
+
blueprintUpdateRequested,
|
|
777
|
+
executePlanRequested,
|
|
866
778
|
prOutcomeText,
|
|
867
779
|
mainCheckoutSyncText,
|
|
868
780
|
changesCommittedText
|
|
@@ -870,32 +782,28 @@ async function readSessionArtifacts(paths) {
|
|
|
870
782
|
readTrimmedFile(path.join(paths.sessionRoot, "status")),
|
|
871
783
|
readTrimmedFile(path.join(paths.sessionRoot, "current_step")),
|
|
872
784
|
readTrimmedFile(path.join(paths.sessionRoot, "issue_url")),
|
|
785
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "issue_number")),
|
|
873
786
|
readTrimmedFile(path.join(paths.sessionRoot, "pr_url")),
|
|
874
787
|
readTextIfExists(path.join(paths.sessionRoot, "issue.md")),
|
|
875
788
|
readTrimmedFile(path.join(paths.sessionRoot, "issue_title")),
|
|
876
|
-
readTextIfExists(
|
|
877
|
-
readTextIfExists(path.join(paths.sessionRoot, "
|
|
878
|
-
readTextIfExists(path.join(paths.sessionRoot, "agent_decisions.md")),
|
|
879
|
-
readTextIfExists(path.join(paths.sessionRoot, "final_report.md")),
|
|
789
|
+
readTextIfExists(path.join(paths.sessionRoot, "final_report")),
|
|
790
|
+
readTextIfExists(path.join(paths.sessionRoot, "pull_request.md")),
|
|
880
791
|
readTextIfExists(path.join(paths.sessionRoot, "github_comments.json")),
|
|
881
792
|
readTrimmedFile(path.join(paths.sessionRoot, "codex_thread_id")),
|
|
882
793
|
readWorkflowVersion(paths),
|
|
883
794
|
readTrimmedFile(path.join(paths.sessionRoot, "base_branch")),
|
|
884
795
|
readTrimmedFile(path.join(paths.sessionRoot, "base_commit")),
|
|
885
|
-
readTextIfExists(
|
|
886
|
-
|
|
796
|
+
readTextIfExists(globalPlanExecutionRecordPath).then(async (text) => text || await readTextIfExists(legacyPlanExecutionRecordPath)),
|
|
797
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "issue_prompt_rendered_requested")),
|
|
798
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "issue_created_requested")),
|
|
799
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "pull_request_file_requested")),
|
|
800
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "make_plan_requested")),
|
|
801
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "blueprint_updated_requested")),
|
|
802
|
+
readTrimmedFile(path.join(paths.sessionRoot, "metadata", "execute_plan_requested")),
|
|
887
803
|
readTextIfExists(path.join(paths.sessionRoot, "pr_outcome.json")),
|
|
888
804
|
readTextIfExists(path.join(paths.sessionRoot, "main_checkout_sync.json")),
|
|
889
805
|
readTextIfExists(path.join(paths.sessionRoot, "changes_committed.json"))
|
|
890
806
|
]);
|
|
891
|
-
let issueMetadata = null;
|
|
892
|
-
if (issueMetadataText) {
|
|
893
|
-
try {
|
|
894
|
-
issueMetadata = JSON.parse(issueMetadataText);
|
|
895
|
-
} catch {
|
|
896
|
-
issueMetadata = null;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
807
|
let githubComments = {};
|
|
900
808
|
if (githubCommentsText) {
|
|
901
809
|
try {
|
|
@@ -942,9 +850,8 @@ async function readSessionArtifacts(paths) {
|
|
|
942
850
|
const worktreeReady = await hasWorktree(paths);
|
|
943
851
|
const worktreeStatus = await readWorktreeStatus(paths, worktreeReady);
|
|
944
852
|
const commandLogPath = path.join(paths.sessionRoot, "command_log.jsonl");
|
|
945
|
-
const
|
|
946
|
-
const
|
|
947
|
-
const planExecutionPromptExists = await fileExists(planExecutionPromptPath);
|
|
853
|
+
const dependencyInstallRecord = await readTextIfExists(path.join(paths.sessionRoot, "steps", "dependencies_installed"));
|
|
854
|
+
const dependencyInstallResult = await readTextIfExists(path.join(paths.sessionRoot, DEPENDENCIES_INSTALL_RESULT_FILE));
|
|
948
855
|
const appRootForArtifacts = worktreeReady ? paths.worktree : paths.targetRoot;
|
|
949
856
|
const appReady = await inspectReadyJskitAppRoot(appRootForArtifacts);
|
|
950
857
|
const blueprintPath = path.join(appRootForArtifacts, ".jskit", "APP_BLUEPRINT.md");
|
|
@@ -952,12 +859,12 @@ async function readSessionArtifacts(paths) {
|
|
|
952
859
|
const currentStep = normalizeStepId(rawCurrentStep);
|
|
953
860
|
let completedSteps = await readCompletedSteps(paths);
|
|
954
861
|
const worktreeRemovalCompleted = completedSteps.includes("session_finished");
|
|
955
|
-
const
|
|
862
|
+
const worktreeStepRecordInvalid = !worktreeReady &&
|
|
956
863
|
completedSteps.includes("worktree_created") &&
|
|
957
864
|
!worktreeRemovalCompleted &&
|
|
958
865
|
status !== SESSION_STATUS.FINISHED &&
|
|
959
866
|
status !== SESSION_STATUS.ABANDONED;
|
|
960
|
-
if (
|
|
867
|
+
if (worktreeStepRecordInvalid) {
|
|
961
868
|
completedSteps = completedSteps.filter((stepId) => !["worktree_created", "dependencies_installed"].includes(stepId));
|
|
962
869
|
}
|
|
963
870
|
const nextStep = resolveNextStep(completedSteps);
|
|
@@ -968,6 +875,8 @@ async function readSessionArtifacts(paths) {
|
|
|
968
875
|
? nextStep
|
|
969
876
|
: currentStep || nextStep;
|
|
970
877
|
const prompt = await readPromptForStep(paths, effectiveCurrentStep, { reviewPasses });
|
|
878
|
+
const dependencyInstallDetails = dependencyInstallRecord.trim() || dependencyInstallResult.trim();
|
|
879
|
+
const dependencyInstallReady = Boolean(dependencyInstallRecord.trim() || dependencyInstallResult.trim());
|
|
971
880
|
|
|
972
881
|
return {
|
|
973
882
|
codexThreadId,
|
|
@@ -987,40 +896,40 @@ async function readSessionArtifacts(paths) {
|
|
|
987
896
|
commandLogExists: await fileExists(commandLogPath),
|
|
988
897
|
commandLogPath,
|
|
989
898
|
dependencyInstall: {
|
|
990
|
-
installed: Boolean(
|
|
991
|
-
|
|
992
|
-
|
|
899
|
+
installed: Boolean(dependencyInstallRecord.trim()),
|
|
900
|
+
ready: dependencyInstallReady,
|
|
901
|
+
details: dependencyInstallDetails,
|
|
902
|
+
status: dependencyInstallRecord.trim()
|
|
993
903
|
? "installed"
|
|
904
|
+
: dependencyInstallResult.trim() ? "ready_to_advance"
|
|
994
905
|
: worktreeReady ? "pending" : "waiting_for_worktree"
|
|
995
906
|
},
|
|
996
907
|
helperMapExists: await fileExists(helperMapPath),
|
|
997
908
|
helperMapPath,
|
|
998
909
|
githubComments,
|
|
999
|
-
|
|
1000
|
-
issueCategory: normalizeText(issueMetadata?.issueCategory || ""),
|
|
1001
|
-
uiImpact: normalizeText(issueMetadata?.uiImpact || ""),
|
|
1002
|
-
agentDecisions: agentDecisions.trim(),
|
|
1003
|
-
agentDecisionsLatest: agentDecisions
|
|
1004
|
-
.split(/\r?\n/u)
|
|
1005
|
-
.map((line) => line.trim())
|
|
1006
|
-
.filter((line) => line && !line.startsWith("#") && !line.startsWith("Session:"))
|
|
1007
|
-
.slice(-5)
|
|
1008
|
-
.join("\n"),
|
|
910
|
+
issueNumber: issueNumber || issueNumberFromUrl(issueUrl),
|
|
1009
911
|
issueTitle,
|
|
1010
912
|
issueText: issueText.trim(),
|
|
1011
913
|
issueUrl,
|
|
1012
914
|
nextStep,
|
|
915
|
+
pullRequestPath: path.join(paths.sessionRoot, "pull_request.md"),
|
|
916
|
+
pullRequestText: pullRequestText.trim(),
|
|
1013
917
|
prUrl,
|
|
1014
918
|
prOutcome,
|
|
1015
919
|
mainCheckoutSync,
|
|
920
|
+
acceptedChangesCommit,
|
|
921
|
+
issueDefinitionRequested: Boolean(issueDefinitionRequested.trim()),
|
|
922
|
+
issueFileRequested: Boolean(issueFileRequested.trim()),
|
|
923
|
+
pullRequestFileRequested: Boolean(pullRequestFileRequested.trim()),
|
|
924
|
+
makePlanRequested: Boolean(makePlanRequested.trim()),
|
|
925
|
+
blueprintUpdateRequested: Boolean(blueprintUpdateRequested.trim()),
|
|
926
|
+
executePlanRequested: Boolean(executePlanRequested.trim()),
|
|
1016
927
|
planExecution: {
|
|
1017
|
-
prompted:
|
|
1018
|
-
promptPath:
|
|
1019
|
-
|
|
1020
|
-
submitted: Boolean(
|
|
928
|
+
prompted: Boolean(executePlanRequested.trim()),
|
|
929
|
+
promptPath: "",
|
|
930
|
+
details: planExecutionRecord.trim(),
|
|
931
|
+
submitted: Boolean(planExecutionRecord.trim())
|
|
1021
932
|
},
|
|
1022
|
-
planText: planText.trim(),
|
|
1023
|
-
issueDetails: issueDetails.trim(),
|
|
1024
933
|
finalReportText: finalReportText.trim(),
|
|
1025
934
|
prompt: prompt.trim(),
|
|
1026
935
|
status: status || SESSION_STATUS.PENDING,
|
|
@@ -1031,14 +940,244 @@ async function readSessionArtifacts(paths) {
|
|
|
1031
940
|
};
|
|
1032
941
|
}
|
|
1033
942
|
|
|
1034
|
-
function
|
|
943
|
+
function stepCanExposeNextCommand(stepId, artifacts = {}) {
|
|
1035
944
|
if (!stepId) {
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
if (stepId === "worktree_created") {
|
|
948
|
+
return artifacts.worktreeReady === true;
|
|
949
|
+
}
|
|
950
|
+
if (stepId === "dependencies_installed") {
|
|
951
|
+
return artifacts.dependencyInstall?.ready === true;
|
|
952
|
+
}
|
|
953
|
+
if (stepId === "issue_prompt_rendered") {
|
|
954
|
+
return Boolean(artifacts.issueText);
|
|
955
|
+
}
|
|
956
|
+
if (stepId === "issue_created") {
|
|
957
|
+
return Boolean(artifacts.issueText);
|
|
958
|
+
}
|
|
959
|
+
if (stepId === "issue_submitted") {
|
|
960
|
+
return Boolean(artifacts.issueUrl);
|
|
961
|
+
}
|
|
962
|
+
if (stepId === "changes_committed") {
|
|
963
|
+
return Boolean(artifacts.acceptedChangesCommit?.commit);
|
|
964
|
+
}
|
|
965
|
+
if (stepId === "final_report_created") {
|
|
966
|
+
return Boolean(artifacts.pullRequestText);
|
|
967
|
+
}
|
|
968
|
+
if (stepId === "session_finished") {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
if (stepId === "plan_made") {
|
|
972
|
+
return artifacts.makePlanRequested === true;
|
|
973
|
+
}
|
|
974
|
+
if (stepId === "plan_executed") {
|
|
975
|
+
return artifacts.executePlanRequested === true;
|
|
976
|
+
}
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function buildNextCommand(sessionId, stepId, artifacts = {}) {
|
|
981
|
+
if (!stepCanExposeNextCommand(stepId, artifacts)) {
|
|
1036
982
|
return "";
|
|
1037
983
|
}
|
|
1038
|
-
const template = STEP_DEFINITION_BY_ID[stepId]?.nextCommandTemplate || `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}}
|
|
984
|
+
const template = STEP_DEFINITION_BY_ID[stepId]?.nextCommandTemplate || `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} next`;
|
|
1039
985
|
return template.replaceAll("{{session_id}}", sessionId);
|
|
1040
986
|
}
|
|
1041
987
|
|
|
988
|
+
function buildStepActionCommands(sessionId, stepId, artifacts = {}) {
|
|
989
|
+
const commandBase = `${JSKIT_CLI_SHELL_COMMAND} session ${sessionId}`;
|
|
990
|
+
if (stepId === "worktree_created") {
|
|
991
|
+
return artifacts.worktreeReady === true
|
|
992
|
+
? []
|
|
993
|
+
: [
|
|
994
|
+
{
|
|
995
|
+
command: `${commandBase} create_worktree`,
|
|
996
|
+
id: "create_worktree",
|
|
997
|
+
label: "Create worktree"
|
|
998
|
+
}
|
|
999
|
+
];
|
|
1000
|
+
}
|
|
1001
|
+
if (stepId === "dependencies_installed") {
|
|
1002
|
+
return artifacts.dependencyInstall?.ready === true
|
|
1003
|
+
? []
|
|
1004
|
+
: [
|
|
1005
|
+
{
|
|
1006
|
+
command: `${commandBase} run_npm_install`,
|
|
1007
|
+
id: "run_npm_install",
|
|
1008
|
+
label: "Run npm install"
|
|
1009
|
+
}
|
|
1010
|
+
];
|
|
1011
|
+
}
|
|
1012
|
+
if (stepId === "issue_prompt_rendered") {
|
|
1013
|
+
return [
|
|
1014
|
+
{
|
|
1015
|
+
command: `${commandBase} define_issue --prompt "<what should change>"`,
|
|
1016
|
+
id: "define_issue",
|
|
1017
|
+
label: "Define issue"
|
|
1018
|
+
},
|
|
1019
|
+
...(artifacts.issueDefinitionRequested
|
|
1020
|
+
? [
|
|
1021
|
+
{
|
|
1022
|
+
command: `${commandBase} create_issue_file`,
|
|
1023
|
+
id: "create_issue_file",
|
|
1024
|
+
label: "Create issue file"
|
|
1025
|
+
}
|
|
1026
|
+
]
|
|
1027
|
+
: [])
|
|
1028
|
+
];
|
|
1029
|
+
}
|
|
1030
|
+
if (stepId === "issue_created") {
|
|
1031
|
+
return [
|
|
1032
|
+
{
|
|
1033
|
+
command: `${commandBase} create_issue_file`,
|
|
1034
|
+
id: "create_issue_file",
|
|
1035
|
+
label: "Create issue file"
|
|
1036
|
+
}
|
|
1037
|
+
];
|
|
1038
|
+
}
|
|
1039
|
+
if (stepId === "issue_submitted") {
|
|
1040
|
+
return artifacts.issueUrl
|
|
1041
|
+
? []
|
|
1042
|
+
: [
|
|
1043
|
+
{
|
|
1044
|
+
command: `${commandBase} create_issue_on_gh`,
|
|
1045
|
+
id: "create_issue_on_gh",
|
|
1046
|
+
label: "Create issue on GH"
|
|
1047
|
+
}
|
|
1048
|
+
];
|
|
1049
|
+
}
|
|
1050
|
+
if (stepId === "plan_made") {
|
|
1051
|
+
return [
|
|
1052
|
+
{
|
|
1053
|
+
command: `${commandBase} make_plan`,
|
|
1054
|
+
id: "make_plan",
|
|
1055
|
+
label: "Make plan"
|
|
1056
|
+
}
|
|
1057
|
+
];
|
|
1058
|
+
}
|
|
1059
|
+
if (stepId === "plan_executed") {
|
|
1060
|
+
return [
|
|
1061
|
+
{
|
|
1062
|
+
command: `${commandBase} execute_plan`,
|
|
1063
|
+
id: "execute_plan",
|
|
1064
|
+
label: "Execute plan"
|
|
1065
|
+
}
|
|
1066
|
+
];
|
|
1067
|
+
}
|
|
1068
|
+
if (stepId === "deep_ui_check_run") {
|
|
1069
|
+
return [
|
|
1070
|
+
{
|
|
1071
|
+
command: `${commandBase} run_deep_ui_check`,
|
|
1072
|
+
id: "run_deep_ui_check",
|
|
1073
|
+
label: "Run deep UI check"
|
|
1074
|
+
}
|
|
1075
|
+
];
|
|
1076
|
+
}
|
|
1077
|
+
if (stepId === "automated_checks_run") {
|
|
1078
|
+
return [
|
|
1079
|
+
{
|
|
1080
|
+
command: `${commandBase} run_automated_checks`,
|
|
1081
|
+
id: "run_automated_checks",
|
|
1082
|
+
label: "Run automated checks"
|
|
1083
|
+
}
|
|
1084
|
+
];
|
|
1085
|
+
}
|
|
1086
|
+
if (stepId === "review_prompt_rendered") {
|
|
1087
|
+
return [
|
|
1088
|
+
{
|
|
1089
|
+
command: `${commandBase} deslop`,
|
|
1090
|
+
id: "deslop",
|
|
1091
|
+
label: "Run deslop"
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
command: `${commandBase} resolve-deslop`,
|
|
1095
|
+
id: "resolve_deslop",
|
|
1096
|
+
label: "Resolve deslop"
|
|
1097
|
+
}
|
|
1098
|
+
];
|
|
1099
|
+
}
|
|
1100
|
+
if (stepId === "blueprint_updated") {
|
|
1101
|
+
return [
|
|
1102
|
+
{
|
|
1103
|
+
command: `${commandBase} update_blueprint`,
|
|
1104
|
+
id: "update_blueprint",
|
|
1105
|
+
label: "Update blueprint"
|
|
1106
|
+
}
|
|
1107
|
+
];
|
|
1108
|
+
}
|
|
1109
|
+
if (stepId === "changes_committed") {
|
|
1110
|
+
return artifacts.acceptedChangesCommit?.commit
|
|
1111
|
+
? []
|
|
1112
|
+
: [
|
|
1113
|
+
{
|
|
1114
|
+
command: `${commandBase} commit_changes`,
|
|
1115
|
+
id: "commit_changes",
|
|
1116
|
+
label: "Commit changes"
|
|
1117
|
+
}
|
|
1118
|
+
];
|
|
1119
|
+
}
|
|
1120
|
+
if (stepId === "final_report_created") {
|
|
1121
|
+
return artifacts.pullRequestText
|
|
1122
|
+
? []
|
|
1123
|
+
: [
|
|
1124
|
+
{
|
|
1125
|
+
command: `${commandBase} create_pull_request_file`,
|
|
1126
|
+
id: "create_pull_request_file",
|
|
1127
|
+
label: "Create PR file"
|
|
1128
|
+
}
|
|
1129
|
+
];
|
|
1130
|
+
}
|
|
1131
|
+
if (stepId === "pr_created") {
|
|
1132
|
+
return artifacts.prUrl
|
|
1133
|
+
? []
|
|
1134
|
+
: [
|
|
1135
|
+
{
|
|
1136
|
+
command: `${commandBase} create_pr_on_gh`,
|
|
1137
|
+
id: "create_pr_on_gh",
|
|
1138
|
+
label: "Create PR on GH"
|
|
1139
|
+
}
|
|
1140
|
+
];
|
|
1141
|
+
}
|
|
1142
|
+
if (stepId === "pr_merge_prepared") {
|
|
1143
|
+
return artifacts.prOutcome?.outcome === "merged" || !artifacts.prUrl
|
|
1144
|
+
? []
|
|
1145
|
+
: [
|
|
1146
|
+
{
|
|
1147
|
+
command: `${commandBase} prepare_for_merge`,
|
|
1148
|
+
id: "prepare_for_merge",
|
|
1149
|
+
label: "Prepare for merge"
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
command: `${commandBase} merge_pr`,
|
|
1153
|
+
id: "merge_pr",
|
|
1154
|
+
label: "Merge"
|
|
1155
|
+
}
|
|
1156
|
+
];
|
|
1157
|
+
}
|
|
1158
|
+
if (stepId === "main_checkout_synced") {
|
|
1159
|
+
return artifacts.prUrl && artifacts.prOutcome?.outcome === "merged" && !artifacts.mainCheckoutSync?.status
|
|
1160
|
+
? [
|
|
1161
|
+
{
|
|
1162
|
+
command: `${commandBase} sync_main_checkout`,
|
|
1163
|
+
id: "sync_main_checkout",
|
|
1164
|
+
label: "Sync main checkout"
|
|
1165
|
+
}
|
|
1166
|
+
]
|
|
1167
|
+
: [];
|
|
1168
|
+
}
|
|
1169
|
+
if (stepId === "session_finished") {
|
|
1170
|
+
return [
|
|
1171
|
+
{
|
|
1172
|
+
command: `${commandBase} finish_session`,
|
|
1173
|
+
id: "finish_session",
|
|
1174
|
+
label: "Finish"
|
|
1175
|
+
}
|
|
1176
|
+
];
|
|
1177
|
+
}
|
|
1178
|
+
return [];
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1042
1181
|
async function buildSessionResponse(paths, {
|
|
1043
1182
|
codex = undefined,
|
|
1044
1183
|
ok = true,
|
|
@@ -1078,26 +1217,28 @@ async function buildSessionResponse(paths, {
|
|
|
1078
1217
|
codex: null,
|
|
1079
1218
|
prompt: "",
|
|
1080
1219
|
nextCommand: "",
|
|
1220
|
+
issueDefinitionRequested: artifacts.issueDefinitionRequested === true,
|
|
1221
|
+
issueFileRequested: artifacts.issueFileRequested === true,
|
|
1222
|
+
issueNumber: artifacts.issueNumber || "",
|
|
1081
1223
|
issueUrl: artifacts.issueUrl || "",
|
|
1082
1224
|
issueTitle: artifacts.issueTitle || "",
|
|
1083
1225
|
issueText: artifacts.issueText || "",
|
|
1084
|
-
|
|
1226
|
+
pullRequestFileRequested: artifacts.pullRequestFileRequested === true,
|
|
1227
|
+
pullRequestPath: artifacts.pullRequestPath || "",
|
|
1228
|
+
pullRequestText: artifacts.pullRequestText || "",
|
|
1085
1229
|
githubComments: cloneContractValue(artifacts.githubComments || {}),
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
agentDecisionsLatest: artifacts.agentDecisionsLatest || "",
|
|
1230
|
+
makePlanRequested: artifacts.makePlanRequested === true,
|
|
1231
|
+
blueprintUpdateRequested: artifacts.blueprintUpdateRequested === true,
|
|
1232
|
+
executePlanRequested: artifacts.executePlanRequested === true,
|
|
1090
1233
|
planExecution: cloneContractValue(artifacts.planExecution || null),
|
|
1091
|
-
|
|
1092
|
-
issueDetails: artifacts.issueDetails || "",
|
|
1093
|
-
issueDetailsPath: artifacts.issueDetails ? path.join(responsePaths.sessionRoot, "issue_details.md") : "",
|
|
1094
|
-
finalReportPath: artifacts.finalReportText ? path.join(responsePaths.sessionRoot, "final_report.md") : "",
|
|
1234
|
+
finalReportPath: artifacts.finalReportText ? path.join(responsePaths.sessionRoot, "final_report") : "",
|
|
1095
1235
|
finalReportText: artifacts.finalReportText || "",
|
|
1096
1236
|
helperMapPath: artifacts.helperMapPath || "",
|
|
1097
1237
|
helperMapExists: artifacts.helperMapExists === true,
|
|
1098
1238
|
prUrl: artifacts.prUrl || "",
|
|
1099
1239
|
prOutcome: cloneContractValue(artifacts.prOutcome || null),
|
|
1100
1240
|
mainCheckoutSync: cloneContractValue(artifacts.mainCheckoutSync || null),
|
|
1241
|
+
acceptedChangesCommit: cloneContractValue(artifacts.acceptedChangesCommit || null),
|
|
1101
1242
|
preconditions,
|
|
1102
1243
|
errors: [
|
|
1103
1244
|
createError({
|
|
@@ -1144,29 +1285,32 @@ async function buildSessionResponse(paths, {
|
|
|
1144
1285
|
commandLogPath: artifacts.commandLogPath || "",
|
|
1145
1286
|
stepDefinitions: buildStepDefinitions(),
|
|
1146
1287
|
currentStepAction: buildCurrentStepAction(currentStep, artifacts),
|
|
1288
|
+
actionCommands: buildStepActionCommands(paths.sessionId || "", currentStep, artifacts),
|
|
1147
1289
|
codex: codex === undefined ? await buildCodexHandoff(currentStep, artifacts) : await publicCodexContract(codex),
|
|
1148
1290
|
prompt: responsePrompt,
|
|
1149
|
-
nextCommand: buildNextCommand(paths.sessionId || "", currentStep),
|
|
1291
|
+
nextCommand: buildNextCommand(paths.sessionId || "", currentStep, artifacts),
|
|
1292
|
+
issueDefinitionRequested: artifacts.issueDefinitionRequested === true,
|
|
1293
|
+
issueFileRequested: artifacts.issueFileRequested === true,
|
|
1294
|
+
issueNumber: artifacts.issueNumber || "",
|
|
1150
1295
|
issueUrl: artifacts.issueUrl || "",
|
|
1151
1296
|
issueTitle: artifacts.issueTitle || "",
|
|
1152
1297
|
issueText: artifacts.issueText || "",
|
|
1153
|
-
|
|
1298
|
+
pullRequestFileRequested: artifacts.pullRequestFileRequested === true,
|
|
1299
|
+
pullRequestPath: artifacts.pullRequestPath || "",
|
|
1300
|
+
pullRequestText: artifacts.pullRequestText || "",
|
|
1154
1301
|
githubComments: cloneContractValue(artifacts.githubComments || {}),
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
agentDecisionsLatest: artifacts.agentDecisionsLatest || "",
|
|
1302
|
+
makePlanRequested: artifacts.makePlanRequested === true,
|
|
1303
|
+
blueprintUpdateRequested: artifacts.blueprintUpdateRequested === true,
|
|
1304
|
+
executePlanRequested: artifacts.executePlanRequested === true,
|
|
1159
1305
|
planExecution: cloneContractValue(artifacts.planExecution || null),
|
|
1160
|
-
|
|
1161
|
-
issueDetails: artifacts.issueDetails || "",
|
|
1162
|
-
issueDetailsPath: artifacts.issueDetails ? path.join(responsePaths.sessionRoot, "issue_details.md") : "",
|
|
1163
|
-
finalReportPath: artifacts.finalReportText ? path.join(responsePaths.sessionRoot, "final_report.md") : "",
|
|
1306
|
+
finalReportPath: artifacts.finalReportText ? path.join(responsePaths.sessionRoot, "final_report") : "",
|
|
1164
1307
|
finalReportText: artifacts.finalReportText || "",
|
|
1165
1308
|
helperMapPath: artifacts.helperMapPath || "",
|
|
1166
1309
|
helperMapExists: artifacts.helperMapExists === true,
|
|
1167
1310
|
prUrl: artifacts.prUrl || "",
|
|
1168
1311
|
prOutcome: cloneContractValue(artifacts.prOutcome || null),
|
|
1169
1312
|
mainCheckoutSync: cloneContractValue(artifacts.mainCheckoutSync || null),
|
|
1313
|
+
acceptedChangesCommit: cloneContractValue(artifacts.acceptedChangesCommit || null),
|
|
1170
1314
|
preconditions,
|
|
1171
1315
|
errors,
|
|
1172
1316
|
warnings: responseWarnings,
|
|
@@ -1229,16 +1373,11 @@ function buildSessionErrorResponse({
|
|
|
1229
1373
|
nextCommand: "",
|
|
1230
1374
|
issueTitle: "",
|
|
1231
1375
|
issueText: "",
|
|
1232
|
-
|
|
1376
|
+
pullRequestFileRequested: false,
|
|
1377
|
+
pullRequestPath: "",
|
|
1378
|
+
pullRequestText: "",
|
|
1233
1379
|
githubComments: {},
|
|
1234
|
-
issueCategory: "",
|
|
1235
|
-
uiImpact: "",
|
|
1236
|
-
agentDecisionsPath: "",
|
|
1237
|
-
agentDecisionsLatest: "",
|
|
1238
1380
|
planExecution: null,
|
|
1239
|
-
planText: "",
|
|
1240
|
-
issueDetails: "",
|
|
1241
|
-
issueDetailsPath: "",
|
|
1242
1381
|
finalReportPath: "",
|
|
1243
1382
|
finalReportText: "",
|
|
1244
1383
|
helperMapPath: "",
|
|
@@ -1268,27 +1407,28 @@ async function markCurrentStep(paths, stepId) {
|
|
|
1268
1407
|
await writeTextFile(path.join(paths.sessionRoot, "current_step"), stepId);
|
|
1269
1408
|
}
|
|
1270
1409
|
|
|
1271
|
-
async function
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1410
|
+
async function writeStepRecord(paths, stepId, message) {
|
|
1411
|
+
const root = isCycleStepId(stepId)
|
|
1412
|
+
? cycleStepsRoot(paths, await readActiveCycle(paths))
|
|
1413
|
+
: path.join(paths.sessionRoot, "steps");
|
|
1274
1414
|
await mkdir(root, { recursive: true });
|
|
1275
1415
|
await writeTextFile(
|
|
1276
1416
|
path.join(root, stepId),
|
|
1277
|
-
`${
|
|
1417
|
+
`${timestampForStepRecord()}\n${normalizeText(message) || STEP_LABEL_BY_ID[stepId] || stepId}`
|
|
1278
1418
|
);
|
|
1279
1419
|
const completedSteps = await readCompletedSteps(paths);
|
|
1280
1420
|
await markCurrentStep(paths, resolveNextStep(completedSteps));
|
|
1281
1421
|
}
|
|
1282
1422
|
|
|
1283
|
-
async function
|
|
1423
|
+
async function writeCycleStepRecord(paths, recordName, message, {
|
|
1284
1424
|
cycle = ""
|
|
1285
1425
|
} = {}) {
|
|
1286
1426
|
const activeCycle = normalizeCycleNumber(cycle || await readActiveCycle(paths));
|
|
1287
1427
|
const root = cycleStepsRoot(paths, activeCycle);
|
|
1288
1428
|
await mkdir(root, { recursive: true });
|
|
1289
1429
|
await writeTextFile(
|
|
1290
|
-
path.join(root, normalizeText(
|
|
1291
|
-
`${
|
|
1430
|
+
path.join(root, normalizeText(recordName)),
|
|
1431
|
+
`${timestampForStepRecord()}\n${normalizeText(message) || normalizeText(recordName)}`
|
|
1292
1432
|
);
|
|
1293
1433
|
}
|
|
1294
1434
|
|
|
@@ -1331,12 +1471,11 @@ export {
|
|
|
1331
1471
|
markStatus,
|
|
1332
1472
|
normalizeReviewPassNumber,
|
|
1333
1473
|
readActiveCycle,
|
|
1334
|
-
|
|
1474
|
+
readStepRecords,
|
|
1335
1475
|
readReviewPasses,
|
|
1336
1476
|
readSessionArtifacts,
|
|
1337
1477
|
reviewPassDirectoryName,
|
|
1338
1478
|
reviewPassRoot,
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
writeReceipt
|
|
1479
|
+
writeCycleStepRecord,
|
|
1480
|
+
writeStepRecord
|
|
1342
1481
|
};
|