@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.
Files changed (32) hide show
  1. package/package.json +4 -4
  2. package/src/server/appBlueprint.js +37 -14
  3. package/src/server/core/argParser.js +0 -12
  4. package/src/server/core/commandCatalog.js +2 -92
  5. package/src/server/core/createCommandHandlers.js +0 -3
  6. package/src/server/helperMap.js +214 -56
  7. package/src/server/index.js +0 -1
  8. package/src/server/{sessionRuntime/prompts → prompts}/app_blueprint.md +1 -1
  9. package/src/server/commandHandlers/session.js +0 -471
  10. package/src/server/sessionRuntime/appReadiness.js +0 -55
  11. package/src/server/sessionRuntime/constants.js +0 -377
  12. package/src/server/sessionRuntime/io.js +0 -97
  13. package/src/server/sessionRuntime/paths.js +0 -163
  14. package/src/server/sessionRuntime/preconditions.js +0 -663
  15. package/src/server/sessionRuntime/promptRenderer.js +0 -41
  16. package/src/server/sessionRuntime/prompts/automated_checks_run.md +0 -28
  17. package/src/server/sessionRuntime/prompts/blueprint_updated.md +0 -29
  18. package/src/server/sessionRuntime/prompts/deep_ui_check_run.md +0 -40
  19. package/src/server/sessionRuntime/prompts/final_comment.md +0 -10
  20. package/src/server/sessionRuntime/prompts/final_report_created.md +0 -44
  21. package/src/server/sessionRuntime/prompts/issue_created.md +0 -26
  22. package/src/server/sessionRuntime/prompts/issue_prompt_rendered.md +0 -1
  23. package/src/server/sessionRuntime/prompts/make_plan.md +0 -57
  24. package/src/server/sessionRuntime/prompts/plan_executed.md +0 -39
  25. package/src/server/sessionRuntime/prompts/pr_failure.md +0 -28
  26. package/src/server/sessionRuntime/prompts/pr_merge_prepared.md +0 -22
  27. package/src/server/sessionRuntime/prompts/review_changes_accepted_resolve.md +0 -12
  28. package/src/server/sessionRuntime/prompts/review_prompt_rendered.md +0 -61
  29. package/src/server/sessionRuntime/prompts/user_check_completed.md +0 -17
  30. package/src/server/sessionRuntime/responses.js +0 -1481
  31. package/src/server/sessionRuntime/worktrees.js +0 -31
  32. package/src/server/sessionRuntime.js +0 -3659
@@ -1,471 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import {
3
- abandonSession,
4
- advanceSessionStep,
5
- adoptCodexThreadId,
6
- buildSessionErrorResponse,
7
- createSession,
8
- inspectSessionDiff,
9
- inspectSessionDetails,
10
- listSessions,
11
- rewindSession,
12
- runSessionStep,
13
- runSessionStepAction
14
- } from "../sessionRuntime.js";
15
-
16
- function writeJson(stdout, payload) {
17
- stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
18
- }
19
-
20
- function writeSessionText(stdout, payload) {
21
- if (payload.sessions) {
22
- stdout.write("JSKIT sessions\n");
23
- if (payload.sessions.length < 1) {
24
- stdout.write("No sessions found.\n");
25
- return;
26
- }
27
- for (const session of payload.sessions) {
28
- stdout.write(`- ${session.sessionId} ${session.status} ${session.currentStep || "done"}\n`);
29
- }
30
- return;
31
- }
32
-
33
- stdout.write(`Session: ${payload.sessionId || "unknown"}\n`);
34
- stdout.write(`Status: ${payload.status || "unknown"}\n`);
35
- stdout.write(`Current step: ${payload.currentStep || "done"}\n`);
36
- if (payload.issueUrl) {
37
- stdout.write(`Issue: ${payload.issueUrl}\n`);
38
- }
39
- if (payload.prUrl) {
40
- stdout.write(`PR: ${payload.prUrl}\n`);
41
- }
42
- if (payload.branch) {
43
- stdout.write(`Branch: ${payload.branch}\n`);
44
- }
45
- if (payload.worktree) {
46
- stdout.write(`Worktree: ${payload.worktree}\n`);
47
- }
48
- if (payload.completedSteps?.length) {
49
- stdout.write("Done steps:\n");
50
- for (const step of payload.completedSteps) {
51
- stdout.write(`- ${step}\n`);
52
- }
53
- }
54
- if (payload.prompt) {
55
- stdout.write("\n");
56
- stdout.write(payload.prompt);
57
- stdout.write("\n");
58
- }
59
- if (payload.gitStatus !== undefined && payload.unstagedDiff !== undefined) {
60
- stdout.write("\nGit status:\n");
61
- stdout.write(payload.gitStatus || "No changes.");
62
- stdout.write("\n");
63
- const diff = [payload.stagedDiff, payload.unstagedDiff, payload.untrackedDiff].filter(Boolean).join("\n");
64
- if (diff) {
65
- stdout.write("\nDiff:\n");
66
- stdout.write(diff);
67
- stdout.write("\n");
68
- }
69
- }
70
- if (payload.errors?.length) {
71
- stdout.write("Errors:\n");
72
- for (const error of payload.errors) {
73
- stdout.write(`- [${error.code}] ${error.message}\n`);
74
- if (error.repairCommand) {
75
- stdout.write(` Repair: ${error.repairCommand}\n`);
76
- }
77
- }
78
- }
79
- if (payload.nextCommand) {
80
- stdout.write(`Next: ${payload.nextCommand}\n`);
81
- }
82
- if (payload.actionCommands?.length) {
83
- stdout.write("Available commands:\n");
84
- for (const command of payload.actionCommands) {
85
- stdout.write(`- ${command.command}\n`);
86
- }
87
- }
88
- }
89
-
90
- async function readStream(stream) {
91
- const chunks = [];
92
- for await (const chunk of stream) {
93
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
94
- }
95
- return Buffer.concat(chunks).toString("utf8");
96
- }
97
-
98
- function resolveInputFilePath(cwd, filePath) {
99
- return filePath.startsWith("/") ? filePath : `${cwd}/${filePath}`;
100
- }
101
-
102
- async function resolveTextInput({
103
- codePrefix,
104
- fileOption,
105
- inlineOptions = {},
106
- io = {},
107
- repairCommand,
108
- cwd,
109
- stdinOption,
110
- textOption,
111
- sessionId
112
- }) {
113
- if (Object.hasOwn(inlineOptions, fileOption)) {
114
- const inputFile = String(inlineOptions[fileOption] || "").trim();
115
- if (!inputFile) {
116
- return { ok: true, value: "" };
117
- }
118
- const resolvedInputFile = resolveInputFilePath(cwd, inputFile);
119
- try {
120
- return {
121
- ok: true,
122
- value: await readFile(resolvedInputFile, "utf8")
123
- };
124
- } catch (error) {
125
- return {
126
- ok: false,
127
- payload: buildSessionErrorResponse({
128
- targetRoot: cwd,
129
- sessionId,
130
- code: `${codePrefix}_file_read_failed`,
131
- message: `Could not read ${codePrefix.replaceAll("_", " ")} file ${resolvedInputFile}: ${error.message}`,
132
- repairCommand
133
- })
134
- };
135
- }
136
- }
137
- if (Object.hasOwn(inlineOptions, textOption)) {
138
- const textValue = String(inlineOptions[textOption] ?? "");
139
- if (textValue === stdinOption) {
140
- return {
141
- ok: true,
142
- value: await readStream(io.stdin)
143
- };
144
- }
145
- return {
146
- ok: true,
147
- value: textValue
148
- };
149
- }
150
- return { ok: true, value: "" };
151
- }
152
-
153
- async function resolveStepInputs({
154
- inlineOptions = {},
155
- io = {},
156
- cwd,
157
- sessionId
158
- }) {
159
- if (Object.hasOwn(inlineOptions, "blueprint") || Object.hasOwn(inlineOptions, "blueprint-file")) {
160
- return {
161
- ok: false,
162
- payload: buildSessionErrorResponse({
163
- targetRoot: cwd,
164
- sessionId,
165
- code: "session_blueprint_input_removed",
166
- message: "The session blueprint step no longer accepts --blueprint input. Run the step to get the Codex prompt, let Codex edit .jskit/APP_BLUEPRINT.md, then run the step again.",
167
- repairCommand: `jskit session ${sessionId} step`
168
- })
169
- };
170
- }
171
-
172
- const reworkNotes = await resolveTextInput({
173
- codePrefix: "rework_notes",
174
- fileOption: "rework-notes-file",
175
- inlineOptions,
176
- io,
177
- repairCommand: `jskit session ${sessionId} step --user-check failed --rework-notes -`,
178
- cwd,
179
- sessionId,
180
- stdinOption: "-",
181
- textOption: "rework-notes"
182
- });
183
- if (reworkNotes.ok === false) {
184
- return reworkNotes;
185
- }
186
-
187
- const skipReason = await resolveTextInput({
188
- codePrefix: "skip_reason",
189
- fileOption: "skip-reason-file",
190
- inlineOptions,
191
- io,
192
- repairCommand: `jskit session ${sessionId} skip --skip-reason "<reason>"`,
193
- cwd,
194
- sessionId,
195
- stdinOption: "-",
196
- textOption: "skip-reason"
197
- });
198
- if (skipReason.ok === false) {
199
- return skipReason;
200
- }
201
-
202
- return {
203
- ok: true,
204
- reworkNotes: reworkNotes.value,
205
- skipReason: skipReason.value
206
- };
207
- }
208
-
209
- function normalizeStepOptions(inlineOptions = {}) {
210
- const options = {
211
- ...inlineOptions,
212
- mergePr: inlineOptions["merge-pr"] === "true" || inlineOptions.mergePr === true,
213
- prompt: inlineOptions.prompt,
214
- reviewFindings: inlineOptions["review-findings"] || inlineOptions.reviewFindings,
215
- resolveDeslop: inlineOptions["resolve-deslop"] === "true" || inlineOptions.resolveDeslop === true,
216
- skipStep: inlineOptions["skip-step"] === "true" ||
217
- inlineOptions.skipStep === true ||
218
- inlineOptions.skip === true ||
219
- inlineOptions.skip === "true",
220
- userCheck: inlineOptions["user-check"] || inlineOptions.userCheck
221
- };
222
- if (Object.hasOwn(inlineOptions, "review-findings-remaining") || Object.hasOwn(inlineOptions, "reviewFindingsRemaining")) {
223
- options.reviewFindingsRemaining = inlineOptions["review-findings-remaining"] === "true" ||
224
- inlineOptions.reviewFindingsRemaining === true;
225
- }
226
- return options;
227
- }
228
-
229
- function resolveListArchiveOption(options = {}) {
230
- const archives = [];
231
- if (options.abandoned) {
232
- archives.push("abandoned");
233
- }
234
- if (options.completed) {
235
- archives.push("completed");
236
- }
237
- if (options.all) {
238
- archives.push("all");
239
- }
240
- return archives.length > 0 ? archives : "active";
241
- }
242
-
243
- async function runSessionStepCommand({
244
- cwd,
245
- inlineOptions = {},
246
- io = {},
247
- sessionId
248
- }) {
249
- const stepInputs = await resolveStepInputs({
250
- inlineOptions,
251
- io,
252
- cwd,
253
- sessionId
254
- });
255
- return stepInputs.ok === false
256
- ? stepInputs.payload
257
- : runSessionStep({
258
- targetRoot: cwd,
259
- sessionId,
260
- options: {
261
- ...normalizeStepOptions(inlineOptions),
262
- reworkNotes: stepInputs.reworkNotes,
263
- skipReason: stepInputs.skipReason
264
- }
265
- });
266
- }
267
-
268
- async function runNextSessionStep({
269
- cwd,
270
- sessionId
271
- }) {
272
- return advanceSessionStep({
273
- targetRoot: cwd,
274
- sessionId
275
- });
276
- }
277
-
278
- async function runSkipSessionStep({
279
- cwd,
280
- inlineOptions = {},
281
- io = {},
282
- sessionId
283
- }) {
284
- return runSessionStepCommand({
285
- cwd,
286
- inlineOptions: {
287
- ...inlineOptions,
288
- skipStep: true
289
- },
290
- io,
291
- sessionId
292
- });
293
- }
294
-
295
- async function runDeslopSessionStep({
296
- cwd,
297
- sessionId
298
- }) {
299
- const details = await inspectSessionDetails({
300
- targetRoot: cwd,
301
- sessionId
302
- });
303
- if (details.ok === false) {
304
- return details;
305
- }
306
- if (details.currentStep === "review_changes_accepted") {
307
- const accepted = await runSessionStep({
308
- targetRoot: cwd,
309
- sessionId,
310
- options: {
311
- reviewFindingsRemaining: true
312
- }
313
- });
314
- if (accepted.ok === false) {
315
- return accepted;
316
- }
317
- } else if (details.currentStep !== "review_prompt_rendered") {
318
- return buildSessionErrorResponse({
319
- targetRoot: cwd,
320
- sessionId,
321
- code: "deslop_not_current_step",
322
- message: `Cannot run deslop while current step is ${details.currentStep || "done"}.`,
323
- repairCommand: `jskit session ${sessionId}`
324
- });
325
- }
326
- return runSessionStep({
327
- targetRoot: cwd,
328
- sessionId,
329
- options: {}
330
- });
331
- }
332
-
333
- function createSessionCommands() {
334
- return {
335
- async commandSession({
336
- positional = [],
337
- options = {},
338
- cwd,
339
- stdout,
340
- io = {}
341
- } = {}) {
342
- const [first, second, third] = positional;
343
- const inlineOptions = options.inlineOptions || {};
344
- let payload;
345
-
346
- if (!first) {
347
- payload = await listSessions({
348
- targetRoot: cwd,
349
- archive: resolveListArchiveOption(options)
350
- });
351
- } else if (first === "create" || first === "new") {
352
- payload = await createSession({ targetRoot: cwd });
353
- } else if (second === "step") {
354
- payload = await inspectSessionDetails({
355
- targetRoot: cwd,
356
- sessionId: first
357
- });
358
- } else if (second === "run") {
359
- const details = await inspectSessionDetails({
360
- targetRoot: cwd,
361
- sessionId: first
362
- });
363
- payload = {
364
- ...details,
365
- ok: false,
366
- errors: [
367
- {
368
- code: "explicit_session_action_required",
369
- message: "Generic run is not a session action. Use one of the available step commands, then run next."
370
- }
371
- ]
372
- };
373
- } else if ([
374
- "create_worktree",
375
- "run_npm_install",
376
- "define_issue",
377
- "create_issue_file",
378
- "create_issue_on_gh",
379
- "make_plan",
380
- "execute_plan",
381
- "run_deep_ui_check",
382
- "run_automated_checks",
383
- "update_blueprint",
384
- "commit_changes",
385
- "create_pull_request_file",
386
- "create_pr_on_gh",
387
- "prepare_for_merge",
388
- "merge_pr",
389
- "sync_main_checkout",
390
- "finish_session"
391
- ].includes(second)) {
392
- payload = await runSessionStepAction({
393
- action: second,
394
- targetRoot: cwd,
395
- sessionId: first,
396
- options: normalizeStepOptions(inlineOptions)
397
- });
398
- } else if (second === "next") {
399
- payload = await runNextSessionStep({
400
- cwd,
401
- sessionId: first
402
- });
403
- } else if (second === "skip") {
404
- payload = await runSkipSessionStep({
405
- cwd,
406
- inlineOptions,
407
- io,
408
- sessionId: first
409
- });
410
- } else if (second === "deslop") {
411
- payload = await runDeslopSessionStep({
412
- cwd,
413
- sessionId: first
414
- });
415
- } else if (second === "resolve-deslop") {
416
- payload = await runSessionStepAction({
417
- action: "resolve_deslop",
418
- targetRoot: cwd,
419
- sessionId: first,
420
- options: {
421
- resolveDeslop: true
422
- }
423
- });
424
- } else if (second === "abandon") {
425
- payload = await abandonSession({
426
- targetRoot: cwd,
427
- sessionId: first
428
- });
429
- } else if (second === "diff") {
430
- payload = await inspectSessionDiff({
431
- targetRoot: cwd,
432
- sessionId: first
433
- });
434
- } else if (second === "rewind") {
435
- payload = await rewindSession({
436
- targetRoot: cwd,
437
- sessionId: first,
438
- stepId: inlineOptions.step || third
439
- });
440
- } else if (second === "adopt-codex-thread") {
441
- payload = await adoptCodexThreadId({
442
- targetRoot: cwd,
443
- sessionId: first,
444
- codexThreadId: inlineOptions["codex-thread-id"] || inlineOptions.codexThreadId
445
- });
446
- } else if (!second) {
447
- payload = await inspectSessionDetails({
448
- targetRoot: cwd,
449
- sessionId: first
450
- });
451
- } else {
452
- payload = buildSessionErrorResponse({
453
- targetRoot: cwd,
454
- sessionId: first,
455
- code: "unknown_session_subcommand",
456
- message: `Unknown session subcommand: ${second}`,
457
- repairCommand: `jskit session ${first}`
458
- });
459
- }
460
-
461
- if (options.json) {
462
- writeJson(stdout, payload);
463
- } else {
464
- writeSessionText(stdout, payload);
465
- }
466
- return payload.ok === false ? 1 : 0;
467
- }
468
- };
469
- }
470
-
471
- export { createSessionCommands };
@@ -1,55 +0,0 @@
1
- import { access, stat } from "node:fs/promises";
2
- import { constants as fsConstants } from "node:fs";
3
- import path from "node:path";
4
-
5
- const REQUIRED_READY_FILES = Object.freeze([
6
- "package.json",
7
- ".jskit/lock.json",
8
- "config/public.js"
9
- ]);
10
- const REQUIRED_READY_DIRECTORIES = Object.freeze([
11
- "src",
12
- "packages"
13
- ]);
14
-
15
- async function pathExists(filePath) {
16
- try {
17
- await access(filePath, fsConstants.F_OK);
18
- return true;
19
- } catch {
20
- return false;
21
- }
22
- }
23
-
24
- async function directoryExists(filePath) {
25
- try {
26
- return (await stat(filePath)).isDirectory();
27
- } catch {
28
- return false;
29
- }
30
- }
31
-
32
- async function inspectReadyJskitAppRoot(rootPath) {
33
- const missing = [];
34
- for (const relativePath of REQUIRED_READY_FILES) {
35
- if (!await pathExists(path.join(rootPath, relativePath))) {
36
- missing.push(relativePath);
37
- }
38
- }
39
- for (const relativePath of REQUIRED_READY_DIRECTORIES) {
40
- if (!await directoryExists(path.join(rootPath, relativePath))) {
41
- missing.push(`${relativePath}/`);
42
- }
43
- }
44
- return {
45
- missing,
46
- ok: missing.length < 1,
47
- requiredDirectories: [...REQUIRED_READY_DIRECTORIES],
48
- requiredFiles: [...REQUIRED_READY_FILES],
49
- rootPath
50
- };
51
- }
52
-
53
- export {
54
- inspectReadyJskitAppRoot
55
- };