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,177 @@
1
+ // External Readiness Checklist - Default items for transitioning to External state
2
+
3
+ const DEFAULT_CHECKLIST = {
4
+ security: [
5
+ {
6
+ key: 'input_validation',
7
+ title: 'Input validation on public endpoints',
8
+ description: 'Add validation middleware to all public API routes. Validate data types, required fields, length limits, format constraints.'
9
+ },
10
+ {
11
+ key: 'sql_injection_prevention',
12
+ title: 'SQL injection prevention',
13
+ description: 'Use parameterized queries or ORM methods exclusively. Never concatenate user input into SQL strings.'
14
+ },
15
+ {
16
+ key: 'xss_protection',
17
+ title: 'XSS protection',
18
+ description: 'Sanitize all user input before rendering. Use Content-Security-Policy headers.'
19
+ },
20
+ {
21
+ key: 'rate_limiting',
22
+ title: 'Rate limiting on public APIs',
23
+ description: 'Implement rate limiting to prevent abuse. Consider per-IP and per-user limits.'
24
+ },
25
+ {
26
+ key: 'auth_implemented',
27
+ title: 'Authentication/authorization',
28
+ description: 'Proper authentication and authorization checks on all protected endpoints.'
29
+ }
30
+ ],
31
+ monitoring: [
32
+ {
33
+ key: 'error_tracking',
34
+ title: 'Error tracking integration',
35
+ description: 'Set up error tracking (Sentry, DataDog, etc.) with proper context and stack traces.'
36
+ },
37
+ {
38
+ key: 'performance_monitoring',
39
+ title: 'Performance monitoring',
40
+ description: 'Monitor response times, database query performance, and system resources.'
41
+ },
42
+ {
43
+ key: 'audit_logging',
44
+ title: 'Audit logging',
45
+ description: 'Log sensitive operations for compliance and debugging. Include user, action, timestamp.'
46
+ }
47
+ ],
48
+ infrastructure: [
49
+ {
50
+ key: 'database_backups',
51
+ title: 'Database backup automation',
52
+ description: 'Automated database backups with tested restore procedures.'
53
+ },
54
+ {
55
+ key: 'rollback_tested',
56
+ title: 'Rollback procedures tested',
57
+ description: 'Document and test procedures for rolling back deployments.'
58
+ },
59
+ {
60
+ key: 'load_tested',
61
+ title: 'Load testing completed',
62
+ description: 'Test with expected user volume. Identify and fix performance bottlenecks.'
63
+ },
64
+ {
65
+ key: 'graceful_degradation',
66
+ title: 'Graceful degradation under load',
67
+ description: 'System handles overload gracefully without cascading failures.'
68
+ }
69
+ ],
70
+ compliance: [
71
+ {
72
+ key: 'privacy_policy',
73
+ title: 'Privacy policy',
74
+ description: 'Privacy policy in place if collecting user data. GDPR compliance if applicable.'
75
+ },
76
+ {
77
+ key: 'data_retention',
78
+ title: 'Data retention policy',
79
+ description: 'Clear policy for how long data is stored and procedures for deletion.'
80
+ },
81
+ {
82
+ key: 'security_headers',
83
+ title: 'Security headers implemented',
84
+ description: 'Implement security headers: HSTS, X-Frame-Options, X-Content-Type-Options, etc.'
85
+ }
86
+ ]
87
+ };
88
+
89
+ /**
90
+ * Initialize checklist with default items if not exists
91
+ * @param {sqlite3.Database} db - Database connection
92
+ * @returns {Promise<void>}
93
+ */
94
+ function initializeChecklist(db) {
95
+ return new Promise((resolve, reject) => {
96
+ // Check if checklist is already populated
97
+ db.get('SELECT COUNT(*) as count FROM external_readiness_checklist', (err, row) => {
98
+ if (err) {
99
+ return reject(err);
100
+ }
101
+
102
+ if (row.count > 0) {
103
+ return resolve(); // Already initialized
104
+ }
105
+
106
+ // Insert default items
107
+ const stmt = db.prepare(`
108
+ INSERT INTO external_readiness_checklist (category, item_key, title, description)
109
+ VALUES (?, ?, ?, ?)
110
+ `);
111
+
112
+ Object.entries(DEFAULT_CHECKLIST).forEach(([category, items]) => {
113
+ items.forEach(item => {
114
+ stmt.run(category, item.key, item.title, item.description);
115
+ });
116
+ });
117
+
118
+ stmt.finalize((err) => {
119
+ if (err) reject(err);
120
+ else resolve();
121
+ });
122
+ });
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Get checklist status grouped by category
128
+ * @param {sqlite3.Database} db - Database connection
129
+ * @returns {Promise<Object>} Checklist grouped by category with completion stats
130
+ */
131
+ function getChecklistStatus(db) {
132
+ return new Promise((resolve, reject) => {
133
+ db.all('SELECT * FROM external_readiness_checklist ORDER BY category, id', (err, rows) => {
134
+ if (err) {
135
+ return reject(err);
136
+ }
137
+
138
+ const grouped = {};
139
+ let totalItems = 0;
140
+ let completedItems = 0;
141
+
142
+ rows.forEach(row => {
143
+ if (!grouped[row.category]) {
144
+ grouped[row.category] = {
145
+ items: [],
146
+ completed: 0,
147
+ total: 0
148
+ };
149
+ }
150
+
151
+ grouped[row.category].items.push(row);
152
+ grouped[row.category].total++;
153
+ totalItems++;
154
+
155
+ if (row.completed) {
156
+ grouped[row.category].completed++;
157
+ completedItems++;
158
+ }
159
+ });
160
+
161
+ resolve({
162
+ categories: grouped,
163
+ overall: {
164
+ total: totalItems,
165
+ completed: completedItems,
166
+ percentage: totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0
167
+ }
168
+ });
169
+ });
170
+ });
171
+ }
172
+
173
+ module.exports = {
174
+ DEFAULT_CHECKLIST,
175
+ initializeChecklist,
176
+ getChecklistStatus
177
+ };
package/lib/git.js ADDED
@@ -0,0 +1,142 @@
1
+ const { execSync } = require('child_process');
2
+
3
+ /**
4
+ * Check if git is installed on the system
5
+ * @returns {boolean} True if git command is available
6
+ */
7
+ function isGitInstalled() {
8
+ try {
9
+ execSync('git --version', { stdio: 'pipe' });
10
+ return true;
11
+ } catch (e) {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Check if current directory is a git repository
18
+ * @returns {boolean} True if inside a git repository
19
+ * @throws {Error} If git is not installed
20
+ */
21
+ function isGitRepo() {
22
+ if (!isGitInstalled()) {
23
+ throw new Error('Git is not installed. Please install git to use JettyPod work tracking features.');
24
+ }
25
+
26
+ try {
27
+ execSync('git rev-parse --git-dir', { stdio: 'pipe' });
28
+ return true;
29
+ } catch (e) {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Validate branch name format
36
+ * @param {string} branchName - Branch name to validate
37
+ * @throws {Error} If branch name is invalid
38
+ */
39
+ function validateBranchName(branchName) {
40
+ if (!branchName || typeof branchName !== 'string') {
41
+ throw new Error('Branch name must be a non-empty string');
42
+ }
43
+
44
+ if (branchName.includes(' ')) {
45
+ throw new Error('Branch name cannot contain spaces');
46
+ }
47
+
48
+ if (branchName.startsWith('-') || branchName.startsWith('.')) {
49
+ throw new Error('Branch name cannot start with - or .');
50
+ }
51
+
52
+ if (/[~^:\\?*\[]/.test(branchName)) {
53
+ throw new Error('Branch name contains invalid characters');
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Create or checkout a git branch
59
+ * @param {string} branchName - Name of the branch to create/checkout
60
+ * @returns {boolean} True if successful
61
+ * @throws {Error} If not in git repo, git not installed, or invalid branch name
62
+ */
63
+ function createOrCheckoutBranch(branchName) {
64
+ validateBranchName(branchName);
65
+
66
+ if (!isGitRepo()) {
67
+ throw new Error('Not in a git repository. Initialize git first: git init');
68
+ }
69
+
70
+ try {
71
+ execSync(`git checkout -b "${branchName}"`, { stdio: 'pipe' });
72
+ return true;
73
+ } catch (e) {
74
+ // Branch might already exist, try to checkout
75
+ try {
76
+ execSync(`git checkout "${branchName}"`, { stdio: 'pipe' });
77
+ return true;
78
+ } catch (e2) {
79
+ throw new Error(`Failed to create or checkout branch "${branchName}". Error: ${e2.message}`);
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get the current git branch name
86
+ * @returns {string|null} Current branch name, or null if not in a git repo
87
+ * @throws {Error} If git is not installed
88
+ */
89
+ function getCurrentBranch() {
90
+ if (!isGitRepo()) {
91
+ return null;
92
+ }
93
+
94
+ try {
95
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' });
96
+ return branch.trim();
97
+ } catch (e) {
98
+ throw new Error(`Failed to get current branch: ${e.message}`);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Convert text to git-safe slug format
104
+ * @param {string} text - Text to slugify
105
+ * @returns {string} Lowercase alphanumeric string with hyphens
106
+ * @throws {Error} If text is not a string
107
+ */
108
+ function slugify(text) {
109
+ if (typeof text !== 'string') {
110
+ throw new Error('slugify requires a string input');
111
+ }
112
+ return text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
113
+ }
114
+
115
+ /**
116
+ * Create a feature branch name from work item details
117
+ * @param {number} workItemId - ID of the work item
118
+ * @param {string} title - Title of the work item
119
+ * @returns {string} Branch name in format: feature/work-{id}-{slug}
120
+ * @throws {Error} If workItemId is not a number or title is not a string
121
+ */
122
+ function createFeatureBranchName(workItemId, title) {
123
+ if (typeof workItemId !== 'number' || workItemId < 1) {
124
+ throw new Error('workItemId must be a positive number');
125
+ }
126
+
127
+ if (typeof title !== 'string' || !title.trim()) {
128
+ throw new Error('title must be a non-empty string');
129
+ }
130
+
131
+ const slug = slugify(title);
132
+ return `feature/work-${workItemId}-${slug}`;
133
+ }
134
+
135
+ module.exports = {
136
+ isGitInstalled,
137
+ isGitRepo,
138
+ createOrCheckoutBranch,
139
+ getCurrentBranch,
140
+ slugify,
141
+ createFeatureBranchName
142
+ };
@@ -0,0 +1,145 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const { createTestEnvironment } = require('./test-helpers');
5
+ const {
6
+ isGitInstalled,
7
+ isGitRepo,
8
+ createOrCheckoutBranch,
9
+ getCurrentBranch,
10
+ slugify,
11
+ createFeatureBranchName
12
+ } = require('./git');
13
+
14
+ describe('Git Module', () => {
15
+ let testEnv;
16
+
17
+ beforeEach(() => {
18
+ testEnv = createTestEnvironment();
19
+ process.chdir(testEnv.testDir);
20
+ });
21
+
22
+ afterEach(() => {
23
+ testEnv.cleanup();
24
+ });
25
+
26
+ describe('isGitInstalled()', () => {
27
+ test('should return true when git is installed', () => {
28
+ expect(isGitInstalled()).toBe(true);
29
+ });
30
+ });
31
+
32
+ describe('isGitRepo()', () => {
33
+ test('should return false in non-git directory', () => {
34
+ expect(isGitRepo()).toBe(false);
35
+ });
36
+
37
+ test('should return true in git repository', () => {
38
+ execSync('git init', { stdio: 'pipe' });
39
+ expect(isGitRepo()).toBe(true);
40
+ });
41
+ });
42
+
43
+ describe('slugify()', () => {
44
+ test('should convert text to lowercase with hyphens', () => {
45
+ expect(slugify('Hello World')).toBe('hello-world');
46
+ });
47
+
48
+ test('should remove special characters', () => {
49
+ expect(slugify('Fix: Bug #123')).toBe('fix-bug-123');
50
+ });
51
+
52
+ test('should handle multiple spaces', () => {
53
+ expect(slugify('Multiple Spaces')).toBe('multiple-spaces');
54
+ });
55
+
56
+ test('should throw error for non-string input', () => {
57
+ expect(() => slugify(123)).toThrow('slugify requires a string input');
58
+ expect(() => slugify(null)).toThrow('slugify requires a string input');
59
+ });
60
+ });
61
+
62
+ describe('createFeatureBranchName()', () => {
63
+ test('should create valid branch name', () => {
64
+ const branch = createFeatureBranchName(42, 'Add user auth');
65
+ expect(branch).toBe('feature/work-42-add-user-auth');
66
+ });
67
+
68
+ test('should throw error for invalid work item ID', () => {
69
+ expect(() => createFeatureBranchName('invalid', 'Title')).toThrow('workItemId must be a positive number');
70
+ expect(() => createFeatureBranchName(0, 'Title')).toThrow('workItemId must be a positive number');
71
+ expect(() => createFeatureBranchName(-5, 'Title')).toThrow('workItemId must be a positive number');
72
+ });
73
+
74
+ test('should throw error for invalid title', () => {
75
+ expect(() => createFeatureBranchName(1, 123)).toThrow('title must be a non-empty string');
76
+ expect(() => createFeatureBranchName(1, '')).toThrow('title must be a non-empty string');
77
+ expect(() => createFeatureBranchName(1, ' ')).toThrow('title must be a non-empty string');
78
+ });
79
+ });
80
+
81
+ describe('createOrCheckoutBranch()', () => {
82
+ beforeEach(() => {
83
+ execSync('git init', { stdio: 'pipe' });
84
+ execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
85
+ execSync('git config user.name "Test User"', { stdio: 'pipe' });
86
+ // Create initial commit
87
+ fs.writeFileSync('README.md', '# Test');
88
+ execSync('git add .', { stdio: 'pipe' });
89
+ execSync('git commit -m "Initial commit"', { stdio: 'pipe' });
90
+ });
91
+
92
+ test('should create new branch', () => {
93
+ const result = createOrCheckoutBranch('feature/test');
94
+ expect(result).toBe(true);
95
+ expect(getCurrentBranch()).toBe('feature/test');
96
+ });
97
+
98
+ test('should checkout existing branch', () => {
99
+ execSync('git checkout -b feature/existing', { stdio: 'pipe' });
100
+ execSync('git checkout main || git checkout master', { stdio: 'pipe' });
101
+
102
+ const result = createOrCheckoutBranch('feature/existing');
103
+ expect(result).toBe(true);
104
+ expect(getCurrentBranch()).toBe('feature/existing');
105
+ });
106
+
107
+ test('should throw error for invalid branch name', () => {
108
+ expect(() => createOrCheckoutBranch('branch name')).toThrow('Branch name cannot contain spaces');
109
+ expect(() => createOrCheckoutBranch('-invalid')).toThrow('Branch name cannot start with - or .');
110
+ expect(() => createOrCheckoutBranch('invalid~name')).toThrow('Branch name contains invalid characters');
111
+ });
112
+
113
+ test('should throw error in non-git repository', () => {
114
+ testEnv.cleanup();
115
+ testEnv = createTestEnvironment();
116
+ process.chdir(testEnv.testDir);
117
+
118
+ expect(() => createOrCheckoutBranch('feature/test')).toThrow('Not in a git repository');
119
+ });
120
+ });
121
+
122
+ describe('getCurrentBranch()', () => {
123
+ beforeEach(() => {
124
+ execSync('git init', { stdio: 'pipe' });
125
+ execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
126
+ execSync('git config user.name "Test User"', { stdio: 'pipe' });
127
+ fs.writeFileSync('README.md', '# Test');
128
+ execSync('git add .', { stdio: 'pipe' });
129
+ execSync('git commit -m "Initial commit"', { stdio: 'pipe' });
130
+ });
131
+
132
+ test('should return current branch name', () => {
133
+ const branch = getCurrentBranch();
134
+ expect(['main', 'master']).toContain(branch);
135
+ });
136
+
137
+ test('should return null in non-git directory', () => {
138
+ testEnv.cleanup();
139
+ testEnv = createTestEnvironment();
140
+ process.chdir(testEnv.testDir);
141
+
142
+ expect(getCurrentBranch()).toBe(null);
143
+ });
144
+ });
145
+ });
package/lib/logo.js ADDED
@@ -0,0 +1,3 @@
1
+ // Re-export terminal logo functionality
2
+ // This provides a cleaner public API at lib/ level
3
+ module.exports = require('../features/terminal-logo');
@@ -0,0 +1,24 @@
1
+ // Migration: Move epic_id data to parent_id
2
+ // Created: 2024-01-17
3
+ // Purpose: After refactoring to use parent_id traversal only, migrate old data
4
+
5
+ module.exports = {
6
+ id: '001-epic-to-parent',
7
+ description: 'Migrate epic_id to parent_id for items with epic but no parent',
8
+
9
+ up: (db) => {
10
+ return new Promise((resolve, reject) => {
11
+ db.run(
12
+ `UPDATE work_items
13
+ SET parent_id = epic_id
14
+ WHERE epic_id IS NOT NULL
15
+ AND parent_id IS NULL
16
+ AND id != epic_id`,
17
+ (err) => {
18
+ if (err) reject(err);
19
+ else resolve();
20
+ }
21
+ );
22
+ });
23
+ }
24
+ };
@@ -0,0 +1,37 @@
1
+ // Migration: Set default mode for work items
2
+ // Created: 2024-10-18
3
+ // Purpose: Ensure all non-epic work items have a mode before making it required
4
+ // Epics don't need a mode - they're containers
5
+
6
+ module.exports = {
7
+ id: '002-default-work-item-modes',
8
+ description: 'Set mode=stable for features/bugs/chores with NULL mode, set epics to NULL',
9
+
10
+ up: (db) => {
11
+ return new Promise((resolve, reject) => {
12
+ db.serialize(() => {
13
+ // Set mode=stable for features, bugs, and chores that don't have a mode
14
+ db.run(
15
+ `UPDATE work_items
16
+ SET mode = 'stable'
17
+ WHERE mode IS NULL
18
+ AND type IN ('feature', 'bug', 'chore')`,
19
+ (err) => {
20
+ if (err) return reject(err);
21
+ }
22
+ );
23
+
24
+ // Ensure epics have NULL mode (they're containers, not work items)
25
+ db.run(
26
+ `UPDATE work_items
27
+ SET mode = NULL
28
+ WHERE type = 'epic'`,
29
+ (err) => {
30
+ if (err) return reject(err);
31
+ else resolve();
32
+ }
33
+ );
34
+ });
35
+ });
36
+ }
37
+ };