jettypod 4.1.2 → 4.1.4

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 (179) hide show
  1. package/.nvmrc +1 -0
  2. package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
  3. package/docs/DECISIONS.md +10 -12
  4. package/docs/NODE_VERSION.md +83 -0
  5. package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
  6. package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
  7. package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
  8. package/hooks/post-checkout +17 -0
  9. package/hooks/post-merge +17 -0
  10. package/hooks/pre-commit +30 -0
  11. package/jettypod.js +259 -120
  12. package/lib/coverage-tracker.js +218 -0
  13. package/lib/database.js +2 -0
  14. package/lib/db-export.js +192 -0
  15. package/lib/db-import.js +193 -0
  16. package/lib/external-transition-handler.js +32 -0
  17. package/lib/git-hook-helpers.js +174 -0
  18. package/lib/git-root.js +90 -0
  19. package/lib/infrastructure-chore-generator.js +45 -0
  20. package/lib/install-hooks.js +52 -0
  21. package/lib/jettypod-backup.js +238 -0
  22. package/lib/merge-lock.js +193 -0
  23. package/lib/migrations/012-add-worktree-path.js +38 -0
  24. package/lib/migrations/013-worktrees-table.js +86 -0
  25. package/lib/migrations/014-migrate-worktree-data.js +161 -0
  26. package/lib/migrations/015-merge-locks-table.js +67 -0
  27. package/lib/pattern-finder.js +152 -0
  28. package/lib/process-manager.js +140 -0
  29. package/lib/production-standards-reader.js +13 -2
  30. package/lib/production-standards-writer.js +85 -0
  31. package/lib/skills/feature-planning/dry-run-validator.js +135 -0
  32. package/lib/skills/feature-planning/validation-formatter.js +160 -0
  33. package/lib/smart-conflict-detection.js +168 -0
  34. package/lib/smart-fetch-rebase.js +614 -0
  35. package/lib/step-definition-parser.js +76 -0
  36. package/lib/unit-test-generator.js +232 -0
  37. package/lib/verification-command-generator.js +66 -0
  38. package/lib/worktree-diagnostics.js +413 -0
  39. package/lib/worktree-facade.js +174 -0
  40. package/lib/worktree-manager.js +636 -0
  41. package/lib/worktree-reconciler.js +429 -0
  42. package/package.json +30 -3
  43. package/skills-templates/external-transition/SKILL.md +34 -3
  44. package/skills-templates/feature-planning/SKILL.md +190 -24
  45. package/skills-templates/production-mode/SKILL.md +127 -9
  46. package/skills-templates/speed-mode/SKILL.md +454 -51
  47. package/skills-templates/stable-mode/SKILL.md +285 -76
  48. package/.claude/PROTECT_SKILLS.md +0 -28
  49. package/.claude/settings.json +0 -24
  50. package/.claude/settings.local.json +0 -16
  51. package/.claude/skills/epic-planning/SKILL.md +0 -297
  52. package/.claude/skills/external-transition/SKILL.md +0 -384
  53. package/.claude/skills/feature-planning/SKILL.md +0 -464
  54. package/.claude/skills/production-mode/SKILL.md +0 -369
  55. package/.claude/skills/speed-mode/SKILL.md +0 -481
  56. package/.claude/skills/stable-mode/SKILL.md +0 -713
  57. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
  58. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
  59. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
  60. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
  61. package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
  62. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
  63. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
  64. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
  65. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
  66. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
  67. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
  68. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
  69. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
  70. package/.devpod/current-work.json +0 -10
  71. package/.devpod/work.db +0 -0
  72. package/.github/workflows/test-safety.yml +0 -85
  73. package/.jettypod/config.json +0 -5
  74. package/.jettypod/current-work.json +0 -10
  75. package/.jettypod/hooks/README.md +0 -77
  76. package/.jettypod/hooks/protect-claude-md.js +0 -338
  77. package/.jettypod/test-work.db +0 -0
  78. package/.jettypod/work.db +0 -0
  79. package/CLAUDE.md +0 -49
  80. package/SPEED-STABLE-AUDIT.md +0 -853
  81. package/SYSTEM-BEHAVIOR.md +0 -2199
  82. package/TEST_SAFETY_AUDIT.md +0 -314
  83. package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
  84. package/cucumber-report.html +0 -45
  85. package/dist/devpod-linux +0 -0
  86. package/dist/devpod-macos +0 -0
  87. package/dist/devpod-win.exe +0 -0
  88. package/docs/features/jettypod-standards-explained.md +0 -543
  89. package/docs/features/standards-inventory.md +0 -257
  90. package/features/auto-generate-production-chores.feature +0 -13
  91. package/features/backlog-command.feature +0 -26
  92. package/features/backlog-filtering-production.feature +0 -10
  93. package/features/claude-md-protection/steps.js +0 -498
  94. package/features/decisions/index.js +0 -490
  95. package/features/decisions/index.test.js +0 -208
  96. package/features/fix-text-wrapping.feature +0 -42
  97. package/features/git-hooks/git-hooks.feature +0 -30
  98. package/features/git-hooks/index.js +0 -93
  99. package/features/git-hooks/index.test.js +0 -137
  100. package/features/git-hooks/post-commit +0 -56
  101. package/features/git-hooks/post-merge +0 -47
  102. package/features/git-hooks/pre-commit +0 -28
  103. package/features/git-hooks/simple-steps.js +0 -53
  104. package/features/git-hooks/simple-test.feature +0 -10
  105. package/features/git-hooks/steps.js +0 -196
  106. package/features/jettypod-update-command.feature +0 -46
  107. package/features/mode-prompts/index.js +0 -95
  108. package/features/mode-prompts/simple-steps.js +0 -44
  109. package/features/mode-prompts/simple-test.feature +0 -9
  110. package/features/mode-prompts/validation.test.js +0 -120
  111. package/features/multiple-claude-instances.feature +0 -121
  112. package/features/production-mode-skill.feature +0 -121
  113. package/features/refactor-mode/steps.js +0 -217
  114. package/features/refactor-mode.feature +0 -49
  115. package/features/simplify-external-transition.feature +0 -166
  116. package/features/skills-update/index.test.js +0 -216
  117. package/features/step_definitions/backlog-command.steps.js +0 -37
  118. package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
  119. package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
  120. package/features/step_definitions/production-mode-skill.steps.js +0 -862
  121. package/features/step_definitions/simplify-external-transition.steps.js +0 -370
  122. package/features/step_definitions/terminal-logo.steps.js +0 -145
  123. package/features/step_definitions/update-command.steps.js +0 -183
  124. package/features/support/hooks.js +0 -9
  125. package/features/terminal-logo/index.js +0 -39
  126. package/features/terminal-logo/terminal-logo.feature +0 -30
  127. package/features/update-command/index.js +0 -181
  128. package/features/update-command/index.test.js +0 -225
  129. package/features/work-commands/bug-workflow-display.feature +0 -22
  130. package/features/work-commands/index.js +0 -498
  131. package/features/work-commands/simple-steps.js +0 -69
  132. package/features/work-commands/stable-tests.feature +0 -57
  133. package/features/work-commands/steps.js +0 -1174
  134. package/features/work-commands/validation.test.js +0 -88
  135. package/features/work-commands/work-commands.feature +0 -13
  136. package/features/work-tracking/discovery-validation.test.js +0 -228
  137. package/features/work-tracking/index.js +0 -1921
  138. package/features/work-tracking/mode-required.feature +0 -112
  139. package/features/work-tracking/phase-tracking.test.js +0 -482
  140. package/features/work-tracking/prototype-tracking.test.js +0 -485
  141. package/features/work-tracking/tree-view.test.js +0 -310
  142. package/features/work-tracking/work-set-mode.feature +0 -71
  143. package/features/work-tracking/work-start-mode.feature +0 -88
  144. package/full-test.txt +0 -0
  145. package/lib/bug-workflow.test.js +0 -177
  146. package/lib/claudemd.test.js +0 -195
  147. package/lib/config.test.js +0 -511
  148. package/lib/constants.test.js +0 -164
  149. package/lib/current-work.test.js +0 -146
  150. package/lib/database-project-config.test.js +0 -111
  151. package/lib/database.test.js +0 -106
  152. package/lib/decisions-generator.test.js +0 -457
  153. package/lib/decisions-helpers.test.js +0 -310
  154. package/lib/git-coordinator.js +0 -167
  155. package/lib/git.test.js +0 -145
  156. package/lib/migrations/002-default-work-item-modes.test.js +0 -351
  157. package/lib/production-chore-generator.test.js +0 -432
  158. package/lib/production-context-detector.test.js +0 -277
  159. package/lib/production-scenario-appender.test.js +0 -235
  160. package/lib/production-scenario-validator.test.js +0 -246
  161. package/lib/production-standards-reader.test.js +0 -270
  162. package/lib/project-state.test.js +0 -92
  163. package/lib/push-queue.js +0 -417
  164. package/lib/queue-processor.js +0 -74
  165. package/lib/test-helpers.js +0 -202
  166. package/lib/test-helpers.test.js +0 -255
  167. package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
  168. package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
  169. package/prototypes/2025-01-11-production-mode-guided.js +0 -217
  170. package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
  171. package/prototypes/2025-01-11-production-standards-example.md +0 -204
  172. package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
  173. package/prototypes/test/index.html +0 -1
  174. package/setup-dist-repo.sh +0 -68
  175. package/test-production-standards-engine.js +0 -130
  176. package/test-results.json +0 -2195
  177. package/test-safety-check.sh +0 -80
  178. package/work-item-tracking-plan.md +0 -199
  179. /package/{.jettypod/devpod.db → jettypod.db} +0 -0
@@ -1,370 +0,0 @@
1
- const { Given, When, Then } = require('@cucumber/cucumber');
2
- const { execSync } = require('child_process');
3
- const fs = require('fs');
4
- const path = require('path');
5
- const assert = require('assert');
6
-
7
- let testDir;
8
- let db;
9
-
10
- Given('I have a JettyPod project in internal state', function() {
11
- // Create test directory
12
- testDir = path.join(__dirname, '../../test-tmp', `external-transition-${Date.now()}`);
13
- fs.mkdirSync(testDir, { recursive: true });
14
-
15
- // Change to test directory
16
- process.chdir(testDir);
17
-
18
- // Initialize JettyPod
19
- execSync('node ' + path.join(__dirname, '../../jettypod.js') + ' init', { cwd: testDir });
20
-
21
- // Verify internal state
22
- const config = require('../../lib/config');
23
- const projectConfig = config.read();
24
- assert.strictEqual(projectConfig.project_state, 'internal');
25
-
26
- // Get database connection
27
- const { getDb } = require('../../lib/database');
28
- db = getDb();
29
- });
30
-
31
- Given('I have some features in stable mode with production chores already created', async function() {
32
- // Create a feature in stable mode
33
- await new Promise((resolve, reject) => {
34
- db.run(
35
- `INSERT INTO work_items (type, title, description, status, mode, phase) VALUES (?, ?, ?, ?, ?, ?)`,
36
- ['feature', 'Test Feature', 'A test feature', 'done', 'stable', 'implementation'],
37
- function(err) {
38
- if (err) reject(err);
39
- else {
40
- const featureId = this.lastID;
41
-
42
- // Create production chores for this feature
43
- const productionChores = [
44
- 'Security hardening for Test Feature',
45
- 'Scale testing for Test Feature',
46
- 'Compliance requirements for Test Feature'
47
- ];
48
-
49
- let completed = 0;
50
- productionChores.forEach(title => {
51
- db.run(
52
- `INSERT INTO work_items (type, title, parent_id, status, mode) VALUES (?, ?, ?, ?, ?)`,
53
- ['chore', title, featureId, 'todo', 'production'],
54
- (err) => {
55
- if (err) reject(err);
56
- completed++;
57
- if (completed === productionChores.length) resolve();
58
- }
59
- );
60
- });
61
- }
62
- }
63
- );
64
- });
65
- });
66
-
67
- Then('the project_state should be updated to {string}', function(expectedState) {
68
- const config = require('../../lib/config');
69
- const projectConfig = config.read();
70
- assert.strictEqual(projectConfig.project_state, expectedState);
71
- });
72
-
73
- Then('an {string} epic should be created', function(epicTitle) {
74
- return new Promise((resolve, reject) => {
75
- db.get(
76
- `SELECT * FROM work_items WHERE type = ? AND title = ?`,
77
- ['epic', epicTitle],
78
- (err, row) => {
79
- if (err) reject(err);
80
- assert.ok(row, `Epic "${epicTitle}" should exist`);
81
- this.epicId = row.id;
82
- resolve();
83
- }
84
- );
85
- });
86
- });
87
-
88
- Then('{int} infrastructure features should be created under the epic', function(expectedCount) {
89
- return new Promise((resolve, reject) => {
90
- db.all(
91
- `SELECT * FROM work_items WHERE type = ? AND parent_id = ?`,
92
- ['feature', this.epicId],
93
- (err, rows) => {
94
- if (err) reject(err);
95
- assert.strictEqual(rows.length, expectedCount, `Should have ${expectedCount} features`);
96
-
97
- // Verify feature titles match expected categories
98
- const titles = rows.map(r => r.title);
99
- assert.ok(titles.some(t => t.includes('Security')), 'Should have Security feature');
100
- assert.ok(titles.some(t => t.includes('Monitoring')), 'Should have Monitoring feature');
101
- assert.ok(titles.some(t => t.includes('Infrastructure')), 'Should have Infrastructure feature');
102
- assert.ok(titles.some(t => t.includes('Compliance')), 'Should have Compliance feature');
103
-
104
- this.featureIds = rows.map(r => r.id);
105
- resolve();
106
- }
107
- );
108
- });
109
- });
110
-
111
- Then('{int} infrastructure chores should be created across the features', function(expectedCount) {
112
- return new Promise((resolve, reject) => {
113
- const placeholders = this.featureIds.map(() => '?').join(',');
114
- db.all(
115
- `SELECT * FROM work_items WHERE type = ? AND parent_id IN (${placeholders})`,
116
- ['chore', ...this.featureIds],
117
- (err, rows) => {
118
- if (err) reject(err);
119
- assert.strictEqual(rows.length, expectedCount, `Should have ${expectedCount} chores`);
120
- resolve();
121
- }
122
- );
123
- });
124
- });
125
-
126
- Then('production mode work items should now be visible in the backlog', function() {
127
- // Verify that production items would be visible
128
- // This is tested by checking that the state allows them to show
129
- const config = require('../../lib/config');
130
- const projectConfig = config.read();
131
- assert.strictEqual(projectConfig.project_state, 'external', 'External state enables production visibility');
132
-
133
- // Verify production chores exist
134
- return new Promise((resolve, reject) => {
135
- db.all(
136
- `SELECT * FROM work_items WHERE mode = ?`,
137
- ['production'],
138
- (err, rows) => {
139
- if (err) reject(err);
140
- assert.ok(rows.length > 0, 'Production items should exist');
141
- resolve();
142
- }
143
- );
144
- });
145
- });
146
-
147
- Then('no duplicate production chores should be created for existing features', function() {
148
- return new Promise((resolve, reject) => {
149
- // Check that we only have the 3 production chores we created in setup
150
- // plus the infrastructure chores (which are not duplicates)
151
- db.all(
152
- `SELECT * FROM work_items WHERE mode = ? AND title LIKE ?`,
153
- ['production', '%Test Feature%'],
154
- (err, rows) => {
155
- if (err) reject(err);
156
- assert.strictEqual(rows.length, 3, 'Should only have the original 3 production chores for Test Feature');
157
- resolve();
158
- }
159
- );
160
- });
161
- });
162
-
163
- // STABLE MODE: Error handling and edge case step definitions
164
-
165
- Given('I have a JettyPod project in external state', function() {
166
- // Create test directory
167
- testDir = path.join(__dirname, '../../test-tmp', `external-transition-${Date.now()}`);
168
- fs.mkdirSync(testDir, { recursive: true });
169
-
170
- // Change to test directory
171
- process.chdir(testDir);
172
-
173
- // Initialize JettyPod
174
- execSync('node ' + path.join(__dirname, '../../jettypod.js') + ' init', { cwd: testDir });
175
-
176
- // Update to external state
177
- const config = require('../../lib/config');
178
- config.update({ project_state: 'external' });
179
-
180
- // Verify external state
181
- const projectConfig = config.read();
182
- assert.strictEqual(projectConfig.project_state, 'external');
183
-
184
- // Get database connection
185
- const { getDb } = require('../../lib/database');
186
- db = getDb();
187
- });
188
-
189
- Given('an {string} epic already exists', async function(epicTitle) {
190
- // Create the epic manually
191
- await new Promise((resolve, reject) => {
192
- db.run(
193
- `INSERT INTO work_items (type, title, description, status) VALUES (?, ?, ?, ?)`,
194
- ['epic', epicTitle, 'Existing infrastructure epic', 'todo'],
195
- function(err) {
196
- if (err) reject(err);
197
- else {
198
- this.existingEpicId = this.lastID;
199
- resolve();
200
- }
201
- }
202
- );
203
- });
204
- });
205
-
206
- Given('the database connection will fail', function() {
207
- // Mock database failure - we'll implement this by closing the db
208
- this.shouldFailDatabase = true;
209
- if (db) {
210
- db.close();
211
- db = null;
212
- }
213
- });
214
-
215
- Given('work item creation will fail after creating the epic', function() {
216
- // This will be tested by mocking the create function
217
- this.shouldFailAfterEpic = true;
218
- });
219
-
220
- Given('the infrastructure checklist has invalid data', function() {
221
- // Mock invalid checklist data - we'll test this by modifying the checklist temporarily
222
- this.shouldHaveInvalidChecklist = true;
223
- });
224
-
225
- Then('I should see a message {string}', function(expectedMessage) {
226
- assert.ok(this.output, 'Should have output');
227
- assert.ok(this.output.includes(expectedMessage), `Output should contain "${expectedMessage}". Got: ${this.output}`);
228
- });
229
-
230
- Then('no duplicate infrastructure items should be created', function() {
231
- return new Promise((resolve, reject) => {
232
- db.all(
233
- `SELECT * FROM work_items WHERE type = 'epic' AND title = 'Infrastructure Readiness'`,
234
- [],
235
- (err, rows) => {
236
- if (err) reject(err);
237
- // Should have at most 1 epic
238
- assert.ok(rows.length <= 1, 'Should not have duplicate Infrastructure Readiness epics');
239
- resolve();
240
- }
241
- );
242
- });
243
- });
244
-
245
- Then('the command should exit successfully', function() {
246
- assert.ok(!this.error || this.error.status === 0, 'Command should exit successfully');
247
- });
248
-
249
- Then('no duplicate {string} epic should be created', function(epicTitle) {
250
- return new Promise((resolve, reject) => {
251
- db.all(
252
- `SELECT * FROM work_items WHERE type = 'epic' AND title = ?`,
253
- [epicTitle],
254
- (err, rows) => {
255
- if (err) reject(err);
256
- assert.strictEqual(rows.length, 1, `Should only have one "${epicTitle}" epic`);
257
- resolve();
258
- }
259
- );
260
- });
261
- });
262
-
263
- Then('existing infrastructure items should be preserved', function() {
264
- return new Promise((resolve, reject) => {
265
- if (!this.existingEpicId) {
266
- return resolve(); // No existing items to check
267
- }
268
-
269
- db.get(
270
- `SELECT * FROM work_items WHERE id = ?`,
271
- [this.existingEpicId],
272
- (err, row) => {
273
- if (err) reject(err);
274
- assert.ok(row, 'Existing epic should still exist');
275
- resolve();
276
- }
277
- );
278
- });
279
- });
280
-
281
- Then('I should see a message about existing infrastructure', function() {
282
- assert.ok(this.output, 'Should have output');
283
- assert.ok(
284
- this.output.includes('infrastructure') || this.output.includes('existing') || this.output.includes('already'),
285
- 'Output should mention existing infrastructure'
286
- );
287
- });
288
-
289
- Then('I should see a clear error message about database failure', function() {
290
- assert.ok(this.error || this.output.includes('error'), 'Should have error output');
291
- const errorText = this.error ? (this.error.message || this.error.stderr || this.error.stdout) : this.output;
292
- assert.ok(
293
- errorText.toLowerCase().includes('database') || errorText.toLowerCase().includes('connection'),
294
- 'Error should mention database or connection'
295
- );
296
- });
297
-
298
- Then('the project_state should remain {string}', function(expectedState) {
299
- const config = require('../../lib/config');
300
- const projectConfig = config.read();
301
- assert.strictEqual(projectConfig.project_state, expectedState);
302
- });
303
-
304
- Then('no partial infrastructure items should be created', function() {
305
- return new Promise((resolve, reject) => {
306
- db.all(
307
- `SELECT * FROM work_items WHERE title LIKE '%Infrastructure%' OR title LIKE '%Security%' OR title LIKE '%Monitoring%' OR title LIKE '%Compliance%'`,
308
- [],
309
- (err, rows) => {
310
- if (err) reject(err);
311
- assert.strictEqual(rows.length, 0, 'Should not have any infrastructure items created');
312
- resolve();
313
- }
314
- );
315
- });
316
- });
317
-
318
- Then('I should see an error message about failed creation', function() {
319
- assert.ok(this.error || this.output.includes('error'), 'Should have error output');
320
- const errorText = this.error ? (this.error.message || this.error.stderr || this.error.stdout) : this.output;
321
- assert.ok(
322
- errorText.toLowerCase().includes('fail') || errorText.toLowerCase().includes('error'),
323
- 'Error should mention failure'
324
- );
325
- });
326
-
327
- Then('any partial work items should be cleaned up', function() {
328
- // Verify that if epic was created, it's been rolled back
329
- return new Promise((resolve, reject) => {
330
- db.all(
331
- `SELECT * FROM work_items WHERE type = 'epic' AND title = 'Infrastructure Readiness'`,
332
- [],
333
- (err, rows) => {
334
- if (err) reject(err);
335
- assert.strictEqual(rows.length, 0, 'Partial epic should be cleaned up');
336
- resolve();
337
- }
338
- );
339
- });
340
- });
341
-
342
- Then('I should see a validation error message', function() {
343
- assert.ok(this.error || this.output.includes('error'), 'Should have error output');
344
- const errorText = this.error ? (this.error.message || this.error.stderr || this.error.stdout) : this.output;
345
- assert.ok(
346
- errorText.toLowerCase().includes('invalid') || errorText.toLowerCase().includes('validation'),
347
- 'Error should mention validation or invalid data'
348
- );
349
- });
350
-
351
- Then('no infrastructure items should be created', function() {
352
- return new Promise((resolve, reject) => {
353
- db.all(
354
- `SELECT * FROM work_items WHERE title LIKE '%Infrastructure%' OR title LIKE '%Security%' OR title LIKE '%Monitoring%' OR title LIKE '%Compliance%'`,
355
- [],
356
- (err, rows) => {
357
- if (err) reject(err);
358
- assert.strictEqual(rows.length, 0, 'Should not have any infrastructure items');
359
- resolve();
360
- }
361
- );
362
- });
363
- });
364
-
365
- // Cleanup
366
- process.on('exit', () => {
367
- if (testDir && fs.existsSync(testDir)) {
368
- fs.rmSync(testDir, { recursive: true, force: true });
369
- }
370
- });
@@ -1,145 +0,0 @@
1
- const { Given, When, Then } = require('@cucumber/cucumber');
2
- const assert = require('assert');
3
- const path = require('path');
4
- const fs = require('fs');
5
-
6
- Given('the logo module exists', function () {
7
- const logoPath = path.join(__dirname, '../../lib/logo.js');
8
- assert(fs.existsSync(logoPath), `Logo module should exist at ${logoPath}`);
9
- this.logoPath = logoPath;
10
- });
11
-
12
- When('I import it', function () {
13
- // Import the logo module
14
- this.logoModule = require(this.logoPath);
15
- });
16
-
17
- Then('it exports a {string} function', function (functionName) {
18
- assert(this.logoModule, 'Logo module should be imported');
19
- assert(typeof this.logoModule[functionName] === 'function', `Should export a ${functionName} function`);
20
- this.exportedFunction = this.logoModule[functionName];
21
- });
22
-
23
- When('I call the showLogo function', function () {
24
- // Capture console.log output
25
- const originalLog = console.log;
26
- const logOutput = [];
27
-
28
- console.log = (...args) => {
29
- logOutput.push(args.join(' '));
30
- };
31
-
32
- try {
33
- // Call the showLogo function
34
- const { showLogo } = require('../../lib/logo.js');
35
- showLogo();
36
- } finally {
37
- console.log = originalLog;
38
- }
39
-
40
- this.logoOutput = logOutput;
41
- });
42
-
43
- Then('it outputs {int} lines of logo art', function (expectedLines) {
44
- assert(this.logoOutput, 'Logo output should exist');
45
- // Count total lines across all console.log calls (split by newlines)
46
- const totalLines = this.logoOutput.reduce((count, output) => {
47
- return count + output.split('\n').filter(line => line.trim().length > 0).length;
48
- }, 0);
49
- assert(totalLines >= expectedLines,
50
- `Expected at least ${expectedLines} lines but got ${totalLines}`);
51
- });
52
-
53
- Then('it includes the version subtitle', function () {
54
- assert(this.logoOutput, 'Logo output should exist');
55
- const hasVersionLine = this.logoOutput.some(line =>
56
- line.includes('version') || line.includes('v') || line.includes('.')
57
- );
58
- assert(hasVersionLine, 'Logo should include version subtitle');
59
- });
60
-
61
- When('I run jettypod init', function () {
62
- const { execSync } = require('child_process');
63
- const jettypodPath = path.join(__dirname, '../../jettypod.js');
64
- const skillsSourceDir = path.join(__dirname, '../../.claude/skills');
65
-
66
- try {
67
- const output = execSync(`node ${jettypodPath} init`, {
68
- cwd: this.testDir || process.cwd(),
69
- encoding: 'utf-8',
70
- env: { ...process.env, JETTYPOD_SKILLS_SOURCE_DIR: skillsSourceDir }
71
- });
72
- this.initOutput = output;
73
- } catch (err) {
74
- this.initOutput = err.stdout || '';
75
- this.initError = err;
76
- }
77
- });
78
-
79
- Then('the output contains the unicode gradient logo', function () {
80
- assert(this.initOutput || this.commandOutput, 'Should have output from jettypod init');
81
- const output = this.initOutput || this.commandOutput;
82
-
83
- // Check for DEV or POD in the output
84
- const hasLogo = output.includes('DEV') || output.includes('POD') ||
85
- output.includes('█') || output.includes('▓') ||
86
- output.includes('▒') || output.includes('░');
87
-
88
- assert(hasLogo, 'Output should contain unicode gradient logo characters');
89
- });
90
-
91
- Then('I see the unicode logo', function () {
92
- assert(this.initOutput || this.commandOutput, 'Should have output from jettypod init');
93
- const output = this.initOutput || this.commandOutput;
94
-
95
- const hasLogo = output.includes('DEV') || output.includes('POD') ||
96
- output.includes('█') || output.includes('▓');
97
-
98
- assert(hasLogo, 'Should see unicode logo in output');
99
- });
100
-
101
- Then('the logo includes {string} and {string} text', function (text1, text2) {
102
- const output = this.initOutput || this.commandOutput;
103
- assert(output, 'Should have output');
104
- // The logo uses box-drawing characters, not literal text
105
- // Just check that the output has the box characters that form the logo
106
- const hasBoxChars = output.includes('█') || output.includes('╔') || output.includes('║');
107
- assert(hasBoxChars, `Logo should include box-drawing characters (forms ${text1} and ${text2})`);
108
- });
109
-
110
- Then('the logo uses ANSI color codes', function () {
111
- const output = this.initOutput || this.commandOutput;
112
- assert(output, 'Should have output');
113
-
114
- // Check for ANSI escape codes
115
- const hasAnsiCodes = output.includes('\x1b[') || output.includes('\u001b[');
116
- assert(hasAnsiCodes, 'Logo should use ANSI color codes');
117
- });
118
-
119
- Then('the .jettypod directory is created', function () {
120
- const jettypodDir = path.join(this.testDir || process.cwd(), '.jettypod');
121
- assert(fs.existsSync(jettypodDir), '.jettypod directory should be created');
122
-
123
- // Verify it contains expected files
124
- const configPath = path.join(jettypodDir, 'config.json');
125
- assert(fs.existsSync(configPath), 'config.json should exist in .jettypod directory');
126
- });
127
-
128
- Then('git hooks are installed', function () {
129
- const hooksDir = path.join(this.testDir || process.cwd(), '.git', 'hooks');
130
-
131
- // Check if .git directory exists (it might not if init was run without git)
132
- const gitDir = path.join(this.testDir || process.cwd(), '.git');
133
- if (!fs.existsSync(gitDir)) {
134
- // Skip this check if not a git repo
135
- return;
136
- }
137
-
138
- // Check for post-commit hook
139
- const postCommitHook = path.join(hooksDir, 'post-commit');
140
- assert(fs.existsSync(postCommitHook), 'post-commit hook should be installed');
141
-
142
- // Check for post-merge hook
143
- const postMergeHook = path.join(hooksDir, 'post-merge');
144
- assert(fs.existsSync(postMergeHook), 'post-merge hook should be installed');
145
- });
@@ -1,183 +0,0 @@
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', 'jettypod-update-test-' + Date.now());
8
- let originalDir;
9
- let testContext = {};
10
-
11
- Given('jettypod version {float}.{int}.{int} is installed', function (major, minor, patch) {
12
- originalDir = process.cwd();
13
- // SAFETY: Only delete if testDir is in /tmp
14
- if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
15
- fs.rmSync(testDir, { recursive: true, force: true });
16
- }
17
- fs.mkdirSync(testDir, { recursive: true });
18
- process.chdir(testDir);
19
-
20
- // Store version for later checks
21
- testContext.currentVersion = `${major}.${minor}.${patch}`;
22
- });
23
-
24
- Given('npm registry has version {float}.{int}.{int} available', function (major, minor, patch) {
25
- testContext.registryVersion = `${major}.${minor}.${patch}`;
26
- // In a real implementation, we'd mock the npm registry
27
- // For now, just store the expected version
28
- });
29
-
30
- Given('jettypod is installed', function () {
31
- originalDir = process.cwd();
32
- // SAFETY: Only delete if testDir is in /tmp
33
- if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
34
- fs.rmSync(testDir, { recursive: true, force: true });
35
- }
36
- fs.mkdirSync(testDir, { recursive: true });
37
- process.chdir(testDir);
38
- });
39
-
40
- Given('there is no internet connection', function () {
41
- testContext.noInternet = true;
42
- // We'll simulate this in the When step
43
- });
44
-
45
- Given('I am in a directory without .claude\\/', function () {
46
- originalDir = process.cwd();
47
- // SAFETY: Only delete if testDir is in /tmp
48
- if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
49
- fs.rmSync(testDir, { recursive: true, force: true });
50
- }
51
- fs.mkdirSync(testDir, { recursive: true });
52
- process.chdir(testDir);
53
-
54
- // Initialize git for the project
55
- execSync('git init', { stdio: 'pipe' });
56
- execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
57
- execSync('git config user.name "Test"', { stdio: 'pipe' });
58
- });
59
-
60
- When('I run update command {string}', async function (command) {
61
- const args = command.replace('jettypod ', '').trim();
62
-
63
- if (args === 'update') {
64
- // Run update command with mocked dependencies
65
- const updateCommand = require('../../features/update-command');
66
- const originalConsoleLog = console.log;
67
- const originalConsoleError = console.error;
68
- let output = '';
69
-
70
- console.log = (...args) => {
71
- output += args.join(' ') + '\n';
72
- };
73
- console.error = (...args) => {
74
- output += args.join(' ') + '\n';
75
- };
76
-
77
- try {
78
- const mockOptions = {
79
- getCurrentVersion: () => testContext.currentVersion || '3.0.0',
80
- getLatestVersion: async () => {
81
- if (testContext.noInternet) {
82
- throw new Error('network error');
83
- }
84
- return testContext.registryVersion || '3.0.0';
85
- },
86
- updateJettyPod: (version) => {
87
- output += `📦 Installing jettypod@${version}...\n`;
88
- return true;
89
- }
90
- };
91
-
92
- await updateCommand.runUpdate(mockOptions);
93
- testContext.exitCode = 0;
94
- } catch (err) {
95
- output += err.message + '\n';
96
- testContext.exitCode = 1;
97
- } finally {
98
- console.log = originalConsoleLog;
99
- console.error = originalConsoleError;
100
- }
101
-
102
- testContext.output = output;
103
- } else {
104
- // Run other commands normally (init, etc)
105
- try {
106
- const skillsSourceDir = path.join(originalDir, '.claude', 'skills');
107
- const output = execSync(
108
- `node ${path.join(originalDir, 'jettypod.js')} ${args}`,
109
- {
110
- cwd: testDir,
111
- encoding: 'utf-8',
112
- env: { ...process.env, JETTYPOD_SKILLS_SOURCE_DIR: skillsSourceDir }
113
- }
114
- );
115
- testContext.output = output;
116
- testContext.exitCode = 0;
117
- } catch (err) {
118
- testContext.output = err.stdout || err.stderr || err.message;
119
- testContext.exitCode = err.status || 1;
120
- }
121
- }
122
- });
123
-
124
- Then('jettypod checks npm registry for latest version', function () {
125
- // This is implementation detail - can't easily test without mocking
126
- // Just verify the command ran
127
- assert(testContext.output, 'Command should produce output');
128
- });
129
-
130
- Then('jettypod shows {string}', function (expectedMessage) {
131
- assert(testContext.output.includes(expectedMessage),
132
- `Output should contain: ${expectedMessage}\nActual output: ${testContext.output}`);
133
- });
134
-
135
- Then('jettypod downloads and installs version {float}.{int}.{int}', function (major, minor, patch) {
136
- // This would require mocking npm install
137
- // For now, just check the command attempted to update
138
- assert(testContext.exitCode === 0 || testContext.output.includes('update'),
139
- 'Should attempt to update');
140
- });
141
-
142
- Then('skills are refreshed in current project', function () {
143
- // Check that skills directory exists or was attempted to be refreshed
144
- const skillsDir = path.join(testDir, '.claude', 'skills');
145
- // After update, skills should be present (if .claude exists) or attempted
146
- // This is satisfied if update command ran successfully
147
- assert(testContext.output, 'Update command should run');
148
- });
149
-
150
- Then('jettypod initializes the project', function () {
151
- assert(testContext.exitCode === 0, 'Init should succeed');
152
- assert(testContext.output.includes('JettyPod initialized') ||
153
- testContext.output.includes('CLAUDE.md'),
154
- 'Should show initialization message');
155
- });
156
-
157
- Then('.claude\\/ directory is created', function () {
158
- const claudeDir = path.join(testDir, '.claude');
159
- assert(fs.existsSync(claudeDir), '.claude directory should exist');
160
- });
161
-
162
- Then('CLAUDE.md is created', function () {
163
- // Use this.testDir if set by another step file, otherwise use local testDir or cwd
164
- const dir = this.testDir || testDir || process.cwd();
165
- const claudeMd = path.join(dir, 'CLAUDE.md');
166
- assert(fs.existsSync(claudeMd), 'CLAUDE.md should exist');
167
- });
168
-
169
- Then('skills are installed', function () {
170
- const skillsDir = path.join(testDir, '.claude', 'skills');
171
- assert(fs.existsSync(skillsDir), 'Skills directory should exist');
172
-
173
- // Cleanup after all checks
174
- if (originalDir && testDir && fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
175
- process.chdir(originalDir);
176
- fs.rmSync(testDir, { recursive: true, force: true });
177
- }
178
- });
179
-
180
- Then('skills are still refreshed in current project using existing jettypod', function () {
181
- // Even without network, skills should be refreshed from local jettypod
182
- assert(testContext.output, 'Command should produce output');
183
- });