@jskit-ai/jskit-cli 0.2.80 → 0.2.82

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 (32) hide show
  1. package/package.json +6 -4
  2. package/src/server/appBlueprint.js +1 -1
  3. package/src/server/commandHandlers/helperMap.js +104 -0
  4. package/src/server/commandHandlers/session.js +179 -4
  5. package/src/server/commandHandlers/show.js +169 -34
  6. package/src/server/core/argParser.js +20 -0
  7. package/src/server/core/commandCatalog.js +70 -7
  8. package/src/server/core/createCommandHandlers.js +4 -1
  9. package/src/server/helperMap.js +463 -0
  10. package/src/server/helperMapPaths.js +7 -0
  11. package/src/server/sessionRuntime/appReadiness.js +55 -0
  12. package/src/server/sessionRuntime/constants.js +298 -135
  13. package/src/server/sessionRuntime/paths.js +2 -4
  14. package/src/server/sessionRuntime/preconditions.js +393 -26
  15. package/src/server/sessionRuntime/promptRenderer.js +15 -2
  16. package/src/server/sessionRuntime/prompts/app_blueprint.md +26 -2
  17. package/src/server/sessionRuntime/prompts/automated_checks.md +42 -0
  18. package/src/server/sessionRuntime/prompts/deep_ui_check.md +53 -0
  19. package/src/server/sessionRuntime/prompts/doctor_failure.md +21 -1
  20. package/src/server/sessionRuntime/prompts/execute_plan.md +61 -0
  21. package/src/server/sessionRuntime/prompts/final_comment.md +3 -1
  22. package/src/server/sessionRuntime/prompts/issue_details.md +52 -0
  23. package/src/server/sessionRuntime/prompts/new_issue.md +34 -3
  24. package/src/server/sessionRuntime/prompts/plan_issue.md +81 -0
  25. package/src/server/sessionRuntime/prompts/pr_failure.md +14 -1
  26. package/src/server/sessionRuntime/prompts/review_changes.md +77 -15
  27. package/src/server/sessionRuntime/prompts/update_blueprint.md +36 -0
  28. package/src/server/sessionRuntime/prompts/user_check.md +22 -4
  29. package/src/server/sessionRuntime/responses.js +877 -30
  30. package/src/server/sessionRuntime/worktrees.js +31 -0
  31. package/src/server/sessionRuntime.js +2070 -244
  32. package/src/server/sessionRuntime/prompts/implement_issue.md +0 -25
@@ -13,18 +13,121 @@ const SESSION_STATUS = Object.freeze({
13
13
 
14
14
  const SESSION_ID_PATTERN = /^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(?:-[a-z0-9]{4})?$/u;
15
15
  const SESSION_STATE_RELATIVE_PATH = ".jskit/sessions";
16
+ const SESSION_WORKFLOW_VERSION = "6";
17
+ const REVIEW_PASS_LIMIT = 0;
16
18
  const PROMPT_DIRECTORY = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "prompts");
19
+ const JSKIT_CLI_SHELL_COMMAND = "npx --no-install jskit";
20
+ const JSKIT_CLI_SHELL_RULE = [
21
+ "Shell command rule:",
22
+ "",
23
+ `- When running JSKIT CLI commands from the shell, use \`${JSKIT_CLI_SHELL_COMMAND} ...\`.`,
24
+ "- Do not run bare `jskit ...` unless you are inside an npm script where `node_modules/.bin` is on PATH.",
25
+ "- Do not run `npx jskit ...` without `--no-install`; it may fetch packages instead of using this app's installed CLI.",
26
+ "- If `npx --no-install jskit ...` is unavailable, continue with filesystem inspection when possible and report that the local JSKIT CLI is missing."
27
+ ].join("\n");
28
+ const DEFAULT_NEXT_COMMAND_TEMPLATE = `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step`;
17
29
 
18
30
  const INPUT_NONE = Object.freeze({ type: "none" });
31
+ const ISSUE_TITLE_INPUT = Object.freeze({
32
+ extract: "issue_title",
33
+ formatHint: "text",
34
+ label: "Approved issue title",
35
+ name: "issueTitle",
36
+ required: true,
37
+ type: "text"
38
+ });
19
39
  const ISSUE_TEXT_INPUT = Object.freeze({
20
40
  extract: "issue_text",
21
41
  formatHint: "markdown",
22
- label: "Approved issue text",
42
+ label: "Approved issue body",
23
43
  multiline: true,
24
44
  name: "issue",
25
45
  required: true,
26
46
  type: "text"
27
47
  });
48
+ const ISSUE_DRAFT_INPUT = Object.freeze({
49
+ fields: Object.freeze([
50
+ ISSUE_TITLE_INPUT,
51
+ ISSUE_TEXT_INPUT
52
+ ]),
53
+ type: "object"
54
+ });
55
+ const ISSUE_TITLE_OUTPUT = Object.freeze({
56
+ extract: "issue_title",
57
+ field: "issueTitle",
58
+ formatHint: "text",
59
+ label: "Issue title",
60
+ required: true
61
+ });
62
+ const ISSUE_TEXT_OUTPUT = Object.freeze({
63
+ extract: "issue_text",
64
+ field: "issue",
65
+ formatHint: "markdown",
66
+ label: "Issue body",
67
+ multiline: true,
68
+ required: true
69
+ });
70
+ const PLAN_INPUT = Object.freeze({
71
+ extract: "plan",
72
+ formatHint: "markdown",
73
+ label: "Approved plan",
74
+ multiline: true,
75
+ name: "plan",
76
+ required: true,
77
+ type: "text"
78
+ });
79
+ const PLAN_OUTPUT = Object.freeze({
80
+ extract: "plan",
81
+ field: "plan",
82
+ formatHint: "markdown",
83
+ label: "Plan",
84
+ multiline: true,
85
+ required: true
86
+ });
87
+ const ISSUE_DETAILS_INPUT = Object.freeze({
88
+ extract: "issue_details",
89
+ formatHint: "markdown",
90
+ label: "Confirmed issue details",
91
+ multiline: true,
92
+ name: "issueDetails",
93
+ required: true,
94
+ type: "text"
95
+ });
96
+ const ISSUE_DETAILS_OUTPUT = Object.freeze({
97
+ extract: "issue_details",
98
+ field: "issueDetails",
99
+ formatHint: "markdown",
100
+ label: "Issue details",
101
+ multiline: true,
102
+ required: true
103
+ });
104
+ const ISSUE_CATEGORY_OUTPUT = Object.freeze({
105
+ extract: "issue_category",
106
+ field: "issueCategory",
107
+ formatHint: "text",
108
+ label: "Issue category",
109
+ options: Object.freeze([
110
+ Object.freeze({ label: "Client", value: "client" }),
111
+ Object.freeze({ label: "Server", value: "server" }),
112
+ Object.freeze({ label: "Client and server", value: "client_server" }),
113
+ Object.freeze({ label: "Tooling", value: "tooling" }),
114
+ Object.freeze({ label: "Unknown", value: "unknown" })
115
+ ]),
116
+ required: true
117
+ });
118
+ const UI_IMPACT_OUTPUT = Object.freeze({
119
+ extract: "ui_impact",
120
+ field: "uiImpact",
121
+ formatHint: "text",
122
+ label: "UI impact",
123
+ options: Object.freeze([
124
+ Object.freeze({ label: "No UI impact", value: "none" }),
125
+ Object.freeze({ label: "Possible UI impact", value: "possible" }),
126
+ Object.freeze({ label: "Definite UI impact", value: "definite" }),
127
+ Object.freeze({ label: "Unknown", value: "unknown" })
128
+ ]),
129
+ required: true
130
+ });
28
131
  const USER_CHECK_INPUT = Object.freeze({
29
132
  label: "User check result",
30
133
  name: "userCheck",
@@ -35,19 +138,50 @@ const USER_CHECK_INPUT = Object.freeze({
35
138
  required: true,
36
139
  type: "choice"
37
140
  });
38
- const CODEX_WORKTREE_OUTPUT = Object.freeze({
39
- field: "worktree",
40
- formatHint: "git_changes"
41
- });
42
-
43
- function codexHandoff(expectedOutput) {
141
+ function codexHandoff(expectedOutput, {
142
+ autoInject = false,
143
+ promptActionLabel = ""
144
+ } = {}) {
145
+ const expectedOutputs = Object.freeze(Array.isArray(expectedOutput) ? [...expectedOutput] : [expectedOutput]);
44
146
  return Object.freeze({
45
- expectedOutput,
147
+ ...(autoInject ? { autoInject: true } : {}),
148
+ expectedOutput: expectedOutputs[expectedOutputs.length - 1] || null,
149
+ expectedOutputs,
46
150
  mode: "inject_prompt",
47
- promptField: "prompt"
151
+ promptField: "prompt",
152
+ ...(promptActionLabel ? { promptActionLabel } : {})
48
153
  });
49
154
  }
50
155
 
156
+ const PLAN_EXECUTION_CODEX_HANDOFF = codexHandoff([], {
157
+ autoInject: true,
158
+ promptActionLabel: "Get Codex to execute plan"
159
+ });
160
+ const ISSUE_DETAILS_CODEX_HANDOFF = codexHandoff([
161
+ ISSUE_CATEGORY_OUTPUT,
162
+ UI_IMPACT_OUTPUT,
163
+ ISSUE_DETAILS_OUTPUT
164
+ ], {
165
+ autoInject: true,
166
+ promptActionLabel: "Start details conversation"
167
+ });
168
+ const REVIEW_EXECUTION_CODEX_HANDOFF = codexHandoff([], {
169
+ autoInject: true,
170
+ promptActionLabel: "Run deslop"
171
+ });
172
+ const DEEP_UI_CHECK_CODEX_HANDOFF = codexHandoff([], {
173
+ autoInject: true,
174
+ promptActionLabel: "Run Deep UI check"
175
+ });
176
+ const AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF = codexHandoff([], {
177
+ autoInject: true,
178
+ promptActionLabel: "Run automated checks"
179
+ });
180
+ const BLUEPRINT_CODEX_HANDOFF = codexHandoff([], {
181
+ autoInject: true,
182
+ promptActionLabel: "Update blueprint"
183
+ });
184
+
51
185
  function defineStep({
52
186
  buttonLabel,
53
187
  codex = undefined,
@@ -56,39 +190,54 @@ function defineStep({
56
190
  input = INPUT_NONE,
57
191
  kind = "automatic",
58
192
  label,
59
- nextCommandTemplate = "jskit session {{session_id}} step",
60
- preconditions = []
193
+ nextCommandTemplate = DEFAULT_NEXT_COMMAND_TEMPLATE,
194
+ preconditions = [],
195
+ requiresExplicitRun = false,
196
+ utilityActions = [],
197
+ displayGroupId = "",
198
+ displayGroupLabel = ""
61
199
  }) {
62
200
  return Object.freeze({
63
201
  buttonLabel,
64
202
  codex,
65
203
  description,
204
+ displayGroupId,
205
+ displayGroupLabel,
66
206
  id,
67
207
  input,
68
208
  kind,
69
209
  label,
70
210
  nextCommandTemplate,
71
- preconditions: Object.freeze([...preconditions])
211
+ preconditions: Object.freeze([...preconditions]),
212
+ requiresExplicitRun,
213
+ utilityActions: Object.freeze([...utilityActions])
72
214
  });
73
215
  }
74
216
 
75
217
  const STEP_DEFINITIONS = Object.freeze([
76
218
  defineStep({
77
219
  buttonLabel: "Create session",
78
- description: "Create the filesystem-backed session state.",
220
+ description: "JSKIT creates the filesystem-backed session record and initial receipt.",
79
221
  id: "session_created",
80
222
  label: "Session created"
81
223
  }),
82
224
  defineStep({
83
225
  buttonLabel: "Create worktree",
84
- description: "Create the isolated session worktree and branch.",
226
+ description: "JSKIT creates the isolated Git branch and session worktree where Codex will work.",
85
227
  id: "worktree_created",
86
228
  label: "Worktree created",
87
229
  preconditions: ["session_exists", "git_repository", "git_current_branch"]
88
230
  }),
89
231
  defineStep({
90
- buttonLabel: "Render issue prompt",
91
- description: "Render the prompt Codex should use to draft the GitHub issue.",
232
+ buttonLabel: "Install dependencies",
233
+ description: "JSKIT installs Node dependencies inside the session worktree before Codex starts.",
234
+ id: "dependencies_installed",
235
+ label: "Dependencies installed",
236
+ preconditions: ["session_exists", "worktree_exists"]
237
+ }),
238
+ defineStep({
239
+ buttonLabel: "Set initial prompt",
240
+ description: "User describes the requested change; JSKIT records it and prepares the Codex issue-drafting prompt.",
92
241
  id: "issue_prompt_rendered",
93
242
  input: Object.freeze({
94
243
  label: "What should change?",
@@ -99,175 +248,169 @@ const STEP_DEFINITIONS = Object.freeze([
99
248
  type: "text"
100
249
  }),
101
250
  kind: "human_input",
102
- label: "Issue prompt rendered",
103
- nextCommandTemplate: "jskit session {{session_id}} step --prompt \"<what should change>\"",
104
- preconditions: ["session_exists"]
251
+ label: "Initial issue prompt",
252
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --prompt "<what should change>"`,
253
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app"]
105
254
  }),
106
255
  defineStep({
107
- buttonLabel: "Save issue text",
108
- codex: codexHandoff(Object.freeze({
109
- extract: "issue_text",
110
- field: "issue",
111
- formatHint: "markdown"
112
- })),
113
- description: "Save the approved issue text produced by Codex.",
256
+ buttonLabel: "Finalise issue",
257
+ codex: codexHandoff([
258
+ ISSUE_TITLE_OUTPUT,
259
+ ISSUE_TEXT_OUTPUT
260
+ ], {
261
+ autoInject: true,
262
+ promptActionLabel: "Get Codex to create issue text"
263
+ }),
264
+ description: "Codex drafts the issue title and body; user reviews or edits them; JSKIT saves the approved draft.",
114
265
  id: "issue_drafted",
115
- input: ISSUE_TEXT_INPUT,
266
+ input: ISSUE_DRAFT_INPUT,
116
267
  kind: "codex_output",
117
268
  label: "Issue drafted",
118
- nextCommandTemplate: "jskit session {{session_id}} step --issue -",
119
- preconditions: ["session_exists"]
269
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --issue -`,
270
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app"]
120
271
  }),
121
272
  defineStep({
122
- buttonLabel: "Create GitHub issue",
123
- description: "Create the GitHub issue with gh.",
273
+ buttonLabel: "Create issue",
274
+ description: "JSKIT creates the GitHub issue from the approved draft and records the issue URL.",
124
275
  id: "issue_created",
125
276
  label: "Issue created",
126
- preconditions: ["session_exists", "issue_text_exists", "github_auth", "github_origin"]
277
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "github_auth", "github_origin"],
278
+ requiresExplicitRun: false
127
279
  }),
128
280
  defineStep({
129
- buttonLabel: "Render implementation prompt",
130
- description: "Render the prompt Codex should use to implement the approved issue.",
131
- id: "implementation_prompt_rendered",
132
- kind: "codex_prompt",
133
- label: "Implementation prompt rendered",
134
- preconditions: ["session_exists", "issue_artifacts"]
281
+ buttonLabel: "Save issue details",
282
+ codex: ISSUE_DETAILS_CODEX_HANDOFF,
283
+ description: "Codex finalises with user issue details and classification; user reviews or edits final outcome; JSKIT saves the confirmed details.",
284
+ displayGroupId: "issue_details",
285
+ displayGroupLabel: "Get issue details",
286
+ id: "issue_details_gathered",
287
+ input: ISSUE_DETAILS_INPUT,
288
+ kind: "codex_output",
289
+ label: "Issue details gathered",
290
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --issue-details -`,
291
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "issue_url_exists"]
135
292
  }),
136
293
  defineStep({
137
- buttonLabel: "Detect implementation changes",
138
- codex: codexHandoff(CODEX_WORKTREE_OUTPUT),
139
- description: "Confirm that Codex changed files in the session worktree.",
140
- id: "implementation_changes_detected",
294
+ buttonLabel: "Save plan",
295
+ codex: codexHandoff(PLAN_OUTPUT, {
296
+ autoInject: true,
297
+ promptActionLabel: "Get Codex to create plan"
298
+ }),
299
+ description: "Codex writes an implementation plan for the active cycle; cycle 001 plans from the issue, later cycles plan from user rework notes.",
300
+ id: "plan_made",
301
+ input: PLAN_INPUT,
141
302
  kind: "codex_output",
142
- label: "Changes detected",
143
- preconditions: ["session_exists", "worktree_exists"]
303
+ label: "Plan made",
304
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --plan -`,
305
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "issue_url_exists", "issue_details_exists", "issue_metadata_exists", "active_cycle_exists"]
144
306
  }),
145
307
  defineStep({
146
- buttonLabel: "Commit implementation",
147
- description: "Commit the implementation changes in the session worktree.",
148
- id: "implementation_changes_committed",
149
- label: "Changes committed",
150
- preconditions: ["session_exists", "worktree_exists"]
308
+ buttonLabel: "Get Codex to execute plan",
309
+ codex: PLAN_EXECUTION_CODEX_HANDOFF,
310
+ description: "JSKIT sends the active cycle plan to Codex; Codex implements it; Studio advances when Codex finishes.",
311
+ id: "plan_executed",
312
+ kind: "codex_prompt",
313
+ label: "Plan executed",
314
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "issue_url_exists", "issue_details_exists", "issue_metadata_exists", "active_cycle_exists", "plan_text_exists"]
151
315
  }),
152
316
  defineStep({
153
- buttonLabel: "Render review prompt 1",
154
- description: "Render the first Codex review prompt for the committed changes.",
155
- id: "initial_review_prompt_rendered",
317
+ buttonLabel: "Run Deep UI check",
318
+ codex: DEEP_UI_CHECK_CODEX_HANDOFF,
319
+ description: "JSKIT asks Codex for a focused UI quality pass when the issue affects UI, or records a skip receipt when it does not.",
320
+ id: "deep_ui_check_run",
156
321
  kind: "codex_prompt",
157
- label: "Review prompt rendered 1",
158
- preconditions: ["session_exists", "worktree_exists"]
322
+ label: "Deep UI check run",
323
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists"]
159
324
  }),
160
325
  defineStep({
161
- buttonLabel: "Detect review changes 1",
162
- codex: codexHandoff(CODEX_WORKTREE_OUTPUT),
163
- description: "Commit any changes Codex made after the first review pass.",
164
- id: "initial_review_changes_detected",
165
- kind: "codex_output",
166
- label: "Review changes detected 1",
167
- preconditions: ["session_exists", "worktree_exists"]
326
+ buttonLabel: "Run deslop",
327
+ codex: REVIEW_EXECUTION_CODEX_HANDOFF,
328
+ description: "JSKIT sends the current implementation to Codex for a review/deslop pass; Codex reports findings and applies the selected cleanup.",
329
+ displayGroupId: "review_deslop",
330
+ displayGroupLabel: "Review/deslop",
331
+ id: "review_prompt_rendered",
332
+ kind: "codex_prompt",
333
+ label: "Review/deslop",
334
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "deep_ui_check_satisfied"]
168
335
  }),
169
336
  defineStep({
170
- buttonLabel: "Save user check 1",
171
- description: "Record whether the first user check passed.",
172
- id: "initial_user_check_completed",
173
- input: USER_CHECK_INPUT,
337
+ buttonLabel: "I am done",
338
+ description: "User confirms the review/deslop loop is done; JSKIT records the loop result without committing yet.",
339
+ displayGroupId: "review_deslop",
340
+ displayGroupLabel: "Review/deslop",
341
+ id: "review_changes_accepted",
174
342
  kind: "user_check",
175
- label: "User check 1",
176
- nextCommandTemplate: "jskit session {{session_id}} step --user-check passed",
177
- preconditions: ["session_exists"]
343
+ label: "Review/deslop",
344
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --review-findings-remaining false`,
345
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "deep_ui_check_satisfied"]
178
346
  }),
179
347
  defineStep({
180
- buttonLabel: "Render review prompt 2",
181
- description: "Render the second Codex review prompt for the committed changes.",
182
- id: "followup_review_prompt_rendered",
348
+ buttonLabel: "Run automated checks",
349
+ codex: AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF,
350
+ description: "JSKIT asks Codex to run automated checks in the worktree, fix failures, and report the final result.",
351
+ id: "automated_checks_run",
183
352
  kind: "codex_prompt",
184
- label: "Review prompt rendered 2",
185
- preconditions: ["session_exists", "worktree_exists"]
186
- }),
187
- defineStep({
188
- buttonLabel: "Detect review changes 2",
189
- codex: codexHandoff(CODEX_WORKTREE_OUTPUT),
190
- description: "Commit any changes Codex made after the second review pass.",
191
- id: "followup_review_changes_detected",
192
- kind: "codex_output",
193
- label: "Review changes detected 2",
194
- preconditions: ["session_exists", "worktree_exists"]
353
+ label: "Automated checks",
354
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "deep_ui_check_satisfied"]
195
355
  }),
196
356
  defineStep({
197
- buttonLabel: "Save user check 2",
198
- description: "Record whether the second user check passed.",
199
- id: "followup_user_check_completed",
357
+ buttonLabel: "Save user check",
358
+ description: "User manually checks the result; JSKIT records pass or collects rework notes for another plan cycle.",
359
+ id: "user_check_completed",
200
360
  input: USER_CHECK_INPUT,
201
361
  kind: "user_check",
202
- label: "User check 2",
203
- nextCommandTemplate: "jskit session {{session_id}} step --user-check passed",
204
- preconditions: ["session_exists"]
362
+ label: "User check",
363
+ nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --user-check passed`,
364
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied"]
205
365
  }),
206
366
  defineStep({
207
- buttonLabel: "Render final review prompt",
208
- description: "Render the final Codex review prompt before verification and PR.",
209
- id: "final_review_prompt_rendered",
210
- kind: "codex_prompt",
211
- label: "Review prompt rendered 3",
212
- preconditions: ["session_exists", "worktree_exists"]
213
- }),
214
- defineStep({
215
- buttonLabel: "Detect final review changes",
216
- codex: codexHandoff(CODEX_WORKTREE_OUTPUT),
217
- description: "Commit any changes Codex made after the final review pass.",
218
- id: "final_review_changes_detected",
219
- kind: "codex_output",
220
- label: "Review changes detected 3",
221
- preconditions: ["session_exists", "worktree_exists"]
367
+ buttonLabel: "Commit accepted changes",
368
+ description: "JSKIT commits the user-accepted session changes in the session worktree.",
369
+ id: "changes_committed",
370
+ label: "Changes committed",
371
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "issue_url_exists", "github_auth", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed"]
222
372
  }),
223
373
  defineStep({
224
- buttonLabel: "Save final user check",
225
- description: "Record whether the final user check passed.",
226
- id: "final_user_check_completed",
227
- input: USER_CHECK_INPUT,
228
- kind: "user_check",
229
- label: "User check 3",
230
- nextCommandTemplate: "jskit session {{session_id}} step --user-check passed",
231
- preconditions: ["session_exists"]
374
+ buttonLabel: "Update blueprint",
375
+ codex: BLUEPRINT_CODEX_HANDOFF,
376
+ description: "JSKIT asks Codex to update durable app memory from the accepted work; Codex edits .jskit/APP_BLUEPRINT.md; JSKIT records and commits the update.",
377
+ id: "blueprint_updated",
378
+ kind: "codex_prompt",
379
+ label: "Blueprint updated",
380
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "accepted_changes_committed"]
232
381
  }),
233
382
  defineStep({
234
383
  buttonLabel: "Run verification",
235
- description: "Run the project verification command in the session worktree.",
384
+ description: "JSKIT runs the final project verification command in the session worktree and records the result.",
236
385
  id: "doctor_run",
237
- label: "Doctor run",
238
- preconditions: ["session_exists", "worktree_exists"]
386
+ label: "Verification run",
387
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "accepted_changes_committed", "blueprint_update_satisfied"]
239
388
  }),
240
389
  defineStep({
241
- buttonLabel: "Push branch",
242
- description: "Push the session branch to origin.",
243
- id: "branch_pushed",
244
- label: "Branch pushed",
245
- preconditions: ["session_exists", "worktree_exists"]
390
+ buttonLabel: "Create final report",
391
+ description: "JSKIT creates the deterministic final session report and comments it on the GitHub issue.",
392
+ id: "final_report_created",
393
+ label: "Final report created",
394
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "accepted_changes_committed", "blueprint_update_satisfied"]
246
395
  }),
247
396
  defineStep({
248
- buttonLabel: "Create PR",
249
- description: "Create a GitHub pull request for the session branch.",
397
+ buttonLabel: "Push branch and create PR",
398
+ description: "JSKIT pushes the session branch to origin, creates or reuses the GitHub pull request, and records the PR URL.",
250
399
  id: "pr_created",
251
- label: "PR created",
252
- preconditions: ["session_exists", "worktree_exists"]
400
+ label: "Branch pushed, PR created",
401
+ preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_metadata_exists", "active_cycle_exists", "automated_checks_passed", "deep_ui_check_satisfied", "active_cycle_user_check_passed", "accepted_changes_committed", "blueprint_update_satisfied", "final_report_exists"]
253
402
  }),
254
403
  defineStep({
255
404
  buttonLabel: "Merge PR",
256
- description: "Merge the pull request and close the GitHub issue.",
257
- id: "pr_merged",
258
- label: "PR merged",
259
- preconditions: ["session_exists", "pr_url_exists"]
260
- }),
261
- defineStep({
262
- buttonLabel: "Remove worktree",
263
- description: "Remove the session worktree after the PR has merged.",
264
- id: "worktree_removed",
265
- label: "Worktree removed",
266
- preconditions: ["session_exists"]
405
+ description: "User chooses whether JSKIT merges the pull request or finishes without merge; JSKIT then removes the session worktree.",
406
+ id: "pr_finalized",
407
+ label: "PR finalized, worktree removed",
408
+ preconditions: ["session_exists", "pr_url_exists", "worktree_exists"],
409
+ requiresExplicitRun: true
267
410
  }),
268
411
  defineStep({
269
412
  buttonLabel: "Finish session",
270
- description: "Write the final receipt and archive the completed session.",
413
+ description: "JSKIT writes the final receipt and archives the completed session.",
271
414
  id: "session_finished",
272
415
  label: "Session finished",
273
416
  preconditions: ["session_exists"]
@@ -277,6 +420,15 @@ const STEP_DEFINITIONS = Object.freeze([
277
420
  const STEP_IDS = Object.freeze(STEP_DEFINITIONS.map((step) => step.id));
278
421
  const STEP_LABEL_BY_ID = Object.freeze(Object.fromEntries(STEP_DEFINITIONS.map((step) => [step.id, step.label])));
279
422
  const STEP_DEFINITION_BY_ID = Object.freeze(Object.fromEntries(STEP_DEFINITIONS.map((step) => [step.id, step])));
423
+ const CYCLE_STEP_IDS = Object.freeze([
424
+ "plan_made",
425
+ "plan_executed",
426
+ "deep_ui_check_run",
427
+ "review_prompt_rendered",
428
+ "review_changes_accepted",
429
+ "automated_checks_run",
430
+ "user_check_completed"
431
+ ]);
280
432
  const STEP_PRECONDITION_NAMES = Object.freeze(Object.fromEntries(
281
433
  STEP_DEFINITIONS
282
434
  .filter((step) => step.id !== "session_created")
@@ -287,10 +439,21 @@ export {
287
439
  PROMPT_DIRECTORY,
288
440
  SESSION_ID_PATTERN,
289
441
  SESSION_STATUS,
442
+ SESSION_WORKFLOW_VERSION,
443
+ REVIEW_PASS_LIMIT,
444
+ CYCLE_STEP_IDS,
290
445
  STEP_DEFINITION_BY_ID,
291
446
  STEP_DEFINITIONS,
292
447
  STEP_IDS,
293
448
  STEP_LABEL_BY_ID,
294
449
  STEP_PRECONDITION_NAMES,
450
+ ISSUE_DETAILS_CODEX_HANDOFF,
451
+ PLAN_EXECUTION_CODEX_HANDOFF,
452
+ REVIEW_EXECUTION_CODEX_HANDOFF,
453
+ DEEP_UI_CHECK_CODEX_HANDOFF,
454
+ AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF,
455
+ BLUEPRINT_CODEX_HANDOFF,
456
+ JSKIT_CLI_SHELL_COMMAND,
457
+ JSKIT_CLI_SHELL_RULE,
295
458
  SESSION_STATE_RELATIVE_PATH
296
459
  };
@@ -68,10 +68,9 @@ function resolveSessionPaths({ targetRoot, sessionId = "" } = {}) {
68
68
  const sessionsRoot = path.join(sessionStateRoot, "active");
69
69
  const completedSessionsRoot = path.join(sessionStateRoot, "completed");
70
70
  const abandonedSessionsRoot = path.join(sessionStateRoot, "abandoned");
71
- const worktreesRoot = path.join(sessionStateRoot, "worktrees");
72
71
  const normalizedSessionId = sessionId ? normalizeSessionId(sessionId) : "";
73
72
  const sessionRoot = normalizedSessionId ? path.join(sessionsRoot, normalizedSessionId) : "";
74
- const worktree = normalizedSessionId ? path.join(worktreesRoot, normalizedSessionId) : "";
73
+ const worktree = normalizedSessionId ? path.join(sessionRoot, "worktree") : "";
75
74
  const branch = normalizedSessionId ? `jskit-studio/${normalizedSessionId}` : "";
76
75
 
77
76
  return Object.freeze({
@@ -85,8 +84,7 @@ function resolveSessionPaths({ targetRoot, sessionId = "" } = {}) {
85
84
  sessionsRoot,
86
85
  sessionStateRoot,
87
86
  targetRoot: normalizedTargetRoot,
88
- worktree,
89
- worktreesRoot
87
+ worktree
90
88
  });
91
89
  }
92
90