@girardmedia/bootspring 2.5.0 → 2.5.2

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 (59) hide show
  1. package/README.md +9 -403
  2. package/bin/bootspring.js +1 -96
  3. package/dist/cli/index.js +65134 -0
  4. package/dist/cli-launcher.js +92 -0
  5. package/dist/core/index.d.ts +2110 -5582
  6. package/dist/core/index.js +2 -0
  7. package/dist/core.js +21123 -5413
  8. package/dist/mcp/index.d.ts +357 -1
  9. package/dist/mcp/index.js +2 -0
  10. package/dist/mcp-server.js +51948 -1976
  11. package/package.json +27 -63
  12. package/scripts/postinstall.cjs +144 -0
  13. package/LICENSE +0 -29
  14. package/dist/cli/index.cjs +0 -20776
  15. package/generators/api-docs.js +0 -827
  16. package/generators/decisions.js +0 -655
  17. package/generators/generate.js +0 -595
  18. package/generators/health.js +0 -942
  19. package/generators/index.ts +0 -82
  20. package/generators/presets/full.js +0 -28
  21. package/generators/presets/index.js +0 -12
  22. package/generators/presets/minimal.js +0 -29
  23. package/generators/presets/standard.js +0 -28
  24. package/generators/questionnaire.js +0 -414
  25. package/generators/sections/advanced.js +0 -136
  26. package/generators/sections/ai.js +0 -106
  27. package/generators/sections/auth.js +0 -89
  28. package/generators/sections/backend.js +0 -146
  29. package/generators/sections/business.js +0 -118
  30. package/generators/sections/content.js +0 -300
  31. package/generators/sections/deployment.js +0 -139
  32. package/generators/sections/features.js +0 -122
  33. package/generators/sections/frontend.js +0 -118
  34. package/generators/sections/identity.js +0 -76
  35. package/generators/sections/index.js +0 -40
  36. package/generators/sections/instructions.js +0 -146
  37. package/generators/sections/payments.js +0 -104
  38. package/generators/sections/plugins.js +0 -142
  39. package/generators/sections/pre-build.js +0 -130
  40. package/generators/sections/security.js +0 -127
  41. package/generators/sections/technical.js +0 -171
  42. package/generators/sections/testing.js +0 -125
  43. package/generators/sections/workflow.js +0 -104
  44. package/generators/sprint.js +0 -675
  45. package/generators/templates/agents.template.js +0 -199
  46. package/generators/templates/assistant-context.template.js +0 -83
  47. package/generators/templates/build-planning.template.js +0 -708
  48. package/generators/templates/claude.template.js +0 -379
  49. package/generators/templates/content.template.js +0 -819
  50. package/generators/templates/index.js +0 -16
  51. package/generators/templates/planning.template.js +0 -515
  52. package/generators/templates/seed.template.js +0 -109
  53. package/generators/visual-doc-generator.js +0 -910
  54. package/scripts/postinstall.js +0 -197
  55. /package/{claude-commands → assets/claude-commands}/agent.md +0 -0
  56. /package/{claude-commands → assets/claude-commands}/bs.md +0 -0
  57. /package/{claude-commands → assets/claude-commands}/build.md +0 -0
  58. /package/{claude-commands → assets/claude-commands}/skill.md +0 -0
  59. /package/{claude-commands → assets/claude-commands}/todo.md +0 -0
@@ -1,655 +0,0 @@
1
- /**
2
- * Bootspring DECISIONS.md Generator
3
- *
4
- * Generates Architecture Decision Record (ADR) documentation
5
- * with decisions, rationale, trade-offs, outcomes, and lessons learned.
6
- *
7
- * @package bootspring
8
- * @module generators/decisions
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- /**
15
- * Decision status values
16
- */
17
- const DECISION_STATUS = {
18
- PROPOSED: 'proposed',
19
- ACCEPTED: 'accepted',
20
- DEPRECATED: 'deprecated',
21
- SUPERSEDED: 'superseded',
22
- REJECTED: 'rejected'
23
- };
24
-
25
- /**
26
- * Decision categories
27
- */
28
- const DECISION_CATEGORIES = {
29
- ARCHITECTURE: { label: 'Architecture', emoji: '🏗️' },
30
- TECHNOLOGY: { label: 'Technology', emoji: '⚙️' },
31
- DESIGN: { label: 'Design', emoji: '🎨' },
32
- SECURITY: { label: 'Security', emoji: '🔐' },
33
- PERFORMANCE: { label: 'Performance', emoji: '⚡' },
34
- INFRASTRUCTURE: { label: 'Infrastructure', emoji: '☁️' },
35
- PROCESS: { label: 'Process', emoji: '📋' },
36
- API: { label: 'API', emoji: '🔌' },
37
- DATA: { label: 'Data', emoji: '💾' },
38
- UI_UX: { label: 'UI/UX', emoji: '🖥️' }
39
- };
40
-
41
- /**
42
- * Impact levels
43
- */
44
- const IMPACT_LEVELS = {
45
- HIGH: { label: 'High', emoji: '🔴', description: 'Affects core architecture or many components' },
46
- MEDIUM: { label: 'Medium', emoji: '🟡', description: 'Affects specific features or modules' },
47
- LOW: { label: 'Low', emoji: '🟢', description: 'Localized impact, easily reversible' }
48
- };
49
-
50
- /**
51
- * Decisions Document Generator
52
- */
53
- class DecisionsGenerator {
54
- constructor(options = {}) {
55
- this.projectRoot = options.projectRoot || process.cwd();
56
- this.decisionsPath = path.join(this.projectRoot, '.bootspring', 'decisions.json');
57
- }
58
-
59
- /**
60
- * Load decisions data
61
- */
62
- loadDecisions() {
63
- try {
64
- if (fs.existsSync(this.decisionsPath)) {
65
- return JSON.parse(fs.readFileSync(this.decisionsPath, 'utf-8'));
66
- }
67
- } catch (_err) {
68
- // Return default
69
- }
70
-
71
- return {
72
- project: this.getProjectName(),
73
- decisions: [],
74
- metadata: {
75
- created: new Date().toISOString(),
76
- lastUpdated: new Date().toISOString(),
77
- totalDecisions: 0
78
- }
79
- };
80
- }
81
-
82
- /**
83
- * Save decisions data
84
- */
85
- saveDecisions(data) {
86
- data.metadata.lastUpdated = new Date().toISOString();
87
- data.metadata.totalDecisions = data.decisions.length;
88
-
89
- const dir = path.dirname(this.decisionsPath);
90
- if (!fs.existsSync(dir)) {
91
- fs.mkdirSync(dir, { recursive: true });
92
- }
93
- fs.writeFileSync(this.decisionsPath, JSON.stringify(data, null, 2));
94
- }
95
-
96
- /**
97
- * Get project name from package.json
98
- */
99
- getProjectName() {
100
- try {
101
- const pkgPath = path.join(this.projectRoot, 'package.json');
102
- if (fs.existsSync(pkgPath)) {
103
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
104
- return pkg.name || 'Project';
105
- }
106
- } catch (_err) {
107
- // Default
108
- }
109
- return 'Project';
110
- }
111
-
112
- /**
113
- * Generate a new decision ID
114
- */
115
- generateDecisionId(decisions) {
116
- const maxId = decisions.reduce((max, d) => {
117
- const num = parseInt(d.id.replace('ADR-', ''), 10);
118
- return num > max ? num : max;
119
- }, 0);
120
- return `ADR-${String(maxId + 1).padStart(4, '0')}`;
121
- }
122
-
123
- /**
124
- * Add a new decision
125
- */
126
- addDecision(data, decision) {
127
- const id = this.generateDecisionId(data.decisions);
128
-
129
- const newDecision = {
130
- id,
131
- title: decision.title,
132
- status: decision.status || DECISION_STATUS.PROPOSED,
133
- category: decision.category || 'ARCHITECTURE',
134
- impact: decision.impact || 'MEDIUM',
135
- date: new Date().toISOString().split('T')[0],
136
-
137
- // Context
138
- context: decision.context || '',
139
- problem: decision.problem || '',
140
-
141
- // Decision
142
- decision: decision.decision || '',
143
- rationale: decision.rationale || '',
144
-
145
- // Options considered
146
- options: decision.options || [],
147
-
148
- // Consequences
149
- consequences: {
150
- positive: decision.consequences?.positive || [],
151
- negative: decision.consequences?.negative || [],
152
- risks: decision.consequences?.risks || []
153
- },
154
-
155
- // Related
156
- relatedDecisions: decision.relatedDecisions || [],
157
- supersedes: decision.supersedes || null,
158
- supersededBy: null,
159
-
160
- // Tracking
161
- outcome: null,
162
- lessonsLearned: [],
163
- reviewDate: decision.reviewDate || null,
164
-
165
- // Metadata
166
- authors: decision.authors || [],
167
- tags: decision.tags || [],
168
- links: decision.links || []
169
- };
170
-
171
- data.decisions.push(newDecision);
172
-
173
- // If this supersedes another decision, update that one
174
- if (decision.supersedes) {
175
- const superseded = data.decisions.find(d => d.id === decision.supersedes);
176
- if (superseded) {
177
- superseded.status = DECISION_STATUS.SUPERSEDED;
178
- superseded.supersededBy = id;
179
- }
180
- }
181
-
182
- return newDecision;
183
- }
184
-
185
- /**
186
- * Update decision status
187
- */
188
- updateDecisionStatus(data, decisionId, status, notes = null) {
189
- const decision = data.decisions.find(d => d.id === decisionId);
190
- if (decision) {
191
- decision.status = status;
192
- if (notes) {
193
- decision.statusNotes = notes;
194
- }
195
- decision.lastUpdated = new Date().toISOString();
196
- }
197
- return decision;
198
- }
199
-
200
- /**
201
- * Record outcome for a decision
202
- */
203
- recordOutcome(data, decisionId, outcome) {
204
- const decision = data.decisions.find(d => d.id === decisionId);
205
- if (decision) {
206
- decision.outcome = {
207
- status: outcome.status, // 'success', 'partial', 'failure'
208
- description: outcome.description,
209
- metrics: outcome.metrics || {},
210
- date: new Date().toISOString().split('T')[0]
211
- };
212
- }
213
- return decision;
214
- }
215
-
216
- /**
217
- * Add lessons learned
218
- */
219
- addLessonLearned(data, decisionId, lesson) {
220
- const decision = data.decisions.find(d => d.id === decisionId);
221
- if (decision) {
222
- decision.lessonsLearned.push({
223
- lesson,
224
- date: new Date().toISOString().split('T')[0]
225
- });
226
- }
227
- return decision;
228
- }
229
-
230
- /**
231
- * Generate DECISIONS.md content
232
- */
233
- generate(data = null) {
234
- if (!data) {
235
- data = this.loadDecisions();
236
- }
237
-
238
- const sections = [];
239
- const now = new Date().toISOString().split('T')[0];
240
-
241
- // Header
242
- sections.push(`# Architecture Decision Records
243
-
244
- **Project:** ${data.project}
245
- **Last Updated:** ${now}
246
- **Total Decisions:** ${data.decisions.length}
247
-
248
- ---
249
-
250
- ## Overview
251
-
252
- This document tracks significant architecture and design decisions made during the development of this project. Each decision is recorded as an Architecture Decision Record (ADR).
253
-
254
- ### Status Legend
255
-
256
- | Status | Meaning |
257
- |--------|---------|
258
- | 📝 Proposed | Under discussion, not yet accepted |
259
- | ✅ Accepted | Decision has been approved and is in effect |
260
- | ⚠️ Deprecated | Decision is being phased out |
261
- | 🔄 Superseded | Replaced by a newer decision |
262
- | ❌ Rejected | Decision was considered but not adopted |
263
-
264
- ---`);
265
-
266
- // Summary Table
267
- const activeDecisions = data.decisions.filter(d =>
268
- d.status === DECISION_STATUS.ACCEPTED || d.status === DECISION_STATUS.PROPOSED
269
- );
270
-
271
- if (data.decisions.length > 0) {
272
- sections.push(`## Decision Index
273
-
274
- | ID | Title | Category | Status | Impact | Date |
275
- |----|-------|----------|--------|--------|------|
276
- ${data.decisions.map(d => {
277
- const category = DECISION_CATEGORIES[d.category] || { emoji: '📋', label: d.category };
278
- const impact = IMPACT_LEVELS[d.impact] || { emoji: '🟡', label: d.impact };
279
- return `| [${d.id}](#${d.id.toLowerCase()}) | ${d.title} | ${category.emoji} ${category.label} | ${this.formatStatus(d.status)} | ${impact.emoji} ${impact.label} | ${d.date} |`;
280
- }).join('\n')}
281
-
282
- ---`);
283
- }
284
-
285
- // Individual Decisions
286
- if (data.decisions.length > 0) {
287
- sections.push('## Decisions\n');
288
-
289
- for (const decision of data.decisions) {
290
- sections.push(this.formatDecision(decision));
291
- }
292
- } else {
293
- sections.push(`## Decisions
294
-
295
- _No decisions recorded yet._
296
-
297
- To add a decision, use:
298
- \`\`\`bash
299
- bootspring docs decisions add "Decision Title"
300
- \`\`\`
301
-
302
- ---`);
303
- }
304
-
305
- // Statistics
306
- const stats = this.calculateStatistics(data);
307
- sections.push(`## Statistics
308
-
309
- | Metric | Value |
310
- |--------|-------|
311
- | Total Decisions | ${stats.total} |
312
- | Accepted | ${stats.accepted} |
313
- | Proposed | ${stats.proposed} |
314
- | Superseded | ${stats.superseded} |
315
- | With Outcomes | ${stats.withOutcomes} |
316
- | Success Rate | ${stats.successRate}% |
317
-
318
- ### By Category
319
-
320
- ${Object.entries(stats.byCategory).map(([cat, count]) => {
321
- const category = DECISION_CATEGORIES[cat] || { emoji: '📋', label: cat };
322
- return `- ${category.emoji} **${category.label}:** ${count}`;
323
- }).join('\n')}
324
-
325
- ### By Impact
326
-
327
- ${Object.entries(stats.byImpact).map(([imp, count]) => {
328
- const impact = IMPACT_LEVELS[imp] || { emoji: '🟡', label: imp };
329
- return `- ${impact.emoji} **${impact.label}:** ${count}`;
330
- }).join('\n')}
331
-
332
- ---`);
333
-
334
- // Lessons Learned Summary
335
- const lessonsLearned = data.decisions
336
- .filter(d => d.lessonsLearned && d.lessonsLearned.length > 0)
337
- .flatMap(d => d.lessonsLearned.map(l => ({ ...l, decisionId: d.id, title: d.title })));
338
-
339
- if (lessonsLearned.length > 0) {
340
- sections.push(`## Lessons Learned
341
-
342
- ${lessonsLearned.slice(0, 10).map(l => `- **[${l.decisionId}]** ${l.lesson}
343
- _From: ${l.title} (${l.date})_`).join('\n\n')}
344
-
345
- ---`);
346
- }
347
-
348
- // Footer
349
- sections.push(`---
350
-
351
- *Generated by [Bootspring](https://bootspring.com) Decisions Generator*
352
-
353
- ### Adding New Decisions
354
-
355
- \`\`\`bash
356
- # Add a new decision interactively
357
- bootspring docs decisions add
358
-
359
- # Add with title
360
- bootspring docs decisions add "Use PostgreSQL for Primary Database"
361
-
362
- # View decision details
363
- bootspring docs decisions show ADR-0001
364
- \`\`\`
365
- `);
366
-
367
- return sections.join('\n\n');
368
- }
369
-
370
- /**
371
- * Format status with emoji
372
- */
373
- formatStatus(status) {
374
- const statusMap = {
375
- [DECISION_STATUS.PROPOSED]: '📝 Proposed',
376
- [DECISION_STATUS.ACCEPTED]: '✅ Accepted',
377
- [DECISION_STATUS.DEPRECATED]: '⚠️ Deprecated',
378
- [DECISION_STATUS.SUPERSEDED]: '🔄 Superseded',
379
- [DECISION_STATUS.REJECTED]: '❌ Rejected'
380
- };
381
- return statusMap[status] || status;
382
- }
383
-
384
- /**
385
- * Format a single decision as markdown
386
- */
387
- formatDecision(decision) {
388
- const category = DECISION_CATEGORIES[decision.category] || { emoji: '📋', label: decision.category };
389
- const impact = IMPACT_LEVELS[decision.impact] || { emoji: '🟡', label: decision.impact };
390
-
391
- let md = `### ${decision.id}
392
-
393
- # ${decision.title}
394
-
395
- **Date:** ${decision.date}
396
- **Status:** ${this.formatStatus(decision.status)}
397
- **Category:** ${category.emoji} ${category.label}
398
- **Impact:** ${impact.emoji} ${impact.label}
399
- ${decision.authors.length > 0 ? `**Authors:** ${decision.authors.join(', ')}` : ''}
400
- ${decision.tags.length > 0 ? `**Tags:** ${decision.tags.map(t => `\`${t}\``).join(' ')}` : ''}
401
-
402
- #### Context
403
-
404
- ${decision.context || '_No context provided._'}
405
-
406
- #### Problem
407
-
408
- ${decision.problem || '_No problem statement provided._'}
409
-
410
- #### Decision
411
-
412
- ${decision.decision || '_No decision recorded._'}
413
-
414
- #### Rationale
415
-
416
- ${decision.rationale || '_No rationale provided._'}
417
- `;
418
-
419
- // Options considered
420
- if (decision.options && decision.options.length > 0) {
421
- md += `
422
- #### Options Considered
423
-
424
- ${decision.options.map((opt, i) => {
425
- const chosen = opt.chosen ? ' ✅ **CHOSEN**' : '';
426
- return `**Option ${i + 1}: ${opt.name}**${chosen}
427
-
428
- ${opt.description || ''}
429
-
430
- - Pros: ${(opt.pros || []).join(', ') || 'None listed'}
431
- - Cons: ${(opt.cons || []).join(', ') || 'None listed'}`;
432
- }).join('\n\n')}
433
- `;
434
- }
435
-
436
- // Consequences
437
- md += `
438
- #### Consequences
439
-
440
- **Positive:**
441
- ${decision.consequences.positive.length > 0 ? decision.consequences.positive.map(c => `- ✅ ${c}`).join('\n') : '- _None identified_'}
442
-
443
- **Negative:**
444
- ${decision.consequences.negative.length > 0 ? decision.consequences.negative.map(c => `- ⚠️ ${c}`).join('\n') : '- _None identified_'}
445
-
446
- **Risks:**
447
- ${decision.consequences.risks.length > 0 ? decision.consequences.risks.map(c => `- 🔴 ${c}`).join('\n') : '- _None identified_'}
448
- `;
449
-
450
- // Related decisions
451
- if (decision.relatedDecisions.length > 0) {
452
- md += `
453
- #### Related Decisions
454
-
455
- ${decision.relatedDecisions.map(r => `- [${r}](#${r.toLowerCase()})`).join('\n')}
456
- `;
457
- }
458
-
459
- // Supersedes
460
- if (decision.supersedes) {
461
- md += `
462
- #### Supersedes
463
-
464
- This decision supersedes [${decision.supersedes}](#${decision.supersedes.toLowerCase()}).
465
- `;
466
- }
467
-
468
- // Superseded by
469
- if (decision.supersededBy) {
470
- md += `
471
- #### Superseded By
472
-
473
- This decision has been superseded by [${decision.supersededBy}](#${decision.supersededBy.toLowerCase()}).
474
- `;
475
- }
476
-
477
- // Outcome
478
- if (decision.outcome) {
479
- const outcomeEmoji = decision.outcome.status === 'success' ? '✅' :
480
- decision.outcome.status === 'partial' ? '🟡' : '❌';
481
-
482
- md += `
483
- #### Outcome
484
-
485
- **Status:** ${outcomeEmoji} ${decision.outcome.status.charAt(0).toUpperCase() + decision.outcome.status.slice(1)}
486
- **Date:** ${decision.outcome.date}
487
-
488
- ${decision.outcome.description || ''}
489
- `;
490
-
491
- if (decision.outcome.metrics && Object.keys(decision.outcome.metrics).length > 0) {
492
- md += `
493
- **Metrics:**
494
- ${Object.entries(decision.outcome.metrics).map(([k, v]) => `- ${k}: ${v}`).join('\n')}
495
- `;
496
- }
497
- }
498
-
499
- // Lessons learned
500
- if (decision.lessonsLearned && decision.lessonsLearned.length > 0) {
501
- md += `
502
- #### Lessons Learned
503
-
504
- ${decision.lessonsLearned.map(l => `- ${l.lesson} _(${l.date})_`).join('\n')}
505
- `;
506
- }
507
-
508
- // Links
509
- if (decision.links && decision.links.length > 0) {
510
- md += `
511
- #### References
512
-
513
- ${decision.links.map(l => `- [${l.title || l.url}](${l.url})`).join('\n')}
514
- `;
515
- }
516
-
517
- md += '\n---\n';
518
-
519
- return md;
520
- }
521
-
522
- /**
523
- * Calculate statistics
524
- */
525
- calculateStatistics(data) {
526
- const decisions = data.decisions;
527
-
528
- const stats = {
529
- total: decisions.length,
530
- accepted: decisions.filter(d => d.status === DECISION_STATUS.ACCEPTED).length,
531
- proposed: decisions.filter(d => d.status === DECISION_STATUS.PROPOSED).length,
532
- superseded: decisions.filter(d => d.status === DECISION_STATUS.SUPERSEDED).length,
533
- deprecated: decisions.filter(d => d.status === DECISION_STATUS.DEPRECATED).length,
534
- rejected: decisions.filter(d => d.status === DECISION_STATUS.REJECTED).length,
535
- withOutcomes: decisions.filter(d => d.outcome).length,
536
- byCategory: {},
537
- byImpact: {}
538
- };
539
-
540
- // Success rate
541
- const withOutcomes = decisions.filter(d => d.outcome);
542
- const successful = withOutcomes.filter(d => d.outcome.status === 'success');
543
- stats.successRate = withOutcomes.length > 0 ? Math.round((successful.length / withOutcomes.length) * 100) : 0;
544
-
545
- // By category
546
- for (const d of decisions) {
547
- stats.byCategory[d.category] = (stats.byCategory[d.category] || 0) + 1;
548
- }
549
-
550
- // By impact
551
- for (const d of decisions) {
552
- stats.byImpact[d.impact] = (stats.byImpact[d.impact] || 0) + 1;
553
- }
554
-
555
- return stats;
556
- }
557
-
558
- /**
559
- * Import decisions from decision intelligence module
560
- */
561
- async importFromDecisionIntelligence(data) {
562
- try {
563
- const { DecisionIntelligence } = require('../intelligence/decision-intelligence');
564
- const di = new DecisionIntelligence(this.projectRoot);
565
- const recentDecisions = await di.getRecentDecisions(50);
566
-
567
- for (const d of recentDecisions) {
568
- // Check if already exists
569
- const exists = data.decisions.some(existing =>
570
- existing.title.toLowerCase() === d.context?.toLowerCase() ||
571
- existing.decision === d.chosen
572
- );
573
-
574
- if (!exists && d.type && d.chosen) {
575
- this.addDecision(data, {
576
- title: d.context || d.type,
577
- category: this.mapTypeToCategory(d.type),
578
- decision: d.chosen,
579
- rationale: d.rationale || '',
580
- status: DECISION_STATUS.ACCEPTED,
581
- tags: d.tags || []
582
- });
583
- }
584
- }
585
- } catch (_err) {
586
- // Decision intelligence not available
587
- }
588
-
589
- return data;
590
- }
591
-
592
- /**
593
- * Map decision type to category
594
- */
595
- mapTypeToCategory(type) {
596
- const mapping = {
597
- architecture: 'ARCHITECTURE',
598
- technology: 'TECHNOLOGY',
599
- database: 'DATA',
600
- api: 'API',
601
- security: 'SECURITY',
602
- performance: 'PERFORMANCE',
603
- ui: 'UI_UX',
604
- deployment: 'INFRASTRUCTURE'
605
- };
606
- return mapping[type] || 'ARCHITECTURE';
607
- }
608
- }
609
-
610
- /**
611
- * Generate DECISIONS.md
612
- */
613
- function generate(options = {}) {
614
- const generator = new DecisionsGenerator(options);
615
- const data = generator.loadDecisions();
616
- return generator.generate(data);
617
- }
618
-
619
- /**
620
- * Load decisions data
621
- */
622
- function loadDecisions(options = {}) {
623
- const generator = new DecisionsGenerator(options);
624
- return generator.loadDecisions();
625
- }
626
-
627
- /**
628
- * Save decisions data
629
- */
630
- function saveDecisions(data, options = {}) {
631
- const generator = new DecisionsGenerator(options);
632
- generator.saveDecisions(data);
633
- }
634
-
635
- /**
636
- * Add a new decision
637
- */
638
- function addDecision(decision, options = {}) {
639
- const generator = new DecisionsGenerator(options);
640
- const data = generator.loadDecisions();
641
- const newDecision = generator.addDecision(data, decision);
642
- generator.saveDecisions(data);
643
- return newDecision;
644
- }
645
-
646
- module.exports = {
647
- DecisionsGenerator,
648
- generate,
649
- loadDecisions,
650
- saveDecisions,
651
- addDecision,
652
- DECISION_STATUS,
653
- DECISION_CATEGORIES,
654
- IMPACT_LEVELS
655
- };