@pennyfarthing/core 7.7.0 → 7.8.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 (58) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor.d.ts +3 -0
  4. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  5. package/packages/core/dist/cli/commands/doctor.js +134 -9
  6. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  7. package/pennyfarthing-dist/agents/sm-setup.md +37 -2
  8. package/pennyfarthing-dist/agents/sm.md +68 -22
  9. package/pennyfarthing-dist/agents/workflow-status-check.md +11 -1
  10. package/pennyfarthing-dist/commands/git-cleanup.md +43 -308
  11. package/pennyfarthing-dist/commands/solo.md +31 -0
  12. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +1 -1
  13. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +83 -83
  14. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +11 -11
  15. package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -2
  16. package/pennyfarthing-dist/scripts/core/check-context.sh +3 -0
  17. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
  18. package/pennyfarthing-dist/scripts/core/prime.sh +3 -157
  19. package/pennyfarthing-dist/scripts/core/run.sh +9 -0
  20. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  21. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +117 -20
  22. package/pennyfarthing-dist/scripts/jira/README.md +10 -7
  23. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +13 -0
  24. package/pennyfarthing-dist/scripts/misc/add_short_names.py +226 -0
  25. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +6 -5
  26. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +319 -0
  27. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.sh +6 -5
  28. package/pennyfarthing-dist/scripts/sprint/import_epic_to_future.py +270 -0
  29. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +59 -0
  30. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +8 -6
  31. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +402 -0
  32. package/pennyfarthing-dist/scripts/workflow/check.sh +3 -476
  33. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +61 -0
  34. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +13 -0
  35. package/pennyfarthing-dist/skills/judge/SKILL.md +57 -0
  36. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +4 -22
  37. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +83 -0
  38. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-02-categorize.md +116 -0
  39. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-03-execute.md +210 -0
  40. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +88 -0
  41. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +71 -0
  42. package/pennyfarthing-dist/workflows/git-cleanup.yaml +59 -0
  43. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.mjs +0 -393
  44. package/pennyfarthing-dist/scripts/hooks/tests/question-reflector.test.mjs +0 -545
  45. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.mjs +0 -327
  46. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.test.mjs +0 -503
  47. package/pennyfarthing-dist/scripts/jira/jira-lib.mjs +0 -443
  48. package/pennyfarthing-dist/scripts/jira/jira-sync-story.mjs +0 -208
  49. package/pennyfarthing-dist/scripts/jira/jira-sync.mjs +0 -198
  50. package/pennyfarthing-dist/scripts/misc/add-short-names.mjs +0 -264
  51. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.mjs +0 -474
  52. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.mjs +0 -377
  53. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.js +0 -492
  54. /package/pennyfarthing-dist/guides/{AGENT-COORDINATION.md → agent-coordination.md} +0 -0
  55. /package/pennyfarthing-dist/guides/{HOOKS.md → hooks.md} +0 -0
  56. /package/pennyfarthing-dist/guides/{PROMPT-PATTERNS.md → prompt-patterns.md} +0 -0
  57. /package/pennyfarthing-dist/guides/{SESSION-ARTIFACTS.md → session-artifacts.md} +0 -0
  58. /package/pennyfarthing-dist/guides/{XML-TAGS.md → xml-tags.md} +0 -0
@@ -1,503 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * jira-bidirectional-sync.test.mjs - Tests for bidirectional Jira sync
4
- *
5
- * Story: MSSCI-11842
6
- * TDD Phase: RED
7
- *
8
- * Run with: node --test pennyfarthing-dist/scripts/utils/jira/jira-bidirectional-sync.test.mjs
9
- */
10
-
11
- import { describe, it, beforeEach, afterEach, mock } from 'node:test';
12
- import assert from 'node:assert';
13
-
14
- // The module under test (will be created by Dev)
15
- // import { ... } from './jira-bidirectional-sync.mjs';
16
-
17
- // For now, import from jira-lib.mjs to test helper functions
18
- import {
19
- mapStatusToJira,
20
- mapJiraToStatus,
21
- extractJiraKey
22
- } from './jira-lib.mjs';
23
-
24
- // =============================================================================
25
- // Test Fixtures
26
- // =============================================================================
27
-
28
- /**
29
- * Sample YAML story data (as parsed from sprint YAML)
30
- */
31
- const sampleYamlStories = [
32
- {
33
- id: 'MSSCI-11842',
34
- title: 'Bidirectional sync script',
35
- status: 'in_progress',
36
- points: 4,
37
- priority: 'P2'
38
- },
39
- {
40
- id: 'MSSCI-11843',
41
- title: 'Document Jira auto-creation',
42
- status: 'backlog',
43
- points: 2,
44
- priority: 'P2'
45
- },
46
- {
47
- id: 'MSSCI-11844',
48
- title: 'YAML-only story',
49
- status: 'backlog',
50
- points: 3,
51
- priority: 'P3'
52
- }
53
- ];
54
-
55
- /**
56
- * Sample Jira story data (as returned from Jira API)
57
- */
58
- const sampleJiraStories = [
59
- {
60
- key: 'MSSCI-11842',
61
- fields: {
62
- summary: 'Bidirectional sync script',
63
- status: { name: 'In Progress' },
64
- customfield_10031: 4, // Story points
65
- priority: { name: 'Medium' }
66
- }
67
- },
68
- {
69
- key: 'MSSCI-11843',
70
- fields: {
71
- summary: 'Document Jira auto-creation',
72
- status: { name: 'Done' }, // Different from YAML!
73
- customfield_10031: 2,
74
- priority: { name: 'Medium' }
75
- }
76
- },
77
- {
78
- key: 'MSSCI-11850',
79
- fields: {
80
- summary: 'Jira-only story',
81
- status: { name: 'To Do' },
82
- customfield_10031: 5,
83
- priority: { name: 'High' }
84
- }
85
- }
86
- ];
87
-
88
- // =============================================================================
89
- // AC1: Script syncs status changes both directions
90
- // =============================================================================
91
-
92
- describe('AC1: Bidirectional status sync', () => {
93
-
94
- describe('YAML → Jira direction', () => {
95
-
96
- it('should detect when YAML status differs from Jira status', async () => {
97
- // Import the function that will be implemented
98
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
99
-
100
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
101
- syncStatus: true
102
- });
103
-
104
- // MSSCI-11843: YAML=backlog, Jira=Done
105
- const story11843 = plan.changes.find(c => c.key === 'MSSCI-11843');
106
- assert.ok(story11843, 'Should detect status difference for MSSCI-11843');
107
- assert.strictEqual(story11843.yamlStatus, 'backlog');
108
- assert.strictEqual(story11843.jiraStatus, 'Done');
109
- });
110
-
111
- it('should generate Jira update action when YAML is source of truth', async () => {
112
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
113
-
114
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
115
- syncStatus: true,
116
- direction: 'yaml-to-jira'
117
- });
118
-
119
- const story11843 = plan.changes.find(c => c.key === 'MSSCI-11843');
120
- assert.ok(story11843);
121
- assert.strictEqual(story11843.action, 'update-jira');
122
- assert.strictEqual(story11843.targetStatus, 'To Do'); // backlog maps to To Do
123
- });
124
-
125
- });
126
-
127
- describe('Jira → YAML direction', () => {
128
-
129
- it('should generate YAML update action when Jira is source of truth', async () => {
130
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
131
-
132
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
133
- syncStatus: true,
134
- direction: 'jira-to-yaml'
135
- });
136
-
137
- const story11843 = plan.changes.find(c => c.key === 'MSSCI-11843');
138
- assert.ok(story11843);
139
- assert.strictEqual(story11843.action, 'update-yaml');
140
- assert.strictEqual(story11843.targetStatus, 'done'); // Done maps to done
141
- });
142
-
143
- it('should correctly map Jira status to YAML status', () => {
144
- // Test existing helper function
145
- assert.strictEqual(mapJiraToStatus('To Do'), 'backlog');
146
- assert.strictEqual(mapJiraToStatus('In Progress'), 'in-progress');
147
- assert.strictEqual(mapJiraToStatus('Done'), 'done');
148
- assert.strictEqual(mapJiraToStatus('In Review'), 'review');
149
- });
150
-
151
- });
152
-
153
- describe('Bidirectional conflict detection', () => {
154
-
155
- it('should detect conflicts when both systems changed', async () => {
156
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
157
-
158
- // Simulate scenario where timestamps indicate both changed
159
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
160
- syncStatus: true,
161
- direction: 'bidirectional',
162
- lastSyncTime: new Date('2026-01-17T00:00:00Z')
163
- });
164
-
165
- const conflicts = plan.conflicts || [];
166
- // Should identify potential conflicts for manual resolution
167
- assert.ok(Array.isArray(conflicts));
168
- });
169
-
170
- it('should use Jira as default winner on conflict', async () => {
171
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
172
-
173
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
174
- syncStatus: true,
175
- direction: 'bidirectional'
176
- // No yamlWins flag, so Jira should win
177
- });
178
-
179
- // Default: Jira wins conflicts
180
- const story11843 = plan.changes.find(c => c.key === 'MSSCI-11843');
181
- if (story11843) {
182
- assert.strictEqual(story11843.action, 'update-yaml');
183
- }
184
- });
185
-
186
- it('should use YAML as winner when --yaml-wins flag is set', async () => {
187
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
188
-
189
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
190
- syncStatus: true,
191
- direction: 'bidirectional',
192
- yamlWins: true
193
- });
194
-
195
- const story11843 = plan.changes.find(c => c.key === 'MSSCI-11843');
196
- if (story11843) {
197
- assert.strictEqual(story11843.action, 'update-jira');
198
- }
199
- });
200
-
201
- });
202
-
203
- });
204
-
205
- // =============================================================================
206
- // AC2: Points updated in Jira match sprint YAML
207
- // =============================================================================
208
-
209
- describe('AC2: Story points sync', () => {
210
-
211
- it('should detect when YAML points differ from Jira points', async () => {
212
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
213
-
214
- // Create test data with mismatched points
215
- const yamlWithDifferentPoints = [
216
- { id: 'MSSCI-11842', status: 'in_progress', points: 5 } // YAML says 5
217
- ];
218
- const jiraWithDifferentPoints = [
219
- { key: 'MSSCI-11842', fields: { status: { name: 'In Progress' }, customfield_10031: 4 } } // Jira says 4
220
- ];
221
-
222
- const plan = generateSyncPlan(yamlWithDifferentPoints, jiraWithDifferentPoints, {
223
- syncPoints: true
224
- });
225
-
226
- const pointsChange = plan.changes.find(c => c.key === 'MSSCI-11842' && c.field === 'points');
227
- assert.ok(pointsChange, 'Should detect points difference');
228
- assert.strictEqual(pointsChange.yamlPoints, 5);
229
- assert.strictEqual(pointsChange.jiraPoints, 4);
230
- });
231
-
232
- it('should sync points from YAML to Jira', async () => {
233
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
234
-
235
- const yamlStories = [{ id: 'MSSCI-11842', status: 'backlog', points: 8 }];
236
- const jiraStories = [{ key: 'MSSCI-11842', fields: { status: { name: 'To Do' }, customfield_10031: 5 } }];
237
-
238
- const plan = generateSyncPlan(yamlStories, jiraStories, {
239
- syncPoints: true,
240
- direction: 'yaml-to-jira'
241
- });
242
-
243
- const change = plan.changes.find(c => c.field === 'points');
244
- assert.ok(change);
245
- assert.strictEqual(change.action, 'update-jira');
246
- assert.strictEqual(change.targetPoints, 8);
247
- });
248
-
249
- it('should sync points from Jira to YAML', async () => {
250
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
251
-
252
- const yamlStories = [{ id: 'MSSCI-11842', status: 'backlog', points: 3 }];
253
- const jiraStories = [{ key: 'MSSCI-11842', fields: { status: { name: 'To Do' }, customfield_10031: 5 } }];
254
-
255
- const plan = generateSyncPlan(yamlStories, jiraStories, {
256
- syncPoints: true,
257
- direction: 'jira-to-yaml'
258
- });
259
-
260
- const change = plan.changes.find(c => c.field === 'points');
261
- assert.ok(change);
262
- assert.strictEqual(change.action, 'update-yaml');
263
- assert.strictEqual(change.targetPoints, 5);
264
- });
265
-
266
- it('should not generate change when points match', async () => {
267
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
268
-
269
- const yamlStories = [{ id: 'MSSCI-11842', status: 'backlog', points: 4 }];
270
- const jiraStories = [{ key: 'MSSCI-11842', fields: { status: { name: 'To Do' }, customfield_10031: 4 } }];
271
-
272
- const plan = generateSyncPlan(yamlStories, jiraStories, {
273
- syncPoints: true
274
- });
275
-
276
- const pointsChange = plan.changes.find(c => c.key === 'MSSCI-11842' && c.field === 'points');
277
- assert.ok(!pointsChange, 'Should not generate change when points match');
278
- });
279
-
280
- });
281
-
282
- // =============================================================================
283
- // AC3: New stories in either system detected
284
- // =============================================================================
285
-
286
- describe('AC3: Detect new stories in either system', () => {
287
-
288
- describe('YAML-only stories', () => {
289
-
290
- it('should detect stories in YAML but not in Jira', async () => {
291
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
292
-
293
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {});
294
-
295
- // MSSCI-11844 is in YAML but not in Jira fixtures
296
- assert.ok(plan.yamlOnly, 'Plan should have yamlOnly array');
297
- assert.ok(plan.yamlOnly.includes('MSSCI-11844'), 'Should detect MSSCI-11844 as YAML-only');
298
- });
299
-
300
- it('should report YAML-only stories for potential Jira creation', async () => {
301
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
302
-
303
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
304
- reportMissing: true
305
- });
306
-
307
- const yamlOnlyReport = plan.reports?.yamlOnly || [];
308
- assert.ok(yamlOnlyReport.length > 0, 'Should report YAML-only stories');
309
- });
310
-
311
- });
312
-
313
- describe('Jira-only stories', () => {
314
-
315
- it('should detect stories in Jira but not in YAML', async () => {
316
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
317
-
318
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {});
319
-
320
- // MSSCI-11850 is in Jira but not in YAML fixtures
321
- assert.ok(plan.jiraOnly, 'Plan should have jiraOnly array');
322
- assert.ok(plan.jiraOnly.includes('MSSCI-11850'), 'Should detect MSSCI-11850 as Jira-only');
323
- });
324
-
325
- it('should report Jira-only stories for potential YAML import', async () => {
326
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
327
-
328
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
329
- reportMissing: true
330
- });
331
-
332
- const jiraOnlyReport = plan.reports?.jiraOnly || [];
333
- assert.ok(jiraOnlyReport.length > 0, 'Should report Jira-only stories');
334
- });
335
-
336
- });
337
-
338
- describe('Stories in both systems', () => {
339
-
340
- it('should identify stories present in both systems', async () => {
341
- const { generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
342
-
343
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {});
344
-
345
- assert.ok(plan.both, 'Plan should have both array');
346
- // MSSCI-11842 and MSSCI-11843 are in both
347
- assert.ok(plan.both.includes('MSSCI-11842'));
348
- assert.ok(plan.both.includes('MSSCI-11843'));
349
- });
350
-
351
- });
352
-
353
- });
354
-
355
- // =============================================================================
356
- // AC4: Dry-run mode shows changes before applying
357
- // =============================================================================
358
-
359
- describe('AC4: Dry-run mode', () => {
360
-
361
- it('should generate plan without executing changes in dry-run mode', async () => {
362
- const { executeSyncPlan, generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
363
-
364
- const plan = generateSyncPlan(sampleYamlStories, sampleJiraStories, {
365
- syncStatus: true,
366
- syncPoints: true
367
- });
368
-
369
- // Execute with dry-run
370
- const result = await executeSyncPlan(plan, { dryRun: true });
371
-
372
- assert.ok(result.dryRun, 'Result should indicate dry-run mode');
373
- assert.ok(result.wouldApply, 'Result should list what would be applied');
374
- assert.strictEqual(result.applied, 0, 'Should not apply any changes');
375
- });
376
-
377
- it('should display planned changes in human-readable format', async () => {
378
- const { formatSyncPlan } = await import('./jira-bidirectional-sync.mjs');
379
-
380
- const plan = {
381
- changes: [
382
- { key: 'MSSCI-11843', field: 'status', action: 'update-yaml', targetStatus: 'done' }
383
- ],
384
- yamlOnly: ['MSSCI-11844'],
385
- jiraOnly: ['MSSCI-11850'],
386
- both: ['MSSCI-11842', 'MSSCI-11843']
387
- };
388
-
389
- const output = formatSyncPlan(plan);
390
-
391
- assert.ok(typeof output === 'string', 'Should return string output');
392
- assert.ok(output.includes('MSSCI-11843'), 'Should include story keys');
393
- assert.ok(output.includes('status'), 'Should include field names');
394
- });
395
-
396
- it('should not modify YAML file in dry-run mode', async () => {
397
- const { executeSyncPlan, generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
398
-
399
- // This test verifies no file writes happen
400
- const plan = generateSyncPlan(
401
- [{ id: 'MSSCI-11842', status: 'backlog', points: 4 }],
402
- [{ key: 'MSSCI-11842', fields: { status: { name: 'Done' }, customfield_10031: 4 } }],
403
- { syncStatus: true, direction: 'jira-to-yaml' }
404
- );
405
-
406
- const result = await executeSyncPlan(plan, { dryRun: true });
407
-
408
- assert.ok(result.dryRun);
409
- assert.strictEqual(result.yamlModified, false, 'YAML should not be modified');
410
- });
411
-
412
- it('should not call Jira API in dry-run mode', async () => {
413
- const { executeSyncPlan, generateSyncPlan } = await import('./jira-bidirectional-sync.mjs');
414
-
415
- const plan = generateSyncPlan(
416
- [{ id: 'MSSCI-11842', status: 'done', points: 4 }],
417
- [{ key: 'MSSCI-11842', fields: { status: { name: 'To Do' }, customfield_10031: 4 } }],
418
- { syncStatus: true, direction: 'yaml-to-jira' }
419
- );
420
-
421
- const result = await executeSyncPlan(plan, { dryRun: true });
422
-
423
- assert.ok(result.dryRun);
424
- assert.strictEqual(result.jiraApiCalls, 0, 'Should not make Jira API calls');
425
- });
426
-
427
- });
428
-
429
- // =============================================================================
430
- // Integration: CLI argument parsing
431
- // =============================================================================
432
-
433
- describe('CLI argument parsing', () => {
434
-
435
- it('should parse --dry-run flag', async () => {
436
- const { parseCliArgs } = await import('./jira-bidirectional-sync.mjs');
437
-
438
- const args = parseCliArgs(['--dry-run']);
439
- assert.strictEqual(args.dryRun, true);
440
- });
441
-
442
- it('should parse --yaml-wins flag', async () => {
443
- const { parseCliArgs } = await import('./jira-bidirectional-sync.mjs');
444
-
445
- const args = parseCliArgs(['--yaml-wins']);
446
- assert.strictEqual(args.yamlWins, true);
447
- });
448
-
449
- it('should parse --status and --points flags', async () => {
450
- const { parseCliArgs } = await import('./jira-bidirectional-sync.mjs');
451
-
452
- const args = parseCliArgs(['--status', '--points']);
453
- assert.strictEqual(args.syncStatus, true);
454
- assert.strictEqual(args.syncPoints, true);
455
- });
456
-
457
- it('should parse --all flag as status + points', async () => {
458
- const { parseCliArgs } = await import('./jira-bidirectional-sync.mjs');
459
-
460
- const args = parseCliArgs(['--all']);
461
- assert.strictEqual(args.syncStatus, true);
462
- assert.strictEqual(args.syncPoints, true);
463
- });
464
-
465
- it('should parse --sprint <id> option', async () => {
466
- const { parseCliArgs } = await import('./jira-bidirectional-sync.mjs');
467
-
468
- const args = parseCliArgs(['--sprint', '275']);
469
- assert.strictEqual(args.sprintId, '275');
470
- });
471
-
472
- });
473
-
474
- // =============================================================================
475
- // Helper function tests (using existing jira-lib.mjs)
476
- // =============================================================================
477
-
478
- describe('Helper functions from jira-lib.mjs', () => {
479
-
480
- it('mapStatusToJira converts Pennyfarthing status to Jira', () => {
481
- assert.strictEqual(mapStatusToJira('backlog'), 'To Do');
482
- assert.strictEqual(mapStatusToJira('in_progress'), 'In Progress');
483
- assert.strictEqual(mapStatusToJira('in-progress'), 'In Progress');
484
- assert.strictEqual(mapStatusToJira('done'), 'Done');
485
- assert.strictEqual(mapStatusToJira('review'), 'In Review');
486
- });
487
-
488
- it('mapJiraToStatus converts Jira status to Pennyfarthing', () => {
489
- assert.strictEqual(mapJiraToStatus('To Do'), 'backlog');
490
- assert.strictEqual(mapJiraToStatus('In Progress'), 'in-progress');
491
- assert.strictEqual(mapJiraToStatus('Done'), 'done');
492
- assert.strictEqual(mapJiraToStatus('In Review'), 'review');
493
- });
494
-
495
- it('extractJiraKey extracts key from URL or returns as-is', () => {
496
- assert.strictEqual(extractJiraKey('MSSCI-11842'), 'MSSCI-11842');
497
- assert.strictEqual(
498
- extractJiraKey('https://1898andco.atlassian.net/browse/MSSCI-11842'),
499
- 'MSSCI-11842'
500
- );
501
- });
502
-
503
- });