jettypod 4.4.120 โ 4.4.121
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/.env +2 -1
- package/Cargo.lock +6450 -0
- package/Cargo.toml +35 -0
- package/README.md +5 -1
- package/TAURI-MIGRATION-PLAN.md +840 -0
- package/apps/dashboard/app/connect-claude/page.tsx +5 -6
- package/apps/dashboard/app/decision/[id]/page.tsx +54 -49
- package/apps/dashboard/app/demo/gates/page.tsx +3 -5
- package/apps/dashboard/app/design-system/page.tsx +1 -1
- package/apps/dashboard/app/globals.css +74 -2
- package/apps/dashboard/app/install-claude/page.tsx +3 -5
- package/apps/dashboard/app/login/page.tsx +17 -20
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +60 -12
- package/apps/dashboard/app/signup/page.tsx +14 -17
- package/apps/dashboard/app/subscribe/page.tsx +0 -2
- package/apps/dashboard/app/tests/page.tsx +37 -4
- package/apps/dashboard/app/welcome/page.tsx +12 -15
- package/apps/dashboard/app/work/[id]/page.tsx +90 -75
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +70 -61
- package/apps/dashboard/components/CardMenu.tsx +0 -1
- package/apps/dashboard/components/ClaudePanel.tsx +541 -283
- package/apps/dashboard/components/ClaudePanelInput.tsx +23 -4
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/CopyableId.tsx +1 -2
- package/apps/dashboard/components/DetailReviewActions.tsx +11 -20
- package/apps/dashboard/components/DragContext.tsx +132 -62
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +5 -6
- package/apps/dashboard/components/EditableDetailDescription.tsx +6 -12
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +0 -1
- package/apps/dashboard/components/ElapsedTimer.tsx +15 -3
- package/apps/dashboard/components/EpicGroup.tsx +100 -70
- package/apps/dashboard/components/GateCard.tsx +0 -1
- package/apps/dashboard/components/GateChoiceCard.tsx +1 -2
- package/apps/dashboard/components/InstallClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/JettyLoader.tsx +0 -1
- package/apps/dashboard/components/KanbanBoard.tsx +319 -173
- package/apps/dashboard/components/KanbanCard.tsx +341 -107
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +0 -1
- package/apps/dashboard/components/MainNav.tsx +24 -25
- package/apps/dashboard/components/MessageBlock.tsx +93 -16
- package/apps/dashboard/components/ModeStartCard.tsx +0 -1
- package/apps/dashboard/components/OnboardingWelcome.tsx +0 -1
- package/apps/dashboard/components/PlaceholderCard.tsx +0 -1
- package/apps/dashboard/components/ProjectSwitcher.tsx +20 -20
- package/apps/dashboard/components/PrototypeTimeline.tsx +47 -26
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +308 -223
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +303 -160
- package/apps/dashboard/components/ReviewFooter.tsx +12 -14
- package/apps/dashboard/components/SessionList.tsx +0 -1
- package/apps/dashboard/components/SubscribeContent.tsx +40 -11
- package/apps/dashboard/components/TestTree.tsx +1 -2
- package/apps/dashboard/components/TipCard.tsx +2 -4
- package/apps/dashboard/components/Toast.tsx +0 -1
- package/apps/dashboard/components/TypeIcon.tsx +7 -8
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +5 -17
- package/apps/dashboard/components/WelcomeScreen.tsx +2 -6
- package/apps/dashboard/components/WorkItemHeader.tsx +0 -1
- package/apps/dashboard/components/WorkItemTree.tsx +2 -4
- package/apps/dashboard/components/settings/AccountSection.tsx +27 -13
- package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
- package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +20 -73
- package/apps/dashboard/components/settings/GeneralSection.tsx +137 -26
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +0 -1
- package/apps/dashboard/components/ui/Button.tsx +1 -1
- package/apps/dashboard/components/ui/Input.tsx +1 -1
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +611 -358
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +0 -1
- package/apps/dashboard/contexts/UsageContext.tsx +62 -31
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1302
- package/apps/dashboard/lib/environment-config.ts +173 -0
- package/apps/dashboard/lib/environment-verification.ts +119 -0
- package/apps/dashboard/lib/kanban-utils.ts +226 -26
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/service-recovery.ts +326 -0
- package/apps/dashboard/lib/session-state-machine.ts +1 -0
- package/apps/dashboard/lib/session-state-utils.ts +0 -164
- package/apps/dashboard/lib/session-stream-manager.ts +253 -122
- package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
- package/apps/dashboard/lib/tauri-bridge.ts +102 -0
- package/apps/dashboard/lib/tauri.ts +106 -0
- package/apps/dashboard/lib/utils.ts +3 -3
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -33
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/pier-icon.png +0 -0
- package/apps/dashboard/public/star-icon.png +0 -0
- package/apps/dashboard/public/wrench-icon.png +0 -0
- package/apps/dashboard/scripts/tauri-build.js +228 -0
- package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
- package/apps/dashboard/src/main.tsx +12 -0
- package/apps/dashboard/src/router.tsx +107 -0
- package/apps/dashboard/src/vite-env.d.ts +1 -0
- package/apps/dashboard/tsconfig.json +7 -12
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
- package/apps/dashboard/vite.config.ts +33 -0
- package/apps/update-server/src/index.ts +167 -30
- package/claude-hooks/global-guardrails.js +14 -13
- package/crates/jettypod-cli/Cargo.toml +19 -0
- package/crates/jettypod-cli/src/commands.rs +1249 -0
- package/crates/jettypod-cli/src/main.rs +595 -0
- package/crates/jettypod-core/Cargo.toml +26 -0
- package/crates/jettypod-core/build.rs +98 -0
- package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
- package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
- package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
- package/crates/jettypod-core/src/auth.rs +294 -0
- package/crates/jettypod-core/src/config.rs +397 -0
- package/crates/jettypod-core/src/db/mod.rs +507 -0
- package/crates/jettypod-core/src/db/recovery.rs +114 -0
- package/crates/jettypod-core/src/db/startup.rs +101 -0
- package/crates/jettypod-core/src/db/validate.rs +149 -0
- package/crates/jettypod-core/src/error.rs +76 -0
- package/crates/jettypod-core/src/git.rs +458 -0
- package/crates/jettypod-core/src/lib.rs +20 -0
- package/crates/jettypod-core/src/sessions.rs +625 -0
- package/crates/jettypod-core/src/skills.rs +556 -0
- package/crates/jettypod-core/src/work.rs +1086 -0
- package/crates/jettypod-core/src/worktree.rs +628 -0
- package/crates/jettypod-core/src/ws.rs +767 -0
- package/cucumber-test.cjs +6 -0
- package/jettypod.js +96 -4
- package/lib/bdd-preflight.js +96 -0
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/work-commands/index.js +58 -16
- package/lib/work-tracking/index.js +108 -8
- package/package.json +1 -1
- package/skills-templates/bug-mode/SKILL.md +43 -1
- package/skills-templates/chore-mode/SKILL.md +40 -1
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +14 -0
- package/skills-templates/feature-planning/SKILL.md +90 -1
- package/skills-templates/production-mode/SKILL.md +20 -0
- package/skills-templates/simple-improvement/SKILL.md +39 -2
- package/skills-templates/speed-mode/SKILL.md +10 -15
- package/skills-templates/stable-mode/SKILL.md +47 -0
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -446
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -280
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -525
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
- package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
- package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
- package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
- package/apps/dashboard/app/api/kanban/route.ts +0 -15
- package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
- package/apps/dashboard/app/api/settings/general/route.ts +0 -21
- package/apps/dashboard/app/api/tests/route.ts +0 -9
- package/apps/dashboard/app/api/tests/run/route.ts +0 -82
- package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
- package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
- package/apps/dashboard/app/api/usage/route.ts +0 -17
- package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/route.ts +0 -35
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -63
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -55
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -30
- package/apps/dashboard/electron/ipc-handlers.js +0 -1026
- package/apps/dashboard/electron/main.js +0 -2306
- package/apps/dashboard/electron/preload.js +0 -125
- package/apps/dashboard/electron/session-manager.js +0 -163
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/backlog-parser.ts +0 -50
- package/apps/dashboard/lib/claude-process-manager.ts +0 -529
- package/apps/dashboard/lib/db-bridge.ts +0 -283
- package/apps/dashboard/lib/prototypes.ts +0 -202
- package/apps/dashboard/lib/test-results-db.ts +0 -307
- package/apps/dashboard/lib/tests.ts +0 -282
- package/apps/dashboard/next.config.js +0 -66
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/bug-icon.svg +0 -9
- package/apps/dashboard/public/buoy-icon.svg +0 -9
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/in-flight-seagull.svg +0 -9
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/pier-icon.svg +0 -14
- package/apps/dashboard/public/star-icon.svg +0 -9
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/public/wrench-icon.svg +0 -9
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
package/jettypod.js
CHANGED
|
@@ -565,6 +565,39 @@ Be direct and opinionated. Skip hedging, apologies, and excessive politeness. Le
|
|
|
565
565
|
// Default for missing project name
|
|
566
566
|
const projectName = config.name || 'unknown-project';
|
|
567
567
|
|
|
568
|
+
// Design system context injection
|
|
569
|
+
let designSystemSection = '';
|
|
570
|
+
const designSystemDir = config.designSystemDir;
|
|
571
|
+
if (designSystemDir && typeof designSystemDir === 'string' && fs.existsSync(designSystemDir)) {
|
|
572
|
+
const allowedExts = ['.css', '.scss', '.less', '.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.txt'];
|
|
573
|
+
const files = fs.readdirSync(designSystemDir).filter(f => {
|
|
574
|
+
const ext = path.extname(f).toLowerCase();
|
|
575
|
+
return allowedExts.includes(ext);
|
|
576
|
+
}).sort();
|
|
577
|
+
|
|
578
|
+
if (files.length > 0) {
|
|
579
|
+
let fileContents = '';
|
|
580
|
+
for (const file of files) {
|
|
581
|
+
const filePath = path.join(designSystemDir, file);
|
|
582
|
+
try {
|
|
583
|
+
const stat = fs.statSync(filePath);
|
|
584
|
+
if (stat.isFile()) {
|
|
585
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
586
|
+
fileContents += `\n### ${file}\n\`\`\`\n${content}\n\`\`\`\n`;
|
|
587
|
+
}
|
|
588
|
+
} catch (e) {
|
|
589
|
+
// Skip unreadable files
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (fileContents) {
|
|
593
|
+
designSystemSection = `
|
|
594
|
+
<design_system>
|
|
595
|
+
## Design System Reference
|
|
596
|
+
${fileContents}</design_system>`;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
568
601
|
return `<claude_context project="${projectName}">
|
|
569
602
|
${currentWorkSection}
|
|
570
603
|
<project_state>
|
|
@@ -573,7 +606,7 @@ ${projectState} - ${projectStateDescription}
|
|
|
573
606
|
<tech_stack>
|
|
574
607
|
${techStack}
|
|
575
608
|
</tech_stack>${modeTag}
|
|
576
|
-
</claude_context>${jettypodEssentials}
|
|
609
|
+
</claude_context>${designSystemSection}${jettypodEssentials}
|
|
577
610
|
${communicationStyle}
|
|
578
611
|
${outputRules}`;
|
|
579
612
|
},
|
|
@@ -671,8 +704,58 @@ async function generateClaude(options = {}) {
|
|
|
671
704
|
}
|
|
672
705
|
}
|
|
673
706
|
|
|
707
|
+
/**
|
|
708
|
+
* Sync Claude hook files from claude-hooks/ source templates to .jettypod/hooks/
|
|
709
|
+
* Uses content hashing โ only copies when source differs from installed.
|
|
710
|
+
* Called on every CLI invocation via ensureClaudeHooks().
|
|
711
|
+
*/
|
|
712
|
+
function syncClaudeHookFiles() {
|
|
713
|
+
const sourceDir = path.join(__dirname, 'claude-hooks');
|
|
714
|
+
const destDir = path.join('.jettypod', 'hooks');
|
|
715
|
+
|
|
716
|
+
if (!fs.existsSync(sourceDir) || !fs.existsSync('.jettypod')) {
|
|
717
|
+
return; // No source hooks or no jettypod project
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (!fs.existsSync(destDir)) {
|
|
721
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Only sync the 3 hooks that are installed by init
|
|
725
|
+
const hooksToSync = [
|
|
726
|
+
'global-guardrails.js',
|
|
727
|
+
'protect-claude-md.js',
|
|
728
|
+
'enforce-skill-activation.js'
|
|
729
|
+
];
|
|
730
|
+
|
|
731
|
+
const crypto = require('crypto');
|
|
732
|
+
const hash = (content) => crypto.createHash('md5').update(content).digest('hex');
|
|
733
|
+
|
|
734
|
+
for (const hookFile of hooksToSync) {
|
|
735
|
+
const sourcePath = path.join(sourceDir, hookFile);
|
|
736
|
+
const destPath = path.join(destDir, hookFile);
|
|
737
|
+
|
|
738
|
+
if (!fs.existsSync(sourcePath)) continue;
|
|
739
|
+
|
|
740
|
+
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
741
|
+
|
|
742
|
+
// Skip if installed hook is already up to date
|
|
743
|
+
if (fs.existsSync(destPath)) {
|
|
744
|
+
const destContent = fs.readFileSync(destPath, 'utf-8');
|
|
745
|
+
if (hash(sourceContent) === hash(destContent)) continue;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
749
|
+
fs.chmodSync(destPath, 0o755);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
674
753
|
// Ensure Claude Code hooks are up to date (called on every jettypod launch)
|
|
675
754
|
function ensureClaudeHooks() {
|
|
755
|
+
// Sync hook FILES from claude-hooks/ source templates to .jettypod/hooks/
|
|
756
|
+
// Uses content hashing โ only copies when source differs from installed
|
|
757
|
+
syncClaudeHookFiles();
|
|
758
|
+
|
|
676
759
|
// Create .claude directory if needed
|
|
677
760
|
if (!fs.existsSync('.claude')) {
|
|
678
761
|
fs.mkdirSync('.claude', { recursive: true });
|
|
@@ -1233,6 +1316,15 @@ if (fs.existsSync('.git')) {
|
|
|
1233
1316
|
// Runs independently of .git check since subdirectories don't have .git files
|
|
1234
1317
|
ensureJettypodSymlink();
|
|
1235
1318
|
|
|
1319
|
+
// Sync Claude Code hook files and settings on every CLI invocation
|
|
1320
|
+
if (fs.existsSync('.jettypod')) {
|
|
1321
|
+
try {
|
|
1322
|
+
ensureClaudeHooks();
|
|
1323
|
+
} catch (err) {
|
|
1324
|
+
// Don't fail CLI if hook sync fails
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1236
1328
|
if (!commandsWithoutDb.includes(command) && fs.existsSync('.jettypod')) {
|
|
1237
1329
|
try {
|
|
1238
1330
|
const { validateOnStartup } = require('./lib/database');
|
|
@@ -1250,7 +1342,8 @@ if (!commandsWithoutDb.includes(command) && fs.existsSync('.jettypod')) {
|
|
|
1250
1342
|
console.log(`๐งน Cleaned ${cleaned} stale merge lock(s) from previous session`);
|
|
1251
1343
|
}
|
|
1252
1344
|
} catch (lockCleanupErr) {
|
|
1253
|
-
// Non-fatal: don't block CLI startup
|
|
1345
|
+
// Non-fatal: don't block CLI startup, but log so failures are visible
|
|
1346
|
+
console.warn(`Warning: Merge lock cleanup failed: ${lockCleanupErr.message}`);
|
|
1254
1347
|
}
|
|
1255
1348
|
} catch (err) {
|
|
1256
1349
|
console.error('');
|
|
@@ -1384,12 +1477,11 @@ switch (command) {
|
|
|
1384
1477
|
const workCommands = require('./lib/work-commands/index.js');
|
|
1385
1478
|
try {
|
|
1386
1479
|
// Parse merge flags and optional work item ID from args
|
|
1387
|
-
const withTransition = args.includes('--with-transition');
|
|
1388
1480
|
const releaseLock = args.includes('--release-lock');
|
|
1389
1481
|
// Find numeric arg that isn't a flag
|
|
1390
1482
|
const workItemId = args.find(a => /^\d+$/.test(a)) ? parseInt(args.find(a => /^\d+$/.test(a))) : null;
|
|
1391
1483
|
|
|
1392
|
-
await workCommands.mergeWork({
|
|
1484
|
+
await workCommands.mergeWork({ releaseLock, workItemId });
|
|
1393
1485
|
} catch (err) {
|
|
1394
1486
|
console.error(`Error: ${err.message}`);
|
|
1395
1487
|
process.exit(1);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function checkCucumberInstalled(projectRoot) {
|
|
7
|
+
const cucumberPath = path.join(projectRoot, 'node_modules', '@cucumber', 'cucumber');
|
|
8
|
+
const exists = fs.existsSync(cucumberPath);
|
|
9
|
+
return {
|
|
10
|
+
id: 'cucumber-installed',
|
|
11
|
+
label: '@cucumber/cucumber installed',
|
|
12
|
+
passed: exists,
|
|
13
|
+
error: exists ? null : '@cucumber/cucumber is not installed. Run: npm install --save-dev @cucumber/cucumber',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function checkTestBddScript(projectRoot) {
|
|
18
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
19
|
+
let hasScript = false;
|
|
20
|
+
if (fs.existsSync(pkgPath)) {
|
|
21
|
+
try {
|
|
22
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
23
|
+
hasScript = !!(pkg.scripts && pkg.scripts['test:bdd']);
|
|
24
|
+
} catch (_) {
|
|
25
|
+
// Corrupt or unreadable package.json โ treat as missing script
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
id: 'test-bdd-script',
|
|
30
|
+
label: 'test:bdd script in package.json',
|
|
31
|
+
passed: hasScript,
|
|
32
|
+
error: hasScript ? null : 'Missing "test:bdd" script in package.json. Add: "test:bdd": "NODE_ENV=test cucumber-js"',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function checkCucumberConfig(projectRoot) {
|
|
37
|
+
const configPath = path.join(projectRoot, 'cucumber.js');
|
|
38
|
+
const exists = fs.existsSync(configPath);
|
|
39
|
+
return {
|
|
40
|
+
id: 'cucumber-config',
|
|
41
|
+
label: 'cucumber.js config file',
|
|
42
|
+
passed: exists,
|
|
43
|
+
error: exists ? null : 'Missing cucumber.js configuration file in project root',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function checkFeaturesDirectory(projectRoot) {
|
|
48
|
+
const featuresDir = path.join(projectRoot, 'features');
|
|
49
|
+
const stepDefsDir = path.join(projectRoot, 'features', 'step_definitions');
|
|
50
|
+
const exists = fs.existsSync(featuresDir) && fs.existsSync(stepDefsDir);
|
|
51
|
+
return {
|
|
52
|
+
id: 'features-directory',
|
|
53
|
+
label: 'features/ directory with step_definitions/',
|
|
54
|
+
passed: exists,
|
|
55
|
+
error: exists ? null : 'Missing features/ directory or features/step_definitions/ subdirectory',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function checkSupportDirectory(projectRoot) {
|
|
60
|
+
const supportDir = path.join(projectRoot, 'features', 'support');
|
|
61
|
+
const hooksFile = path.join(supportDir, 'hooks.js');
|
|
62
|
+
const exists = fs.existsSync(supportDir) && fs.existsSync(hooksFile);
|
|
63
|
+
return {
|
|
64
|
+
id: 'support-hooks',
|
|
65
|
+
label: 'features/support/hooks.js',
|
|
66
|
+
passed: exists,
|
|
67
|
+
error: exists ? null : 'Missing features/support/hooks.js file',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildSetupPrompt(projectRoot, failedChecks) {
|
|
72
|
+
const missing = failedChecks.map(c => `- ${c.label}: ${c.error}`).join('\n');
|
|
73
|
+
return `The following BDD test infrastructure is missing in this project:\n\n${missing}\n\nPlease set up the missing BDD test infrastructure so tests can run.`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function checkBddPrerequisites(projectRoot) {
|
|
77
|
+
const checks = [
|
|
78
|
+
checkCucumberInstalled(projectRoot),
|
|
79
|
+
checkTestBddScript(projectRoot),
|
|
80
|
+
checkCucumberConfig(projectRoot),
|
|
81
|
+
checkFeaturesDirectory(projectRoot),
|
|
82
|
+
checkSupportDirectory(projectRoot),
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const ready = checks.every(c => c.passed);
|
|
86
|
+
const failed = checks.filter(c => !c.passed);
|
|
87
|
+
|
|
88
|
+
const result = { ready, checks };
|
|
89
|
+
if (!ready) {
|
|
90
|
+
result.setupPrompt = buildSetupPrompt(projectRoot, failed);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { checkBddPrerequisites };
|