jettypod 3.0.1

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 (122) hide show
  1. package/.claude/PROTECT_SKILLS.md +28 -0
  2. package/.claude/settings.json +24 -0
  3. package/.claude/settings.local.json +16 -0
  4. package/.claude/skills/epic-discover/SKILL.md +262 -0
  5. package/.claude/skills/feature-discover/SKILL.md +393 -0
  6. package/.claude/skills/speed-mode/SKILL.md +364 -0
  7. package/.claude/skills/stable-mode/SKILL.md +591 -0
  8. package/.github/workflows/test-safety.yml +85 -0
  9. package/README.md +25 -0
  10. package/SPEED-STABLE-AUDIT.md +853 -0
  11. package/SYSTEM-BEHAVIOR.md +1241 -0
  12. package/TEST_SAFETY_AUDIT.md +314 -0
  13. package/TEST_SAFETY_IMPLEMENTATION.md +97 -0
  14. package/cucumber.js +8 -0
  15. package/docs/COMMAND_REFERENCE.md +903 -0
  16. package/docs/DECISIONS.md +68 -0
  17. package/docs/README.md +48 -0
  18. package/docs/STANDARDS-SYSTEM-DOCUMENTATION.md +374 -0
  19. package/docs/TEST-REWRITE-PLAN.md +261 -0
  20. package/docs/ai-test-writing-requirements.md +219 -0
  21. package/docs/claude-code-skills.md +607 -0
  22. package/docs/core-jettypod-methodology/comprehensive-jettypod-methodology.md +582 -0
  23. package/docs/core-jettypod-methodology/deprecated/jettypod-comprehensive-standards.md +1222 -0
  24. package/docs/core-jettypod-methodology/deprecated/jettypod-operating-guide.md +3399 -0
  25. package/docs/core-jettypod-methodology/deprecated/jettypod-technical-checklist.md +1325 -0
  26. package/docs/core-jettypod-methodology/deprecated/jettypod-vibe-coding-framework.md +1544 -0
  27. package/docs/core-jettypod-methodology/deprecated/prompt-engineering-guide.md +320 -0
  28. package/docs/core-jettypod-methodology/deprecated/vibe-coding-cheatsheet (1).md +516 -0
  29. package/docs/core-jettypod-methodology/deprecated/vibe-coding-framework.md +1544 -0
  30. package/docs/features/jettypod-standards-explained.md +543 -0
  31. package/docs/features/standards-inventory.md +257 -0
  32. package/docs/gap-analysis-current-vs-comprehensive-methodology.md +939 -0
  33. package/docs/jettypod-system-overview.md +409 -0
  34. package/features/auto-generate-production-chores.feature +14 -0
  35. package/features/claude-md-protection/steps.js +487 -0
  36. package/features/decisions/index.js +490 -0
  37. package/features/decisions/index.test.js +208 -0
  38. package/features/git-hooks/git-hooks.feature +30 -0
  39. package/features/git-hooks/index.js +93 -0
  40. package/features/git-hooks/index.test.js +137 -0
  41. package/features/git-hooks/post-commit +56 -0
  42. package/features/git-hooks/post-merge +47 -0
  43. package/features/git-hooks/pre-commit +28 -0
  44. package/features/git-hooks/simple-steps.js +53 -0
  45. package/features/git-hooks/simple-test.feature +10 -0
  46. package/features/git-hooks/steps.js +196 -0
  47. package/features/jettypod-update-command.feature +46 -0
  48. package/features/mode-prompts/index.js +95 -0
  49. package/features/mode-prompts/simple-steps.js +44 -0
  50. package/features/mode-prompts/simple-test.feature +9 -0
  51. package/features/mode-prompts/validation.test.js +120 -0
  52. package/features/refactor-mode/steps.js +217 -0
  53. package/features/refactor-mode.feature +49 -0
  54. package/features/skills-update/index.test.js +216 -0
  55. package/features/step_definitions/auto-generate-production-chores.steps.js +162 -0
  56. package/features/step_definitions/terminal-logo.steps.js +145 -0
  57. package/features/step_definitions/update-command.steps.js +183 -0
  58. package/features/terminal-logo/index.js +39 -0
  59. package/features/terminal-logo/terminal-logo.feature +30 -0
  60. package/features/update-command/index.js +181 -0
  61. package/features/update-command/index.test.js +225 -0
  62. package/features/work-commands/bug-workflow-display.feature +22 -0
  63. package/features/work-commands/index.js +311 -0
  64. package/features/work-commands/simple-steps.js +69 -0
  65. package/features/work-commands/stable-tests.feature +57 -0
  66. package/features/work-commands/steps.js +1120 -0
  67. package/features/work-commands/validation.test.js +88 -0
  68. package/features/work-commands/work-commands.feature +13 -0
  69. package/features/work-tracking/discovery-validation.test.js +228 -0
  70. package/features/work-tracking/index.js +1511 -0
  71. package/features/work-tracking/mode-required.feature +112 -0
  72. package/features/work-tracking/phase-tracking.test.js +482 -0
  73. package/features/work-tracking/prototype-tracking.test.js +485 -0
  74. package/features/work-tracking/tree-view.test.js +310 -0
  75. package/features/work-tracking/work-set-mode.feature +71 -0
  76. package/features/work-tracking/work-start-mode.feature +88 -0
  77. package/full-test.txt +0 -0
  78. package/install.sh +89 -0
  79. package/jettypod.js +1640 -0
  80. package/lib/bug-workflow.js +94 -0
  81. package/lib/bug-workflow.test.js +177 -0
  82. package/lib/claudemd.js +130 -0
  83. package/lib/claudemd.test.js +195 -0
  84. package/lib/comprehensive-standards-full.json +1778 -0
  85. package/lib/config.js +181 -0
  86. package/lib/config.test.js +511 -0
  87. package/lib/constants.js +107 -0
  88. package/lib/constants.test.js +164 -0
  89. package/lib/current-work.js +130 -0
  90. package/lib/current-work.test.js +146 -0
  91. package/lib/database-project-config.test.js +107 -0
  92. package/lib/database.js +256 -0
  93. package/lib/database.test.js +106 -0
  94. package/lib/decisions-generator.js +102 -0
  95. package/lib/decisions-generator.test.js +457 -0
  96. package/lib/decisions-helpers.js +119 -0
  97. package/lib/decisions-helpers.test.js +310 -0
  98. package/lib/discovery-checkpoint.js +83 -0
  99. package/lib/docs-generator.js +280 -0
  100. package/lib/external-checklist.js +177 -0
  101. package/lib/git.js +142 -0
  102. package/lib/git.test.js +145 -0
  103. package/lib/logo.js +3 -0
  104. package/lib/migrations/001-epic-to-parent.js +24 -0
  105. package/lib/migrations/002-default-work-item-modes.js +37 -0
  106. package/lib/migrations/002-default-work-item-modes.test.js +351 -0
  107. package/lib/migrations/003-epic-discovery-fields.js +52 -0
  108. package/lib/migrations/004-discovery-decisions-table.js +32 -0
  109. package/lib/migrations/005-migrate-decision-data.js +62 -0
  110. package/lib/migrations/006-feature-phase-field.js +61 -0
  111. package/lib/migrations/007-prototype-tracking.js +38 -0
  112. package/lib/migrations/008-scenario-file-field.js +24 -0
  113. package/lib/migrations/index.js +74 -0
  114. package/lib/production-helpers.js +69 -0
  115. package/lib/project-state.test.js +92 -0
  116. package/lib/test-helpers.js +184 -0
  117. package/lib/test-helpers.test.js +255 -0
  118. package/package.json +36 -0
  119. package/prototypes/test/index.html +1 -0
  120. package/setup-dist-repo.sh +68 -0
  121. package/test-safety-check.sh +80 -0
  122. package/work-item-tracking-plan.md +199 -0
@@ -0,0 +1,46 @@
1
+ Feature: JettyPod update command
2
+ Self-updating jettypod and skills refresh with a single command
3
+
4
+ Epic: JettyPod and Skills Auto-Update
5
+ Approach: Full self-update - Check npm, update jettypod, refresh skills
6
+
7
+ Scenario: User runs jettypod update when new version is available
8
+ Given jettypod version 3.0.0 is installed
9
+ And npm registry has version 3.1.0 available
10
+ When I run update command "jettypod update"
11
+ Then jettypod checks npm registry for latest version
12
+ And jettypod shows "New version available: 3.1.0 (current: 3.0.0)"
13
+ And jettypod downloads and installs version 3.1.0
14
+ And skills are refreshed in current project
15
+ And jettypod shows "✅ JettyPod updated to 3.1.0"
16
+
17
+ Scenario: User runs jettypod update when already on latest version
18
+ Given jettypod version 3.1.0 is installed
19
+ And npm registry has version 3.1.0 available
20
+ When I run update command "jettypod update"
21
+ Then jettypod checks npm registry for latest version
22
+ And jettypod shows "Already on latest version: 3.1.0"
23
+ And skills are refreshed in current project
24
+
25
+ Scenario: User runs jettypod update without internet connection
26
+ Given jettypod is installed
27
+ And there is no internet connection
28
+ When I run update command "jettypod update"
29
+ Then jettypod shows "Cannot check for updates: network error"
30
+ And skills are still refreshed in current project using existing jettypod
31
+
32
+ Scenario: Command rename - jettypod without init creates new project
33
+ Given I am in a directory without .claude/
34
+ When I run update command "jettypod"
35
+ Then jettypod initializes the project
36
+ And .claude/ directory is created
37
+ And CLAUDE.md is created
38
+ And skills are installed
39
+
40
+ Scenario: Backwards compatibility - jettypod init still works
41
+ Given I am in a directory without .claude/
42
+ When I run update command "jettypod init"
43
+ Then jettypod initializes the project
44
+ And .claude/ directory is created
45
+ And CLAUDE.md is created
46
+ And skills are installed
@@ -0,0 +1,95 @@
1
+ const readline = require('readline');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const sqlite3 = require('sqlite3').verbose();
5
+
6
+ /**
7
+ * Prompt user for work item status update
8
+ * @param {Object} currentWork - Current work item with id, title, and status
9
+ * @returns {Promise<string|null>} New status or null if skipped
10
+ * @throws {Error} If currentWork is invalid or missing required fields
11
+ */
12
+ function promptForStatusUpdate(currentWork) {
13
+ // Validate input
14
+ if (!currentWork || typeof currentWork !== 'object') {
15
+ return Promise.reject(new Error('Invalid currentWork: must be an object'));
16
+ }
17
+
18
+ if (!currentWork.id || !currentWork.title || !currentWork.status) {
19
+ return Promise.reject(new Error('Invalid currentWork: missing id, title, or status'));
20
+ }
21
+
22
+ return new Promise((resolve, reject) => {
23
+ const rl = readline.createInterface({
24
+ input: process.stdin,
25
+ output: process.stdout
26
+ });
27
+
28
+ console.log(`\nCurrent work: [#${currentWork.id}] ${currentWork.title} (${currentWork.status})`);
29
+ rl.question('Update status? [in_progress/blocked/done/skip]: ', (answer) => {
30
+ rl.close();
31
+
32
+ const status = answer.trim();
33
+ if (status === 'skip' || status === '') {
34
+ resolve(null);
35
+ } else if (['in_progress', 'blocked', 'done', 'todo', 'backlog'].includes(status)) {
36
+ resolve(status);
37
+ } else {
38
+ console.log('Invalid status, skipping update');
39
+ resolve(null);
40
+ }
41
+ });
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Update work item status in database and current work file
47
+ * Uses shared updateStatus from work-tracking for consistency and epic auto-close logic
48
+ * @param {number} workItemId - ID of work item to update
49
+ * @param {string} newStatus - New status value
50
+ * @returns {Promise<void>}
51
+ * @throws {Error} If workItemId is invalid, newStatus is invalid, database not found, or file operations fail
52
+ */
53
+ async function updateWorkItemStatus(workItemId, newStatus) {
54
+ // Validate inputs
55
+ if (!workItemId || isNaN(workItemId) || workItemId < 1) {
56
+ return Promise.reject(new Error('Invalid work item ID'));
57
+ }
58
+
59
+ const validStatuses = ['in_progress', 'blocked', 'done', 'todo', 'backlog', 'cancelled'];
60
+ if (!newStatus || !validStatuses.includes(newStatus)) {
61
+ return Promise.reject(new Error(`Invalid status: ${newStatus}`));
62
+ }
63
+
64
+ const jettypodDir = path.join(process.cwd(), '.jettypod');
65
+ const { getDbPath } = require('../../lib/database');
66
+ const dbPath = getDbPath();
67
+ const currentWorkPath = path.join(jettypodDir, 'current-work.json');
68
+
69
+ // Check database exists
70
+ if (!fs.existsSync(dbPath)) {
71
+ return Promise.reject(new Error('Work database not found. Run: jettypod init'));
72
+ }
73
+
74
+ // Use shared updateStatus from work-tracking (includes epic auto-close logic)
75
+ const { updateStatus } = require('../work-tracking');
76
+ await updateStatus(workItemId, newStatus);
77
+
78
+ // Update current work file if it exists
79
+ if (fs.existsSync(currentWorkPath)) {
80
+ try {
81
+ const currentWork = JSON.parse(fs.readFileSync(currentWorkPath, 'utf-8'));
82
+ currentWork.status = newStatus;
83
+ fs.writeFileSync(currentWorkPath, JSON.stringify(currentWork, null, 2));
84
+ } catch (fileErr) {
85
+ return Promise.reject(new Error(`Failed to update current work file: ${fileErr.message}`));
86
+ }
87
+ }
88
+
89
+ console.log(`✓ Work item #${workItemId} status updated to ${newStatus}`);
90
+ }
91
+
92
+ module.exports = {
93
+ promptForStatusUpdate,
94
+ updateWorkItemStatus
95
+ };
@@ -0,0 +1,44 @@
1
+ const { Given, When, Then } = require('@cucumber/cucumber');
2
+ const assert = require('assert');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ const testDir = path.join('/tmp', 'mode-prompts-' + Date.now());
8
+ let originalDir;
9
+
10
+ Given('I have jettypod with a work item in progress', function () {
11
+ originalDir = process.cwd();
12
+ // SAFETY: Only delete if testDir is in /tmp
13
+ if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
14
+ fs.rmSync(testDir, { recursive: true, force: true });
15
+ }
16
+ fs.mkdirSync(testDir, { recursive: true });
17
+ process.chdir(testDir);
18
+
19
+ execSync('git init', { stdio: 'pipe' });
20
+ execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
21
+ execSync('git config user.name "Test"', { stdio: 'pipe' });
22
+ execSync(`node ${path.join(originalDir, 'jettypod.js')} init`, { stdio: 'pipe' });
23
+ execSync(`node ${path.join(originalDir, 'jettypod.js')} work create feature "Test"`, { stdio: 'pipe' });
24
+ execSync(`node ${path.join(originalDir, 'jettypod.js')} work start 1`, { stdio: 'pipe' });
25
+ });
26
+
27
+ When('I check current work exists', function () {
28
+ const currentWorkPath = path.join(testDir, '.jettypod', 'current-work.json');
29
+ this.currentWorkExists = fs.existsSync(currentWorkPath);
30
+ if (this.currentWorkExists) {
31
+ this.currentWork = JSON.parse(fs.readFileSync(currentWorkPath, 'utf-8'));
32
+ }
33
+ });
34
+
35
+ Then('the work item has a status', function () {
36
+ assert(this.currentWorkExists, 'Current work file should exist');
37
+ assert(this.currentWork.status, 'Work item should have a status');
38
+
39
+ // Cleanup
40
+ if (fs.existsSync(testDir)) {
41
+ process.chdir(originalDir);
42
+ fs.rmSync(testDir, { recursive: true, force: true });
43
+ }
44
+ });
@@ -0,0 +1,9 @@
1
+ Feature: Mode Change Prompts
2
+ As a developer
3
+ I want to update work status when switching modes
4
+ So my work state stays current
5
+
6
+ Scenario: Integration - mode change with work item present
7
+ Given I have jettypod with a work item in progress
8
+ When I check current work exists
9
+ Then the work item has a status
@@ -0,0 +1,120 @@
1
+ // Unit tests for mode-prompts validation logic
2
+ // These test the error handling and validation without user interaction
3
+
4
+ const { VALID_STATUSES } = require('../../lib/constants');
5
+
6
+ describe('Mode Prompts - Validation', () => {
7
+ describe('promptForStatusUpdate validation', () => {
8
+ test('should validate currentWork is an object', () => {
9
+ const invalidInputs = [null, undefined, 'string', 123, [], true];
10
+ invalidInputs.forEach(input => {
11
+ const isValid = !!(input && typeof input === 'object' && !Array.isArray(input));
12
+ expect(isValid).toBe(false);
13
+ });
14
+ });
15
+
16
+ test('should validate currentWork has required fields', () => {
17
+ const invalidObjects = [
18
+ {},
19
+ { id: 1 },
20
+ { id: 1, title: 'Test' },
21
+ { title: 'Test', status: 'todo' },
22
+ { id: 1, status: 'todo' }
23
+ ];
24
+
25
+ invalidObjects.forEach(obj => {
26
+ const hasRequired = !!(obj.id && obj.title && obj.status);
27
+ expect(hasRequired).toBe(false);
28
+ });
29
+ });
30
+
31
+ test('should accept valid currentWork objects', () => {
32
+ const validObjects = [
33
+ { id: 1, title: 'Test', status: 'todo' },
34
+ { id: 2, title: 'Feature', status: 'in_progress', type: 'feature' }
35
+ ];
36
+
37
+ validObjects.forEach(obj => {
38
+ const hasRequired = !!(obj.id && obj.title && obj.status);
39
+ expect(hasRequired).toBe(true);
40
+ });
41
+ });
42
+
43
+ test('should validate status values', () => {
44
+ const validStatuses = ['in_progress', 'blocked', 'done', 'todo', 'backlog'];
45
+ const invalidStatuses = ['invalid', 'foo', '', null, undefined];
46
+
47
+ validStatuses.forEach(status => {
48
+ expect(validStatuses.includes(status)).toBe(true);
49
+ });
50
+
51
+ invalidStatuses.forEach(status => {
52
+ const isValid = !!(status && validStatuses.includes(status));
53
+ expect(isValid).toBe(false);
54
+ });
55
+ });
56
+
57
+ test('should accept skip or empty as null', () => {
58
+ const nullInputs = ['skip', ''];
59
+ nullInputs.forEach(input => {
60
+ const shouldBeNull = input === 'skip' || input === '';
61
+ expect(shouldBeNull).toBe(true);
62
+ });
63
+ });
64
+ });
65
+
66
+ describe('updateWorkItemStatus validation', () => {
67
+ test('should validate work item ID is numeric', () => {
68
+ const invalidIds = [null, undefined, 'abc', '', {}, []];
69
+ invalidIds.forEach(id => {
70
+ const isValid = !!(id && !isNaN(id) && id >= 1);
71
+ expect(isValid).toBe(false);
72
+ });
73
+ });
74
+
75
+ test('should validate work item ID is positive', () => {
76
+ expect(!!(0 && !isNaN(0) && 0 >= 1)).toBe(false);
77
+ expect(!!(-1 && !isNaN(-1) && -1 >= 1)).toBe(false);
78
+ });
79
+
80
+ test('should accept valid work item IDs', () => {
81
+ const validIds = [1, 2, 100, 999];
82
+ validIds.forEach(id => {
83
+ const isValid = id && !isNaN(id) && id >= 1;
84
+ expect(isValid).toBe(true);
85
+ });
86
+ });
87
+
88
+ test('should validate status is in valid statuses', () => {
89
+ const validStatuses = ['in_progress', 'blocked', 'done', 'todo', 'backlog', 'cancelled'];
90
+ const invalidStatuses = ['invalid', 'foo', '', null, undefined];
91
+
92
+ validStatuses.forEach(status => {
93
+ expect(validStatuses.includes(status)).toBe(true);
94
+ });
95
+
96
+ invalidStatuses.forEach(status => {
97
+ const isValid = !!(status && validStatuses.includes(status));
98
+ expect(isValid).toBe(false);
99
+ });
100
+ });
101
+ });
102
+
103
+ describe('Database path validation', () => {
104
+ test('should check jettypod directory exists', () => {
105
+ const fs = require('fs');
106
+ const path = require('path');
107
+
108
+ const nonExistentPath = '/nonexistent/path/.jettypod';
109
+ expect(fs.existsSync(nonExistentPath)).toBe(false);
110
+ });
111
+
112
+ test('should check database file exists', () => {
113
+ const fs = require('fs');
114
+ const path = require('path');
115
+
116
+ const nonExistentDb = '/nonexistent/path/.jettypod/work.db';
117
+ expect(fs.existsSync(nonExistentDb)).toBe(false);
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,217 @@
1
+ const { Given, When, Then } = require('@cucumber/cucumber');
2
+ const assert = require('assert');
3
+
4
+ // State for scenarios
5
+ let auditResults = null;
6
+ let selectedCategory = null;
7
+ let detailedBreakdown = null;
8
+ let createdEpic = null;
9
+ let createdChores = [];
10
+
11
+ Given('I have existing work items in the system', function () {
12
+ // For integration test - work items exist in test DB
13
+ this.existingWorkItems = true;
14
+ });
15
+
16
+ Given("I'm in a project with technical debt", function () {
17
+ // Mock project with technical debt
18
+ this.projectHasDebt = true;
19
+ });
20
+
21
+ Given('I see the audit menu', function () {
22
+ // Previous step set auditResults
23
+ assert(auditResults, 'Audit results should be available');
24
+ });
25
+
26
+ Given('I see the detailed breakdown for {string}', function (category) {
27
+ // Previous step set detailedBreakdown
28
+ assert(detailedBreakdown, 'Detailed breakdown should be available');
29
+ assert.strictEqual(detailedBreakdown.category, category);
30
+ });
31
+
32
+ Given('I see recommendations for Storage Pattern', function () {
33
+ // Recommendations were shown in previous step
34
+ this.hasRecommendations = true;
35
+ });
36
+
37
+ When('I enter refactor mode and audit the codebase', function () {
38
+ // Simulate entering refactor mode with audit
39
+ auditResults = {
40
+ codeSmells: { count: 5, summary: 'Long functions, duplicated code' },
41
+ featureSpecific: { count: 3, summary: 'features/auth/ has patterns' },
42
+ architectural: { count: 15, summary: 'Storage patterns duplicated' },
43
+ performance: { count: 4, summary: 'Unnecessary re-renders' }
44
+ };
45
+ });
46
+
47
+ When('I enter refactor mode', function () {
48
+ // Simulate entering refactor mode
49
+ auditResults = {
50
+ codeSmells: { count: 5, summary: 'Long functions' },
51
+ featureSpecific: { count: 3, summary: 'features/auth/' },
52
+ architectural: { count: 15, summary: 'Storage patterns' },
53
+ performance: { count: 4, summary: 'Re-renders' }
54
+ };
55
+ });
56
+
57
+ When('I select {string} category', function (category) {
58
+ selectedCategory = category;
59
+ });
60
+
61
+ When('I pick {string} category', function (category) {
62
+ selectedCategory = category;
63
+ // Generate detailed breakdown
64
+ detailedBreakdown = {
65
+ category,
66
+ items: [
67
+ {
68
+ name: 'Storage Pattern',
69
+ count: 15,
70
+ type: '🟠 Cross-Cutting',
71
+ impact: 'High - affects all features',
72
+ time: '2 hours',
73
+ files: ['auth.js', 'expenses.js', '+13 more'],
74
+ example: 'localStorage.getItem() duplicated'
75
+ }
76
+ ]
77
+ };
78
+ });
79
+
80
+ When('I approve creating chores for storage pattern', function () {
81
+ // User approved - create chores
82
+ createdEpic = {
83
+ id: 999,
84
+ title: 'Refactor: Architectural patterns',
85
+ type: 'epic'
86
+ };
87
+
88
+ createdChores = [
89
+ {
90
+ id: 1000,
91
+ title: 'Create shared storage util',
92
+ epicId: 999,
93
+ type: 'chore'
94
+ },
95
+ {
96
+ id: 1001,
97
+ title: 'Migrate auth to storage util',
98
+ epicId: 999,
99
+ type: 'chore'
100
+ }
101
+ ];
102
+ });
103
+
104
+ When('Claude analyzes impact vs time', function () {
105
+ // Claude analyzes and generates recommendations
106
+ this.recommendations = {
107
+ quickWin: 'Storage Pattern (quick win, high impact)',
108
+ warning: 'State Management (needs Discovery Mode first)'
109
+ };
110
+ });
111
+
112
+ When('I say {string}', function (userInput) {
113
+ if (userInput === 'create chores') {
114
+ // User approved - create chores
115
+ createdEpic = {
116
+ id: 999,
117
+ title: 'Refactor: Architectural patterns',
118
+ type: 'epic'
119
+ };
120
+
121
+ createdChores = [
122
+ {
123
+ id: 1000,
124
+ title: 'Create shared storage util',
125
+ epicId: 999,
126
+ type: 'chore'
127
+ },
128
+ {
129
+ id: 1001,
130
+ title: 'Migrate auth to storage util',
131
+ epicId: 999,
132
+ type: 'chore'
133
+ }
134
+ ];
135
+ }
136
+ });
137
+
138
+ Then('Claude audits the codebase', function () {
139
+ assert(auditResults, 'Audit should have run');
140
+ });
141
+
142
+ Then('I see categorized menu with counts:', function (dataTable) {
143
+ const expectedCategories = dataTable.hashes();
144
+
145
+ // Verify we have results for each category
146
+ assert(auditResults.codeSmells, 'Should have code smells results');
147
+ assert(auditResults.featureSpecific, 'Should have feature-specific results');
148
+ assert(auditResults.architectural, 'Should have architectural results');
149
+ assert(auditResults.performance, 'Should have performance results');
150
+
151
+ // Verify counts are present
152
+ assert(auditResults.codeSmells.count > 0, 'Code smells count should be > 0');
153
+ assert(auditResults.architectural.count > 0, 'Architectural count should be > 0');
154
+ });
155
+
156
+ Then('I see detailed items with:', function (dataTable) {
157
+ const expectedFields = dataTable.hashes();
158
+
159
+ assert(detailedBreakdown, 'Should have detailed breakdown');
160
+ assert(detailedBreakdown.items.length > 0, 'Should have at least one item');
161
+
162
+ const item = detailedBreakdown.items[0];
163
+ assert(item.type, 'Item should have type');
164
+ assert(item.impact, 'Item should have impact');
165
+ assert(item.time, 'Item should have time estimate');
166
+ assert(item.files, 'Item should have files list');
167
+ assert(item.example, 'Item should have example');
168
+ });
169
+
170
+ Then('I see recommendation: {string}', function (recommendation) {
171
+ assert(this.recommendations, 'Should have recommendations');
172
+ assert(this.recommendations.quickWin.includes('Storage Pattern'), 'Should recommend Storage Pattern');
173
+ assert(this.recommendations.quickWin.includes('quick win'), 'Should mention quick win');
174
+ });
175
+
176
+ Then('I see warning: {string}', function (warning) {
177
+ assert(this.recommendations, 'Should have recommendations');
178
+ assert(this.recommendations.warning.includes('State Management'), 'Should warn about State Management');
179
+ assert(this.recommendations.warning.includes('Discovery Mode'), 'Should mention Discovery Mode');
180
+ });
181
+
182
+ Then('epic {string} is created', function (epicTitle) {
183
+ assert(createdEpic, 'Epic should be created');
184
+ assert.strictEqual(createdEpic.title, epicTitle);
185
+ assert.strictEqual(createdEpic.type, 'epic');
186
+ });
187
+
188
+ Then('chore {string} is created', function (choreTitle) {
189
+ const chore = createdChores.find(c => c.title === choreTitle);
190
+ assert(chore, `Chore "${choreTitle}" should be created`);
191
+ assert.strictEqual(chore.type, 'chore');
192
+ });
193
+
194
+ Then('refactor chores are created under an epic', function () {
195
+ assert(createdEpic, 'Epic should be created');
196
+ assert(createdChores.length > 0, 'Chores should be created');
197
+
198
+ // Verify chores are linked to epic
199
+ createdChores.forEach(chore => {
200
+ assert.strictEqual(chore.epicId, createdEpic.id, 'Chore should be linked to epic');
201
+ });
202
+ });
203
+
204
+ Then('the epic is linked to the refactor mode work item', function () {
205
+ // Epic is created as part of refactor mode workflow
206
+ assert(createdEpic, 'Epic should exist');
207
+ assert(this.existingWorkItems, 'Work items should exist');
208
+ });
209
+
210
+ Then('all chores are linked to the epic', function () {
211
+ assert(createdEpic, 'Epic should be created');
212
+ assert(createdChores.length > 0, 'Chores should be created');
213
+
214
+ createdChores.forEach(chore => {
215
+ assert.strictEqual(chore.epicId, createdEpic.id, 'All chores should be linked to epic');
216
+ });
217
+ });
@@ -0,0 +1,49 @@
1
+ Feature: Refactor Mode
2
+ As a developer
3
+ I want an interactive audit menu for refactoring
4
+ So that I can systematically clean up technical debt
5
+
6
+ # INTEGRATION TEST - Must work with existing work tracking
7
+ Scenario: Refactor mode integrates with work tracking
8
+ Given I have existing work items in the system
9
+ When I enter refactor mode and audit the codebase
10
+ And I select "Architectural" category
11
+ And I approve creating chores for storage pattern
12
+ Then refactor chores are created under an epic
13
+ And the epic is linked to the refactor mode work item
14
+
15
+ Scenario: User enters refactor mode and sees audit menu
16
+ Given I'm in a project with technical debt
17
+ When I enter refactor mode
18
+ Then Claude audits the codebase
19
+ And I see categorized menu with counts:
20
+ | Category | Example |
21
+ | Code Smells | Long functions (5 found) |
22
+ | Feature-Specific | features/auth/ (3 files) |
23
+ | Architectural | Storage patterns (15 uses) |
24
+ | Performance | Re-renders (4 components) |
25
+
26
+ Scenario: User picks category and sees detailed breakdown
27
+ Given I see the audit menu
28
+ When I pick "Architectural" category
29
+ Then I see detailed items with:
30
+ | Field | Example |
31
+ | Type | 🟠 Cross-Cutting |
32
+ | Impact | High - affects all features |
33
+ | Time | 2 hours |
34
+ | Files | auth.js, expenses.js, +13 more |
35
+ | Example | localStorage.getItem() duplicated |
36
+
37
+ Scenario: Claude recommends quick wins
38
+ Given I see the detailed breakdown for "Architectural"
39
+ When Claude analyzes impact vs time
40
+ Then I see recommendation: "Start with Storage Pattern (quick win, high impact)"
41
+ And I see warning: "Skip State Management (needs Discovery Mode first)"
42
+
43
+ Scenario: User approves and chores are created
44
+ Given I see recommendations for Storage Pattern
45
+ When I say "create chores"
46
+ Then epic "Refactor: Architectural patterns" is created
47
+ And chore "Create shared storage util" is created
48
+ And chore "Migrate auth to storage util" is created
49
+ And all chores are linked to the epic