@jskit-ai/jskit-cli 0.2.81 → 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.
- package/package.json +6 -4
- package/src/server/appBlueprint.js +1 -1
- package/src/server/commandHandlers/helperMap.js +104 -0
- package/src/server/commandHandlers/session.js +110 -3
- package/src/server/commandHandlers/show.js +169 -34
- package/src/server/core/argParser.js +8 -0
- package/src/server/core/commandCatalog.js +58 -2
- package/src/server/core/createCommandHandlers.js +4 -1
- package/src/server/helperMap.js +463 -0
- package/src/server/helperMapPaths.js +7 -0
- package/src/server/sessionRuntime/appReadiness.js +55 -0
- package/src/server/sessionRuntime/constants.js +217 -78
- package/src/server/sessionRuntime/preconditions.js +382 -5
- package/src/server/sessionRuntime/promptRenderer.js +15 -2
- package/src/server/sessionRuntime/prompts/automated_checks.md +42 -0
- package/src/server/sessionRuntime/prompts/deep_ui_check.md +53 -0
- package/src/server/sessionRuntime/prompts/doctor_failure.md +11 -2
- package/src/server/sessionRuntime/prompts/execute_plan.md +32 -6
- package/src/server/sessionRuntime/prompts/final_comment.md +3 -1
- package/src/server/sessionRuntime/prompts/issue_details.md +52 -0
- package/src/server/sessionRuntime/prompts/new_issue.md +15 -2
- package/src/server/sessionRuntime/prompts/plan_issue.md +40 -9
- package/src/server/sessionRuntime/prompts/review_changes.md +46 -5
- package/src/server/sessionRuntime/prompts/update_blueprint.md +36 -0
- package/src/server/sessionRuntime/prompts/user_check.md +15 -1
- package/src/server/sessionRuntime/responses.js +776 -56
- package/src/server/sessionRuntime.js +1658 -123
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { constants as fsConstants } from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import {
|
|
9
|
+
JSKIT_CLI_SHELL_COMMAND,
|
|
9
10
|
SESSION_STATE_RELATIVE_PATH
|
|
10
11
|
} from "./constants.js";
|
|
11
12
|
import {
|
|
@@ -24,6 +25,13 @@ import {
|
|
|
24
25
|
import {
|
|
25
26
|
hasWorktree
|
|
26
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
|
+
}
|
|
27
35
|
|
|
28
36
|
async function assertTargetRootWritable(targetRoot) {
|
|
29
37
|
try {
|
|
@@ -228,7 +236,7 @@ async function assertSessionExists(paths) {
|
|
|
228
236
|
error: createError({
|
|
229
237
|
code: "session_missing",
|
|
230
238
|
message: `Session does not exist: ${paths.sessionId}`,
|
|
231
|
-
repairCommand: "
|
|
239
|
+
repairCommand: jskitCommand("session create")
|
|
232
240
|
}),
|
|
233
241
|
precondition: createPrecondition({
|
|
234
242
|
id: "session_exists",
|
|
@@ -238,6 +246,255 @@ async function assertSessionExists(paths) {
|
|
|
238
246
|
};
|
|
239
247
|
}
|
|
240
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} step`)
|
|
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
|
+
const receiptPath = path.join(paths.sessionRoot, "steps", `cycle_${activeCycle}`, "user_check_completed");
|
|
341
|
+
if (await pathExists(receiptPath)) {
|
|
342
|
+
return {
|
|
343
|
+
ok: true,
|
|
344
|
+
precondition: createPrecondition({
|
|
345
|
+
id: "active_cycle_user_check_passed",
|
|
346
|
+
ok: true,
|
|
347
|
+
message: "The active cycle user check has passed."
|
|
348
|
+
})
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
ok: false,
|
|
353
|
+
error: createError({
|
|
354
|
+
code: "user_check_not_passed",
|
|
355
|
+
message: "Finalization cannot continue until the active cycle user check has passed.",
|
|
356
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --user-check passed`)
|
|
357
|
+
}),
|
|
358
|
+
precondition: createPrecondition({
|
|
359
|
+
id: "active_cycle_user_check_passed",
|
|
360
|
+
ok: false,
|
|
361
|
+
message: "The active cycle user check has passed."
|
|
362
|
+
})
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function assertAcceptedChangesCommitted(paths) {
|
|
367
|
+
const receiptPath = path.join(paths.sessionRoot, "steps", "changes_committed");
|
|
368
|
+
if (await pathExists(receiptPath)) {
|
|
369
|
+
return {
|
|
370
|
+
ok: true,
|
|
371
|
+
precondition: createPrecondition({
|
|
372
|
+
id: "accepted_changes_committed",
|
|
373
|
+
ok: true,
|
|
374
|
+
message: "Accepted changes have been committed."
|
|
375
|
+
})
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
ok: false,
|
|
380
|
+
error: createError({
|
|
381
|
+
code: "accepted_changes_not_committed",
|
|
382
|
+
message: "Accepted changes must be committed before app memory and finalization steps continue.",
|
|
383
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
384
|
+
}),
|
|
385
|
+
precondition: createPrecondition({
|
|
386
|
+
id: "accepted_changes_committed",
|
|
387
|
+
ok: false,
|
|
388
|
+
message: "Accepted changes have been committed."
|
|
389
|
+
})
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function assertActiveCycleStepReceipt(paths, {
|
|
394
|
+
code,
|
|
395
|
+
id,
|
|
396
|
+
message,
|
|
397
|
+
stepId
|
|
398
|
+
}) {
|
|
399
|
+
const activeCycle = await readTrimmedFile(path.join(paths.sessionRoot, "active_cycle"));
|
|
400
|
+
const receiptPath = path.join(paths.sessionRoot, "steps", `cycle_${activeCycle}`, stepId);
|
|
401
|
+
if (await pathExists(receiptPath)) {
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
precondition: createPrecondition({
|
|
405
|
+
id,
|
|
406
|
+
ok: true,
|
|
407
|
+
message
|
|
408
|
+
})
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
error: createError({
|
|
414
|
+
code,
|
|
415
|
+
message,
|
|
416
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
417
|
+
}),
|
|
418
|
+
precondition: createPrecondition({
|
|
419
|
+
id,
|
|
420
|
+
ok: false,
|
|
421
|
+
message
|
|
422
|
+
})
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function assertAutomatedChecksPassed(paths) {
|
|
427
|
+
return assertActiveCycleStepReceipt(paths, {
|
|
428
|
+
code: "automated_checks_not_passed",
|
|
429
|
+
id: "automated_checks_passed",
|
|
430
|
+
message: "Automated checks have passed.",
|
|
431
|
+
stepId: "automated_checks_run"
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function assertDeepUiCheckSatisfied(paths) {
|
|
436
|
+
return assertActiveCycleStepReceipt(paths, {
|
|
437
|
+
code: "deep_ui_check_not_satisfied",
|
|
438
|
+
id: "deep_ui_check_satisfied",
|
|
439
|
+
message: "Deep UI check is satisfied.",
|
|
440
|
+
stepId: "deep_ui_check_run"
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async function assertIssueMetadataExists(paths) {
|
|
445
|
+
const source = await readTextIfExists(path.join(paths.sessionRoot, "issue_metadata.json"));
|
|
446
|
+
if (!source) {
|
|
447
|
+
return {
|
|
448
|
+
ok: false,
|
|
449
|
+
error: createError({
|
|
450
|
+
code: "issue_metadata_missing",
|
|
451
|
+
message: "Cannot continue before issue_metadata.json records the issue category and UI impact.",
|
|
452
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --issue-details -`)
|
|
453
|
+
}),
|
|
454
|
+
precondition: createPrecondition({
|
|
455
|
+
id: "issue_metadata_exists",
|
|
456
|
+
ok: false,
|
|
457
|
+
message: "Issue metadata records issue category and UI impact."
|
|
458
|
+
})
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
let metadata = null;
|
|
463
|
+
try {
|
|
464
|
+
metadata = JSON.parse(source);
|
|
465
|
+
} catch {
|
|
466
|
+
metadata = null;
|
|
467
|
+
}
|
|
468
|
+
const issueCategory = String(metadata?.issueCategory || "").trim().toLowerCase();
|
|
469
|
+
const uiImpact = String(metadata?.uiImpact || "").trim().toLowerCase();
|
|
470
|
+
const validIssueCategories = new Set(["client", "server", "client_server", "tooling", "unknown"]);
|
|
471
|
+
const validUiImpacts = new Set(["none", "possible", "definite", "unknown"]);
|
|
472
|
+
if (validIssueCategories.has(issueCategory) && validUiImpacts.has(uiImpact)) {
|
|
473
|
+
return {
|
|
474
|
+
ok: true,
|
|
475
|
+
precondition: createPrecondition({
|
|
476
|
+
id: "issue_metadata_exists",
|
|
477
|
+
ok: true,
|
|
478
|
+
message: "Issue metadata records issue category and UI impact."
|
|
479
|
+
})
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
ok: false,
|
|
485
|
+
error: createError({
|
|
486
|
+
code: "issue_metadata_invalid",
|
|
487
|
+
message: "issue_metadata.json must include a valid issueCategory and uiImpact.",
|
|
488
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --issue-details -`)
|
|
489
|
+
}),
|
|
490
|
+
precondition: createPrecondition({
|
|
491
|
+
id: "issue_metadata_exists",
|
|
492
|
+
ok: false,
|
|
493
|
+
message: "Issue metadata records issue category and UI impact."
|
|
494
|
+
})
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
241
498
|
async function assertIssueTextExists(paths) {
|
|
242
499
|
const issueText = await readTrimmedFile(path.join(paths.sessionRoot, "issue.md"));
|
|
243
500
|
if (issueText) {
|
|
@@ -255,7 +512,7 @@ async function assertIssueTextExists(paths) {
|
|
|
255
512
|
error: createError({
|
|
256
513
|
code: "issue_text_missing",
|
|
257
514
|
message: "Cannot create a GitHub issue before issue.md exists.",
|
|
258
|
-
repairCommand: `
|
|
515
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --issue -`)
|
|
259
516
|
}),
|
|
260
517
|
precondition: createPrecondition({
|
|
261
518
|
id: "issue_text_exists",
|
|
@@ -282,7 +539,7 @@ async function assertIssueUrlExists(paths) {
|
|
|
282
539
|
error: createError({
|
|
283
540
|
code: "issue_url_missing",
|
|
284
541
|
message: "Cannot create a plan before the GitHub issue exists.",
|
|
285
|
-
repairCommand: `
|
|
542
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
286
543
|
}),
|
|
287
544
|
precondition: createPrecondition({
|
|
288
545
|
id: "issue_url_exists",
|
|
@@ -292,6 +549,114 @@ async function assertIssueUrlExists(paths) {
|
|
|
292
549
|
};
|
|
293
550
|
}
|
|
294
551
|
|
|
552
|
+
async function assertPlanTextExists(paths) {
|
|
553
|
+
const activeCycle = await readTrimmedFile(path.join(paths.sessionRoot, "active_cycle"));
|
|
554
|
+
const planPath = path.join(paths.sessionRoot, "cycles", `cycle_${activeCycle || "001"}`, "plan.md");
|
|
555
|
+
const planText = await readTrimmedFile(planPath);
|
|
556
|
+
if (planText) {
|
|
557
|
+
return {
|
|
558
|
+
ok: true,
|
|
559
|
+
precondition: createPrecondition({
|
|
560
|
+
id: "plan_text_exists",
|
|
561
|
+
ok: true,
|
|
562
|
+
message: "Plan text exists."
|
|
563
|
+
})
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
ok: false,
|
|
568
|
+
error: createError({
|
|
569
|
+
code: "plan_text_missing",
|
|
570
|
+
message: "Cannot execute a plan before the active cycle plan exists.",
|
|
571
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --plan -`)
|
|
572
|
+
}),
|
|
573
|
+
precondition: createPrecondition({
|
|
574
|
+
id: "plan_text_exists",
|
|
575
|
+
ok: false,
|
|
576
|
+
message: "Plan text exists."
|
|
577
|
+
})
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async function assertIssueDetailsExists(paths) {
|
|
582
|
+
const issueDetails = await readTrimmedFile(path.join(paths.sessionRoot, "issue_details.md"));
|
|
583
|
+
if (issueDetails) {
|
|
584
|
+
return {
|
|
585
|
+
ok: true,
|
|
586
|
+
precondition: createPrecondition({
|
|
587
|
+
id: "issue_details_exists",
|
|
588
|
+
ok: true,
|
|
589
|
+
message: "Issue details exist."
|
|
590
|
+
})
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
return {
|
|
594
|
+
ok: false,
|
|
595
|
+
error: createError({
|
|
596
|
+
code: "issue_details_missing",
|
|
597
|
+
message: "Cannot create a plan before issue_details.md exists.",
|
|
598
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step --issue-details -`)
|
|
599
|
+
}),
|
|
600
|
+
precondition: createPrecondition({
|
|
601
|
+
id: "issue_details_exists",
|
|
602
|
+
ok: false,
|
|
603
|
+
message: "Issue details exist."
|
|
604
|
+
})
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function assertBlueprintUpdateSatisfied(paths) {
|
|
609
|
+
if (await pathExists(path.join(paths.sessionRoot, "steps", "blueprint_updated"))) {
|
|
610
|
+
return {
|
|
611
|
+
ok: true,
|
|
612
|
+
precondition: createPrecondition({
|
|
613
|
+
id: "blueprint_update_satisfied",
|
|
614
|
+
ok: true,
|
|
615
|
+
message: "Blueprint update step is complete."
|
|
616
|
+
})
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
ok: false,
|
|
621
|
+
error: createError({
|
|
622
|
+
code: "blueprint_update_missing",
|
|
623
|
+
message: "Cannot continue before the blueprint update step is complete.",
|
|
624
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
625
|
+
}),
|
|
626
|
+
precondition: createPrecondition({
|
|
627
|
+
id: "blueprint_update_satisfied",
|
|
628
|
+
ok: false,
|
|
629
|
+
message: "Blueprint update step is complete."
|
|
630
|
+
})
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function assertFinalReportExists(paths) {
|
|
635
|
+
if (await pathExists(path.join(paths.sessionRoot, "final_report.md"))) {
|
|
636
|
+
return {
|
|
637
|
+
ok: true,
|
|
638
|
+
precondition: createPrecondition({
|
|
639
|
+
id: "final_report_exists",
|
|
640
|
+
ok: true,
|
|
641
|
+
message: "Final report exists."
|
|
642
|
+
})
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
ok: false,
|
|
647
|
+
error: createError({
|
|
648
|
+
code: "final_report_missing",
|
|
649
|
+
message: "Cannot publish the PR before final_report.md exists.",
|
|
650
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
651
|
+
}),
|
|
652
|
+
precondition: createPrecondition({
|
|
653
|
+
id: "final_report_exists",
|
|
654
|
+
ok: false,
|
|
655
|
+
message: "Final report exists."
|
|
656
|
+
})
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
295
660
|
async function assertWorktreeExists(paths) {
|
|
296
661
|
if (await hasWorktree(paths)) {
|
|
297
662
|
return {
|
|
@@ -308,7 +673,7 @@ async function assertWorktreeExists(paths) {
|
|
|
308
673
|
error: createError({
|
|
309
674
|
code: "worktree_missing",
|
|
310
675
|
message: "Session worktree does not exist.",
|
|
311
|
-
repairCommand: `
|
|
676
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
312
677
|
}),
|
|
313
678
|
precondition: createPrecondition({
|
|
314
679
|
id: "worktree_exists",
|
|
@@ -335,7 +700,7 @@ async function assertPrUrlExists(paths) {
|
|
|
335
700
|
error: createError({
|
|
336
701
|
code: "pr_url_missing",
|
|
337
702
|
message: "Cannot merge before pr_url exists.",
|
|
338
|
-
repairCommand: `
|
|
703
|
+
repairCommand: jskitCommand(`session ${paths.sessionId} step`)
|
|
339
704
|
}),
|
|
340
705
|
precondition: createPrecondition({
|
|
341
706
|
id: "pr_url_exists",
|
|
@@ -347,13 +712,25 @@ async function assertPrUrlExists(paths) {
|
|
|
347
712
|
|
|
348
713
|
export {
|
|
349
714
|
applyPreconditions,
|
|
715
|
+
assertAcceptedChangesCommitted,
|
|
716
|
+
assertActiveCycleExists,
|
|
717
|
+
assertActiveCycleUserCheckPassed,
|
|
718
|
+
assertBlueprintUpdateSatisfied,
|
|
719
|
+
assertDeepUiCheckSatisfied,
|
|
720
|
+
assertDependenciesInstalled,
|
|
721
|
+
assertFinalReportExists,
|
|
350
722
|
assertGhAuth,
|
|
351
723
|
assertGitCurrentBranch,
|
|
352
724
|
assertGitRepository,
|
|
353
725
|
assertGithubOrigin,
|
|
726
|
+
assertIssueMetadataExists,
|
|
354
727
|
assertIssueTextExists,
|
|
355
728
|
assertIssueUrlExists,
|
|
729
|
+
assertAutomatedChecksPassed,
|
|
730
|
+
assertIssueDetailsExists,
|
|
731
|
+
assertPlanTextExists,
|
|
356
732
|
assertPrUrlExists,
|
|
733
|
+
assertReadyJskitApp,
|
|
357
734
|
assertSessionExists,
|
|
358
735
|
assertTargetRootWritable,
|
|
359
736
|
assertWorktreeExists,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import {
|
|
3
|
+
JSKIT_CLI_SHELL_RULE,
|
|
3
4
|
PROMPT_DIRECTORY,
|
|
4
5
|
SESSION_STATE_RELATIVE_PATH
|
|
5
6
|
} from "./constants.js";
|
|
@@ -24,9 +25,20 @@ function renderTemplate(source, values = {}) {
|
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
function withShellCommandRule(template) {
|
|
29
|
+
const body = String(template || "").trim();
|
|
30
|
+
if (!body) {
|
|
31
|
+
return JSKIT_CLI_SHELL_RULE;
|
|
32
|
+
}
|
|
33
|
+
if (body.includes("When running JSKIT CLI commands from the shell")) {
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
return `${JSKIT_CLI_SHELL_RULE}\n\n---\n\n${body}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
async function renderPrompt(paths, templateName, values = {}) {
|
|
28
40
|
const template = await readPromptTemplate(paths.targetRoot, templateName);
|
|
29
|
-
return renderTemplate(template, {
|
|
41
|
+
return renderTemplate(withShellCommandRule(template), {
|
|
30
42
|
branch: paths.branch,
|
|
31
43
|
session_id: paths.sessionId,
|
|
32
44
|
worktree: paths.worktree,
|
|
@@ -37,5 +49,6 @@ async function renderPrompt(paths, templateName, values = {}) {
|
|
|
37
49
|
export {
|
|
38
50
|
readPromptTemplate,
|
|
39
51
|
renderPrompt,
|
|
40
|
-
renderTemplate
|
|
52
|
+
renderTemplate,
|
|
53
|
+
withShellCommandRule
|
|
41
54
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Run automated checks for JSKIT session {{session_id}}.
|
|
2
|
+
|
|
3
|
+
Worktree:
|
|
4
|
+
|
|
5
|
+
{{worktree}}
|
|
6
|
+
|
|
7
|
+
Command:
|
|
8
|
+
|
|
9
|
+
{{check_command}}
|
|
10
|
+
|
|
11
|
+
Run the command in the session worktree. If it fails, diagnose the root cause, fix the worktree, and rerun the command. Keep repeating until the command passes or until a real blocker prevents progress.
|
|
12
|
+
|
|
13
|
+
Rules:
|
|
14
|
+
|
|
15
|
+
- Fix the underlying cause. Do not remove checks, weaken verification, or hide failures to make the command pass.
|
|
16
|
+
- Prefer JSKIT-owned helpers, generators, package seams, and documented commands over local hacks.
|
|
17
|
+
- Use `npx --no-install jskit ...` for JSKIT CLI commands you run directly from the shell.
|
|
18
|
+
- Keep fixes scoped to the current issue, issue details, and active plan.
|
|
19
|
+
- Do not create commits, branches, issues, pull requests, merges, or worktree cleanup. JSKIT session owns those steps.
|
|
20
|
+
|
|
21
|
+
When finished, report:
|
|
22
|
+
|
|
23
|
+
- root cause
|
|
24
|
+
- files changed
|
|
25
|
+
- final check command and result
|
|
26
|
+
- anything still unverified
|
|
27
|
+
|
|
28
|
+
If the repair records a meaningful verification decision, tradeoff, missing coverage, or root-cause explanation future steps should know, include concise entries with reasons in this exact marker block:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
[agent_decisions]
|
|
32
|
+
<verification or repair decisions, or "No new decisions.">
|
|
33
|
+
[/agent_decisions]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
At the very end, include this completion block so Studio knows the step is complete:
|
|
37
|
+
|
|
38
|
+
[jskit_step_result]
|
|
39
|
+
status: complete
|
|
40
|
+
step: automated_checks_run
|
|
41
|
+
summary: Short summary of the final check command, result, and any repairs.
|
|
42
|
+
[/jskit_step_result]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Deep UI quality check for JSKIT session {{session_id}}.
|
|
2
|
+
|
|
3
|
+
Phase: {{phase}}
|
|
4
|
+
GitHub issue: {{issue_url}}
|
|
5
|
+
Issue number: {{issue_number}}
|
|
6
|
+
Issue title: {{issue_title}}
|
|
7
|
+
Issue body file: {{issue_file}}
|
|
8
|
+
Issue details file (`issue_details.md`): {{issue_details_file}}
|
|
9
|
+
Plan file: {{plan_file}}
|
|
10
|
+
UI impact: {{ui_impact}}
|
|
11
|
+
Worktree: {{worktree}}
|
|
12
|
+
|
|
13
|
+
Changed files since the session base:
|
|
14
|
+
|
|
15
|
+
{{changed_files}}
|
|
16
|
+
|
|
17
|
+
Run a focused UI quality pass for the current worktree. If this is not UI-impacting after inspection, say exactly why and do not edit files. If the issue touches UI, inspect the changed routes, views, components, placements, layouts, and styles.
|
|
18
|
+
|
|
19
|
+
Check:
|
|
20
|
+
|
|
21
|
+
- Material Design quality.
|
|
22
|
+
- Vuetify best practices.
|
|
23
|
+
- visual hierarchy and density.
|
|
24
|
+
- spacing and alignment.
|
|
25
|
+
- responsive behavior on mobile, tablet, and desktop.
|
|
26
|
+
- loading, empty, error, disabled, and success states where relevant.
|
|
27
|
+
- accessibility basics.
|
|
28
|
+
- route and navigation coherence.
|
|
29
|
+
- consistency with the existing app style.
|
|
30
|
+
|
|
31
|
+
When clear scoped UI issues exist, fix them in the worktree. Keep fixes limited to the issue, confirmed issue details, and approved plan.
|
|
32
|
+
|
|
33
|
+
Use Playwright for a meaningful route check when possible. If login is required, use a development-only auth bootstrap path. When possible, record UI verification with:
|
|
34
|
+
|
|
35
|
+
`npx --no-install jskit app verify-ui --command "<playwright command>" --feature "<label>" --auth-mode <mode>`
|
|
36
|
+
|
|
37
|
+
Do not create commits, branches, issues, pull requests, merges, or worktree cleanup. JSKIT session owns those steps.
|
|
38
|
+
|
|
39
|
+
If this pass makes UI fixes, intentionally skips UI work after inspection, changes a design direction, or leaves a meaningful UI verification gap, include concise decision entries with reasons in this exact marker block:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
[agent_decisions]
|
|
43
|
+
<Deep UI decisions or "No new decisions.">
|
|
44
|
+
[/agent_decisions]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
At the very end, include this completion block so Studio knows the step is complete:
|
|
48
|
+
|
|
49
|
+
[jskit_step_result]
|
|
50
|
+
status: complete
|
|
51
|
+
step: deep_ui_check_run
|
|
52
|
+
summary: Short summary of UI findings, fixes, verification, or why no UI work applied.
|
|
53
|
+
[/jskit_step_result]
|
|
@@ -16,11 +16,20 @@ Fix the root cause in this worktree. Do not silence the failure or remove checks
|
|
|
16
16
|
|
|
17
17
|
Diagnosis rules:
|
|
18
18
|
|
|
19
|
+
- Re-read the issue, `issue_details.md`, the active cycle plan file, `agent_decisions.md`, `.jskit/APP_BLUEPRINT.md`, `.jskit/helper-map.md`, check receipts, and UI check receipts before repairing.
|
|
19
20
|
- Identify whether the failure is dependency/setup, JSKIT metadata, generated contract drift, routing/surface wiring, CRUD ownership, UI verification receipt, test-auth, or ordinary application code.
|
|
20
21
|
- Prefer repairing the JSKIT-owned contract or generated metadata over adding local-path hacks.
|
|
21
22
|
- Do not run `npm install` only because optional agent docs are missing. Run installs only when the failure or a JSKIT setup/session step requires dependency repair.
|
|
22
|
-
- If a generator/package command is the correct repair, use the `jskit` command rather than hand-recreating generated structure.
|
|
23
|
-
- For UI receipt failures, run the relevant Playwright check and record it with `jskit app verify-ui --command "<playwright command>" --feature "<label>" --auth-mode <mode>` when possible.
|
|
23
|
+
- If a generator/package command is the correct repair, use the `npx --no-install jskit` command rather than hand-recreating generated structure.
|
|
24
|
+
- For UI receipt failures, run the relevant Playwright check and record it with `npx --no-install jskit app verify-ui --command "<playwright command>" --feature "<label>" --auth-mode <mode>` when possible.
|
|
24
25
|
- If login is required, use the app's development auth bootstrap path rather than a live external auth flow.
|
|
25
26
|
|
|
26
27
|
Do not push, open a PR, merge, or remove the worktree. When the fix is ready, report the root cause, files changed, verification, and anything still unverified. The user or Studio will rerun the JSKIT session step.
|
|
28
|
+
|
|
29
|
+
If the repair records a meaningful verification decision, tradeoff, missing coverage, or root-cause explanation future steps should know, include concise entries with reasons in this exact marker block:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
[agent_decisions]
|
|
33
|
+
<verification or repair decisions, or "No new decisions.">
|
|
34
|
+
[/agent_decisions]
|
|
35
|
+
```
|
|
@@ -4,17 +4,28 @@ GitHub issue: {{issue_url}}
|
|
|
4
4
|
Issue number: {{issue_number}}
|
|
5
5
|
Issue title: {{issue_title}}
|
|
6
6
|
Issue body file: {{issue_file}}
|
|
7
|
+
Issue details file (`issue_details.md`): {{issue_details_file}}
|
|
7
8
|
Plan file: {{plan_file}}
|
|
8
9
|
Worktree: {{worktree}}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
Confirmed issue details:
|
|
12
|
+
|
|
13
|
+
{{issue_details_text}}
|
|
14
|
+
|
|
15
|
+
Approved plan:
|
|
16
|
+
|
|
17
|
+
{{plan_text}}
|
|
18
|
+
|
|
19
|
+
Implement the plan in the session worktree. Keep the change scoped to the issue, confirmed issue details, and approved plan.
|
|
11
20
|
|
|
12
21
|
Implementation rules:
|
|
13
22
|
|
|
14
|
-
- Inspect the current app before editing.
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
23
|
+
- Inspect the current app before editing. App setup has already passed; if required JSKIT app files are missing, report setup failure rather than inventing recovery work.
|
|
24
|
+
- Follow both `{{issue_details_file}}` and `{{plan_file}}`. If they disagree, ask for clarification before changing files.
|
|
25
|
+
- Read `.jskit/helper-map.md` when it exists before creating helpers, composables, service functions, maps, or package glue.
|
|
26
|
+
- Prefer existing JSKIT helpers, app-local helpers, package runtime seams, generated scaffolds, and documented generators over new local helpers.
|
|
27
|
+
- If the plan calls for a generator or package install, use the planned `npx --no-install jskit` command unless inspection proves it does not apply. If you skip a generator, explain the exact gap.
|
|
28
|
+
- For non-CRUD route pages, use `npx --no-install jskit generate ui-generator page ...` when it fits instead of hand-writing both route files and placement entries.
|
|
18
29
|
- For CRUD work, scaffold the server side first with `crud-server-generator` before CRUD UI or CRUD route work.
|
|
19
30
|
- Do not hand-write a separate CRUD migration for a table owned by `crud-server-generator`.
|
|
20
31
|
- Do not hand-build CRUD endpoints or page trees before the server CRUD package and shared resource file exist.
|
|
@@ -29,7 +40,22 @@ After making changes:
|
|
|
29
40
|
|
|
30
41
|
- Review for repeated code, unnecessary helpers, fragmented functions, placeholder work, missing states, broken route wiring, ownership mistakes, and weak JSKIT reuse.
|
|
31
42
|
- Run the smallest relevant checks you can run safely in the worktree.
|
|
32
|
-
- For changed user-facing UI, run or clearly identify the Playwright verification path. When possible, record UI verification with `jskit app verify-ui --command "<playwright command>" --feature "<label>" --auth-mode <mode>`.
|
|
43
|
+
- For changed user-facing UI, run or clearly identify the Playwright verification path. When possible, record UI verification with `npx --no-install jskit app verify-ui --command "<playwright command>" --feature "<label>" --auth-mode <mode>`.
|
|
33
44
|
- Summarize changed files and checks run.
|
|
45
|
+
- If implementation deviated from the approved plan, generator choices, package ownership, helper reuse, UI verification path, or data ownership, include concise decision entries with reasons in this exact marker block:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
[agent_decisions]
|
|
49
|
+
<implementation decisions or "No new decisions.">
|
|
50
|
+
[/agent_decisions]
|
|
51
|
+
```
|
|
34
52
|
|
|
35
53
|
Do not create commits, branches, issues, pull requests, merges, or worktree cleanup yourself. JSKIT session will handle those steps.
|
|
54
|
+
|
|
55
|
+
At the very end, include this completion block so Studio knows the step is complete:
|
|
56
|
+
|
|
57
|
+
[jskit_step_result]
|
|
58
|
+
status: complete
|
|
59
|
+
step: plan_executed
|
|
60
|
+
summary: Short summary of what changed and what was checked.
|
|
61
|
+
[/jskit_step_result]
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
Codex thread id: {{codex_thread_id}}
|
|
2
2
|
Issue session status: finished
|
|
3
3
|
PR: {{pr_url}}
|
|
4
|
+
PR outcome: {{pr_outcome}}
|
|
5
|
+
Outcome reason: {{pr_outcome_reason}}
|
|
4
6
|
Local transcript: {{transcript_log}}
|
|
5
7
|
|
|
6
8
|
Summary:
|
|
7
9
|
|
|
8
|
-
Session {{session_id}} finished
|
|
10
|
+
Session {{session_id}} finished with PR outcome `{{pr_outcome}}`.
|