@jskit-ai/jskit-cli 0.2.96 → 0.2.97
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/appBlueprint.js +37 -14
- package/src/server/core/argParser.js +0 -12
- package/src/server/core/commandCatalog.js +2 -92
- package/src/server/core/createCommandHandlers.js +0 -3
- package/src/server/index.js +0 -1
- package/src/server/{sessionRuntime/prompts → prompts}/app_blueprint.md +1 -1
- package/src/server/commandHandlers/session.js +0 -471
- package/src/server/sessionRuntime/appReadiness.js +0 -55
- package/src/server/sessionRuntime/constants.js +0 -377
- package/src/server/sessionRuntime/io.js +0 -97
- package/src/server/sessionRuntime/paths.js +0 -163
- package/src/server/sessionRuntime/preconditions.js +0 -663
- package/src/server/sessionRuntime/promptRenderer.js +0 -41
- package/src/server/sessionRuntime/prompts/automated_checks_run.md +0 -28
- package/src/server/sessionRuntime/prompts/blueprint_updated.md +0 -29
- package/src/server/sessionRuntime/prompts/deep_ui_check_run.md +0 -40
- package/src/server/sessionRuntime/prompts/final_comment.md +0 -10
- package/src/server/sessionRuntime/prompts/final_report_created.md +0 -44
- package/src/server/sessionRuntime/prompts/issue_created.md +0 -26
- package/src/server/sessionRuntime/prompts/issue_prompt_rendered.md +0 -1
- package/src/server/sessionRuntime/prompts/make_plan.md +0 -57
- package/src/server/sessionRuntime/prompts/plan_executed.md +0 -39
- package/src/server/sessionRuntime/prompts/pr_failure.md +0 -28
- package/src/server/sessionRuntime/prompts/pr_merge_prepared.md +0 -22
- package/src/server/sessionRuntime/prompts/review_changes_accepted_resolve.md +0 -12
- package/src/server/sessionRuntime/prompts/review_prompt_rendered.md +0 -61
- package/src/server/sessionRuntime/prompts/user_check_completed.md +0 -17
- package/src/server/sessionRuntime/responses.js +0 -1481
- package/src/server/sessionRuntime/worktrees.js +0 -31
- package/src/server/sessionRuntime.js +0 -3659
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
const SESSION_STATUS = Object.freeze({
|
|
5
|
-
ABANDONED: "abandoned",
|
|
6
|
-
BLOCKED: "blocked",
|
|
7
|
-
FAILED: "failed",
|
|
8
|
-
FINISHED: "finished",
|
|
9
|
-
PENDING: "pending",
|
|
10
|
-
RUNNING: "running",
|
|
11
|
-
WAITING_FOR_USER: "waiting_for_user"
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const SESSION_ID_PATTERN = /^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(?:-[a-z0-9]{4})?$/u;
|
|
15
|
-
const SESSION_STATE_RELATIVE_PATH = ".jskit/sessions";
|
|
16
|
-
const SESSION_WORKFLOW_VERSION = "7";
|
|
17
|
-
const DEPENDENCIES_INSTALL_RESULT_FILE = "dependencies_install_result";
|
|
18
|
-
const REVIEW_PASS_LIMIT = 0;
|
|
19
|
-
const PROMPT_DIRECTORY = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "prompts");
|
|
20
|
-
const JSKIT_CLI_SHELL_COMMAND = "npx --no-install jskit";
|
|
21
|
-
const DEFAULT_NEXT_COMMAND_TEMPLATE = `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} next`;
|
|
22
|
-
|
|
23
|
-
const INPUT_NONE = Object.freeze({ type: "none" });
|
|
24
|
-
const USER_CHECK_INPUT = Object.freeze({
|
|
25
|
-
label: "Choose user check result",
|
|
26
|
-
name: "userCheck",
|
|
27
|
-
options: Object.freeze([
|
|
28
|
-
Object.freeze({ label: "Passed", value: "passed" }),
|
|
29
|
-
Object.freeze({ label: "Failed", value: "failed" })
|
|
30
|
-
]),
|
|
31
|
-
required: true,
|
|
32
|
-
type: "choice"
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
function codexHandoff({
|
|
36
|
-
promptActionLabel = "",
|
|
37
|
-
promptIntroText = "",
|
|
38
|
-
promptWaitingText = "",
|
|
39
|
-
sendPrompt = false
|
|
40
|
-
} = {}) {
|
|
41
|
-
return Object.freeze({
|
|
42
|
-
mode: "inject_prompt",
|
|
43
|
-
promptField: "prompt",
|
|
44
|
-
...(promptActionLabel ? { promptActionLabel } : {}),
|
|
45
|
-
...(promptIntroText ? { promptIntroText } : {}),
|
|
46
|
-
...(promptWaitingText ? { promptWaitingText } : {}),
|
|
47
|
-
...(sendPrompt ? { sendPrompt: true } : {})
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function stepAutomationFor({
|
|
52
|
-
codex = undefined,
|
|
53
|
-
id,
|
|
54
|
-
kind,
|
|
55
|
-
requiresExplicitRun = false
|
|
56
|
-
}) {
|
|
57
|
-
if (requiresExplicitRun) {
|
|
58
|
-
return Object.freeze({ mode: "manual" });
|
|
59
|
-
}
|
|
60
|
-
if (id === "dependencies_installed") {
|
|
61
|
-
return Object.freeze({ mode: "terminal" });
|
|
62
|
-
}
|
|
63
|
-
if (kind === "automatic") {
|
|
64
|
-
return Object.freeze({ mode: "manual" });
|
|
65
|
-
}
|
|
66
|
-
if (kind === "codex_prompt" && codex?.sendPrompt === true) {
|
|
67
|
-
return Object.freeze({ mode: "codex_prompt" });
|
|
68
|
-
}
|
|
69
|
-
return Object.freeze({ mode: "manual" });
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const ISSUE_DEFINITION_CODEX_HANDOFF = codexHandoff({
|
|
73
|
-
promptActionLabel: "Define issue",
|
|
74
|
-
sendPrompt: true
|
|
75
|
-
});
|
|
76
|
-
const ISSUE_FILE_CODEX_HANDOFF = codexHandoff({
|
|
77
|
-
promptActionLabel: "Create issue file",
|
|
78
|
-
sendPrompt: true
|
|
79
|
-
});
|
|
80
|
-
const PLAN_CODEX_HANDOFF = codexHandoff({
|
|
81
|
-
promptActionLabel: "Make plan",
|
|
82
|
-
sendPrompt: true
|
|
83
|
-
});
|
|
84
|
-
const PLAN_EXECUTION_CODEX_HANDOFF = codexHandoff({
|
|
85
|
-
promptActionLabel: "Execute plan",
|
|
86
|
-
sendPrompt: true
|
|
87
|
-
});
|
|
88
|
-
const REVIEW_EXECUTION_CODEX_HANDOFF = codexHandoff({
|
|
89
|
-
promptActionLabel: "Run deslop",
|
|
90
|
-
sendPrompt: true
|
|
91
|
-
});
|
|
92
|
-
const RESOLVE_DESLOP_CODEX_HANDOFF = codexHandoff({
|
|
93
|
-
promptActionLabel: "Resolve deslop",
|
|
94
|
-
sendPrompt: true
|
|
95
|
-
});
|
|
96
|
-
const DEEP_UI_CHECK_CODEX_HANDOFF = codexHandoff({
|
|
97
|
-
promptActionLabel: "Run deep UI check",
|
|
98
|
-
sendPrompt: true
|
|
99
|
-
});
|
|
100
|
-
const AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF = codexHandoff({
|
|
101
|
-
promptActionLabel: "Run automated checks",
|
|
102
|
-
sendPrompt: true
|
|
103
|
-
});
|
|
104
|
-
const BLUEPRINT_CODEX_HANDOFF = codexHandoff({
|
|
105
|
-
promptActionLabel: "Update blueprint",
|
|
106
|
-
sendPrompt: true
|
|
107
|
-
});
|
|
108
|
-
const PR_FILE_CODEX_HANDOFF = codexHandoff({
|
|
109
|
-
promptActionLabel: "Create PR file",
|
|
110
|
-
sendPrompt: true
|
|
111
|
-
});
|
|
112
|
-
const PR_MERGE_PREP_CODEX_HANDOFF = codexHandoff({
|
|
113
|
-
promptActionLabel: "Prepare PR merge",
|
|
114
|
-
promptWaitingText: "Codex is preparing the PR for merge. Continue only when you decide the PR is ready.",
|
|
115
|
-
sendPrompt: true
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
function defineStep({
|
|
119
|
-
buttonLabel,
|
|
120
|
-
codex = undefined,
|
|
121
|
-
description,
|
|
122
|
-
id,
|
|
123
|
-
input = INPUT_NONE,
|
|
124
|
-
kind = "automatic",
|
|
125
|
-
label,
|
|
126
|
-
nextCommandTemplate = DEFAULT_NEXT_COMMAND_TEMPLATE,
|
|
127
|
-
preconditions = [],
|
|
128
|
-
requiresExplicitRun = false,
|
|
129
|
-
submitOptions = {},
|
|
130
|
-
automation = undefined,
|
|
131
|
-
utilityActions = [],
|
|
132
|
-
displayGroupId = "",
|
|
133
|
-
displayGroupLabel = ""
|
|
134
|
-
}) {
|
|
135
|
-
const resolvedAutomation = automation || stepAutomationFor({
|
|
136
|
-
codex,
|
|
137
|
-
id,
|
|
138
|
-
kind,
|
|
139
|
-
requiresExplicitRun
|
|
140
|
-
});
|
|
141
|
-
return Object.freeze({
|
|
142
|
-
automation: Object.freeze({ ...resolvedAutomation }),
|
|
143
|
-
buttonLabel,
|
|
144
|
-
codex,
|
|
145
|
-
description,
|
|
146
|
-
displayGroupId,
|
|
147
|
-
displayGroupLabel,
|
|
148
|
-
id,
|
|
149
|
-
input,
|
|
150
|
-
kind,
|
|
151
|
-
label,
|
|
152
|
-
nextCommandTemplate,
|
|
153
|
-
preconditions: Object.freeze([...preconditions]),
|
|
154
|
-
requiresExplicitRun,
|
|
155
|
-
submitOptions: Object.freeze({ ...submitOptions }),
|
|
156
|
-
utilityActions: Object.freeze([...utilityActions])
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const STEP_DEFINITIONS = Object.freeze([
|
|
161
|
-
defineStep({
|
|
162
|
-
buttonLabel: "Create worktree",
|
|
163
|
-
description: "JSKIT creates the isolated Git branch and session worktree where Codex will work.",
|
|
164
|
-
id: "worktree_created",
|
|
165
|
-
label: "Create worktree",
|
|
166
|
-
preconditions: ["session_exists", "git_repository", "git_current_branch"]
|
|
167
|
-
}),
|
|
168
|
-
defineStep({
|
|
169
|
-
buttonLabel: "Install dependencies",
|
|
170
|
-
description: "JSKIT installs Node dependencies inside the session worktree before Codex starts.",
|
|
171
|
-
id: "dependencies_installed",
|
|
172
|
-
label: "Install dependencies",
|
|
173
|
-
preconditions: ["session_exists", "worktree_exists"]
|
|
174
|
-
}),
|
|
175
|
-
defineStep({
|
|
176
|
-
buttonLabel: "Define issue",
|
|
177
|
-
description: "User describes the requested change; Codex helps scope and define the issue in the terminal.",
|
|
178
|
-
displayGroupId: "define_create_issue",
|
|
179
|
-
displayGroupLabel: "Define issue and create file",
|
|
180
|
-
id: "issue_prompt_rendered",
|
|
181
|
-
input: Object.freeze({
|
|
182
|
-
label: "What should change?",
|
|
183
|
-
multiline: true,
|
|
184
|
-
name: "prompt",
|
|
185
|
-
placeholder: "Describe the feature, bug, or change request.",
|
|
186
|
-
required: true,
|
|
187
|
-
type: "text"
|
|
188
|
-
}),
|
|
189
|
-
kind: "human_input",
|
|
190
|
-
label: "Define issue",
|
|
191
|
-
nextCommandTemplate: DEFAULT_NEXT_COMMAND_TEMPLATE,
|
|
192
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app"]
|
|
193
|
-
}),
|
|
194
|
-
defineStep({
|
|
195
|
-
buttonLabel: "Create issue file",
|
|
196
|
-
description: "JSKIT renders the issue-file prompt; Codex writes issue.md and issue_title for review.",
|
|
197
|
-
displayGroupId: "define_create_issue",
|
|
198
|
-
displayGroupLabel: "Define issue and create file",
|
|
199
|
-
id: "issue_created",
|
|
200
|
-
label: "Create issue file",
|
|
201
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app"],
|
|
202
|
-
requiresExplicitRun: false
|
|
203
|
-
}),
|
|
204
|
-
defineStep({
|
|
205
|
-
buttonLabel: "Create issue on GH",
|
|
206
|
-
description: "JSKIT creates the GitHub issue from the reviewed issue files and records the issue metadata.",
|
|
207
|
-
id: "issue_submitted",
|
|
208
|
-
label: "Edit and submit issue",
|
|
209
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists"],
|
|
210
|
-
requiresExplicitRun: false
|
|
211
|
-
}),
|
|
212
|
-
defineStep({
|
|
213
|
-
buttonLabel: "Make plan",
|
|
214
|
-
codex: PLAN_CODEX_HANDOFF,
|
|
215
|
-
description: "Codex writes the plan in the terminal; JSKIT records it when the user continues.",
|
|
216
|
-
id: "plan_made",
|
|
217
|
-
kind: "codex_prompt",
|
|
218
|
-
label: "Make plan",
|
|
219
|
-
nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} next`,
|
|
220
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "issue_url_exists"]
|
|
221
|
-
}),
|
|
222
|
-
defineStep({
|
|
223
|
-
buttonLabel: "Execute plan",
|
|
224
|
-
codex: PLAN_EXECUTION_CODEX_HANDOFF,
|
|
225
|
-
description: "JSKIT sends the plan to Codex; Codex implements it; the user advances after reviewing completion.",
|
|
226
|
-
id: "plan_executed",
|
|
227
|
-
kind: "codex_prompt",
|
|
228
|
-
label: "Execute plan",
|
|
229
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_text_exists", "issue_url_exists"]
|
|
230
|
-
}),
|
|
231
|
-
defineStep({
|
|
232
|
-
buttonLabel: "Run deep UI check",
|
|
233
|
-
codex: DEEP_UI_CHECK_CODEX_HANDOFF,
|
|
234
|
-
description: "JSKIT can ask Codex for a focused UI quality pass; use Next to continue without one.",
|
|
235
|
-
id: "deep_ui_check_run",
|
|
236
|
-
kind: "codex_prompt",
|
|
237
|
-
label: "Run deep UI check",
|
|
238
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app"]
|
|
239
|
-
}),
|
|
240
|
-
defineStep({
|
|
241
|
-
buttonLabel: "Run deslop",
|
|
242
|
-
codex: REVIEW_EXECUTION_CODEX_HANDOFF,
|
|
243
|
-
description: "JSKIT sends the current implementation to Codex for a review/deslop pass; the user decides whether to resolve findings, run deslop again, or continue.",
|
|
244
|
-
id: "review_prompt_rendered",
|
|
245
|
-
kind: "codex_prompt",
|
|
246
|
-
label: "Run deslop",
|
|
247
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "deep_ui_check_satisfied"]
|
|
248
|
-
}),
|
|
249
|
-
defineStep({
|
|
250
|
-
buttonLabel: "Accept review/deslop",
|
|
251
|
-
description: "User chooses whether to resolve the last deslop result, run deslop again, or continue.",
|
|
252
|
-
id: "review_changes_accepted",
|
|
253
|
-
kind: "user_check",
|
|
254
|
-
label: "Accept review/deslop",
|
|
255
|
-
nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --review-findings-remaining false`,
|
|
256
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "deep_ui_check_satisfied"],
|
|
257
|
-
submitOptions: Object.freeze({
|
|
258
|
-
reviewFindingsRemaining: false
|
|
259
|
-
})
|
|
260
|
-
}),
|
|
261
|
-
defineStep({
|
|
262
|
-
buttonLabel: "Run automated checks",
|
|
263
|
-
codex: AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF,
|
|
264
|
-
description: "JSKIT can ask Codex to run the official verification command in the worktree; use Next to continue without one.",
|
|
265
|
-
id: "automated_checks_run",
|
|
266
|
-
kind: "codex_prompt",
|
|
267
|
-
label: "Run automated checks",
|
|
268
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "deep_ui_check_satisfied"]
|
|
269
|
-
}),
|
|
270
|
-
defineStep({
|
|
271
|
-
buttonLabel: "Complete user check",
|
|
272
|
-
description: "User manually checks the result; if it fails, rewind to the step that should be redone.",
|
|
273
|
-
id: "user_check_completed",
|
|
274
|
-
input: USER_CHECK_INPUT,
|
|
275
|
-
kind: "user_check",
|
|
276
|
-
label: "Complete user check",
|
|
277
|
-
nextCommandTemplate: `${JSKIT_CLI_SHELL_COMMAND} session {{session_id}} step --user-check passed`,
|
|
278
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "automated_checks_passed", "deep_ui_check_satisfied"],
|
|
279
|
-
utilityActions: Object.freeze([
|
|
280
|
-
Object.freeze({
|
|
281
|
-
id: "session_app_test",
|
|
282
|
-
kind: "app_test",
|
|
283
|
-
label: "Test app"
|
|
284
|
-
})
|
|
285
|
-
])
|
|
286
|
-
}),
|
|
287
|
-
defineStep({
|
|
288
|
-
buttonLabel: "Update blueprint",
|
|
289
|
-
codex: BLUEPRINT_CODEX_HANDOFF,
|
|
290
|
-
description: "JSKIT asks Codex to update durable app memory from the accepted work; Codex edits .jskit/APP_BLUEPRINT.md; JSKIT records the update for the accepted-work commit.",
|
|
291
|
-
id: "blueprint_updated",
|
|
292
|
-
kind: "codex_prompt",
|
|
293
|
-
label: "Update blueprint",
|
|
294
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "automated_checks_passed", "deep_ui_check_satisfied", "user_check_passed"]
|
|
295
|
-
}),
|
|
296
|
-
defineStep({
|
|
297
|
-
buttonLabel: "Commit changes",
|
|
298
|
-
description: "JSKIT commits the accepted session changes, including durable app memory updates, in the session worktree.",
|
|
299
|
-
id: "changes_committed",
|
|
300
|
-
label: "Commit changes",
|
|
301
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "issue_url_exists", "github_auth", "automated_checks_passed", "deep_ui_check_satisfied", "user_check_passed", "blueprint_update_satisfied"]
|
|
302
|
-
}),
|
|
303
|
-
defineStep({
|
|
304
|
-
buttonLabel: "Create PR file",
|
|
305
|
-
codex: PR_FILE_CODEX_HANDOFF,
|
|
306
|
-
description: "JSKIT renders the PR-file prompt; Codex writes pull_request.md for review.",
|
|
307
|
-
id: "final_report_created",
|
|
308
|
-
kind: "codex_prompt",
|
|
309
|
-
label: "Create PR file",
|
|
310
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "automated_checks_passed", "deep_ui_check_satisfied", "user_check_passed", "blueprint_update_satisfied", "accepted_changes_committed"]
|
|
311
|
-
}),
|
|
312
|
-
defineStep({
|
|
313
|
-
buttonLabel: "Create PR on GH",
|
|
314
|
-
description: "JSKIT creates the GitHub pull request from the reviewed pull_request.md and records the PR URL.",
|
|
315
|
-
id: "pr_created",
|
|
316
|
-
label: "Edit and create PR",
|
|
317
|
-
preconditions: ["session_exists", "worktree_exists", "dependencies_installed", "ready_jskit_app", "automated_checks_passed", "deep_ui_check_satisfied", "user_check_passed", "accepted_changes_committed", "blueprint_update_satisfied", "pull_request_file_exists"]
|
|
318
|
-
}),
|
|
319
|
-
defineStep({
|
|
320
|
-
buttonLabel: "Merge",
|
|
321
|
-
codex: PR_MERGE_PREP_CODEX_HANDOFF,
|
|
322
|
-
description: "Prepare the pull request for merge, merge it, or use Next to continue without merging.",
|
|
323
|
-
id: "pr_merge_prepared",
|
|
324
|
-
label: "Merge PR",
|
|
325
|
-
preconditions: ["session_exists", "worktree_exists"]
|
|
326
|
-
}),
|
|
327
|
-
defineStep({
|
|
328
|
-
buttonLabel: "Sync main checkout",
|
|
329
|
-
description: "JSKIT fast-forwards the main checkout after a merged PR. If the PR was not merged, use Next to record that no sync was needed.",
|
|
330
|
-
id: "main_checkout_synced",
|
|
331
|
-
label: "Sync main checkout",
|
|
332
|
-
preconditions: ["session_exists", "worktree_exists"]
|
|
333
|
-
}),
|
|
334
|
-
defineStep({
|
|
335
|
-
buttonLabel: "Finish",
|
|
336
|
-
description: "Congratulations! Finish the session by removing the session worktree and archiving the completed session.",
|
|
337
|
-
id: "session_finished",
|
|
338
|
-
label: "Congratulations!",
|
|
339
|
-
preconditions: ["session_exists", "main_checkout_sync_satisfied"]
|
|
340
|
-
})
|
|
341
|
-
]);
|
|
342
|
-
|
|
343
|
-
const STEP_IDS = Object.freeze(STEP_DEFINITIONS.map((step) => step.id));
|
|
344
|
-
const STEP_LABEL_BY_ID = Object.freeze(Object.fromEntries(STEP_DEFINITIONS.map((step) => [step.id, step.label])));
|
|
345
|
-
const STEP_DEFINITION_BY_ID = Object.freeze(Object.fromEntries(STEP_DEFINITIONS.map((step) => [step.id, step])));
|
|
346
|
-
const CYCLE_STEP_IDS = Object.freeze([]);
|
|
347
|
-
const STEP_PRECONDITION_NAMES = Object.freeze(Object.fromEntries(
|
|
348
|
-
STEP_DEFINITIONS.map((step) => [step.id, step.preconditions])
|
|
349
|
-
));
|
|
350
|
-
|
|
351
|
-
export {
|
|
352
|
-
PROMPT_DIRECTORY,
|
|
353
|
-
SESSION_ID_PATTERN,
|
|
354
|
-
SESSION_STATUS,
|
|
355
|
-
SESSION_WORKFLOW_VERSION,
|
|
356
|
-
DEPENDENCIES_INSTALL_RESULT_FILE,
|
|
357
|
-
REVIEW_PASS_LIMIT,
|
|
358
|
-
CYCLE_STEP_IDS,
|
|
359
|
-
STEP_DEFINITION_BY_ID,
|
|
360
|
-
STEP_DEFINITIONS,
|
|
361
|
-
STEP_IDS,
|
|
362
|
-
STEP_LABEL_BY_ID,
|
|
363
|
-
STEP_PRECONDITION_NAMES,
|
|
364
|
-
ISSUE_DEFINITION_CODEX_HANDOFF,
|
|
365
|
-
ISSUE_FILE_CODEX_HANDOFF,
|
|
366
|
-
PLAN_CODEX_HANDOFF,
|
|
367
|
-
PLAN_EXECUTION_CODEX_HANDOFF,
|
|
368
|
-
REVIEW_EXECUTION_CODEX_HANDOFF,
|
|
369
|
-
RESOLVE_DESLOP_CODEX_HANDOFF,
|
|
370
|
-
DEEP_UI_CHECK_CODEX_HANDOFF,
|
|
371
|
-
AUTOMATED_CHECK_REPAIR_CODEX_HANDOFF,
|
|
372
|
-
BLUEPRINT_CODEX_HANDOFF,
|
|
373
|
-
PR_FILE_CODEX_HANDOFF,
|
|
374
|
-
PR_MERGE_PREP_CODEX_HANDOFF,
|
|
375
|
-
JSKIT_CLI_SHELL_COMMAND,
|
|
376
|
-
SESSION_STATE_RELATIVE_PATH
|
|
377
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { constants as fsConstants } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
|
|
7
|
-
const execFileAsync = promisify(execFile);
|
|
8
|
-
|
|
9
|
-
function normalizeText(value) {
|
|
10
|
-
return String(value || "").trim();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function timestampForStepRecord(now = new Date()) {
|
|
14
|
-
return now.toISOString();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function fileExists(filePath) {
|
|
18
|
-
try {
|
|
19
|
-
await access(filePath, fsConstants.F_OK);
|
|
20
|
-
return true;
|
|
21
|
-
} catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function readTextIfExists(filePath) {
|
|
27
|
-
if (!filePath || !(await fileExists(filePath))) {
|
|
28
|
-
return "";
|
|
29
|
-
}
|
|
30
|
-
return readFile(filePath, "utf8");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function readTrimmedFile(filePath) {
|
|
34
|
-
return normalizeText(await readTextIfExists(filePath));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function writeTextFile(filePath, value) {
|
|
38
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
39
|
-
await writeFile(filePath, `${String(value || "").replace(/\s*$/u, "")}\n`, "utf8");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function runCommand(command, args = [], { cwd, env = {}, timeout = 30000 } = {}) {
|
|
43
|
-
try {
|
|
44
|
-
const result = await execFileAsync(command, args, {
|
|
45
|
-
cwd,
|
|
46
|
-
env: {
|
|
47
|
-
...process.env,
|
|
48
|
-
...env
|
|
49
|
-
},
|
|
50
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
51
|
-
timeout
|
|
52
|
-
});
|
|
53
|
-
return {
|
|
54
|
-
exitCode: 0,
|
|
55
|
-
ok: true,
|
|
56
|
-
output: String([result.stdout, result.stderr].filter(Boolean).join("\n")).trim(),
|
|
57
|
-
stderr: String(result.stderr || "").trim(),
|
|
58
|
-
stdout: String(result.stdout || "").trim()
|
|
59
|
-
};
|
|
60
|
-
} catch (error) {
|
|
61
|
-
const stdout = String(error?.stdout || "").trim();
|
|
62
|
-
const stderr = String(error?.stderr || "").trim();
|
|
63
|
-
return {
|
|
64
|
-
exitCode: typeof error?.code === "number" ? error.code : 1,
|
|
65
|
-
ok: false,
|
|
66
|
-
output: String(error?.message || [stdout, stderr].filter(Boolean).join("\n")).trim(),
|
|
67
|
-
stderr,
|
|
68
|
-
stdout
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function runGit(targetRoot, args = [], options = {}) {
|
|
74
|
-
return runCommand("git", args, {
|
|
75
|
-
cwd: targetRoot,
|
|
76
|
-
...options
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function runGitInWorktree(worktree, args = [], options = {}) {
|
|
81
|
-
return runCommand("git", args, {
|
|
82
|
-
cwd: worktree,
|
|
83
|
-
...options
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export {
|
|
88
|
-
fileExists,
|
|
89
|
-
normalizeText,
|
|
90
|
-
readTextIfExists,
|
|
91
|
-
readTrimmedFile,
|
|
92
|
-
runCommand,
|
|
93
|
-
runGit,
|
|
94
|
-
runGitInWorktree,
|
|
95
|
-
timestampForStepRecord,
|
|
96
|
-
writeTextFile
|
|
97
|
-
};
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { mkdir, rename } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
SESSION_ID_PATTERN,
|
|
5
|
-
SESSION_STATE_RELATIVE_PATH
|
|
6
|
-
} from "./constants.js";
|
|
7
|
-
import {
|
|
8
|
-
fileExists,
|
|
9
|
-
normalizeText
|
|
10
|
-
} from "./io.js";
|
|
11
|
-
|
|
12
|
-
function formatDatePart(value) {
|
|
13
|
-
return String(value).padStart(2, "0");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createSessionId(now = new Date()) {
|
|
17
|
-
const year = now.getFullYear();
|
|
18
|
-
const month = formatDatePart(now.getMonth() + 1);
|
|
19
|
-
const day = formatDatePart(now.getDate());
|
|
20
|
-
const hour = formatDatePart(now.getHours());
|
|
21
|
-
const minute = formatDatePart(now.getMinutes());
|
|
22
|
-
const second = formatDatePart(now.getSeconds());
|
|
23
|
-
return `${year}-${month}-${day}_${hour}-${minute}-${second}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function createAvailableSessionId(targetRoot, now = new Date()) {
|
|
27
|
-
const baseSessionId = createSessionId(now);
|
|
28
|
-
const basePaths = resolveSessionPaths({ targetRoot, sessionId: baseSessionId });
|
|
29
|
-
if (
|
|
30
|
-
!(await fileExists(basePaths.sessionRoot)) &&
|
|
31
|
-
!(await fileExists(basePaths.completedSessionRoot)) &&
|
|
32
|
-
!(await fileExists(basePaths.abandonedSessionRoot))
|
|
33
|
-
) {
|
|
34
|
-
return baseSessionId;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (let index = 1; index <= 36 ** 4 - 1; index += 1) {
|
|
38
|
-
const suffix = index.toString(36).padStart(4, "0");
|
|
39
|
-
const candidate = `${baseSessionId}-${suffix}`;
|
|
40
|
-
const candidatePaths = resolveSessionPaths({ targetRoot, sessionId: candidate });
|
|
41
|
-
if (
|
|
42
|
-
!(await fileExists(candidatePaths.sessionRoot)) &&
|
|
43
|
-
!(await fileExists(candidatePaths.completedSessionRoot)) &&
|
|
44
|
-
!(await fileExists(candidatePaths.abandonedSessionRoot))
|
|
45
|
-
) {
|
|
46
|
-
return candidate;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
throw new Error(`No available session id found for timestamp ${baseSessionId}.`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function isValidSessionId(sessionId = "") {
|
|
54
|
-
return SESSION_ID_PATTERN.test(normalizeText(sessionId));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function normalizeSessionId(sessionId = "") {
|
|
58
|
-
const normalized = normalizeText(sessionId);
|
|
59
|
-
if (!isValidSessionId(normalized)) {
|
|
60
|
-
throw new Error(`Invalid session id "${sessionId}". Expected YYYY-MM-DD_HH-MM-SS.`);
|
|
61
|
-
}
|
|
62
|
-
return normalized;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function resolveSessionPaths({ targetRoot, sessionId = "" } = {}) {
|
|
66
|
-
const normalizedTargetRoot = path.resolve(normalizeText(targetRoot) || process.cwd());
|
|
67
|
-
const sessionStateRoot = path.join(normalizedTargetRoot, SESSION_STATE_RELATIVE_PATH);
|
|
68
|
-
const sessionsRoot = path.join(sessionStateRoot, "active");
|
|
69
|
-
const completedSessionsRoot = path.join(sessionStateRoot, "completed");
|
|
70
|
-
const abandonedSessionsRoot = path.join(sessionStateRoot, "abandoned");
|
|
71
|
-
const normalizedSessionId = sessionId ? normalizeSessionId(sessionId) : "";
|
|
72
|
-
const sessionRoot = normalizedSessionId ? path.join(sessionsRoot, normalizedSessionId) : "";
|
|
73
|
-
const worktree = normalizedSessionId ? path.join(sessionRoot, "worktree") : "";
|
|
74
|
-
const branch = normalizedSessionId ? `jskit-studio/${normalizedSessionId}` : "";
|
|
75
|
-
|
|
76
|
-
return Object.freeze({
|
|
77
|
-
abandonedSessionRoot: normalizedSessionId ? path.join(abandonedSessionsRoot, normalizedSessionId) : "",
|
|
78
|
-
abandonedSessionsRoot,
|
|
79
|
-
branch,
|
|
80
|
-
completedSessionRoot: normalizedSessionId ? path.join(completedSessionsRoot, normalizedSessionId) : "",
|
|
81
|
-
completedSessionsRoot,
|
|
82
|
-
sessionId: normalizedSessionId,
|
|
83
|
-
sessionRoot,
|
|
84
|
-
sessionsRoot,
|
|
85
|
-
sessionStateRoot,
|
|
86
|
-
targetRoot: normalizedTargetRoot,
|
|
87
|
-
worktree
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function resolveExistingSessionRoot(paths) {
|
|
92
|
-
if (paths.archive && await fileExists(paths.sessionRoot)) {
|
|
93
|
-
return {
|
|
94
|
-
archive: paths.archive,
|
|
95
|
-
root: paths.sessionRoot
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
if (await fileExists(paths.sessionRoot)) {
|
|
99
|
-
return {
|
|
100
|
-
archive: "active",
|
|
101
|
-
root: paths.sessionRoot
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (await fileExists(paths.completedSessionRoot)) {
|
|
105
|
-
return {
|
|
106
|
-
archive: "completed",
|
|
107
|
-
root: paths.completedSessionRoot
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (await fileExists(paths.abandonedSessionRoot)) {
|
|
111
|
-
return {
|
|
112
|
-
archive: "abandoned",
|
|
113
|
-
root: paths.abandonedSessionRoot
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
return {
|
|
117
|
-
archive: "",
|
|
118
|
-
root: ""
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function pathsForExistingSession(paths) {
|
|
123
|
-
const existing = await resolveExistingSessionRoot(paths);
|
|
124
|
-
if (!existing.root || existing.root === paths.sessionRoot) {
|
|
125
|
-
return paths;
|
|
126
|
-
}
|
|
127
|
-
return Object.freeze({
|
|
128
|
-
...paths,
|
|
129
|
-
archive: existing.archive,
|
|
130
|
-
sessionRoot: existing.root
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function archiveSession(paths, archive) {
|
|
135
|
-
const archiveRoot = archive === "completed" ? paths.completedSessionRoot : paths.abandonedSessionRoot;
|
|
136
|
-
if (!archiveRoot || paths.sessionRoot === archiveRoot) {
|
|
137
|
-
return paths;
|
|
138
|
-
}
|
|
139
|
-
if (!(await fileExists(paths.sessionRoot))) {
|
|
140
|
-
return pathsForExistingSession(paths);
|
|
141
|
-
}
|
|
142
|
-
if (await fileExists(archiveRoot)) {
|
|
143
|
-
throw new Error(`Cannot archive session ${paths.sessionId}; target already exists: ${archiveRoot}`);
|
|
144
|
-
}
|
|
145
|
-
await mkdir(path.dirname(archiveRoot), { recursive: true });
|
|
146
|
-
await rename(paths.sessionRoot, archiveRoot);
|
|
147
|
-
return Object.freeze({
|
|
148
|
-
...paths,
|
|
149
|
-
archive,
|
|
150
|
-
sessionRoot: archiveRoot
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export {
|
|
155
|
-
archiveSession,
|
|
156
|
-
createAvailableSessionId,
|
|
157
|
-
createSessionId,
|
|
158
|
-
isValidSessionId,
|
|
159
|
-
normalizeSessionId,
|
|
160
|
-
resolveExistingSessionRoot,
|
|
161
|
-
resolveSessionPaths,
|
|
162
|
-
pathsForExistingSession
|
|
163
|
-
};
|