vibe64 0.1.21 → 0.1.22

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.
Files changed (143) hide show
  1. package/dist/assets/ProjectTypeGate-B3NHteXC.js +1 -0
  2. package/dist/assets/ProjectTypeGate-fJcYj6sR.css +1 -0
  3. package/dist/assets/{SetupReadinessGate-DkkorR2Y.js → SetupReadinessGate-2twHjoYZ.js} +1 -1
  4. package/dist/assets/{ShellLayout-BcKS2HG9.js → ShellLayout-BjAbXfzf.js} +1 -1
  5. package/dist/assets/{VTabs-BJ8MKoig.js → VTabs-BLGGdbms.js} +1 -1
  6. package/dist/assets/{error-CwT53qTu.js → error-DvUPciuh.js} +1 -1
  7. package/dist/assets/history-DfM4ewyV.js +1 -0
  8. package/dist/assets/home-sh4v0vz7.js +1 -0
  9. package/dist/assets/index-C4JbNnml.css +1 -0
  10. package/dist/assets/{index-BJxDQvyK.js → index-CdFWokfE.js} +1 -1
  11. package/dist/assets/index-D4oqjV9n.js +132 -0
  12. package/dist/assets/{index-CY-wDjNv.js → index-DqoYKexs.js} +15 -15
  13. package/dist/assets/{placement-7FCYadh2.js → placement-QfRaYTFY.js} +1 -1
  14. package/dist/assets/{setup-BzzdD38G.js → setup-B-WOti3H.js} +2 -2
  15. package/dist/assets/{streamEvents-Ba9kHw-1.js → streamEvents-CIqegoLa.js} +1 -1
  16. package/dist/assets/{studioGateApi-hkZdLr1Y.js → studioGateApi-CFtez6lH.js} +1 -1
  17. package/dist/assets/{target-scripts-8hczu84t.js → target-scripts-CI-nAISO.js} +1 -1
  18. package/dist/assets/useStudioTerminal-I8tuBymw.js +1 -0
  19. package/dist/assets/vibe64SessionPanelModel-DLdexNxC.js +1 -0
  20. package/dist/index.html +1 -1
  21. package/node_modules/@local/setup-doctor-core/src/server/doctorToolchain.js +4 -0
  22. package/node_modules/@local/studio-terminal-core/src/server/gitGithubTransport.js +33 -0
  23. package/node_modules/@local/studio-terminal-core/src/server/index.js +1 -0
  24. package/node_modules/@local/studio-terminal-core/src/server/terminalSessions.js +51 -1
  25. package/node_modules/@local/vibe64-adapters/src/server/adapter.js +4 -0
  26. package/node_modules/@local/vibe64-adapters/src/server/adapters/laravel/adapter.js +5 -0
  27. package/node_modules/@local/vibe64-adapters/src/server/adapters/laravel/databaseRuntime.js +50 -0
  28. package/node_modules/@local/vibe64-adapters/src/server/configStore.js +22 -1
  29. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/agent_conversation.txt +0 -2
  30. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/draft_issue.txt +18 -0
  31. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/make_plan.txt +14 -2
  32. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/prepare_for_merge.txt +2 -0
  33. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/resolve_pull_request.txt +1 -0
  34. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/run_deep_ui_check.txt +7 -3
  35. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/commitPush.js +4 -22
  36. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/factMetadata.js +21 -20
  37. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/issuePr.js +27 -4
  38. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/mergeSync.js +65 -8
  39. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/worktreeDependencies.js +12 -25
  40. package/node_modules/@local/vibe64-adapters/src/server/workflowSessionActions.js +77 -20
  41. package/node_modules/@local/vibe64-core/src/server/terminalWebSocketRoutes.js +14 -5
  42. package/node_modules/@local/vibe64-project/src/server/actions.js +21 -0
  43. package/node_modules/@local/vibe64-project/src/server/registerRoutes.js +6 -0
  44. package/node_modules/@local/vibe64-project/src/server/service.js +125 -8
  45. package/node_modules/@local/vibe64-runtime/src/server/coreProjectTools.js +135 -0
  46. package/node_modules/@local/vibe64-runtime/src/server/index.js +4 -0
  47. package/node_modules/@local/vibe64-runtime/src/server/projectToolRegistry.js +434 -0
  48. package/node_modules/@local/vibe64-runtime/src/server/runtime.js +40 -16
  49. package/node_modules/@local/vibe64-runtime/src/server/sessionWorktreeState.js +25 -0
  50. package/node_modules/@local/vibe64-runtime/src/server/terminalFailureFixRequest.js +112 -0
  51. package/node_modules/@local/vibe64-runtime/src/server/workflowArtifacts.js +2 -2
  52. package/node_modules/@local/vibe64-runtime/src/server/workflowDefinitionBuilders.js +1 -0
  53. package/node_modules/@local/vibe64-runtime/src/server/workflowDefinitionComposers.js +192 -0
  54. package/node_modules/@local/vibe64-runtime/src/server/workflowMachine.js +5 -0
  55. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreCoding.js +1309 -419
  56. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreLifecycle.js +540 -358
  57. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreMaintenance.js +5 -26
  58. package/node_modules/@local/vibe64-runtime/src/server/workflowPresentation.js +153 -26
  59. package/node_modules/@local/vibe64-runtime/src/server/workflowStepMachineHelpers.js +63 -10
  60. package/node_modules/@local/vibe64-runtime/src/server/workflowStepMachines.js +63 -2
  61. package/node_modules/@local/vibe64-sessions/src/server/inputSchemas.js +5 -0
  62. package/node_modules/@local/vibe64-sessions/src/server/service.js +8 -9
  63. package/node_modules/@local/vibe64-terminals/src/server/actions.js +65 -0
  64. package/node_modules/@local/vibe64-terminals/src/server/codexTerminal.js +608 -23
  65. package/node_modules/@local/vibe64-terminals/src/server/commandTerminal.js +261 -4
  66. package/node_modules/@local/vibe64-terminals/src/server/fixCodexJobs.js +420 -0
  67. package/node_modules/@local/vibe64-terminals/src/server/inputSchemas.js +240 -0
  68. package/node_modules/@local/vibe64-terminals/src/server/launchTargetTerminal.js +2 -0
  69. package/node_modules/@local/vibe64-terminals/src/server/registerRoutes.js +226 -0
  70. package/node_modules/@local/vibe64-terminals/src/server/service.js +140 -1
  71. package/node_modules/@local/vibe64-terminals/src/server/targetToolchainTerminal.js +1 -1
  72. package/node_modules/@local/vibe64-terminals/src/server/terminalShared.js +34 -0
  73. package/node_modules/node-pty/build/Makefile +369 -0
  74. package/node_modules/node-pty/build/Release/pty.node +0 -0
  75. package/node_modules/node-pty/build/binding.Makefile +6 -0
  76. package/node_modules/node-pty/build/config.gypi +440 -0
  77. package/node_modules/node-pty/build/pty.target.mk +166 -0
  78. package/node_modules/node-pty/node-addon-api/node_addon_api.Makefile +6 -0
  79. package/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +104 -0
  80. package/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +108 -0
  81. package/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +104 -0
  82. package/node_modules/which/CHANGELOG.md +166 -0
  83. package/package.json +2 -2
  84. package/packages/setup-doctor-core/src/server/doctorToolchain.js +4 -0
  85. package/packages/studio-terminal-core/src/server/gitGithubTransport.js +33 -0
  86. package/packages/studio-terminal-core/src/server/index.js +1 -0
  87. package/packages/studio-terminal-core/src/server/terminalSessions.js +51 -1
  88. package/packages/vibe64-adapters/src/server/adapter.js +4 -0
  89. package/packages/vibe64-adapters/src/server/adapters/laravel/adapter.js +5 -0
  90. package/packages/vibe64-adapters/src/server/adapters/laravel/databaseRuntime.js +50 -0
  91. package/packages/vibe64-adapters/src/server/configStore.js +22 -1
  92. package/packages/vibe64-adapters/src/server/systemPrompts/agent_conversation.txt +0 -2
  93. package/packages/vibe64-adapters/src/server/systemPrompts/draft_issue.txt +18 -0
  94. package/packages/vibe64-adapters/src/server/systemPrompts/make_plan.txt +14 -2
  95. package/packages/vibe64-adapters/src/server/systemPrompts/prepare_for_merge.txt +2 -0
  96. package/packages/vibe64-adapters/src/server/systemPrompts/resolve_pull_request.txt +1 -0
  97. package/packages/vibe64-adapters/src/server/systemPrompts/run_deep_ui_check.txt +7 -3
  98. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/commitPush.js +4 -22
  99. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/factMetadata.js +21 -20
  100. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/issuePr.js +27 -4
  101. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/mergeSync.js +65 -8
  102. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/worktreeDependencies.js +12 -25
  103. package/packages/vibe64-adapters/src/server/workflowSessionActions.js +77 -20
  104. package/packages/vibe64-core/src/server/terminalWebSocketRoutes.js +14 -5
  105. package/packages/vibe64-project/src/server/actions.js +21 -0
  106. package/packages/vibe64-project/src/server/registerRoutes.js +6 -0
  107. package/packages/vibe64-project/src/server/service.js +125 -8
  108. package/packages/vibe64-runtime/src/server/coreProjectTools.js +135 -0
  109. package/packages/vibe64-runtime/src/server/index.js +4 -0
  110. package/packages/vibe64-runtime/src/server/projectToolRegistry.js +434 -0
  111. package/packages/vibe64-runtime/src/server/runtime.js +40 -16
  112. package/packages/vibe64-runtime/src/server/sessionWorktreeState.js +25 -0
  113. package/packages/vibe64-runtime/src/server/terminalFailureFixRequest.js +112 -0
  114. package/packages/vibe64-runtime/src/server/workflowArtifacts.js +2 -2
  115. package/packages/vibe64-runtime/src/server/workflowDefinitionBuilders.js +1 -0
  116. package/packages/vibe64-runtime/src/server/workflowDefinitionComposers.js +192 -0
  117. package/packages/vibe64-runtime/src/server/workflowMachine.js +5 -0
  118. package/packages/vibe64-runtime/src/server/workflowModules/coreCoding.js +1309 -419
  119. package/packages/vibe64-runtime/src/server/workflowModules/coreLifecycle.js +540 -358
  120. package/packages/vibe64-runtime/src/server/workflowModules/coreMaintenance.js +5 -26
  121. package/packages/vibe64-runtime/src/server/workflowPresentation.js +153 -26
  122. package/packages/vibe64-runtime/src/server/workflowStepMachineHelpers.js +63 -10
  123. package/packages/vibe64-runtime/src/server/workflowStepMachines.js +63 -2
  124. package/packages/vibe64-sessions/src/server/inputSchemas.js +5 -0
  125. package/packages/vibe64-sessions/src/server/service.js +8 -9
  126. package/packages/vibe64-terminals/src/server/actions.js +65 -0
  127. package/packages/vibe64-terminals/src/server/codexTerminal.js +608 -23
  128. package/packages/vibe64-terminals/src/server/commandTerminal.js +261 -4
  129. package/packages/vibe64-terminals/src/server/fixCodexJobs.js +420 -0
  130. package/packages/vibe64-terminals/src/server/inputSchemas.js +240 -0
  131. package/packages/vibe64-terminals/src/server/launchTargetTerminal.js +2 -0
  132. package/packages/vibe64-terminals/src/server/registerRoutes.js +226 -0
  133. package/packages/vibe64-terminals/src/server/service.js +140 -1
  134. package/packages/vibe64-terminals/src/server/targetToolchainTerminal.js +1 -1
  135. package/packages/vibe64-terminals/src/server/terminalShared.js +34 -0
  136. package/dist/assets/ProjectTypeGate-DStq4W4X.js +0 -1
  137. package/dist/assets/ProjectTypeGate-Dh2ycSoM.css +0 -1
  138. package/dist/assets/history-HQ_XliX2.js +0 -1
  139. package/dist/assets/home-B89EbUmA.js +0 -1
  140. package/dist/assets/index-16P0GeGq.js +0 -134
  141. package/dist/assets/index-eMhOXhf_.css +0 -1
  142. package/dist/assets/useStudioTerminal-D9TNb50w.js +0 -1
  143. package/dist/assets/vibe64SessionPanelModel-DvY_IbW3.js +0 -1
@@ -5,14 +5,15 @@ import {
5
5
  import { deepFreeze } from "@local/vibe64-core/server/deepFreeze";
6
6
  import {
7
7
  PULL_REQUEST_BODY_DRAFT_ARTIFACT,
8
- PULL_REQUEST_TITLE_DRAFT_ARTIFACT
8
+ PULL_REQUEST_TITLE_DRAFT_ARTIFACT,
9
+ REPORT_ARTIFACT
9
10
  } from "../workflowArtifacts.js";
10
11
  import { when } from "../workflowConditions.js";
11
12
  import {
13
+ LET_CODEX_DECIDE_INPUT,
12
14
  STEP_INPUT_KIND,
13
15
  STEP_STATUS,
14
16
  actionCreatedMetadata,
15
- allMetadataExists,
16
17
  artifactIsReady,
17
18
  artifactText,
18
19
  assertAgentResultSource,
@@ -23,7 +24,6 @@ import {
23
24
  disableAction,
24
25
  machineState,
25
26
  markCommandActionStarted,
26
- markPromptActionStarted,
27
27
  metadataExists,
28
28
  nextForSession,
29
29
  normalizeMachineInput,
@@ -45,14 +45,16 @@ const sessionCreatedStepId = "session_created";
45
45
  const workSourceSelectedStepId = "work_source_selected";
46
46
  const worktreeCreatedStepId = "worktree_created";
47
47
  const dependenciesInstalledStepId = "dependencies_installed";
48
- const projectValidatedStepId = "project_validated";
49
48
  const changesCommittedStepId = "changes_committed";
50
- const createPullRequestStepId = "create_pull_request";
51
- const prMergedStepId = "pr_merged";
52
- const mainCheckoutSyncedStepId = "main_checkout_synced";
49
+ const createAndMergePullRequestStepId = "create_and_merge_pull_request";
53
50
  const sessionFinishedStepId = "session_finished";
54
51
  const installDependenciesActionId = "install_dependencies";
55
52
  const dependenciesInstalledMetadataName = "dependencies_installed";
53
+ const syncMainCheckoutActionId = "sync_main_checkout";
54
+ const mainCheckoutSyncedMetadataName = "main_checkout_synced";
55
+ const mergePreparationSummaryMetadataName = "merge_preparation_summary";
56
+ const finalReportStartMarker = "<!-- vibe64:final-report:start -->";
57
+ const finalReportEndMarker = "<!-- vibe64:final-report:end -->";
56
58
 
57
59
  async function recordMergeIntent(ctx = {}) {
58
60
  await ctx.writeMetadata("autopilot_merge_intent", "merge_and_sync");
@@ -66,7 +68,7 @@ async function skipMergeAndFinish(ctx = {}) {
66
68
  }
67
69
 
68
70
  const coreLifecycleWorkflowIntentHandlers = deepFreeze({
69
- [prMergedStepId]: {
71
+ [createAndMergePullRequestStepId]: {
70
72
  merge_and_sync: recordMergeIntent,
71
73
  skip_merge: skipMergeAndFinish
72
74
  }
@@ -84,15 +86,33 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
84
86
  {
85
87
  disabledReason: "Work source is already selected.",
86
88
  disabledWhen: [when.metadataExists("work_source")],
89
+ advanceOnSuccess: true,
87
90
  icon: "branch",
88
- id: "use_new_branch",
89
- label: "Use new branch",
91
+ id: "use_new_issue",
92
+ label: "Start fresh with a new issue",
90
93
  type: "adapter"
91
94
  },
92
95
  {
93
- adapterCapability: "use_existing_pr",
94
96
  disabledReason: "Work source is already selected.",
95
97
  disabledWhen: [when.metadataExists("work_source")],
98
+ advanceOnSuccess: true,
99
+ icon: "github",
100
+ id: "use_existing_issue",
101
+ inputFields: [
102
+ {
103
+ label: "Issue URL or number",
104
+ name: "issueRef",
105
+ placeholder: "123, #123, or https://github.com/org/repo/issues/123",
106
+ requiredMessage: "Issue URL or number is required."
107
+ }
108
+ ],
109
+ label: "Solve existing issue",
110
+ type: "adapter"
111
+ },
112
+ {
113
+ disabledReason: "Work source is already selected.",
114
+ disabledWhen: [when.metadataExists("work_source")],
115
+ advanceOnSuccess: true,
96
116
  icon: "github",
97
117
  id: "use_existing_pr",
98
118
  inputFields: [
@@ -105,26 +125,70 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
105
125
  ],
106
126
  label: "Use existing PR",
107
127
  type: "adapter"
128
+ },
129
+ {
130
+ disabledReason: "Work source is already selected.",
131
+ disabledWhen: [when.metadataExists("work_source")],
132
+ advanceOnSuccess: true,
133
+ icon: "message-square-plus",
134
+ id: "use_description",
135
+ label: "Describe work without an issue",
136
+ type: "adapter"
108
137
  }
109
138
  ],
110
139
  autopilot: {
111
- actionId: "use_new_branch",
112
- advanceOnSuccess: true,
113
140
  completeWhen: [when.metadataExists("work_source")],
114
- label: "Choose work source"
141
+ kind: "work_source",
142
+ label: "Choose starting point",
143
+ stop: true
115
144
  },
116
- description: "Choose whether this session starts from a new branch or an existing pull request.",
145
+ description: "Choose whether this session starts fresh with a new issue, solves an existing issue, builds on an existing pull request, or starts from a plain work description.",
117
146
  id: workSourceSelectedStepId,
118
- interaction: {
119
- kind: "run_action",
120
- primaryActionLabel: "Use new branch",
121
- title: "Choose work source"
122
- },
123
- label: "Choose work source",
147
+ label: "Choose starting point",
124
148
  next: {
125
- disabledReason: "Choose a work source before continuing.",
149
+ disabledReason: "Choose a starting point before continuing.",
126
150
  enabledWhen: [when.metadataExists("work_source")]
127
151
  },
152
+ presentation: {
153
+ stop: {
154
+ intents: [
155
+ {
156
+ actionId: "use_new_issue",
157
+ id: "use_new_issue",
158
+ label: "Start fresh with a new issue",
159
+ style: "primary",
160
+ type: "action"
161
+ },
162
+ {
163
+ actionId: "use_existing_issue",
164
+ id: "use_existing_issue",
165
+ label: "Solve existing issue",
166
+ style: "secondary",
167
+ type: "action"
168
+ },
169
+ {
170
+ actionId: "use_existing_pr",
171
+ id: "use_existing_pr",
172
+ label: "Use existing PR",
173
+ style: "secondary",
174
+ type: "action"
175
+ },
176
+ {
177
+ actionId: "use_description",
178
+ id: "use_description",
179
+ label: "Describe work without an issue",
180
+ style: "secondary",
181
+ type: "action"
182
+ }
183
+ ],
184
+ screen: {
185
+ kind: "work_source",
186
+ message: "Start fresh with a new issue, solve an existing issue, build on an existing pull request, or describe work without creating an issue.",
187
+ sections: [],
188
+ title: "Choose starting point"
189
+ }
190
+ }
191
+ },
128
192
  rewindable: false
129
193
  },
130
194
  [worktreeCreatedStepId]: {
@@ -194,62 +258,6 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
194
258
  metadata: [dependenciesInstalledMetadataName, "dependencies_path"]
195
259
  }
196
260
  },
197
- [projectValidatedStepId]: {
198
- actions: [
199
- {
200
- adapterCapability: "update_code_index",
201
- icon: "sync",
202
- id: "update_code_index",
203
- label: "Update code index",
204
- type: "command"
205
- },
206
- {
207
- adapterCapability: "run_automated_checks",
208
- enabledWhen: [when.metadataExists("code_index_updated")],
209
- enabledWhenReason: "Update the code index before running automated checks.",
210
- icon: "run",
211
- id: "run_automated_checks",
212
- label: "Run automated checks",
213
- type: "command"
214
- }
215
- ],
216
- autopilot: {
217
- actionSequence: [
218
- {
219
- actionId: "update_code_index",
220
- completeWhen: [when.metadataExists("code_index_updated")],
221
- label: "Update code index"
222
- },
223
- {
224
- actionId: "run_automated_checks",
225
- completeWhen: [when.metadataExists("automated_checks_passed")],
226
- label: "Run automated checks"
227
- }
228
- ],
229
- label: "Validate project"
230
- },
231
- description: "Update the adapter-provided code index and run automated checks.",
232
- id: projectValidatedStepId,
233
- label: "Validate project",
234
- next: {
235
- disabledReason: "Update the code index and run automated checks successfully before continuing.",
236
- enabledWhen: [
237
- when.metadataExists("code_index_updated"),
238
- when.metadataExists("automated_checks_passed")
239
- ]
240
- },
241
- rewindCleanup: {
242
- actionResults: ["update_code_index", "run_automated_checks"],
243
- metadata: [
244
- "code_index_command_source",
245
- "code_index_package_manager",
246
- "code_index_path",
247
- "code_index_updated",
248
- "automated_checks_package_manager",
249
- "automated_checks_passed"
250
- ]
251
- }
252
- },
253
261
  [changesCommittedStepId]: {
254
262
  actions: [
255
263
  {
@@ -280,7 +288,7 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
280
288
  metadata: ["accepted_commit", "branch_pushed"]
281
289
  }
282
290
  },
283
- [createPullRequestStepId]: {
291
+ [createAndMergePullRequestStepId]: {
284
292
  actions: [
285
293
  {
286
294
  enabledWhen: [when.metadataExists("pr_url")],
@@ -291,8 +299,14 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
291
299
  type: "link"
292
300
  },
293
301
  {
302
+ auditMessage: "Draft pull request.",
294
303
  disabledReason: "Pull request details are already ready for review.",
295
- disabledWhen: [when.allArtifactsReady(PULL_REQUEST_TITLE_DRAFT_ARTIFACT, PULL_REQUEST_BODY_DRAFT_ARTIFACT)],
304
+ disabledWhen: [
305
+ when.any(
306
+ when.allArtifactsReady(PULL_REQUEST_TITLE_DRAFT_ARTIFACT, PULL_REQUEST_BODY_DRAFT_ARTIFACT),
307
+ when.metadataExists("pr_url")
308
+ )
309
+ ],
296
310
  id: "resolve_pull_request",
297
311
  label: "Draft PR",
298
312
  promptId: "resolve_pull_request",
@@ -300,6 +314,7 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
300
314
  },
301
315
  {
302
316
  adapterCapability: "create_pr_on_gh",
317
+ auditMessage: "Pull request draft accepted; creating GitHub pull request.",
303
318
  disabledReason: "Commit and push changes before creating the GitHub pull request.",
304
319
  disabledWhen: [when.metadataExists("pr_url")],
305
320
  disabledWhenReason: "The GitHub pull request already exists.",
@@ -312,63 +327,11 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
312
327
  icon: "github",
313
328
  id: "create_pr_on_gh",
314
329
  label: "Create PR on GH",
330
+ saveCurrentStepInputBeforeRun: true,
315
331
  type: "command"
316
- }
317
- ],
318
- autopilot: {
319
- actionSequence: [
320
- {
321
- actionId: "resolve_pull_request",
322
- completeWhen: [when.allArtifactsReady(PULL_REQUEST_TITLE_DRAFT_ARTIFACT, PULL_REQUEST_BODY_DRAFT_ARTIFACT)],
323
- label: "Draft PR"
324
- },
325
- {
326
- actionId: "create_pr_on_gh",
327
- completeWhen: [when.metadataExists("pr_url")],
328
- label: "Create PR on GH"
329
- }
330
- ],
331
- label: "Create pull request"
332
- },
333
- description: "Submit the pull request body and create the GitHub pull request.",
334
- id: createPullRequestStepId,
335
- label: "Create pull request",
336
- next: {
337
- disabledReason: "Create the pull request before continuing.",
338
- enabledWhen: [when.metadataExists("pr_url")]
339
- },
340
- rewindCleanup: {
341
- actionResults: ["create_pr_on_gh"],
342
- artifacts: [
343
- PULL_REQUEST_BODY_DRAFT_ARTIFACT,
344
- PULL_REQUEST_TITLE_DRAFT_ARTIFACT,
345
- "create_pull_request.url.txt",
346
- "create_pull_request.number.txt",
347
- "create_pull_request.source.txt"
348
- ],
349
- metadata: [
350
- {
351
- name: "pr_url",
352
- unlessMetadata: {
353
- name: "pr_source",
354
- value: "existing"
355
- }
356
- },
357
- "pr_number",
358
- "pr_title",
359
- {
360
- name: "pr_source",
361
- unlessMetadata: {
362
- name: "pr_source",
363
- value: "existing"
364
- }
365
- }
366
- ]
367
- }
368
- },
369
- [prMergedStepId]: {
370
- actions: [
332
+ },
371
333
  {
334
+ auditMessage: "Prepare pull request for merge.",
372
335
  disabledReason: "Create the pull request before preparing for merge.",
373
336
  disabledWhen: [
374
337
  when.metadataExists("pr_merged"),
@@ -383,6 +346,7 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
383
346
  },
384
347
  {
385
348
  adapterCapability: "merge_pr",
349
+ auditMessage: "Merge pull request.",
386
350
  disabledReason: "Create the pull request before merging.",
387
351
  disabledWhen: [
388
352
  when.metadataExists("pr_merged"),
@@ -396,6 +360,26 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
396
360
  type: "command"
397
361
  },
398
362
  {
363
+ adapterCapability: syncMainCheckoutActionId,
364
+ auditMessage: "Update main checkout after merge.",
365
+ disabledReason: "Merge the pull request before syncing the main checkout.",
366
+ disabledWhen: [
367
+ when.metadataExists(mainCheckoutSyncedMetadataName),
368
+ when.metadataExists("merge_skipped")
369
+ ],
370
+ disabledWhenReason: "The main checkout sync has already been resolved.",
371
+ enabledWhen: [
372
+ when.metadataExists("pr_url"),
373
+ when.metadataExists("pr_merged")
374
+ ],
375
+ enabledWhenReason: "Merge the pull request before syncing the main checkout.",
376
+ icon: "sync",
377
+ id: syncMainCheckoutActionId,
378
+ label: "Sync main checkout",
379
+ type: "command"
380
+ },
381
+ {
382
+ auditMessage: "Pull request will not be merged.",
399
383
  disabledReason: "A merge decision has already been recorded.",
400
384
  disabledWhen: [
401
385
  when.metadataExists("pr_merged"),
@@ -409,17 +393,28 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
409
393
  }
410
394
  ],
411
395
  autopilot: {
412
- kind: "merge_review",
413
- stop: true
396
+ actionSequence: [
397
+ {
398
+ actionId: "resolve_pull_request",
399
+ completeWhen: [when.allArtifactsReady(PULL_REQUEST_TITLE_DRAFT_ARTIFACT, PULL_REQUEST_BODY_DRAFT_ARTIFACT)],
400
+ label: "Draft PR"
401
+ },
402
+ {
403
+ actionId: "create_pr_on_gh",
404
+ completeWhen: [when.metadataExists("pr_url")],
405
+ label: "Create PR on GH"
406
+ }
407
+ ],
408
+ label: "Create pull request, possibly merge"
414
409
  },
415
- description: "Prepare and merge the pull request.",
416
- id: prMergedStepId,
417
- label: "Merge PR",
410
+ description: "Submit the pull request body, create the GitHub pull request, then merge and sync the main checkout or skip merging.",
411
+ id: createAndMergePullRequestStepId,
412
+ label: "Create pull request, possibly merge",
418
413
  next: {
419
- disabledReason: "Merge the pull request or choose not to merge before continuing.",
414
+ disabledReason: "Create the pull request, then merge and sync the main checkout or choose not to merge before continuing.",
420
415
  enabledWhen: [
421
416
  when.any(
422
- when.metadataExists("pr_merged"),
417
+ when.metadataExists(mainCheckoutSyncedMetadataName),
423
418
  when.metadataExists("merge_skipped")
424
419
  )
425
420
  ]
@@ -432,18 +427,22 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
432
427
  metadataName: "autopilot_merge_intent",
433
428
  metadataValue: "merge_and_sync",
434
429
  prepareActionId: "prepare_for_merge",
430
+ syncActionId: syncMainCheckoutActionId,
431
+ syncedMetadataName: mainCheckoutSyncedMetadataName,
435
432
  skippedMetadataName: "merge_skipped"
436
433
  }
437
434
  },
438
435
  stop: {
439
436
  intents: [
440
437
  {
438
+ auditMessage: "Merge pull request and update main checkout.",
441
439
  enabledWhenAction: "prepare_for_merge",
442
440
  id: "merge_and_sync",
443
441
  label: "Merge and update main checkout",
444
442
  style: "primary"
445
443
  },
446
444
  {
445
+ auditMessage: "Pull request will not be merged.",
447
446
  enabledWhenAction: "skip_merge",
448
447
  id: "skip_merge",
449
448
  label: "Do not merge"
@@ -458,45 +457,37 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
458
457
  }
459
458
  },
460
459
  rewindCleanup: {
461
- actionResults: ["prepare_for_merge", "merge_pr", "skip_merge"],
462
- metadata: ["pr_merged", "merge_skipped", "autopilot_merge_intent"]
463
- }
464
- },
465
- [mainCheckoutSyncedStepId]: {
466
- actions: [
467
- {
468
- adapterCapability: "sync_main_checkout",
469
- disabledReason: "Merge the pull request before syncing the main checkout.",
470
- enabledWhen: [
471
- when.metadataExists("pr_url"),
472
- when.metadataExists("pr_merged")
473
- ],
474
- icon: "sync",
475
- id: "sync_main_checkout",
476
- label: "Sync main checkout",
477
- type: "command"
478
- }
479
- ],
480
- autopilot: {
481
- actionId: "sync_main_checkout",
482
- completeWhen: [when.metadataExists("main_checkout_synced")],
483
- label: "Sync main checkout"
484
- },
485
- description: "Sync the main checkout after a successful merge.",
486
- id: mainCheckoutSyncedStepId,
487
- label: "Sync main checkout",
488
- next: {
489
- disabledReason: "Sync the main checkout after merging before continuing.",
490
- enabledWhen: [
491
- when.any(
492
- when.metadataExists("main_checkout_synced"),
493
- when.metadataExists("merge_skipped")
494
- )
460
+ actionResults: ["resolve_pull_request", "create_pr_on_gh", "prepare_for_merge", "merge_pr", syncMainCheckoutActionId, "skip_merge"],
461
+ artifacts: [
462
+ PULL_REQUEST_BODY_DRAFT_ARTIFACT,
463
+ PULL_REQUEST_TITLE_DRAFT_ARTIFACT,
464
+ "create_and_merge_pull_request.url.txt",
465
+ "create_and_merge_pull_request.number.txt",
466
+ "create_and_merge_pull_request.source.txt"
467
+ ],
468
+ metadata: [
469
+ {
470
+ name: "pr_url",
471
+ unlessMetadata: {
472
+ name: "pr_source",
473
+ value: "existing"
474
+ }
475
+ },
476
+ "pr_number",
477
+ "pr_title",
478
+ {
479
+ name: "pr_source",
480
+ unlessMetadata: {
481
+ name: "pr_source",
482
+ value: "existing"
483
+ }
484
+ },
485
+ "pr_merged",
486
+ mainCheckoutSyncedMetadataName,
487
+ "merge_skipped",
488
+ "autopilot_merge_intent",
489
+ mergePreparationSummaryMetadataName
495
490
  ]
496
- },
497
- rewindCleanup: {
498
- actionResults: ["sync_main_checkout"],
499
- metadata: ["main_checkout_synced"]
500
491
  }
501
492
  },
502
493
  [sessionFinishedStepId]: {
@@ -507,7 +498,7 @@ const coreLifecycleStepDefinitionsById = deepFreeze({
507
498
  enabledWhen: [
508
499
  when.metadataExists("pr_url"),
509
500
  when.any(
510
- when.metadataExists("main_checkout_synced"),
501
+ when.metadataExists(mainCheckoutSyncedMetadataName),
511
502
  when.metadataExists("merge_skipped")
512
503
  )
513
504
  ],
@@ -599,7 +590,7 @@ const workSourceSelectedMachine = {
599
590
  default:
600
591
  return {
601
592
  next: nextForSession(context.session, {
602
- disabledReason: "Choose a work source before continuing."
593
+ disabledReason: "Choose a starting point before continuing."
603
594
  }),
604
595
  stepMachine: publicState(this, state)
605
596
  };
@@ -607,7 +598,7 @@ const workSourceSelectedMachine = {
607
598
  },
608
599
 
609
600
  async actionFinished(context = {}) {
610
- if (!["use_new_branch", "use_existing_pr"].includes(context.actionId)) {
601
+ if (!["use_new_issue", "use_existing_issue", "use_existing_pr", "use_description"].includes(context.actionId)) {
611
602
  return;
612
603
  }
613
604
 
@@ -886,47 +877,6 @@ const dependenciesInstalledMachine = {
886
877
  }
887
878
  };
888
879
 
889
- const projectValidatedMachine = {
890
- stepId: projectValidatedStepId,
891
-
892
- initialState(context = {}) {
893
- return allMetadataExists(context.session, ["code_index_updated", "automated_checks_passed"])
894
- ? machineState(STEP_STATUS.DONE)
895
- : machineState(STEP_STATUS.READY);
896
- },
897
-
898
- async view(context = {}) {
899
- let state = await readState(context, this);
900
- if (allMetadataExists(context.session, ["code_index_updated", "automated_checks_passed"])) {
901
- state = machineState(STEP_STATUS.DONE);
902
- }
903
- return commandStepView(context, this, state, {
904
- disabledReason: "Update the code index and run automated checks successfully before continuing.",
905
- failurePrompt: "The project validation command failed. Explain what should happen, then retry validation.",
906
- failureTitle: "Validation needs attention"
907
- });
908
- },
909
-
910
- async submitInput(context = {}) {
911
- return submitCommandFailureInput(context, this);
912
- },
913
-
914
- async actionStarted(context = {}) {
915
- return markCommandActionStarted(context, this, ["update_code_index", "run_automated_checks"]);
916
- },
917
-
918
- async actionFinished(context = {}) {
919
- return writeCommandActionFinishedState(context, this, {
920
- actionIds: ["update_code_index", "run_automated_checks"],
921
- done: allMetadataExists(await context.runtime.getSession(context.session.sessionId), [
922
- "code_index_updated",
923
- "automated_checks_passed"
924
- ]),
925
- failureTitle: "Validation needs attention"
926
- });
927
- }
928
- };
929
-
930
880
  const changesCommittedMachine = {
931
881
  stepId: changesCommittedStepId,
932
882
 
@@ -965,37 +915,180 @@ const changesCommittedMachine = {
965
915
  }
966
916
  };
967
917
 
968
- const pullRequestMergedMachine = {
918
+ const pullRequestPhase = Object.freeze({
919
+ CREATING_PR: "creating_pr",
920
+ DRAFTING: "drafting",
921
+ MERGE_READY: "merge_ready",
922
+ MERGING: "merging",
923
+ PREPARING_MERGE: "preparing_merge",
924
+ REVIEW_DRAFT: "review_draft",
925
+ SYNC_READY: "sync_ready",
926
+ SYNCING_MAIN: "syncing_main"
927
+ });
928
+
929
+ function pullRequestStepComplete(session = {}) {
930
+ return metadataExists(session, mainCheckoutSyncedMetadataName) || metadataExists(session, "merge_skipped");
931
+ }
932
+
933
+ function mainCheckoutSyncPending(session = {}) {
934
+ return metadataExists(session, "pr_merged") && !metadataExists(session, mainCheckoutSyncedMetadataName);
935
+ }
936
+
937
+ function mergeReviewAutopilot() {
938
+ return {
939
+ kind: "merge_review",
940
+ stage: null,
941
+ stop: true
942
+ };
943
+ }
944
+
945
+ const createAndMergePullRequestMachine = {
969
946
  promptActionId: "prepare_for_merge",
970
- stepId: prMergedStepId,
947
+ stepId: createAndMergePullRequestStepId,
971
948
 
972
949
  initialState(context = {}) {
973
- return metadataExists(context.session, "pr_merged") || metadataExists(context.session, "merge_skipped")
974
- ? machineState(STEP_STATUS.DONE)
975
- : machineState(STEP_STATUS.READY);
950
+ if (pullRequestStepComplete(context.session)) {
951
+ return machineState(STEP_STATUS.DONE);
952
+ }
953
+ if (mainCheckoutSyncPending(context.session)) {
954
+ return machineState(STEP_STATUS.READY, {
955
+ phase: pullRequestPhase.SYNC_READY
956
+ });
957
+ }
958
+ if (metadataExists(context.session, "pr_url")) {
959
+ return machineState(STEP_STATUS.READY, {
960
+ phase: pullRequestPhase.MERGE_READY
961
+ });
962
+ }
963
+ return pullRequestFilesAreReady(context.session)
964
+ ? machineState(STEP_STATUS.CONFIRM_FILES, {
965
+ phase: pullRequestPhase.REVIEW_DRAFT
966
+ })
967
+ : machineState(STEP_STATUS.READY, {
968
+ phase: pullRequestPhase.DRAFTING
969
+ });
976
970
  },
977
971
 
978
972
  async view(context = {}) {
979
973
  let state = await readState(context, this);
980
- if (metadataExists(context.session, "pr_merged") || metadataExists(context.session, "merge_skipped")) {
974
+ if (pullRequestStepComplete(context.session)) {
981
975
  state = machineState(STEP_STATUS.DONE);
976
+ } else if (mainCheckoutSyncPending(context.session) && ![
977
+ STEP_STATUS.ATTEMPTING_EXECUTION,
978
+ STEP_STATUS.WAITING_FOR_INPUT
979
+ ].includes(state.status)) {
980
+ state = machineState(STEP_STATUS.READY, {
981
+ phase: pullRequestPhase.SYNC_READY
982
+ });
983
+ } else if (metadataExists(context.session, "pr_url") && ![
984
+ STEP_STATUS.AWAITING_AGENT_RESULT,
985
+ STEP_STATUS.ATTEMPTING_EXECUTION,
986
+ STEP_STATUS.WAITING_FOR_INPUT
987
+ ].includes(state.status)) {
988
+ state = machineState(STEP_STATUS.READY, {
989
+ phase: pullRequestPhase.MERGE_READY,
990
+ promptComplete: state.promptComplete === true
991
+ });
992
+ } else if (
993
+ pullRequestFilesAreReady(context.session) &&
994
+ state.status !== STEP_STATUS.CONFIRM_FILES &&
995
+ ![
996
+ pullRequestPhase.CREATING_PR,
997
+ pullRequestPhase.PREPARING_MERGE,
998
+ pullRequestPhase.MERGING,
999
+ pullRequestPhase.SYNCING_MAIN
1000
+ ].includes(state.phase)
1001
+ ) {
1002
+ state = machineState(STEP_STATUS.CONFIRM_FILES, {
1003
+ phase: pullRequestPhase.REVIEW_DRAFT
1004
+ });
982
1005
  }
1006
+
983
1007
  switch (state.status) {
984
1008
  case STEP_STATUS.DONE:
985
1009
  return promptStepDoneView(context, this, state);
986
1010
 
1011
+ case STEP_STATUS.CONFIRM_FILES: {
1012
+ const values = await readPullRequestFieldValues(context);
1013
+ return {
1014
+ interaction: pullRequestInputInteraction(values),
1015
+ next: nextForSession(context.session, {
1016
+ disabledReason: "Create the pull request before choosing whether to merge."
1017
+ }),
1018
+ stepMachine: publicState(this, {
1019
+ ...state,
1020
+ message: state.message || "Review the pull request draft."
1021
+ })
1022
+ };
1023
+ }
1024
+
987
1025
  case STEP_STATUS.WAITING_FOR_INPUT:
1026
+ if ([pullRequestPhase.CREATING_PR, pullRequestPhase.MERGING, pullRequestPhase.SYNCING_MAIN].includes(state.phase)) {
1027
+ const waitingActionId = state.phase === pullRequestPhase.MERGING
1028
+ ? "merge_pr"
1029
+ : (state.phase === pullRequestPhase.SYNCING_MAIN ? syncMainCheckoutActionId : "create_pr_on_gh");
1030
+ return {
1031
+ actions: disableAction(
1032
+ context.session,
1033
+ waitingActionId,
1034
+ state.phase === pullRequestPhase.MERGING
1035
+ ? "Resolve the merge command before retrying."
1036
+ : (state.phase === pullRequestPhase.SYNCING_MAIN
1037
+ ? "Resolve the main checkout sync command before retrying."
1038
+ : "Resolve the pull request command before retrying.")
1039
+ ),
1040
+ interaction: commandFailureInteraction({
1041
+ prompt: state.message || (state.phase === pullRequestPhase.SYNCING_MAIN
1042
+ ? "The main checkout sync command failed. Explain what should happen, then retry it."
1043
+ : "The pull request command failed. Explain what should happen, then retry."),
1044
+ title: state.title || (state.phase === pullRequestPhase.SYNCING_MAIN
1045
+ ? "Main checkout sync needs attention"
1046
+ : "Pull request needs attention")
1047
+ }),
1048
+ next: nextForSession(context.session, {
1049
+ disabledReason: state.phase === pullRequestPhase.SYNCING_MAIN
1050
+ ? "Resolve the main checkout sync command before continuing."
1051
+ : "Resolve the pull request command before continuing."
1052
+ }),
1053
+ stepMachine: publicState(this, state)
1054
+ };
1055
+ }
988
1056
  return promptStepWaitingForInputView(context, this, {
989
1057
  ...state,
990
- message: state.message || "The merge step needs input before it can continue."
1058
+ message: state.message || "The pull request step needs input before it can continue."
1059
+ }, {
1060
+ actionId: state.phase === pullRequestPhase.DRAFTING ? "resolve_pull_request" : "prepare_for_merge",
1061
+ prompt: state.message || "The pull request step needs input before it can continue.",
1062
+ skipInput: LET_CODEX_DECIDE_INPUT,
1063
+ title: state.phase === pullRequestPhase.DRAFTING ? "Pull request needs input" : "Merge needs input"
991
1064
  });
992
1065
 
993
1066
  case STEP_STATUS.READY:
1067
+ if (state.phase === pullRequestPhase.MERGE_READY) {
1068
+ return {
1069
+ next: nextForSession(context.session, {
1070
+ disabledReason: "Merge the pull request and sync the main checkout, or choose not to merge before continuing."
1071
+ }),
1072
+ stepMachine: publicState(this, state),
1073
+ workflowAutopilot: mergeReviewAutopilot()
1074
+ };
1075
+ }
1076
+ if (state.phase === pullRequestPhase.SYNC_READY) {
1077
+ return {
1078
+ next: nextForSession(context.session, {
1079
+ disabledReason: "Sync the main checkout after merging before continuing."
1080
+ }),
1081
+ stepMachine: publicState(this, state),
1082
+ workflowAutopilot: mergeReviewAutopilot()
1083
+ };
1084
+ }
1085
+ return promptStepWaitingView(context, this, state, "Create the pull request before choosing whether to merge.");
1086
+
994
1087
  case STEP_STATUS.AWAITING_AGENT_RESULT:
995
1088
  case STEP_STATUS.ATTEMPTING_EXECUTION:
996
1089
  case STEP_STATUS.FAILED:
997
1090
  default:
998
- return promptStepWaitingView(context, this, state, "Merge the pull request or choose not to merge before continuing.");
1091
+ return promptStepWaitingView(context, this, state, "Create the pull request, then merge and sync the main checkout or choose not to merge before continuing.");
999
1092
  }
1000
1093
  },
1001
1094
 
@@ -1003,52 +1096,119 @@ const pullRequestMergedMachine = {
1003
1096
  const state = await readState(context, this);
1004
1097
  const input = normalizeMachineInput(context.input);
1005
1098
  switch (state.status) {
1006
- case STEP_STATUS.READY:
1007
1099
  case STEP_STATUS.AWAITING_AGENT_RESULT:
1008
1100
  case STEP_STATUS.WAITING_FOR_INPUT:
1101
+ case STEP_STATUS.CONFIRM_FILES:
1009
1102
  case STEP_STATUS.FAILED:
1010
1103
  if (state.status === STEP_STATUS.AWAITING_AGENT_RESULT) {
1011
1104
  assertAgentResultSource(context.session, input);
1012
1105
  }
1013
1106
  if (input.kind === STEP_INPUT_KIND.WAITING_FOR_INPUT) {
1014
1107
  await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1015
- from: state.status === STEP_STATUS.ATTEMPTING_EXECUTION
1016
- ? STEP_STATUS.ATTEMPTING_EXECUTION
1017
- : STEP_STATUS.AWAITING_AGENT_RESULT,
1018
1108
  message: input.message,
1109
+ phase: state.phase || pullRequestPhase.DRAFTING,
1019
1110
  source: input.source
1020
1111
  }));
1021
1112
  return;
1022
1113
  }
1023
1114
  if (input.kind === STEP_INPUT_KIND.USER_RESPONSE || input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED) {
1115
+ if (state.phase === pullRequestPhase.CREATING_PR) {
1116
+ await writeState(context, this, pullRequestFilesAreReady(context.session)
1117
+ ? machineState(STEP_STATUS.CONFIRM_FILES, {
1118
+ message: input.message,
1119
+ phase: pullRequestPhase.REVIEW_DRAFT,
1120
+ response: input.text || input.fields.response,
1121
+ source: input.source
1122
+ })
1123
+ : machineState(STEP_STATUS.READY, {
1124
+ message: input.message,
1125
+ phase: pullRequestPhase.DRAFTING,
1126
+ response: input.text || input.fields.response,
1127
+ source: input.source
1128
+ }));
1129
+ return;
1130
+ }
1131
+ if (state.phase === pullRequestPhase.SYNCING_MAIN) {
1132
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1133
+ phase: pullRequestPhase.SYNC_READY,
1134
+ response: input.text || input.fields.response,
1135
+ source: input.source
1136
+ }));
1137
+ return;
1138
+ }
1024
1139
  await writeState(context, this, machineState(STEP_STATUS.READY, {
1140
+ phase: state.phase || pullRequestPhase.MERGE_READY,
1025
1141
  response: input.text || input.fields.response,
1026
1142
  source: input.source
1027
1143
  }));
1028
1144
  return;
1029
1145
  }
1030
- if (input.kind === STEP_INPUT_KIND.READY) {
1146
+ if (input.kind === STEP_INPUT_KIND.READY && state.phase === pullRequestPhase.PREPARING_MERGE) {
1147
+ await writeMergePreparationSummary(context, input.fields.mergePreparationSummary);
1031
1148
  await writeState(context, this, machineState(STEP_STATUS.READY, {
1032
1149
  message: input.message,
1150
+ phase: pullRequestPhase.MERGE_READY,
1033
1151
  promptComplete: true,
1034
1152
  source: input.source
1035
1153
  }));
1036
1154
  return;
1037
1155
  }
1156
+ if (input.kind === STEP_INPUT_KIND.READY || input.kind === STEP_INPUT_KIND.CONFIRM_FILES) {
1157
+ await writePullRequestFieldValues(context, input.fields);
1158
+ await writeState(context, this, machineState(STEP_STATUS.CONFIRM_FILES, {
1159
+ phase: pullRequestPhase.REVIEW_DRAFT,
1160
+ source: input.source
1161
+ }));
1162
+ return;
1163
+ }
1038
1164
  throw unsupportedInputKind(input.kind, this.stepId);
1039
1165
 
1166
+ case STEP_STATUS.READY:
1040
1167
  case STEP_STATUS.ATTEMPTING_EXECUTION:
1041
1168
  case STEP_STATUS.DONE:
1042
1169
  default:
1043
- throw vibe64Error("The merge step cannot accept input right now.", "vibe64_step_input_not_available");
1170
+ throw vibe64Error("The pull request step cannot accept input right now.", "vibe64_step_input_not_available");
1044
1171
  }
1045
1172
  },
1046
1173
 
1047
1174
  async actionStarted(context = {}) {
1175
+ const state = await readState(context, this);
1176
+ if (context.actionId === "resolve_pull_request") {
1177
+ await writeState(context, this, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1178
+ phase: pullRequestPhase.DRAFTING,
1179
+ response: state.response,
1180
+ source: state.source
1181
+ }));
1182
+ return;
1183
+ }
1048
1184
  if (context.actionId === "prepare_for_merge") {
1049
- return markPromptActionStarted(context, this, "prepare_for_merge");
1185
+ await writeState(context, this, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1186
+ phase: pullRequestPhase.PREPARING_MERGE,
1187
+ response: state.response,
1188
+ source: state.source
1189
+ }));
1190
+ return;
1191
+ }
1192
+ if (context.actionId === "create_pr_on_gh") {
1193
+ await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION, {
1194
+ actionId: context.actionId,
1195
+ phase: pullRequestPhase.CREATING_PR
1196
+ }));
1197
+ return;
1198
+ }
1199
+ if (context.actionId === "merge_pr") {
1200
+ await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION, {
1201
+ actionId: context.actionId,
1202
+ phase: pullRequestPhase.MERGING
1203
+ }));
1204
+ return;
1205
+ }
1206
+ if (context.actionId === syncMainCheckoutActionId) {
1207
+ await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION, {
1208
+ actionId: context.actionId,
1209
+ phase: pullRequestPhase.SYNCING_MAIN
1210
+ }));
1050
1211
  }
1051
- return markCommandActionStarted(context, this, ["merge_pr"]);
1052
1212
  },
1053
1213
 
1054
1214
  async actionFinished(context = {}) {
@@ -1056,56 +1216,84 @@ const pullRequestMergedMachine = {
1056
1216
  await writeState(context, this, machineState(STEP_STATUS.DONE));
1057
1217
  return;
1058
1218
  }
1059
- return writeCommandActionFinishedState(context, this, {
1060
- actionIds: ["merge_pr"],
1061
- done: await actionCreatedMetadata(context, "pr_merged"),
1062
- failureTitle: "Merge needs attention"
1063
- });
1064
- },
1065
-
1066
- promptInstruction() {
1067
- return currentStepHelperInstruction({
1068
- doneMeaning: "The pull request and main checkout are ready for the merge command.",
1069
- waitingForInputMeaning: "The merge preparation found a blocker that needs user input."
1070
- });
1071
- }
1072
- };
1073
-
1074
- const mainCheckoutSyncedMachine = {
1075
- stepId: mainCheckoutSyncedStepId,
1076
-
1077
- initialState(context = {}) {
1078
- return metadataExists(context.session, "main_checkout_synced") || metadataExists(context.session, "merge_skipped")
1079
- ? machineState(STEP_STATUS.DONE)
1080
- : machineState(STEP_STATUS.READY);
1081
- },
1082
-
1083
- async view(context = {}) {
1084
- let state = await readState(context, this);
1085
- if (metadataExists(context.session, "main_checkout_synced") || metadataExists(context.session, "merge_skipped")) {
1086
- state = machineState(STEP_STATUS.DONE);
1219
+ if (context.actionId === "create_pr_on_gh") {
1220
+ await writeState(context, this, await actionCreatedMetadata(context, "pr_url")
1221
+ ? machineState(STEP_STATUS.READY, {
1222
+ phase: pullRequestPhase.MERGE_READY
1223
+ })
1224
+ : machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1225
+ from: STEP_STATUS.ATTEMPTING_EXECUTION,
1226
+ message: normalizeText(context.actionResult?.message),
1227
+ output: normalizeText(context.actionResult?.output),
1228
+ phase: pullRequestPhase.CREATING_PR,
1229
+ title: "Pull request needs attention"
1230
+ }));
1231
+ return;
1232
+ }
1233
+ if (context.actionId === "merge_pr") {
1234
+ await writeState(context, this, await actionCreatedMetadata(context, "pr_merged")
1235
+ ? machineState(STEP_STATUS.READY, {
1236
+ phase: pullRequestPhase.SYNC_READY
1237
+ })
1238
+ : machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1239
+ from: STEP_STATUS.ATTEMPTING_EXECUTION,
1240
+ message: normalizeText(context.actionResult?.message),
1241
+ output: normalizeText(context.actionResult?.output),
1242
+ phase: pullRequestPhase.MERGING,
1243
+ title: "Merge needs attention"
1244
+ }));
1245
+ return;
1246
+ }
1247
+ if (context.actionId === syncMainCheckoutActionId) {
1248
+ await writeState(context, this, await actionCreatedMetadata(context, mainCheckoutSyncedMetadataName)
1249
+ ? machineState(STEP_STATUS.DONE)
1250
+ : machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1251
+ from: STEP_STATUS.ATTEMPTING_EXECUTION,
1252
+ message: normalizeText(context.actionResult?.message),
1253
+ output: normalizeText(context.actionResult?.output),
1254
+ phase: pullRequestPhase.SYNCING_MAIN,
1255
+ title: "Main checkout sync needs attention"
1256
+ }));
1087
1257
  }
1088
- return commandStepView(context, this, state, {
1089
- disabledReason: "Sync the main checkout after merging before continuing.",
1090
- failurePrompt: "The main checkout sync command failed. Explain what should happen, then retry it.",
1091
- failureTitle: "Main checkout sync needs attention"
1092
- });
1093
- },
1094
-
1095
- async submitInput(context = {}) {
1096
- return submitCommandFailureInput(context, this);
1097
1258
  },
1098
1259
 
1099
- async actionStarted(context = {}) {
1100
- return markCommandActionStarted(context, this, ["sync_main_checkout"]);
1260
+ inputCompletionMessage(context = {}) {
1261
+ const input = normalizeMachineInput(context.input);
1262
+ if (input.kind !== STEP_INPUT_KIND.READY && input.kind !== STEP_INPUT_KIND.CONFIRM_FILES) {
1263
+ return "";
1264
+ }
1265
+ if (
1266
+ context.session.stepMachine?.phase === pullRequestPhase.PREPARING_MERGE ||
1267
+ Object.hasOwn(input.fields || {}, "mergePreparationSummary")
1268
+ ) {
1269
+ return "Merge preparation completed.";
1270
+ }
1271
+ if (input.source === "codex") {
1272
+ return pullRequestDraftConversationMessage(input, "Proposed");
1273
+ }
1274
+ if (input.source === "ui") {
1275
+ return pullRequestDraftConversationMessage(input, "Saved");
1276
+ }
1277
+ return "Pull request draft submitted for review.";
1101
1278
  },
1102
1279
 
1103
- async actionFinished(context = {}) {
1104
- return writeCommandActionFinishedState(context, this, {
1105
- actionIds: ["sync_main_checkout"],
1106
- done: await actionCreatedMetadata(context, "main_checkout_synced"),
1107
- failureTitle: "Main checkout sync needs attention"
1108
- });
1280
+ promptInstruction({ action = {} } = {}) {
1281
+ return normalizeText(action.id) === "prepare_for_merge"
1282
+ ? currentStepHelperInstruction({
1283
+ doneFields: {
1284
+ mergePreparationSummary: "Markdown summary of extra merge-preparation work performed after pull request creation. Leave empty when no extra work was needed."
1285
+ },
1286
+ doneMeaning: "The pull request and main checkout are ready for the merge command.",
1287
+ waitingForInputMeaning: "The merge preparation found a blocker that needs user input."
1288
+ })
1289
+ : currentStepHelperInstruction({
1290
+ doneFields: {
1291
+ body: "Markdown pull request body",
1292
+ title: "Pull request title"
1293
+ },
1294
+ doneMeaning: "The pull request title and body are ready for user confirmation.",
1295
+ waitingForInputMeaning: "You cannot draft the pull request without a user decision or missing repository context."
1296
+ });
1109
1297
  }
1110
1298
  };
1111
1299
 
@@ -1168,6 +1356,7 @@ async function readPullRequestFieldValues(context = {}) {
1168
1356
  async function writePullRequestFieldValues(context = {}, fields = {}) {
1169
1357
  const title = requireInputValue(fields.title, "Pull request title is required.");
1170
1358
  const body = requireInputValue(fields.body, "Pull request body is required.");
1359
+ const bodyWithReport = await pullRequestBodyWithFinalReport(context, body);
1171
1360
  await Promise.all([
1172
1361
  context.runtime.store.writeArtifact(
1173
1362
  context.session.sessionId,
@@ -1177,11 +1366,70 @@ async function writePullRequestFieldValues(context = {}, fields = {}) {
1177
1366
  context.runtime.store.writeArtifact(
1178
1367
  context.session.sessionId,
1179
1368
  PULL_REQUEST_BODY_DRAFT_ARTIFACT,
1180
- artifactText(body)
1369
+ artifactText(bodyWithReport)
1181
1370
  )
1182
1371
  ]);
1183
1372
  }
1184
1373
 
1374
+ async function pullRequestBodyWithFinalReport(context = {}, body = "") {
1375
+ const normalizedBody = normalizeText(body);
1376
+ if (normalizedBody.includes(finalReportStartMarker)) {
1377
+ return normalizedBody;
1378
+ }
1379
+ const report = normalizeText(await context.runtime.store.readArtifact(context.session.sessionId, REPORT_ARTIFACT));
1380
+ if (!report) {
1381
+ return normalizedBody;
1382
+ }
1383
+ return [
1384
+ normalizedBody,
1385
+ "",
1386
+ finalReportStartMarker,
1387
+ "## Vibe64 final report",
1388
+ "",
1389
+ "<details>",
1390
+ "<summary>Final session report</summary>",
1391
+ "",
1392
+ report,
1393
+ "",
1394
+ "</details>",
1395
+ finalReportEndMarker
1396
+ ].join("\n");
1397
+ }
1398
+
1399
+ async function writeMergePreparationSummary(context = {}, summary = "") {
1400
+ const normalizedSummary = normalizeText(summary);
1401
+ if (normalizedSummary) {
1402
+ await context.runtime.store.writeMetadataValue(
1403
+ context.session.sessionId,
1404
+ mergePreparationSummaryMetadataName,
1405
+ normalizedSummary
1406
+ );
1407
+ return;
1408
+ }
1409
+ await context.runtime.store.deleteMetadataValue(
1410
+ context.session.sessionId,
1411
+ mergePreparationSummaryMetadataName
1412
+ );
1413
+ }
1414
+
1415
+ function conversationSection(label = "", value = "") {
1416
+ return [
1417
+ `${label}:`,
1418
+ normalizeText(value) || "(empty)"
1419
+ ].join("\n");
1420
+ }
1421
+
1422
+ function pullRequestDraftConversationMessage(input = {}, verb = "Proposed") {
1423
+ const fields = input.fields || {};
1424
+ return [
1425
+ `${verb} pull request draft.`,
1426
+ "",
1427
+ conversationSection("Title", fields.title),
1428
+ "",
1429
+ conversationSection("Body", fields.body)
1430
+ ].join("\n");
1431
+ }
1432
+
1185
1433
  function pullRequestInputInteraction(values = {}) {
1186
1434
  return {
1187
1435
  fields: [
@@ -1205,8 +1453,8 @@ function pullRequestInputInteraction(values = {}) {
1205
1453
  kind: "collect_input_run_command",
1206
1454
  prompt: "Review the pull request details. Save changes here, or continue to create the GitHub pull request.",
1207
1455
  submitKind: STEP_INPUT_KIND.CONFIRM_FILES,
1208
- submitLabel: "Update PR",
1209
- title: "Create pull request"
1456
+ submitLabel: "Save draft",
1457
+ title: "Create pull request, possibly merge"
1210
1458
  };
1211
1459
  }
1212
1460
 
@@ -1231,78 +1479,15 @@ const coreLifecycleSteps = Object.freeze([
1231
1479
  id: dependenciesInstalledStepId,
1232
1480
  machine: dependenciesInstalledMachine
1233
1481
  },
1234
- {
1235
- definition: coreLifecycleStepDefinitionsById[projectValidatedStepId],
1236
- id: projectValidatedStepId,
1237
- machine: projectValidatedMachine
1238
- },
1239
1482
  {
1240
1483
  definition: coreLifecycleStepDefinitionsById[changesCommittedStepId],
1241
1484
  id: changesCommittedStepId,
1242
1485
  machine: changesCommittedMachine
1243
1486
  },
1244
1487
  {
1245
- config: {
1246
- command: {
1247
- actionId: "create_pr_on_gh",
1248
- doneMetadata: "pr_url",
1249
- failureState: (context = {}) => machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1250
- from: STEP_STATUS.ATTEMPTING_EXECUTION,
1251
- message: normalizeText(context.actionResult?.message),
1252
- output: normalizeText(context.actionResult?.output)
1253
- })
1254
- },
1255
- done: (session = {}) => metadataExists(session, "pr_url"),
1256
- draftOrigin: "prompt",
1257
- draftReady: pullRequestFilesAreReady,
1258
- interaction: (_status, values = {}) => pullRequestInputInteraction(values),
1259
- nextWhenConfirmed: {
1260
- disabledReason: "Create the pull request before continuing."
1261
- },
1262
- nextWhenDrafting: {
1263
- disabledReason: "Resolve the pull request content before continuing."
1264
- },
1265
- nextWhenWaitingForInput: {
1266
- disabledReason: "Resolve the pull request input request before continuing."
1267
- },
1268
- nextWhenWorking: {
1269
- disabledReason: "Create the pull request before continuing."
1270
- },
1271
- onWaitingActions: (context = {}) => disableAction(context.session, "create_pr_on_gh", "Resolve the pull request input request before retrying."),
1272
- promptInstruction() {
1273
- return currentStepHelperInstruction({
1274
- doneFields: {
1275
- body: "Markdown pull request body",
1276
- title: "Pull request title"
1277
- },
1278
- doneMeaning: "The pull request title and body are ready for user confirmation.",
1279
- waitingForInputMeaning: "You cannot draft the pull request without a user decision or missing repository context."
1280
- });
1281
- },
1282
- readValues: readPullRequestFieldValues,
1283
- saveValues: writePullRequestFieldValues,
1284
- unsupportedDoneMessage: "The pull request step cannot accept input right now.",
1285
- userResponseResumeStatus: (state = {}) => state.from === STEP_STATUS.ATTEMPTING_EXECUTION
1286
- ? STEP_STATUS.CONFIRM_FILES
1287
- : STEP_STATUS.AWAITING_AGENT_RESULT,
1288
- waitingInteraction: (state = {}) => commandFailureInteraction({
1289
- prompt: state.message || "Codex needs more information before the pull request can continue.",
1290
- title: "Pull request needs input"
1291
- })
1292
- },
1293
- definition: coreLifecycleStepDefinitionsById[createPullRequestStepId],
1294
- factoryId: "editable_artifact_review",
1295
- id: createPullRequestStepId
1296
- },
1297
- {
1298
- definition: coreLifecycleStepDefinitionsById[prMergedStepId],
1299
- id: prMergedStepId,
1300
- machine: pullRequestMergedMachine
1301
- },
1302
- {
1303
- definition: coreLifecycleStepDefinitionsById[mainCheckoutSyncedStepId],
1304
- id: mainCheckoutSyncedStepId,
1305
- machine: mainCheckoutSyncedMachine
1488
+ definition: coreLifecycleStepDefinitionsById[createAndMergePullRequestStepId],
1489
+ id: createAndMergePullRequestStepId,
1490
+ machine: createAndMergePullRequestMachine
1306
1491
  },
1307
1492
  {
1308
1493
  definition: coreLifecycleStepDefinitionsById[sessionFinishedStepId],
@@ -1324,11 +1509,8 @@ const _testing = deepFreeze({
1324
1509
  workSourceSelectedStepId,
1325
1510
  worktreeCreatedStepId,
1326
1511
  dependenciesInstalledStepId,
1327
- projectValidatedStepId,
1328
1512
  changesCommittedStepId,
1329
- createPullRequestStepId,
1330
- prMergedStepId,
1331
- mainCheckoutSyncedStepId,
1513
+ createAndMergePullRequestStepId,
1332
1514
  sessionFinishedStepId
1333
1515
  ]
1334
1516
  });