@jskit-ai/jskit-cli 0.2.92 → 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/helperMap.js +214 -56
- 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,663 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
access,
|
|
3
|
-
appendFile,
|
|
4
|
-
mkdir
|
|
5
|
-
} from "node:fs/promises";
|
|
6
|
-
import { constants as fsConstants } from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import {
|
|
9
|
-
JSKIT_CLI_SHELL_COMMAND,
|
|
10
|
-
SESSION_STATE_RELATIVE_PATH
|
|
11
|
-
} from "./constants.js";
|
|
12
|
-
import {
|
|
13
|
-
createError,
|
|
14
|
-
createPrecondition
|
|
15
|
-
} from "./responses.js";
|
|
16
|
-
import {
|
|
17
|
-
readTextIfExists,
|
|
18
|
-
readTrimmedFile,
|
|
19
|
-
runCommand,
|
|
20
|
-
runGit
|
|
21
|
-
} from "./io.js";
|
|
22
|
-
import {
|
|
23
|
-
resolveExistingSessionRoot
|
|
24
|
-
} from "./paths.js";
|
|
25
|
-
import {
|
|
26
|
-
hasWorktree
|
|
27
|
-
} from "./worktrees.js";
|
|
28
|
-
import {
|
|
29
|
-
inspectReadyJskitAppRoot
|
|
30
|
-
} from "./appReadiness.js";
|
|
31
|
-
|
|
32
|
-
function jskitCommand(args = "") {
|
|
33
|
-
return `${JSKIT_CLI_SHELL_COMMAND}${args ? ` ${args}` : ""}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function assertTargetRootWritable(targetRoot) {
|
|
37
|
-
try {
|
|
38
|
-
await access(targetRoot, fsConstants.R_OK | fsConstants.W_OK);
|
|
39
|
-
return {
|
|
40
|
-
ok: true,
|
|
41
|
-
precondition: createPrecondition({
|
|
42
|
-
id: "target_root_writable",
|
|
43
|
-
ok: true,
|
|
44
|
-
message: "Target root exists and is readable/writable."
|
|
45
|
-
})
|
|
46
|
-
};
|
|
47
|
-
} catch (error) {
|
|
48
|
-
return {
|
|
49
|
-
ok: false,
|
|
50
|
-
error: createError({
|
|
51
|
-
code: "target_root_not_writable",
|
|
52
|
-
message: `Target root is not readable/writable: ${error?.message || error}`,
|
|
53
|
-
repairCommand: `test -w ${targetRoot}`
|
|
54
|
-
}),
|
|
55
|
-
precondition: createPrecondition({
|
|
56
|
-
id: "target_root_writable",
|
|
57
|
-
ok: false,
|
|
58
|
-
message: "Target root exists and is readable/writable."
|
|
59
|
-
})
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function resolveGitCommonDirectory(targetRoot) {
|
|
65
|
-
const result = await runGit(targetRoot, ["rev-parse", "--git-common-dir"]);
|
|
66
|
-
if (!result.ok || !result.stdout) {
|
|
67
|
-
return "";
|
|
68
|
-
}
|
|
69
|
-
return path.resolve(targetRoot, result.stdout);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function assertGitRepository(targetRoot) {
|
|
73
|
-
const result = await runGit(targetRoot, ["rev-parse", "--is-inside-work-tree"]);
|
|
74
|
-
if (result.ok && result.stdout === "true") {
|
|
75
|
-
return {
|
|
76
|
-
ok: true,
|
|
77
|
-
precondition: createPrecondition({
|
|
78
|
-
id: "git_repository",
|
|
79
|
-
ok: true,
|
|
80
|
-
message: "Target root is inside a git work tree."
|
|
81
|
-
})
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
ok: false,
|
|
86
|
-
error: createError({
|
|
87
|
-
code: "git_repository_missing",
|
|
88
|
-
message: "Target root is not inside a git work tree.",
|
|
89
|
-
repairCommand: "git init"
|
|
90
|
-
}),
|
|
91
|
-
precondition: createPrecondition({
|
|
92
|
-
id: "git_repository",
|
|
93
|
-
ok: false,
|
|
94
|
-
message: "Target root is inside a git work tree."
|
|
95
|
-
})
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function assertGitCurrentBranch(targetRoot) {
|
|
100
|
-
const [branchResult, headResult] = await Promise.all([
|
|
101
|
-
runGit(targetRoot, ["branch", "--show-current"]),
|
|
102
|
-
runGit(targetRoot, ["rev-parse", "--verify", "HEAD"])
|
|
103
|
-
]);
|
|
104
|
-
if (branchResult.ok && branchResult.stdout && headResult.ok) {
|
|
105
|
-
return {
|
|
106
|
-
ok: true,
|
|
107
|
-
precondition: createPrecondition({
|
|
108
|
-
id: "git_current_branch",
|
|
109
|
-
ok: true,
|
|
110
|
-
message: "Target repository has a named current branch with an initial commit."
|
|
111
|
-
})
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
return {
|
|
115
|
-
ok: false,
|
|
116
|
-
error: createError({
|
|
117
|
-
code: "git_current_branch_missing",
|
|
118
|
-
message: "Target repository does not have a named current branch with an initial commit.",
|
|
119
|
-
repairCommand: "git checkout -b main && git add . && git commit -m \"Initial commit\""
|
|
120
|
-
}),
|
|
121
|
-
precondition: createPrecondition({
|
|
122
|
-
id: "git_current_branch",
|
|
123
|
-
ok: false,
|
|
124
|
-
message: "Target repository has a named current branch with an initial commit."
|
|
125
|
-
})
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async function assertGhAuth(targetRoot) {
|
|
130
|
-
const result = await runCommand("gh", ["auth", "status"], {
|
|
131
|
-
cwd: targetRoot,
|
|
132
|
-
timeout: 15000
|
|
133
|
-
});
|
|
134
|
-
if (result.ok) {
|
|
135
|
-
return {
|
|
136
|
-
ok: true,
|
|
137
|
-
precondition: createPrecondition({
|
|
138
|
-
id: "github_auth",
|
|
139
|
-
ok: true,
|
|
140
|
-
message: "GitHub CLI is authenticated."
|
|
141
|
-
})
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
ok: false,
|
|
146
|
-
error: createError({
|
|
147
|
-
code: "github_auth_missing",
|
|
148
|
-
message: "GitHub CLI is not authenticated.",
|
|
149
|
-
repairCommand: "gh auth login"
|
|
150
|
-
}),
|
|
151
|
-
precondition: createPrecondition({
|
|
152
|
-
id: "github_auth",
|
|
153
|
-
ok: false,
|
|
154
|
-
message: "GitHub CLI is authenticated."
|
|
155
|
-
})
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function assertGithubOrigin(targetRoot) {
|
|
160
|
-
const result = await runGit(targetRoot, ["remote", "get-url", "origin"]);
|
|
161
|
-
if (result.ok && /github\.com[:/]/u.test(result.stdout)) {
|
|
162
|
-
return {
|
|
163
|
-
ok: true,
|
|
164
|
-
precondition: createPrecondition({
|
|
165
|
-
id: "github_origin",
|
|
166
|
-
ok: true,
|
|
167
|
-
message: "Origin remote points at GitHub."
|
|
168
|
-
})
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
ok: false,
|
|
173
|
-
error: createError({
|
|
174
|
-
code: "github_origin_missing",
|
|
175
|
-
message: "Origin remote is missing or is not a GitHub remote.",
|
|
176
|
-
repairCommand: "git remote add origin https://github.com/<owner>/<repo>.git"
|
|
177
|
-
}),
|
|
178
|
-
precondition: createPrecondition({
|
|
179
|
-
id: "github_origin",
|
|
180
|
-
ok: false,
|
|
181
|
-
message: "Origin remote points at GitHub."
|
|
182
|
-
})
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async function applyPreconditions(paths, checks = []) {
|
|
187
|
-
const preconditions = [];
|
|
188
|
-
for (const check of checks) {
|
|
189
|
-
const result = await check();
|
|
190
|
-
if (result?.precondition) {
|
|
191
|
-
preconditions.push(result.precondition);
|
|
192
|
-
}
|
|
193
|
-
if (result?.ok !== true) {
|
|
194
|
-
return {
|
|
195
|
-
ok: false,
|
|
196
|
-
error: result.error,
|
|
197
|
-
preconditions
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
void paths;
|
|
202
|
-
return {
|
|
203
|
-
ok: true,
|
|
204
|
-
preconditions
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
async function ensureStudioGitExclude(targetRoot) {
|
|
209
|
-
const gitCommonDirectory = await resolveGitCommonDirectory(targetRoot);
|
|
210
|
-
if (!gitCommonDirectory) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const excludePath = path.join(gitCommonDirectory, "info", "exclude");
|
|
214
|
-
await mkdir(path.dirname(excludePath), { recursive: true });
|
|
215
|
-
const current = await readTextIfExists(excludePath);
|
|
216
|
-
const lines = current.split(/\r?\n/u).map((line) => line.trim());
|
|
217
|
-
if (!lines.includes(`${SESSION_STATE_RELATIVE_PATH}/`)) {
|
|
218
|
-
await appendFile(excludePath, `${current.endsWith("\n") || current.length === 0 ? "" : "\n"}${SESSION_STATE_RELATIVE_PATH}/\n`, "utf8");
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async function assertSessionExists(paths) {
|
|
223
|
-
const existing = await resolveExistingSessionRoot(paths);
|
|
224
|
-
if (existing.root) {
|
|
225
|
-
return {
|
|
226
|
-
ok: true,
|
|
227
|
-
precondition: createPrecondition({
|
|
228
|
-
id: "session_exists",
|
|
229
|
-
ok: true,
|
|
230
|
-
message: "Session state directory exists."
|
|
231
|
-
})
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
return {
|
|
235
|
-
ok: false,
|
|
236
|
-
error: createError({
|
|
237
|
-
code: "session_missing",
|
|
238
|
-
message: `Session does not exist: ${paths.sessionId}`,
|
|
239
|
-
repairCommand: jskitCommand("session create")
|
|
240
|
-
}),
|
|
241
|
-
precondition: createPrecondition({
|
|
242
|
-
id: "session_exists",
|
|
243
|
-
ok: false,
|
|
244
|
-
message: "Session state directory exists."
|
|
245
|
-
})
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async function pathExists(filePath) {
|
|
250
|
-
try {
|
|
251
|
-
await access(filePath, fsConstants.F_OK);
|
|
252
|
-
return true;
|
|
253
|
-
} catch {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async function assertDependenciesInstalled(paths) {
|
|
259
|
-
if (await pathExists(path.join(paths.sessionRoot, "steps", "dependencies_installed"))) {
|
|
260
|
-
return {
|
|
261
|
-
ok: true,
|
|
262
|
-
precondition: createPrecondition({
|
|
263
|
-
id: "dependencies_installed",
|
|
264
|
-
ok: true,
|
|
265
|
-
message: "Session worktree dependencies have been installed."
|
|
266
|
-
})
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
return {
|
|
270
|
-
ok: false,
|
|
271
|
-
error: createError({
|
|
272
|
-
code: "dependencies_not_installed",
|
|
273
|
-
message: "Cannot start issue work until dependencies are installed in the session worktree.",
|
|
274
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} run_npm_install`)
|
|
275
|
-
}),
|
|
276
|
-
precondition: createPrecondition({
|
|
277
|
-
id: "dependencies_installed",
|
|
278
|
-
ok: false,
|
|
279
|
-
message: "Session worktree dependencies have been installed."
|
|
280
|
-
})
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async function assertReadyJskitApp(paths) {
|
|
285
|
-
const root = paths.worktree || paths.targetRoot;
|
|
286
|
-
const readiness = await inspectReadyJskitAppRoot(root);
|
|
287
|
-
if (readiness.ok) {
|
|
288
|
-
return {
|
|
289
|
-
ok: true,
|
|
290
|
-
precondition: createPrecondition({
|
|
291
|
-
id: "ready_jskit_app",
|
|
292
|
-
ok: true,
|
|
293
|
-
message: "Session worktree has the required JSKIT app markers."
|
|
294
|
-
})
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
ok: false,
|
|
299
|
-
error: createError({
|
|
300
|
-
code: "app_setup_required",
|
|
301
|
-
message: `Issue sessions require a ready JSKIT app. Missing: ${readiness.missing.join(", ")}.`,
|
|
302
|
-
repairCommand: "Run the app setup flow before starting issue work."
|
|
303
|
-
}),
|
|
304
|
-
precondition: createPrecondition({
|
|
305
|
-
id: "ready_jskit_app",
|
|
306
|
-
ok: false,
|
|
307
|
-
message: "Session worktree has the required JSKIT app markers."
|
|
308
|
-
})
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async function assertActiveCycleExists(paths) {
|
|
313
|
-
const activeCycle = await readTrimmedFile(path.join(paths.sessionRoot, "active_cycle"));
|
|
314
|
-
if (/^\d+$/u.test(activeCycle)) {
|
|
315
|
-
return {
|
|
316
|
-
ok: true,
|
|
317
|
-
precondition: createPrecondition({
|
|
318
|
-
id: "active_cycle_exists",
|
|
319
|
-
ok: true,
|
|
320
|
-
message: "Active cycle marker exists."
|
|
321
|
-
})
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
return {
|
|
325
|
-
ok: false,
|
|
326
|
-
error: createError({
|
|
327
|
-
code: "active_cycle_missing",
|
|
328
|
-
message: "Session active_cycle is missing or invalid."
|
|
329
|
-
}),
|
|
330
|
-
precondition: createPrecondition({
|
|
331
|
-
id: "active_cycle_exists",
|
|
332
|
-
ok: false,
|
|
333
|
-
message: "Active cycle marker exists."
|
|
334
|
-
})
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
async function assertActiveCycleUserCheckPassed(paths) {
|
|
339
|
-
const activeCycle = await readTrimmedFile(path.join(paths.sessionRoot, "active_cycle"));
|
|
340
|
-
if (
|
|
341
|
-
await pathExists(path.join(paths.sessionRoot, "steps", "user_check_completed")) ||
|
|
342
|
-
await pathExists(path.join(paths.sessionRoot, "steps", `cycle_${activeCycle || "001"}`, "user_check_completed"))
|
|
343
|
-
) {
|
|
344
|
-
return {
|
|
345
|
-
ok: true,
|
|
346
|
-
precondition: createPrecondition({
|
|
347
|
-
id: "user_check_passed",
|
|
348
|
-
ok: true,
|
|
349
|
-
message: "The user check has passed."
|
|
350
|
-
})
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
return {
|
|
354
|
-
ok: false,
|
|
355
|
-
error: createError({
|
|
356
|
-
code: "user_check_not_passed",
|
|
357
|
-
message: "Finalization cannot continue until the user check has passed.",
|
|
358
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step --user-check passed`)
|
|
359
|
-
}),
|
|
360
|
-
precondition: createPrecondition({
|
|
361
|
-
id: "user_check_passed",
|
|
362
|
-
ok: false,
|
|
363
|
-
message: "The user check has passed."
|
|
364
|
-
})
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async function assertUserCheckPassed(paths) {
|
|
369
|
-
return assertActiveCycleUserCheckPassed(paths);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async function assertAcceptedChangesCommitted(paths) {
|
|
373
|
-
const recordPath = path.join(paths.sessionRoot, "steps", "changes_committed");
|
|
374
|
-
if (await pathExists(recordPath)) {
|
|
375
|
-
return {
|
|
376
|
-
ok: true,
|
|
377
|
-
precondition: createPrecondition({
|
|
378
|
-
id: "accepted_changes_committed",
|
|
379
|
-
ok: true,
|
|
380
|
-
message: "Accepted changes have been committed."
|
|
381
|
-
})
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
return {
|
|
385
|
-
ok: false,
|
|
386
|
-
error: createError({
|
|
387
|
-
code: "accepted_changes_not_committed",
|
|
388
|
-
message: "Accepted changes must be committed before finalization steps continue.",
|
|
389
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
390
|
-
}),
|
|
391
|
-
precondition: createPrecondition({
|
|
392
|
-
id: "accepted_changes_committed",
|
|
393
|
-
ok: false,
|
|
394
|
-
message: "Accepted changes have been committed."
|
|
395
|
-
})
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function assertActiveCycleStepRecord(paths, {
|
|
400
|
-
code,
|
|
401
|
-
id,
|
|
402
|
-
message,
|
|
403
|
-
stepId
|
|
404
|
-
}) {
|
|
405
|
-
const activeCycle = await readTrimmedFile(path.join(paths.sessionRoot, "active_cycle"));
|
|
406
|
-
if (
|
|
407
|
-
await pathExists(path.join(paths.sessionRoot, "steps", stepId)) ||
|
|
408
|
-
await pathExists(path.join(paths.sessionRoot, "steps", `cycle_${activeCycle || "001"}`, stepId))
|
|
409
|
-
) {
|
|
410
|
-
return {
|
|
411
|
-
ok: true,
|
|
412
|
-
precondition: createPrecondition({
|
|
413
|
-
id,
|
|
414
|
-
ok: true,
|
|
415
|
-
message
|
|
416
|
-
})
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
return {
|
|
420
|
-
ok: false,
|
|
421
|
-
error: createError({
|
|
422
|
-
code,
|
|
423
|
-
message,
|
|
424
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
425
|
-
}),
|
|
426
|
-
precondition: createPrecondition({
|
|
427
|
-
id,
|
|
428
|
-
ok: false,
|
|
429
|
-
message
|
|
430
|
-
})
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async function assertAutomatedChecksPassed(paths) {
|
|
435
|
-
return assertActiveCycleStepRecord(paths, {
|
|
436
|
-
code: "automated_checks_not_passed",
|
|
437
|
-
id: "automated_checks_passed",
|
|
438
|
-
message: "Run automated checks completed successfully.",
|
|
439
|
-
stepId: "automated_checks_run"
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async function assertDeepUiCheckSatisfied(paths) {
|
|
444
|
-
return assertActiveCycleStepRecord(paths, {
|
|
445
|
-
code: "deep_ui_check_not_satisfied",
|
|
446
|
-
id: "deep_ui_check_satisfied",
|
|
447
|
-
message: "Deep UI check is satisfied.",
|
|
448
|
-
stepId: "deep_ui_check_run"
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
async function assertIssueTextExists(paths) {
|
|
453
|
-
const issueText = await readTrimmedFile(path.join(paths.sessionRoot, "issue.md"));
|
|
454
|
-
if (issueText) {
|
|
455
|
-
return {
|
|
456
|
-
ok: true,
|
|
457
|
-
precondition: createPrecondition({
|
|
458
|
-
id: "issue_text_exists",
|
|
459
|
-
ok: true,
|
|
460
|
-
message: "Issue text exists."
|
|
461
|
-
})
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
ok: false,
|
|
466
|
-
error: createError({
|
|
467
|
-
code: "issue_text_missing",
|
|
468
|
-
message: "Cannot create a GitHub issue before issue.md exists.",
|
|
469
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
470
|
-
}),
|
|
471
|
-
precondition: createPrecondition({
|
|
472
|
-
id: "issue_text_exists",
|
|
473
|
-
ok: false,
|
|
474
|
-
message: "Issue text exists."
|
|
475
|
-
})
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async function assertIssueUrlExists(paths) {
|
|
480
|
-
const issueUrl = await readTrimmedFile(path.join(paths.sessionRoot, "issue_url"));
|
|
481
|
-
if (issueUrl) {
|
|
482
|
-
return {
|
|
483
|
-
ok: true,
|
|
484
|
-
precondition: createPrecondition({
|
|
485
|
-
id: "issue_url_exists",
|
|
486
|
-
ok: true,
|
|
487
|
-
message: "GitHub issue URL exists."
|
|
488
|
-
})
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
return {
|
|
492
|
-
ok: false,
|
|
493
|
-
error: createError({
|
|
494
|
-
code: "issue_url_missing",
|
|
495
|
-
message: "Cannot create a plan before the GitHub issue exists.",
|
|
496
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
497
|
-
}),
|
|
498
|
-
precondition: createPrecondition({
|
|
499
|
-
id: "issue_url_exists",
|
|
500
|
-
ok: false,
|
|
501
|
-
message: "GitHub issue URL exists."
|
|
502
|
-
})
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
async function assertBlueprintUpdateSatisfied(paths) {
|
|
507
|
-
if (await pathExists(path.join(paths.sessionRoot, "steps", "blueprint_updated"))) {
|
|
508
|
-
return {
|
|
509
|
-
ok: true,
|
|
510
|
-
precondition: createPrecondition({
|
|
511
|
-
id: "blueprint_update_satisfied",
|
|
512
|
-
ok: true,
|
|
513
|
-
message: "Blueprint update step is complete."
|
|
514
|
-
})
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
return {
|
|
518
|
-
ok: false,
|
|
519
|
-
error: createError({
|
|
520
|
-
code: "blueprint_update_missing",
|
|
521
|
-
message: "Cannot continue before the blueprint update step is complete.",
|
|
522
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
523
|
-
}),
|
|
524
|
-
precondition: createPrecondition({
|
|
525
|
-
id: "blueprint_update_satisfied",
|
|
526
|
-
ok: false,
|
|
527
|
-
message: "Blueprint update step is complete."
|
|
528
|
-
})
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
async function assertPullRequestFileExists(paths) {
|
|
533
|
-
if (await pathExists(path.join(paths.sessionRoot, "pull_request.md"))) {
|
|
534
|
-
return {
|
|
535
|
-
ok: true,
|
|
536
|
-
precondition: createPrecondition({
|
|
537
|
-
id: "pull_request_file_exists",
|
|
538
|
-
ok: true,
|
|
539
|
-
message: "Pull request draft exists."
|
|
540
|
-
})
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
return {
|
|
544
|
-
ok: false,
|
|
545
|
-
error: createError({
|
|
546
|
-
code: "pull_request_file_missing",
|
|
547
|
-
message: "Cannot publish the PR before pull_request.md exists.",
|
|
548
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} create_pull_request_file`)
|
|
549
|
-
}),
|
|
550
|
-
precondition: createPrecondition({
|
|
551
|
-
id: "pull_request_file_exists",
|
|
552
|
-
ok: false,
|
|
553
|
-
message: "Pull request draft exists."
|
|
554
|
-
})
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
async function assertWorktreeExists(paths) {
|
|
559
|
-
if (await hasWorktree(paths)) {
|
|
560
|
-
return {
|
|
561
|
-
ok: true,
|
|
562
|
-
precondition: createPrecondition({
|
|
563
|
-
id: "worktree_exists",
|
|
564
|
-
ok: true,
|
|
565
|
-
message: "Session worktree exists."
|
|
566
|
-
})
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
return {
|
|
570
|
-
ok: false,
|
|
571
|
-
error: createError({
|
|
572
|
-
code: "worktree_missing",
|
|
573
|
-
message: "Session worktree does not exist.",
|
|
574
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} create_worktree`)
|
|
575
|
-
}),
|
|
576
|
-
precondition: createPrecondition({
|
|
577
|
-
id: "worktree_exists",
|
|
578
|
-
ok: false,
|
|
579
|
-
message: "Session worktree exists."
|
|
580
|
-
})
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
async function assertPrUrlExists(paths) {
|
|
585
|
-
const prUrl = await readTrimmedFile(path.join(paths.sessionRoot, "pr_url"));
|
|
586
|
-
if (prUrl) {
|
|
587
|
-
return {
|
|
588
|
-
ok: true,
|
|
589
|
-
precondition: createPrecondition({
|
|
590
|
-
id: "pr_url_exists",
|
|
591
|
-
ok: true,
|
|
592
|
-
message: "PR URL exists."
|
|
593
|
-
})
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
ok: false,
|
|
598
|
-
error: createError({
|
|
599
|
-
code: "pr_url_missing",
|
|
600
|
-
message: "Cannot merge before pr_url exists.",
|
|
601
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
602
|
-
}),
|
|
603
|
-
precondition: createPrecondition({
|
|
604
|
-
id: "pr_url_exists",
|
|
605
|
-
ok: false,
|
|
606
|
-
message: "PR URL exists."
|
|
607
|
-
})
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
async function assertMainCheckoutSyncSatisfied(paths) {
|
|
612
|
-
const recordPath = path.join(paths.sessionRoot, "steps", "main_checkout_synced");
|
|
613
|
-
if (await pathExists(recordPath)) {
|
|
614
|
-
return {
|
|
615
|
-
ok: true,
|
|
616
|
-
precondition: createPrecondition({
|
|
617
|
-
id: "main_checkout_sync_satisfied",
|
|
618
|
-
ok: true,
|
|
619
|
-
message: "Main checkout sync has been completed or skipped."
|
|
620
|
-
})
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
return {
|
|
624
|
-
ok: false,
|
|
625
|
-
error: createError({
|
|
626
|
-
code: "main_checkout_sync_required",
|
|
627
|
-
message: "Main checkout sync must be completed or explicitly skipped before session cleanup.",
|
|
628
|
-
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
629
|
-
}),
|
|
630
|
-
precondition: createPrecondition({
|
|
631
|
-
id: "main_checkout_sync_satisfied",
|
|
632
|
-
ok: false,
|
|
633
|
-
message: "Main checkout sync has been completed or skipped."
|
|
634
|
-
})
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
export {
|
|
639
|
-
applyPreconditions,
|
|
640
|
-
assertAcceptedChangesCommitted,
|
|
641
|
-
assertActiveCycleExists,
|
|
642
|
-
assertActiveCycleUserCheckPassed,
|
|
643
|
-
assertUserCheckPassed,
|
|
644
|
-
assertBlueprintUpdateSatisfied,
|
|
645
|
-
assertDeepUiCheckSatisfied,
|
|
646
|
-
assertDependenciesInstalled,
|
|
647
|
-
assertPullRequestFileExists,
|
|
648
|
-
assertGhAuth,
|
|
649
|
-
assertGitCurrentBranch,
|
|
650
|
-
assertGitRepository,
|
|
651
|
-
assertGithubOrigin,
|
|
652
|
-
assertIssueTextExists,
|
|
653
|
-
assertIssueUrlExists,
|
|
654
|
-
assertAutomatedChecksPassed,
|
|
655
|
-
assertMainCheckoutSyncSatisfied,
|
|
656
|
-
assertPrUrlExists,
|
|
657
|
-
assertReadyJskitApp,
|
|
658
|
-
assertSessionExists,
|
|
659
|
-
assertTargetRootWritable,
|
|
660
|
-
assertWorktreeExists,
|
|
661
|
-
ensureStudioGitExclude,
|
|
662
|
-
hasWorktree
|
|
663
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
PROMPT_DIRECTORY,
|
|
4
|
-
SESSION_STATE_RELATIVE_PATH
|
|
5
|
-
} from "./constants.js";
|
|
6
|
-
import {
|
|
7
|
-
fileExists,
|
|
8
|
-
normalizeText,
|
|
9
|
-
readTextIfExists
|
|
10
|
-
} from "./io.js";
|
|
11
|
-
|
|
12
|
-
async function readPromptTemplate(targetRoot, name) {
|
|
13
|
-
const normalizedName = normalizeText(name);
|
|
14
|
-
const overridePath = path.join(targetRoot, SESSION_STATE_RELATIVE_PATH, "prompts", normalizedName);
|
|
15
|
-
if (await fileExists(overridePath)) {
|
|
16
|
-
return readTextIfExists(overridePath);
|
|
17
|
-
}
|
|
18
|
-
return readTextIfExists(path.join(PROMPT_DIRECTORY, normalizedName));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function renderTemplate(source, values = {}) {
|
|
22
|
-
return String(source || "").replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/gu, (_match, key) => {
|
|
23
|
-
return String(values[key] ?? "");
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function renderPrompt(paths, templateName, values = {}) {
|
|
28
|
-
const template = await readPromptTemplate(paths.targetRoot, templateName);
|
|
29
|
-
return renderTemplate(template, {
|
|
30
|
-
branch: paths.branch,
|
|
31
|
-
session_id: paths.sessionId,
|
|
32
|
-
worktree: paths.worktree,
|
|
33
|
-
...values
|
|
34
|
-
}).trim();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
readPromptTemplate,
|
|
39
|
-
renderPrompt,
|
|
40
|
-
renderTemplate
|
|
41
|
-
};
|