jettypod 4.4.118 → 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 +4 -3
- 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 +63 -58
- package/apps/dashboard/app/demo/gates/page.tsx +43 -45
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +80 -4
- package/apps/dashboard/app/install-claude/page.tsx +4 -6
- package/apps/dashboard/app/login/page.tsx +72 -54
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +61 -13
- package/apps/dashboard/app/signup/page.tsx +242 -0
- 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 +13 -16
- package/apps/dashboard/app/work/[id]/page.tsx +117 -118
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +92 -85
- package/apps/dashboard/components/CardMenu.tsx +45 -12
- package/apps/dashboard/components/ClaudePanel.tsx +771 -850
- package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
- package/apps/dashboard/components/CopyableId.tsx +3 -4
- package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
- package/apps/dashboard/components/DragContext.tsx +134 -63
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +6 -7
- package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +26 -7
- package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
- package/apps/dashboard/components/EpicGroup.tsx +359 -0
- package/apps/dashboard/components/GateCard.tsx +79 -17
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
- package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
- package/apps/dashboard/components/JettyLoader.tsx +37 -0
- package/apps/dashboard/components/KanbanBoard.tsx +368 -958
- package/apps/dashboard/components/KanbanCard.tsx +740 -0
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
- package/apps/dashboard/components/MainNav.tsx +38 -73
- package/apps/dashboard/components/MessageBlock.tsx +468 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -16
- package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
- package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
- package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
- package/apps/dashboard/components/ReviewFooter.tsx +139 -0
- package/apps/dashboard/components/SessionList.tsx +19 -19
- package/apps/dashboard/components/SubscribeContent.tsx +91 -47
- package/apps/dashboard/components/TestTree.tsx +16 -16
- package/apps/dashboard/components/TipCard.tsx +16 -17
- package/apps/dashboard/components/Toast.tsx +5 -6
- package/apps/dashboard/components/TypeIcon.tsx +55 -0
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
- package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
- package/apps/dashboard/components/WorkItemTree.tsx +11 -32
- package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
- 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 +74 -152
- package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
- package/apps/dashboard/contexts/UsageContext.tsx +87 -32
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1265
- 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 +270 -0
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- 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 +308 -134
- package/apps/dashboard/lib/shadows.ts +7 -0
- 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 +6 -0
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -32
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.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/scripts/ws-server.js +191 -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 +228 -80
- 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/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +145 -116
- package/lib/bdd-preflight.js +96 -0
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/027-plan-at-creation-column.js +3 -1
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +10 -5
- package/lib/seed-onboarding.js +1 -1
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +144 -19
- package/lib/work-tracking/index.js +148 -27
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +79 -20
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +171 -69
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +82 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +173 -74
- package/skills-templates/production-mode/SKILL.md +69 -49
- package/skills-templates/request-routing/SKILL.md +4 -4
- package/skills-templates/simple-improvement/SKILL.md +74 -29
- package/skills-templates/speed-mode/SKILL.md +217 -141
- package/skills-templates/stable-mode/SKILL.md +148 -89
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
- 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 -378
- 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]/status/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -43
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
- package/apps/dashboard/electron/ipc-handlers.js +0 -1028
- package/apps/dashboard/electron/main.js +0 -2124
- package/apps/dashboard/electron/preload.js +0 -123
- package/apps/dashboard/electron/session-manager.js +0 -141
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/claude-process-manager.ts +0 -492
- package/apps/dashboard/lib/db-bridge.ts +0 -282
- 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 -50
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
- package/docs/bdd-guidance.md +0 -390
package/jettypod.js
CHANGED
|
@@ -496,9 +496,9 @@ Skills auto-activate and MUST complete their full workflow:
|
|
|
496
496
|
✅ ALWAYS let skills complete autonomously before taking manual actions
|
|
497
497
|
|
|
498
498
|
## Basic Commands (for non-workflow operations)
|
|
499
|
-
|
|
500
|
-
jettypod work create
|
|
501
|
-
|
|
499
|
+
# To create work items: Write JSON to /tmp/jettypod-create.json, then:
|
|
500
|
+
jettypod work create --from=/tmp/jettypod-create.json
|
|
501
|
+
# JSON format: {"type":"epic|feature|chore|bug","title":"...","description":"...","parent":<id>}
|
|
502
502
|
jettypod work start <id> # Creates worktree branch
|
|
503
503
|
jettypod work merge # Merges worktree back to main
|
|
504
504
|
jettypod work cleanup <id> # Cleanup worktree after merge
|
|
@@ -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('');
|
|
@@ -1264,110 +1357,10 @@ if (!commandsWithoutDb.includes(command) && fs.existsSync('.jettypod')) {
|
|
|
1264
1357
|
|
|
1265
1358
|
switch (command) {
|
|
1266
1359
|
case 'update': {
|
|
1267
|
-
//
|
|
1360
|
+
// CLI is bundled with the app — no independent update mechanism
|
|
1268
1361
|
const updateCommand = require('./lib/update-command');
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
// Always refresh skills and hooks in current project after update attempt
|
|
1272
|
-
if (fs.existsSync('.jettypod')) {
|
|
1273
|
-
// Update Claude Code hooks first
|
|
1274
|
-
ensureClaudeHooks();
|
|
1275
|
-
|
|
1276
|
-
console.log('');
|
|
1277
|
-
console.log('🔄 Refreshing skills in current project...');
|
|
1278
|
-
|
|
1279
|
-
// Use the existing skills update logic from initializeProject
|
|
1280
|
-
// Allow tests to override skills source directory
|
|
1281
|
-
const skillsSourceDir = process.env.JETTYPOD_SKILLS_SOURCE_DIR ||
|
|
1282
|
-
path.join(__dirname, 'skills-templates');
|
|
1283
|
-
const skillsDestDir = path.join('.claude', 'skills');
|
|
1284
|
-
let backupDir = null;
|
|
1285
|
-
|
|
1286
|
-
// Backup existing skills before updating
|
|
1287
|
-
if (fs.existsSync(skillsDestDir)) {
|
|
1288
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1289
|
-
backupDir = path.join('.claude', `skills.backup-${timestamp}`);
|
|
1290
|
-
|
|
1291
|
-
let counter = 1;
|
|
1292
|
-
while (fs.existsSync(backupDir)) {
|
|
1293
|
-
backupDir = path.join('.claude', `skills.backup-${timestamp}-${counter}`);
|
|
1294
|
-
counter++;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
try {
|
|
1298
|
-
fs.renameSync(skillsDestDir, backupDir);
|
|
1299
|
-
|
|
1300
|
-
if (!fs.existsSync(backupDir)) {
|
|
1301
|
-
throw new Error('Backup directory was not created');
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
const backupContents = fs.readdirSync(backupDir);
|
|
1305
|
-
if (backupContents.length === 0) {
|
|
1306
|
-
throw new Error('Backup directory is empty');
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
console.log(`💾 Backed up existing skills to ${backupDir}`);
|
|
1310
|
-
} catch (err) {
|
|
1311
|
-
console.error(`❌ Could not backup skills: ${err.message}`);
|
|
1312
|
-
throw new Error(`Could not backup existing skills: ${err.message}`);
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// Copy skills from jettypod to project
|
|
1317
|
-
if (!fs.existsSync(skillsSourceDir)) {
|
|
1318
|
-
console.warn('⚠️ Skills source directory not found');
|
|
1319
|
-
} else {
|
|
1320
|
-
try {
|
|
1321
|
-
const copyRecursive = (src, dest) => {
|
|
1322
|
-
if (!fs.existsSync(dest)) {
|
|
1323
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
1324
|
-
}
|
|
1325
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
1326
|
-
for (const entry of entries) {
|
|
1327
|
-
const srcPath = path.join(src, entry.name);
|
|
1328
|
-
const destPath = path.join(dest, entry.name);
|
|
1329
|
-
if (entry.isDirectory()) {
|
|
1330
|
-
copyRecursive(srcPath, destPath);
|
|
1331
|
-
} else {
|
|
1332
|
-
fs.copyFileSync(srcPath, destPath);
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
};
|
|
1336
|
-
|
|
1337
|
-
copyRecursive(skillsSourceDir, skillsDestDir);
|
|
1338
|
-
console.log('✅ Skills refreshed');
|
|
1339
|
-
} catch (err) {
|
|
1340
|
-
console.error(`❌ Could not refresh skills: ${err.message}`);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
// Ensure session.md is gitignored (fixes existing projects)
|
|
1345
|
-
ensureJettypodGitignores();
|
|
1346
|
-
|
|
1347
|
-
// Auto-commit any changes made by update (skills, hooks, gitignore, etc.)
|
|
1348
|
-
// This prevents untracked changes from breaking worktree merges
|
|
1349
|
-
try {
|
|
1350
|
-
const { execSync } = require('child_process');
|
|
1351
|
-
// Check if there are any changes to commit
|
|
1352
|
-
const status = execSync('git status --porcelain', { encoding: 'utf-8' });
|
|
1353
|
-
if (status.trim()) {
|
|
1354
|
-
execSync('git add -A');
|
|
1355
|
-
// Use --no-verify to bypass pre-commit hooks - this is an automated system update
|
|
1356
|
-
execSync('git commit --no-verify -m "chore: jettypod update - refresh skills and hooks"');
|
|
1357
|
-
console.log('✅ Update changes committed');
|
|
1358
|
-
}
|
|
1359
|
-
} catch (err) {
|
|
1360
|
-
// Log the error so users know what happened
|
|
1361
|
-
console.log('');
|
|
1362
|
-
console.log('⚠️ Could not auto-commit skill updates');
|
|
1363
|
-
console.log(' Reason:', err.message.split('\n')[0]);
|
|
1364
|
-
console.log('');
|
|
1365
|
-
console.log(' You may have untracked changes in .claude/skills/');
|
|
1366
|
-
console.log(' Run: git add .claude && git commit -m "chore: update skills"');
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
process.exit(success ? 0 : 1);
|
|
1362
|
+
await updateCommand.runUpdate();
|
|
1363
|
+
process.exit(0);
|
|
1371
1364
|
break;
|
|
1372
1365
|
}
|
|
1373
1366
|
|
|
@@ -1454,13 +1447,14 @@ switch (command) {
|
|
|
1454
1447
|
} else if (subcommand === 'cleanup') {
|
|
1455
1448
|
const workCommands = require('./lib/work-commands/index.js');
|
|
1456
1449
|
const dryRun = args.includes('--dry-run');
|
|
1450
|
+
const force = args.includes('--force');
|
|
1457
1451
|
// Find numeric arg for specific work item cleanup
|
|
1458
1452
|
const workItemId = args.find(a => /^\d+$/.test(a)) ? parseInt(args.find(a => /^\d+$/.test(a))) : null;
|
|
1459
1453
|
|
|
1460
1454
|
try {
|
|
1461
1455
|
if (workItemId) {
|
|
1462
1456
|
// Clean up specific work item's worktree
|
|
1463
|
-
await workCommands.cleanupWorkItem(workItemId);
|
|
1457
|
+
await workCommands.cleanupWorkItem(workItemId, { force });
|
|
1464
1458
|
} else {
|
|
1465
1459
|
// Batch cleanup of all orphaned worktrees
|
|
1466
1460
|
const results = await workCommands.cleanupWorktrees({ dryRun });
|
|
@@ -1483,12 +1477,11 @@ switch (command) {
|
|
|
1483
1477
|
const workCommands = require('./lib/work-commands/index.js');
|
|
1484
1478
|
try {
|
|
1485
1479
|
// Parse merge flags and optional work item ID from args
|
|
1486
|
-
const withTransition = args.includes('--with-transition');
|
|
1487
1480
|
const releaseLock = args.includes('--release-lock');
|
|
1488
1481
|
// Find numeric arg that isn't a flag
|
|
1489
1482
|
const workItemId = args.find(a => /^\d+$/.test(a)) ? parseInt(args.find(a => /^\d+$/.test(a))) : null;
|
|
1490
1483
|
|
|
1491
|
-
await workCommands.mergeWork({
|
|
1484
|
+
await workCommands.mergeWork({ releaseLock, workItemId });
|
|
1492
1485
|
} catch (err) {
|
|
1493
1486
|
console.error(`Error: ${err.message}`);
|
|
1494
1487
|
process.exit(1);
|
|
@@ -2353,7 +2346,33 @@ switch (command) {
|
|
|
2353
2346
|
break;
|
|
2354
2347
|
}
|
|
2355
2348
|
|
|
2356
|
-
case undefined:
|
|
2349
|
+
case undefined: {
|
|
2350
|
+
// Auto-update: if running from a stale global install, reinstall from source
|
|
2351
|
+
// Skip in worktrees - worktree copies always differ from source and updating
|
|
2352
|
+
// the global install won't change the worktree file, causing infinite recursion
|
|
2353
|
+
const devLinkPath = path.join(require('os').homedir(), '.jettypod-dev.json');
|
|
2354
|
+
if (fs.existsSync(devLinkPath) && !__dirname.includes('.jettypod-work')) {
|
|
2355
|
+
try {
|
|
2356
|
+
const devInfo = JSON.parse(fs.readFileSync(devLinkPath, 'utf-8'));
|
|
2357
|
+
const sourceDir = devInfo.sourceDir;
|
|
2358
|
+
if (sourceDir && fs.existsSync(path.join(sourceDir, 'jettypod.js'))) {
|
|
2359
|
+
const sourceContent = fs.readFileSync(path.join(sourceDir, 'jettypod.js'));
|
|
2360
|
+
const installedContent = fs.readFileSync(path.join(__dirname, 'jettypod.js'));
|
|
2361
|
+
if (!sourceContent.equals(installedContent)) {
|
|
2362
|
+
console.log('🔄 Source has changed, updating jettypod...');
|
|
2363
|
+
const { execSync: execSyncUpdate } = require('child_process');
|
|
2364
|
+
execSyncUpdate('npm install -g .', { cwd: sourceDir, stdio: 'inherit' });
|
|
2365
|
+
console.log('✅ Updated. Relaunching...');
|
|
2366
|
+
const { execFileSync } = require('child_process');
|
|
2367
|
+
execFileSync(process.argv[0], process.argv.slice(1), { stdio: 'inherit', cwd: process.cwd() });
|
|
2368
|
+
process.exit(0);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
} catch (e) {
|
|
2372
|
+
// Auto-update check failed — continue with current version
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2357
2376
|
// Smart detection when no command provided
|
|
2358
2377
|
if (!fs.existsSync('.jettypod/config.json')) {
|
|
2359
2378
|
// New project - auto-initialize
|
|
@@ -2457,12 +2476,13 @@ switch (command) {
|
|
|
2457
2476
|
|
|
2458
2477
|
// Start dashboard in dev mode for live reload
|
|
2459
2478
|
console.log('🚀 Starting dashboard (dev mode)...');
|
|
2460
|
-
const dashboardProcess = spawn('npm', ['run', 'dev'
|
|
2479
|
+
const dashboardProcess = spawn('npm', ['run', 'dev'], {
|
|
2461
2480
|
cwd: dashboardPath,
|
|
2462
2481
|
detached: true,
|
|
2463
2482
|
stdio: 'ignore',
|
|
2464
2483
|
env: {
|
|
2465
2484
|
...process.env,
|
|
2485
|
+
PORT: String(availablePort),
|
|
2466
2486
|
JETTYPOD_PROJECT_PATH: process.cwd(),
|
|
2467
2487
|
JETTYPOD_WS_PORT: String(WS_PORT)
|
|
2468
2488
|
}
|
|
@@ -2520,6 +2540,7 @@ Quick commands:
|
|
|
2520
2540
|
}
|
|
2521
2541
|
}
|
|
2522
2542
|
break;
|
|
2543
|
+
}
|
|
2523
2544
|
|
|
2524
2545
|
case 'trash': {
|
|
2525
2546
|
// Trash management commands
|
|
@@ -2652,9 +2673,17 @@ Quick commands:
|
|
|
2652
2673
|
const impact = graph.getImpact(targetFile);
|
|
2653
2674
|
|
|
2654
2675
|
if (impact.error) {
|
|
2655
|
-
console.
|
|
2656
|
-
|
|
2657
|
-
|
|
2676
|
+
console.log(`\n❌ ${impact.error}`);
|
|
2677
|
+
if (impact.suggestions && impact.suggestions.length > 0) {
|
|
2678
|
+
console.log('');
|
|
2679
|
+
console.log('Did you mean one of these?');
|
|
2680
|
+
for (const s of impact.suggestions) {
|
|
2681
|
+
console.log(` ${s}`);
|
|
2682
|
+
}
|
|
2683
|
+
} else {
|
|
2684
|
+
console.log('');
|
|
2685
|
+
console.log('Make sure the file path is relative to the project root.');
|
|
2686
|
+
}
|
|
2658
2687
|
process.exit(1);
|
|
2659
2688
|
}
|
|
2660
2689
|
|
|
@@ -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 };
|
package/lib/chore-taxonomy.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chore Type Taxonomy
|
|
3
|
-
* Defines the
|
|
3
|
+
* Defines the 5 chore types and their workflow guidance for the chore-planning and chore-mode skills.
|
|
4
|
+
*
|
|
5
|
+
* Types fall into two categories:
|
|
6
|
+
* - Behavior-preserving (refactor, dependency, cleanup, tooling): Run existing affected tests, never write new tests
|
|
7
|
+
* - Behavior-adding (enhancement): TDD with red-green-refactor cycle, writes new tests first
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -11,7 +15,8 @@ const CHORE_TYPES = Object.freeze({
|
|
|
11
15
|
REFACTOR: 'refactor',
|
|
12
16
|
DEPENDENCY: 'dependency',
|
|
13
17
|
CLEANUP: 'cleanup',
|
|
14
|
-
TOOLING: 'tooling'
|
|
18
|
+
TOOLING: 'tooling',
|
|
19
|
+
ENHANCEMENT: 'enhancement'
|
|
15
20
|
});
|
|
16
21
|
|
|
17
22
|
/**
|
|
@@ -33,14 +38,14 @@ const CHORE_TYPE_GUIDANCE = Object.freeze({
|
|
|
33
38
|
'Consider breaking into smaller refactors if scope is large'
|
|
34
39
|
],
|
|
35
40
|
verification: [
|
|
36
|
-
'
|
|
41
|
+
'Affected tests pass without modification',
|
|
37
42
|
'No new functionality added (that requires new tests)',
|
|
38
43
|
'Code review confirms behavior preservation',
|
|
39
44
|
'Performance is not degraded'
|
|
40
45
|
],
|
|
41
46
|
testHandling: {
|
|
42
47
|
required: true,
|
|
43
|
-
approach: 'Run
|
|
48
|
+
approach: 'Run tests for affected and potentially impacted modules. Update test file paths/imports if moved. Do NOT change test assertions - if tests fail, the refactor broke behavior.'
|
|
44
49
|
}
|
|
45
50
|
},
|
|
46
51
|
[CHORE_TYPES.DEPENDENCY]: {
|
|
@@ -51,14 +56,14 @@ const CHORE_TYPE_GUIDANCE = Object.freeze({
|
|
|
51
56
|
'Consider update strategy: one at a time vs batch'
|
|
52
57
|
],
|
|
53
58
|
verification: [
|
|
54
|
-
'
|
|
59
|
+
'Affected and potentially impacted tests pass after update',
|
|
55
60
|
'Application builds successfully',
|
|
56
61
|
'No new deprecation warnings (or documented)',
|
|
57
62
|
'Security vulnerabilities addressed (if security update)'
|
|
58
63
|
],
|
|
59
64
|
testHandling: {
|
|
60
65
|
required: false,
|
|
61
|
-
approach: 'Run
|
|
66
|
+
approach: 'Run tests for affected and potentially impacted modules to catch regressions. No new tests needed unless migrating to new API patterns. Document any test changes needed due to library API changes.'
|
|
62
67
|
}
|
|
63
68
|
},
|
|
64
69
|
[CHORE_TYPES.CLEANUP]: {
|
|
@@ -69,14 +74,14 @@ const CHORE_TYPE_GUIDANCE = Object.freeze({
|
|
|
69
74
|
'Consider impact on git history/blame'
|
|
70
75
|
],
|
|
71
76
|
verification: [
|
|
72
|
-
'
|
|
77
|
+
'Affected tests still pass',
|
|
73
78
|
'No broken imports or references',
|
|
74
79
|
'Application runs correctly',
|
|
75
80
|
'Removed code was actually unused'
|
|
76
81
|
],
|
|
77
82
|
testHandling: {
|
|
78
83
|
required: false,
|
|
79
|
-
approach: 'Run
|
|
84
|
+
approach: 'Run tests for affected modules to ensure nothing breaks. Remove tests only if they test deleted code. No new tests needed for cleanup work.'
|
|
80
85
|
}
|
|
81
86
|
},
|
|
82
87
|
[CHORE_TYPES.TOOLING]: {
|
|
@@ -96,6 +101,24 @@ const CHORE_TYPE_GUIDANCE = Object.freeze({
|
|
|
96
101
|
required: false,
|
|
97
102
|
approach: 'Verify tooling changes work via manual testing or CI runs. Add integration tests only if tooling is complex. Focus on verification over unit testing for infrastructure.'
|
|
98
103
|
}
|
|
104
|
+
},
|
|
105
|
+
[CHORE_TYPES.ENHANCEMENT]: {
|
|
106
|
+
scope: [
|
|
107
|
+
'Define what new behavior is being added',
|
|
108
|
+
'Identify where the behavior integrates with existing code',
|
|
109
|
+
'Plan test cases BEFORE writing implementation (TDD)',
|
|
110
|
+
'Keep scope focused - one behavior per chore'
|
|
111
|
+
],
|
|
112
|
+
verification: [
|
|
113
|
+
'New tests written BEFORE implementation (TDD red phase)',
|
|
114
|
+
'All new tests pass (TDD green phase)',
|
|
115
|
+
'Code is clean and well-structured (TDD refactor phase)',
|
|
116
|
+
'Existing affected tests still pass'
|
|
117
|
+
],
|
|
118
|
+
testHandling: {
|
|
119
|
+
required: true,
|
|
120
|
+
approach: 'TDD red-green-refactor: Write failing tests first that define the new behavior, then implement minimum code to pass, then refactor. Run affected tests to ensure no regressions.'
|
|
121
|
+
}
|
|
99
122
|
}
|
|
100
123
|
});
|
|
101
124
|
|
|
@@ -136,7 +159,7 @@ function isValidChoreType(type) {
|
|
|
136
159
|
function getGuidance(type) {
|
|
137
160
|
if (type === null || type === undefined) {
|
|
138
161
|
throw new Error(
|
|
139
|
-
'Chore type is required. Valid types: refactor, dependency, cleanup, tooling'
|
|
162
|
+
'Chore type is required. Valid types: refactor, dependency, cleanup, tooling, enhancement'
|
|
140
163
|
);
|
|
141
164
|
}
|
|
142
165
|
|
|
@@ -144,7 +167,7 @@ function getGuidance(type) {
|
|
|
144
167
|
|
|
145
168
|
if (!normalized || !VALID_CHORE_TYPES.includes(normalized)) {
|
|
146
169
|
throw new Error(
|
|
147
|
-
`Invalid chore type: "${type}". Valid types: refactor, dependency, cleanup, tooling`
|
|
170
|
+
`Invalid chore type: "${type}". Valid types: refactor, dependency, cleanup, tooling, enhancement`
|
|
148
171
|
);
|
|
149
172
|
}
|
|
150
173
|
|