@pennyfarthing/core 7.4.1 → 7.6.0

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 (127) hide show
  1. package/package.json +1 -1
  2. package/packages/core/dist/cli/commands/doctor-legacy.test.d.ts +13 -0
  3. package/packages/core/dist/cli/commands/doctor-legacy.test.d.ts.map +1 -0
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js +207 -0
  5. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -0
  6. package/packages/core/dist/cli/commands/doctor.d.ts +16 -0
  7. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  8. package/packages/core/dist/cli/commands/doctor.js +130 -2
  9. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  10. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/init.js +17 -27
  12. package/packages/core/dist/cli/commands/init.js.map +1 -1
  13. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  14. package/packages/core/dist/cli/commands/update.js +21 -52
  15. package/packages/core/dist/cli/commands/update.js.map +1 -1
  16. package/packages/core/dist/cli/utils/symlinks.d.ts +15 -0
  17. package/packages/core/dist/cli/utils/symlinks.d.ts.map +1 -1
  18. package/packages/core/dist/cli/utils/symlinks.js +148 -2
  19. package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
  20. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
  21. package/packages/core/dist/cli/utils/themes.js +9 -0
  22. package/packages/core/dist/cli/utils/themes.js.map +1 -1
  23. package/pennyfarthing-dist/agents/dev.md +29 -24
  24. package/pennyfarthing-dist/agents/handoff.md +42 -119
  25. package/pennyfarthing-dist/agents/reviewer.md +32 -37
  26. package/pennyfarthing-dist/agents/sm-handoff.md +43 -66
  27. package/pennyfarthing-dist/agents/sm.md +52 -35
  28. package/pennyfarthing-dist/agents/tea.md +25 -8
  29. package/pennyfarthing-dist/agents/testing-runner.md +4 -4
  30. package/pennyfarthing-dist/commands/architect.md +0 -55
  31. package/pennyfarthing-dist/commands/dev.md +1 -54
  32. package/pennyfarthing-dist/commands/devops.md +0 -52
  33. package/pennyfarthing-dist/commands/health-check.md +33 -0
  34. package/pennyfarthing-dist/commands/orchestrator.md +0 -49
  35. package/pennyfarthing-dist/commands/pm.md +0 -53
  36. package/pennyfarthing-dist/commands/reviewer.md +1 -58
  37. package/pennyfarthing-dist/commands/sm.md +1 -64
  38. package/pennyfarthing-dist/commands/sprint.md +133 -0
  39. package/pennyfarthing-dist/commands/standalone.md +194 -0
  40. package/pennyfarthing-dist/commands/tea.md +1 -57
  41. package/pennyfarthing-dist/commands/tech-writer.md +0 -46
  42. package/pennyfarthing-dist/commands/theme-maker.md +10 -5
  43. package/pennyfarthing-dist/commands/ux-designer.md +0 -55
  44. package/pennyfarthing-dist/guides/XML-TAGS.md +156 -0
  45. package/pennyfarthing-dist/guides/agent-behavior.md +64 -38
  46. package/pennyfarthing-dist/guides/measurement-framework.md +210 -0
  47. package/pennyfarthing-dist/personas/themes/a-team.yaml +130 -0
  48. package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +1 -1
  49. package/pennyfarthing-dist/personas/themes/ancient-strategists.yaml +1 -1
  50. package/pennyfarthing-dist/personas/themes/arcane.yaml +1 -1
  51. package/pennyfarthing-dist/personas/themes/better-call-saul.yaml +1 -1
  52. package/pennyfarthing-dist/personas/themes/big-lebowski.yaml +1 -1
  53. package/pennyfarthing-dist/personas/themes/black-sails.yaml +1 -1
  54. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +1 -1
  55. package/pennyfarthing-dist/personas/themes/bobiverse.yaml +1 -1
  56. package/pennyfarthing-dist/personas/themes/breaking-bad.yaml +1 -1
  57. package/pennyfarthing-dist/personas/themes/count-of-monte-cristo.yaml +1 -1
  58. package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +1 -1
  59. package/pennyfarthing-dist/personas/themes/deadwood.yaml +1 -1
  60. package/pennyfarthing-dist/personas/themes/dickens.yaml +1 -1
  61. package/pennyfarthing-dist/personas/themes/discworld.yaml +1 -1
  62. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +1 -1
  63. package/pennyfarthing-dist/personas/themes/don-quixote.yaml +1 -1
  64. package/pennyfarthing-dist/personas/themes/dune.yaml +1 -1
  65. package/pennyfarthing-dist/personas/themes/enlightenment-thinkers.yaml +1 -1
  66. package/pennyfarthing-dist/personas/themes/expeditionary-force.yaml +1 -1
  67. package/pennyfarthing-dist/personas/themes/futurama.yaml +1 -1
  68. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +1 -1
  69. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +131 -1
  70. package/pennyfarthing-dist/personas/themes/gothic-literature.yaml +1 -1
  71. package/pennyfarthing-dist/personas/themes/great-gatsby.yaml +1 -1
  72. package/pennyfarthing-dist/personas/themes/hannibal.yaml +1 -1
  73. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +1 -1
  74. package/pennyfarthing-dist/personas/themes/his-dark-materials.yaml +1 -1
  75. package/pennyfarthing-dist/personas/themes/inspector-morse.yaml +1 -1
  76. package/pennyfarthing-dist/personas/themes/jane-austen.yaml +1 -1
  77. package/pennyfarthing-dist/personas/themes/legion-of-doom.yaml +130 -0
  78. package/pennyfarthing-dist/personas/themes/mad-max.yaml +1 -1
  79. package/pennyfarthing-dist/personas/themes/moby-dick.yaml +1 -1
  80. package/pennyfarthing-dist/personas/themes/neuromancer.yaml +1 -1
  81. package/pennyfarthing-dist/personas/themes/parks-and-rec.yaml +130 -0
  82. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +130 -0
  83. package/pennyfarthing-dist/personas/themes/renaissance-masters.yaml +1 -1
  84. package/pennyfarthing-dist/personas/themes/russian-masters.yaml +1 -1
  85. package/pennyfarthing-dist/personas/themes/sandman.yaml +1 -1
  86. package/pennyfarthing-dist/personas/themes/scientific-revolutionaries.yaml +1 -1
  87. package/pennyfarthing-dist/personas/themes/shakespeare.yaml +1 -1
  88. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +139 -3
  89. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +124 -0
  90. package/pennyfarthing-dist/personas/themes/star-wars.yaml +1 -1
  91. package/pennyfarthing-dist/personas/themes/succession.yaml +1 -1
  92. package/pennyfarthing-dist/personas/themes/superfriends.yaml +131 -1
  93. package/pennyfarthing-dist/personas/themes/ted-lasso.yaml +131 -1
  94. package/pennyfarthing-dist/personas/themes/the-americans.yaml +1 -1
  95. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +131 -1
  96. package/pennyfarthing-dist/personas/themes/the-good-place.yaml +1 -1
  97. package/pennyfarthing-dist/personas/themes/the-matrix.yaml +1 -1
  98. package/pennyfarthing-dist/personas/themes/the-sopranos.yaml +1 -1
  99. package/pennyfarthing-dist/personas/themes/west-wing.yaml +6 -6
  100. package/pennyfarthing-dist/personas/themes/world-explorers.yaml +1 -1
  101. package/pennyfarthing-dist/personas/themes/wwii-leaders.yaml +1 -1
  102. package/pennyfarthing-dist/scripts/core/check-context.sh +23 -6
  103. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +95 -0
  104. package/pennyfarthing-dist/scripts/git/release.sh +3 -2
  105. package/pennyfarthing-dist/scripts/health/drift-detection.sh +162 -0
  106. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +87 -0
  107. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -1
  108. package/pennyfarthing-dist/scripts/misc/deploy.sh +1 -1
  109. package/pennyfarthing-dist/scripts/misc/statusline.sh +25 -32
  110. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.mjs +377 -0
  111. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.sh +9 -0
  112. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.js +492 -0
  113. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +8 -200
  114. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +38 -5
  115. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +40 -0
  116. package/pennyfarthing-dist/skills/theme-creation/SKILL.md +12 -7
  117. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-04-final-validation.md +11 -3
  118. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +122 -0
  119. package/pennyfarthing-dist/workflows/epics-and-stories/workflow.yaml +3 -2
  120. package/packages/core/dist/workflow/generic-handoff.d.ts +0 -281
  121. package/packages/core/dist/workflow/generic-handoff.d.ts.map +0 -1
  122. package/packages/core/dist/workflow/generic-handoff.js +0 -411
  123. package/packages/core/dist/workflow/generic-handoff.js.map +0 -1
  124. package/packages/core/dist/workflow/generic-handoff.test.d.ts +0 -21
  125. package/packages/core/dist/workflow/generic-handoff.test.d.ts.map +0 -1
  126. package/packages/core/dist/workflow/generic-handoff.test.js +0 -499
  127. package/packages/core/dist/workflow/generic-handoff.test.js.map +0 -1
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * import-epic-to-future.mjs - Import epics-and-stories workflow output to future.yaml
4
+ *
5
+ * Transforms the markdown output from the epics-and-stories workflow into
6
+ * the YAML format used by sprint/future.yaml.
7
+ *
8
+ * Usage: node import-epic-to-future.mjs [--dry-run] <epics-md-file> [initiative-name]
9
+ *
10
+ * Example:
11
+ * node import-epic-to-future.mjs docs/planning/reflector-epics-and-stories.md "Reflector Consolidation"
12
+ */
13
+
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ // =============================================================================
22
+ // Colors
23
+ // =============================================================================
24
+
25
+ const colors = {
26
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
27
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
28
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
29
+ blue: (s) => `\x1b[34m${s}\x1b[0m`,
30
+ };
31
+
32
+ const log = {
33
+ info: (msg) => console.log(`${colors.blue('INFO')}: ${msg}`),
34
+ success: (msg) => console.log(`${colors.green('SUCCESS')}: ${msg}`),
35
+ warn: (msg) => console.log(`${colors.yellow('WARN')}: ${msg}`),
36
+ error: (msg) => console.error(`${colors.red('ERROR')}: ${msg}`),
37
+ };
38
+
39
+ // =============================================================================
40
+ // Find project root
41
+ // =============================================================================
42
+
43
+ function findProjectRoot() {
44
+ let dir = process.cwd();
45
+ while (dir !== '/') {
46
+ if (fs.existsSync(path.join(dir, '.claude'))) {
47
+ return dir;
48
+ }
49
+ dir = path.dirname(dir);
50
+ }
51
+ return process.cwd();
52
+ }
53
+
54
+ // =============================================================================
55
+ // Parse epics markdown file
56
+ // =============================================================================
57
+
58
+ function parseEpicsMarkdown(content) {
59
+ const result = {
60
+ title: '',
61
+ description: '',
62
+ totalPoints: 0,
63
+ epicTitle: '',
64
+ epicDescription: '',
65
+ stories: [],
66
+ };
67
+
68
+ const lines = content.split('\n');
69
+
70
+ // Extract title from first # heading
71
+ const titleMatch = content.match(/^# (.+?)( - Epic Breakdown)?$/m);
72
+ if (titleMatch) {
73
+ result.title = titleMatch[1].replace(' - Epics and Stories', '');
74
+ }
75
+
76
+ // Extract description from Overview section
77
+ const overviewMatch = content.match(/## Overview\s*\n\s*\n(.+?)(?=\n\n##|\n##)/s);
78
+ if (overviewMatch) {
79
+ result.description = overviewMatch[1].trim();
80
+ }
81
+
82
+ // Extract total points
83
+ const pointsMatch = content.match(/\*\*Points:\*\*\s*(\d+)/);
84
+ if (pointsMatch) {
85
+ result.totalPoints = parseInt(pointsMatch[1], 10);
86
+ } else {
87
+ const effortMatch = content.match(/Total Effort.*?(\d+)\s*story points/i);
88
+ if (effortMatch) {
89
+ result.totalPoints = parseInt(effortMatch[1], 10);
90
+ }
91
+ }
92
+
93
+ // Extract epic title
94
+ const epicTitleMatch = content.match(/^## Epic \d+:\s*(.+)$/m);
95
+ if (epicTitleMatch) {
96
+ result.epicTitle = epicTitleMatch[1];
97
+ }
98
+
99
+ // Extract epic description (User Outcome)
100
+ const userOutcomeMatch = content.match(/\*\*User Outcome:\*\*\s*(.+)/);
101
+ if (userOutcomeMatch) {
102
+ result.epicDescription = userOutcomeMatch[1];
103
+ }
104
+
105
+ // Parse stories
106
+ const storyRegex = /^### Story \d+\.(\d+):\s*(.+)$/gm;
107
+ const iWantRegex = /^I want \*\*(.+?)\*\*,/m;
108
+
109
+ let match;
110
+ let storyPositions = [];
111
+
112
+ // Find all story positions
113
+ while ((match = storyRegex.exec(content)) !== null) {
114
+ storyPositions.push({
115
+ num: parseInt(match[1], 10),
116
+ title: match[2],
117
+ index: match.index,
118
+ });
119
+ }
120
+
121
+ // Extract description for each story
122
+ for (let i = 0; i < storyPositions.length; i++) {
123
+ const story = storyPositions[i];
124
+ const startIndex = story.index;
125
+ const endIndex = i < storyPositions.length - 1
126
+ ? storyPositions[i + 1].index
127
+ : content.length;
128
+
129
+ const storyContent = content.slice(startIndex, endIndex);
130
+
131
+ // Extract "I want" description
132
+ const iWantMatch = storyContent.match(iWantRegex);
133
+ let description = '';
134
+ if (iWantMatch) {
135
+ description = iWantMatch[1];
136
+ }
137
+
138
+ result.stories.push({
139
+ num: story.num,
140
+ title: story.title,
141
+ description: description,
142
+ });
143
+ }
144
+
145
+ return result;
146
+ }
147
+
148
+ // =============================================================================
149
+ // Get next epic number from future.yaml
150
+ // =============================================================================
151
+
152
+ function getNextEpicNumber(futureYamlPath) {
153
+ if (!fs.existsSync(futureYamlPath)) {
154
+ return 60;
155
+ }
156
+
157
+ const content = fs.readFileSync(futureYamlPath, 'utf-8');
158
+
159
+ // Look for "Next Available Epic Number: XX"
160
+ const nextMatch = content.match(/Next Available Epic Number:\s*(\d+)/i);
161
+ if (nextMatch) {
162
+ return parseInt(nextMatch[1], 10);
163
+ }
164
+
165
+ // Fallback: find highest epic-XX number
166
+ const epicMatches = content.matchAll(/epic-(\d+)/g);
167
+ let maxNum = 59;
168
+ for (const m of epicMatches) {
169
+ const num = parseInt(m[1], 10);
170
+ if (num > maxNum) {
171
+ maxNum = num;
172
+ }
173
+ }
174
+
175
+ return maxNum + 1;
176
+ }
177
+
178
+ // =============================================================================
179
+ // Generate YAML for initiative
180
+ // =============================================================================
181
+
182
+ function generateYaml(parsed, epicNum, initiativeName, epicsFile) {
183
+ const date = new Date().toISOString().split('T')[0];
184
+
185
+ // Indent helper
186
+ const indent = (text, spaces) => {
187
+ return text.split('\n').map(line => ' '.repeat(spaces) + line).join('\n');
188
+ };
189
+
190
+ let yaml = `
191
+ # ==========================================================================
192
+ # ${initiativeName}
193
+ # Imported from: ${epicsFile}
194
+ # Date: ${date}
195
+ # ==========================================================================
196
+ - name: "${initiativeName}"
197
+ description: |
198
+ ${parsed.description}
199
+ status: ready
200
+ blocked_by: null
201
+ total_points: ${parsed.totalPoints}
202
+ prd: docs/planning/reflector-prd.md
203
+ epics_doc: ${epicsFile}
204
+ epics:
205
+ - id: epic-${epicNum}
206
+ title: "${parsed.epicTitle || initiativeName}"
207
+ description: |
208
+ ${parsed.epicDescription || parsed.description}
209
+ points: ${parsed.totalPoints}
210
+ priority: P1
211
+ marker: "reflector"
212
+ repos: pennyfarthing
213
+ status: planning
214
+ stories:
215
+ `;
216
+
217
+ // Add stories
218
+ for (const story of parsed.stories) {
219
+ yaml += ` - id: "${epicNum}-${story.num}"
220
+ title: "${story.title}"
221
+ description: |
222
+ ${story.description || story.title}
223
+ points: 1
224
+ priority: P0
225
+ status: planning
226
+ repos: pennyfarthing
227
+ `;
228
+ }
229
+
230
+ return yaml;
231
+ }
232
+
233
+ // =============================================================================
234
+ // Update future.yaml
235
+ // =============================================================================
236
+
237
+ function updateFutureYaml(futureYamlPath, newContent, nextEpicNum) {
238
+ let content = fs.readFileSync(futureYamlPath, 'utf-8');
239
+
240
+ // Update next epic number comment
241
+ content = content.replace(
242
+ /Next Available Epic Number:\s*\d+/i,
243
+ `Next Available Epic Number: ${nextEpicNum + 1}`
244
+ );
245
+
246
+ // Find insertion point (before SUMMARY section)
247
+ const summaryIndex = content.indexOf('# =============================================================================\n# SUMMARY');
248
+
249
+ if (summaryIndex === -1) {
250
+ // No summary section, append at end
251
+ content += newContent;
252
+ } else {
253
+ // Insert before summary
254
+ content = content.slice(0, summaryIndex) + newContent + '\n' + content.slice(summaryIndex);
255
+ }
256
+
257
+ // Update epic numbering comment (append new epic number to the range)
258
+ const epicNumMatch = content.match(/# Epic Numbering Used: ([\d\-,\s]+)\n/);
259
+ if (epicNumMatch) {
260
+ const current = epicNumMatch[1].trim();
261
+ if (!current.includes(nextEpicNum.toString())) {
262
+ // Parse the last range and extend it, or add new number
263
+ const lastRange = current.match(/(\d+)-(\d+)$/);
264
+ let newNumbering;
265
+ if (lastRange && parseInt(lastRange[2], 10) === nextEpicNum - 1) {
266
+ // Extend the range (e.g., 55-59 becomes 55-61)
267
+ newNumbering = current.replace(/(\d+)-(\d+)$/, `$1-${nextEpicNum}`);
268
+ } else {
269
+ // Add new number (e.g., 55-59, 61)
270
+ newNumbering = `${current}, ${nextEpicNum}`;
271
+ }
272
+ content = content.replace(
273
+ /# Epic Numbering Used: [\d\-,\s]+\n/,
274
+ `# Epic Numbering Used: ${newNumbering}\n`
275
+ );
276
+ }
277
+ }
278
+
279
+ // Update summary table
280
+ const summaryTableMatch = content.match(/# TOTAL\s+\|\s+(\d+)\s+\|\s+(\d+)/);
281
+ if (summaryTableMatch) {
282
+ const oldEpics = parseInt(summaryTableMatch[1], 10);
283
+ const oldPoints = parseInt(summaryTableMatch[2], 10);
284
+ // We'd need to parse the new points, but for simplicity just note it needs manual update
285
+ }
286
+
287
+ return content;
288
+ }
289
+
290
+ // =============================================================================
291
+ // Main
292
+ // =============================================================================
293
+
294
+ function main() {
295
+ const args = process.argv.slice(2);
296
+
297
+ let dryRun = false;
298
+ let epicsFile = '';
299
+ let initiativeName = '';
300
+
301
+ // Parse arguments
302
+ for (const arg of args) {
303
+ if (arg === '--dry-run') {
304
+ dryRun = true;
305
+ } else if (arg === '--help' || arg === '-h') {
306
+ console.log(`Usage: node import-epic-to-future.mjs [--dry-run] <epics-md-file> [initiative-name]
307
+
308
+ Arguments:
309
+ epics-md-file Path to the markdown file from epics-and-stories workflow
310
+ initiative-name Name for the initiative (optional, extracted from file if not provided)
311
+
312
+ Options:
313
+ --dry-run Print YAML to stdout instead of updating future.yaml
314
+ --help, -h Show this help message`);
315
+ process.exit(0);
316
+ } else if (!epicsFile) {
317
+ epicsFile = arg;
318
+ } else if (!initiativeName) {
319
+ initiativeName = arg;
320
+ }
321
+ }
322
+
323
+ if (!epicsFile) {
324
+ log.error('No epics markdown file specified');
325
+ console.log('Usage: node import-epic-to-future.mjs [--dry-run] <epics-md-file> [initiative-name]');
326
+ process.exit(1);
327
+ }
328
+
329
+ const projectRoot = findProjectRoot();
330
+ const fullEpicsPath = path.isAbsolute(epicsFile)
331
+ ? epicsFile
332
+ : path.join(projectRoot, epicsFile);
333
+
334
+ if (!fs.existsSync(fullEpicsPath)) {
335
+ log.error(`File not found: ${fullEpicsPath}`);
336
+ process.exit(1);
337
+ }
338
+
339
+ const futureYamlPath = path.join(projectRoot, 'sprint', 'future.yaml');
340
+
341
+ // Read and parse epics file
342
+ const content = fs.readFileSync(fullEpicsPath, 'utf-8');
343
+ const parsed = parseEpicsMarkdown(content);
344
+
345
+ // Use provided name or extract from file
346
+ if (!initiativeName) {
347
+ initiativeName = parsed.title || 'Imported Initiative';
348
+ }
349
+
350
+ // Get next epic number
351
+ const nextEpicNum = getNextEpicNumber(futureYamlPath);
352
+
353
+ log.info(`Next epic number: ${nextEpicNum}`);
354
+ log.info(`Initiative name: ${initiativeName}`);
355
+ log.info(`Total points: ${parsed.totalPoints}`);
356
+ log.info(`Epic title: ${parsed.epicTitle}`);
357
+ log.info(`Stories found: ${parsed.stories.length}`);
358
+
359
+ // Generate YAML
360
+ const newYaml = generateYaml(parsed, nextEpicNum, initiativeName, epicsFile);
361
+
362
+ if (dryRun) {
363
+ console.log(`\n${colors.yellow('=== DRY RUN: Would append to future.yaml ===')}`);
364
+ console.log(newYaml);
365
+ console.log(`${colors.yellow('=== End of YAML ===')}\n`);
366
+ console.log(`${colors.green('To apply, run without --dry-run')}`);
367
+ } else {
368
+ // Update future.yaml
369
+ const updatedContent = updateFutureYaml(futureYamlPath, newYaml, nextEpicNum);
370
+ fs.writeFileSync(futureYamlPath, updatedContent);
371
+
372
+ log.success(`Added epic-${nextEpicNum} to ${futureYamlPath}`);
373
+ log.success(`Next available epic number is now: ${nextEpicNum + 1}`);
374
+ }
375
+ }
376
+
377
+ main();
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # import-epic-to-future.sh - Thin wrapper for import-epic-to-future.mjs
3
+ #
4
+ # Usage: ./scripts/sprint/import-epic-to-future.sh [--dry-run] <epics-md-file> [initiative-name]
5
+ #
6
+ # Delegates to Node script for cleaner markdown parsing and YAML generation.
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ exec node "${SCRIPT_DIR}/import-epic-to-future.mjs" "$@"