@jskit-ai/jskit-cli 0.2.80 → 0.2.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/package.json +6 -4
  2. package/src/server/appBlueprint.js +1 -1
  3. package/src/server/commandHandlers/helperMap.js +104 -0
  4. package/src/server/commandHandlers/session.js +179 -4
  5. package/src/server/commandHandlers/show.js +169 -34
  6. package/src/server/core/argParser.js +20 -0
  7. package/src/server/core/commandCatalog.js +70 -7
  8. package/src/server/core/createCommandHandlers.js +4 -1
  9. package/src/server/helperMap.js +463 -0
  10. package/src/server/helperMapPaths.js +7 -0
  11. package/src/server/sessionRuntime/appReadiness.js +55 -0
  12. package/src/server/sessionRuntime/constants.js +298 -135
  13. package/src/server/sessionRuntime/paths.js +2 -4
  14. package/src/server/sessionRuntime/preconditions.js +393 -26
  15. package/src/server/sessionRuntime/promptRenderer.js +15 -2
  16. package/src/server/sessionRuntime/prompts/app_blueprint.md +26 -2
  17. package/src/server/sessionRuntime/prompts/automated_checks.md +42 -0
  18. package/src/server/sessionRuntime/prompts/deep_ui_check.md +53 -0
  19. package/src/server/sessionRuntime/prompts/doctor_failure.md +21 -1
  20. package/src/server/sessionRuntime/prompts/execute_plan.md +61 -0
  21. package/src/server/sessionRuntime/prompts/final_comment.md +3 -1
  22. package/src/server/sessionRuntime/prompts/issue_details.md +52 -0
  23. package/src/server/sessionRuntime/prompts/new_issue.md +34 -3
  24. package/src/server/sessionRuntime/prompts/plan_issue.md +81 -0
  25. package/src/server/sessionRuntime/prompts/pr_failure.md +14 -1
  26. package/src/server/sessionRuntime/prompts/review_changes.md +77 -15
  27. package/src/server/sessionRuntime/prompts/update_blueprint.md +36 -0
  28. package/src/server/sessionRuntime/prompts/user_check.md +22 -4
  29. package/src/server/sessionRuntime/responses.js +877 -30
  30. package/src/server/sessionRuntime/worktrees.js +31 -0
  31. package/src/server/sessionRuntime.js +2070 -244
  32. package/src/server/sessionRuntime/prompts/implement_issue.md +0 -25
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  access,
3
3
  appendFile,
4
- mkdir,
5
- stat
4
+ mkdir
6
5
  } from "node:fs/promises";
7
6
  import { constants as fsConstants } from "node:fs";
8
7
  import path from "node:path";
9
8
  import {
9
+ JSKIT_CLI_SHELL_COMMAND,
10
10
  SESSION_STATE_RELATIVE_PATH
11
11
  } from "./constants.js";
12
12
  import {
@@ -22,6 +22,16 @@ import {
22
22
  import {
23
23
  resolveExistingSessionRoot
24
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
+ }
25
35
 
26
36
  async function assertTargetRootWritable(targetRoot) {
27
37
  try {
@@ -226,7 +236,7 @@ async function assertSessionExists(paths) {
226
236
  error: createError({
227
237
  code: "session_missing",
228
238
  message: `Session does not exist: ${paths.sessionId}`,
229
- repairCommand: "jskit session create"
239
+ repairCommand: jskitCommand("session create")
230
240
  }),
231
241
  precondition: createPrecondition({
232
242
  id: "session_exists",
@@ -236,32 +246,251 @@ async function assertSessionExists(paths) {
236
246
  };
237
247
  }
238
248
 
239
- async function assertIssueArtifacts(paths) {
240
- const [issueText, issueUrl] = await Promise.all([
241
- readTrimmedFile(path.join(paths.sessionRoot, "issue.md")),
242
- readTrimmedFile(path.join(paths.sessionRoot, "issue_url"))
243
- ]);
244
- if (issueText && issueUrl) {
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)) {
245
369
  return {
246
370
  ok: true,
247
371
  precondition: createPrecondition({
248
- id: "issue_artifacts",
372
+ id: "accepted_changes_committed",
249
373
  ok: true,
250
- message: "Issue text and GitHub issue URL exist."
374
+ message: "Accepted changes have been committed."
251
375
  })
252
376
  };
253
377
  }
254
378
  return {
255
379
  ok: false,
256
380
  error: createError({
257
- code: "issue_artifacts_missing",
258
- message: "Issue text and GitHub issue URL are required.",
259
- repairCommand: `jskit session ${paths.sessionId} step`
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`)
260
384
  }),
261
385
  precondition: createPrecondition({
262
- id: "issue_artifacts",
386
+ id: "accepted_changes_committed",
263
387
  ok: false,
264
- message: "Issue text and GitHub issue URL exist."
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."
265
494
  })
266
495
  };
267
496
  }
@@ -283,7 +512,7 @@ async function assertIssueTextExists(paths) {
283
512
  error: createError({
284
513
  code: "issue_text_missing",
285
514
  message: "Cannot create a GitHub issue before issue.md exists.",
286
- repairCommand: `jskit session ${paths.sessionId} step --issue -`
515
+ repairCommand: jskitCommand(`session ${paths.sessionId} step --issue -`)
287
516
  }),
288
517
  precondition: createPrecondition({
289
518
  id: "issue_text_exists",
@@ -293,13 +522,139 @@ async function assertIssueTextExists(paths) {
293
522
  };
294
523
  }
295
524
 
296
- async function hasWorktree(paths) {
297
- try {
298
- const stats = await stat(paths.worktree);
299
- return stats.isDirectory();
300
- } catch {
301
- return false;
525
+ async function assertIssueUrlExists(paths) {
526
+ const issueUrl = await readTrimmedFile(path.join(paths.sessionRoot, "issue_url"));
527
+ if (issueUrl) {
528
+ return {
529
+ ok: true,
530
+ precondition: createPrecondition({
531
+ id: "issue_url_exists",
532
+ ok: true,
533
+ message: "GitHub issue URL exists."
534
+ })
535
+ };
302
536
  }
537
+ return {
538
+ ok: false,
539
+ error: createError({
540
+ code: "issue_url_missing",
541
+ message: "Cannot create a plan before the GitHub issue exists.",
542
+ repairCommand: jskitCommand(`session ${paths.sessionId} step`)
543
+ }),
544
+ precondition: createPrecondition({
545
+ id: "issue_url_exists",
546
+ ok: false,
547
+ message: "GitHub issue URL exists."
548
+ })
549
+ };
550
+ }
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
+ };
303
658
  }
304
659
 
305
660
  async function assertWorktreeExists(paths) {
@@ -318,7 +673,7 @@ async function assertWorktreeExists(paths) {
318
673
  error: createError({
319
674
  code: "worktree_missing",
320
675
  message: "Session worktree does not exist.",
321
- repairCommand: `jskit session ${paths.sessionId} step`
676
+ repairCommand: jskitCommand(`session ${paths.sessionId} step`)
322
677
  }),
323
678
  precondition: createPrecondition({
324
679
  id: "worktree_exists",
@@ -345,7 +700,7 @@ async function assertPrUrlExists(paths) {
345
700
  error: createError({
346
701
  code: "pr_url_missing",
347
702
  message: "Cannot merge before pr_url exists.",
348
- repairCommand: `jskit session ${paths.sessionId} step`
703
+ repairCommand: jskitCommand(`session ${paths.sessionId} step`)
349
704
  }),
350
705
  precondition: createPrecondition({
351
706
  id: "pr_url_exists",
@@ -357,13 +712,25 @@ async function assertPrUrlExists(paths) {
357
712
 
358
713
  export {
359
714
  applyPreconditions,
715
+ assertAcceptedChangesCommitted,
716
+ assertActiveCycleExists,
717
+ assertActiveCycleUserCheckPassed,
718
+ assertBlueprintUpdateSatisfied,
719
+ assertDeepUiCheckSatisfied,
720
+ assertDependenciesInstalled,
721
+ assertFinalReportExists,
360
722
  assertGhAuth,
361
723
  assertGitCurrentBranch,
362
724
  assertGitRepository,
363
725
  assertGithubOrigin,
364
- assertIssueArtifacts,
726
+ assertIssueMetadataExists,
365
727
  assertIssueTextExists,
728
+ assertIssueUrlExists,
729
+ assertAutomatedChecksPassed,
730
+ assertIssueDetailsExists,
731
+ assertPlanTextExists,
366
732
  assertPrUrlExists,
733
+ assertReadyJskitApp,
367
734
  assertSessionExists,
368
735
  assertTargetRootWritable,
369
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
  };
@@ -4,21 +4,45 @@ App brief:
4
4
 
5
5
  {{app_brief}}
6
6
 
7
- Produce a concise but useful app-level blueprint. It must describe product intent, not implementation steps for the current issue.
7
+ Produce a concise but useful app-level blueprint. It must describe product intent, platform choices, and architectural boundaries. It must not become an implementation workboard for one issue.
8
+
9
+ Before writing the blueprint, classify the app state if local files are available:
10
+
11
+ - empty
12
+ - non_jskit_repo
13
+ - partial_jskit_app
14
+ - jskit_app
15
+
16
+ Use these markers for a real JSKIT app when they exist:
17
+
18
+ - package.json
19
+ - config/public.js
20
+ - src/main.js
21
+ - packages/main/package.descriptor.mjs
22
+ - .jskit/lock.json
23
+
24
+ If the app is empty or only a fresh minimal scaffold, keep platform choices explicit and provisional until decided. Do not treat a missing `config.tenancyMode` line or untouched minimal scaffold as a final tenancy decision.
8
25
 
9
26
  Cover:
10
27
 
11
28
  - App purpose and what the app will do in general.
12
29
  - Primary users and actors.
13
30
  - Type of multihoming or tenancy: none, personal, workspaces, or another explicit model from the brief.
31
+ - Database engine, if the app needs persistence.
32
+ - Auth provider, if the app needs auth.
14
33
  - The role of each surface: app, admin, console, settings, public, workspace, or any app-specific surface from the brief.
15
34
  - Global view of the main product areas and navigation destinations.
16
35
  - Key domain concepts and data objects, without inventing database schemas unless the brief clearly requires them.
36
+ - Ownership model per persistent entity when it is already clear: public, user, workspace, or workspace_user.
37
+ - Baseline JSKIT package workflows to accept as defaults and any intended overrides.
38
+ - Package install, generator, and custom-code areas at a high level.
39
+ - CRUDs likely to need server ownership, and any narrow exceptions that should be called out later.
17
40
  - Important non-goals and constraints.
18
41
  - First useful screen and what it should show.
19
42
  - Settings, admin, and operator expectations when relevant.
43
+ - Verification expectations, including UI checks for user-facing screens.
20
44
 
21
- If the brief is ambiguous, state the assumption in the blueprint instead of asking questions.
45
+ If the brief is ambiguous, state the assumption in the blueprint instead of asking questions. Do not invent detailed feature behavior that the brief does not support.
22
46
 
23
47
  When the blueprint is ready, output only the final markdown surrounded by these exact markers:
24
48
 
@@ -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]