bigpowers 2.1.0 → 2.1.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [2.1.2](https://github.com/danielvm-git/bigpowers/compare/v2.1.1...v2.1.2) (2026-06-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **dashboard:** support alternative epic id and title keys in yaml parser ([d911e8f](https://github.com/danielvm-git/bigpowers/commit/d911e8f61c608a96d8d3d42cb4a4d19cff71ee3d))
7
+
8
+ ## [2.1.1](https://github.com/danielvm-git/bigpowers/compare/v2.1.0...v2.1.1) (2026-06-11)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **dashboard/tui:** replace invalid {dim} tags with {gray-fg} across all render modules ([f029360](https://github.com/danielvm-git/bigpowers/commit/f02936068b024107e7685accb62fe2bb1b0917b7))
14
+
1
15
  # [2.1.0](https://github.com/danielvm-git/bigpowers/compare/v2.0.1...v2.1.0) (2026-06-11)
2
16
 
3
17
 
@@ -76,8 +76,8 @@ function readEpicShards(projectRoot) {
76
76
  const content = fs.readFileSync(filePath, 'utf8');
77
77
  const data = yaml.load(content);
78
78
  epics.push({
79
- id: data.id || null,
80
- title: data.title || null,
79
+ id: data.id || data.epic || null,
80
+ title: data.title || data.name || null,
81
81
  stories: data.stories || []
82
82
  });
83
83
  }
@@ -4,7 +4,7 @@ function renderEpicQueue(box, epics, executionStatus) {
4
4
  }
5
5
 
6
6
  if (!epics || epics.length === 0) {
7
- box.setContent('{dim}no epic shards found{/dim}');
7
+ box.setContent('{gray-fg}no epic shards found{/gray-fg}');
8
8
  return;
9
9
  }
10
10
 
@@ -16,7 +16,7 @@ function renderEpicQueue(box, epics, executionStatus) {
16
16
  const s = statusMap.get(id) || '';
17
17
  if (doneStatuses.has(s)) return '{green-fg}●{/green-fg}';
18
18
  if (activeStatuses.has(s)) return '{yellow-fg}●{/yellow-fg}';
19
- return '{dim}·{/dim}';
19
+ return '{gray-fg}·{/gray-fg}';
20
20
  }
21
21
 
22
22
  function epicDot(epic) {
@@ -27,7 +27,7 @@ function renderEpicQueue(box, epics, executionStatus) {
27
27
  if (stories.length > 0 && stories.every(st => doneStatuses.has(statusMap.get(st.id) || ''))) {
28
28
  return '{green-fg}●{/green-fg}';
29
29
  }
30
- return '{dim}·{/dim}';
30
+ return '{gray-fg}·{/gray-fg}';
31
31
  }
32
32
 
33
33
  const lines = [];
@@ -35,7 +35,7 @@ function renderEpicQueue(box, epics, executionStatus) {
35
35
  const perEpicBcp = [];
36
36
 
37
37
  epics.forEach(epic => {
38
- lines.push(`${epicDot(epic)} {bold}${epic.id}{/bold} {dim}·{/dim} ${epic.title || '—'}`);
38
+ lines.push(`${epicDot(epic)} {bold}${epic.id}{/bold} {gray-fg}·{/gray-fg} ${epic.title || '—'}`);
39
39
 
40
40
  const stories = epic.stories || [];
41
41
  let epicBcpDone = 0;
@@ -51,25 +51,25 @@ function renderEpicQueue(box, epics, executionStatus) {
51
51
 
52
52
  const bcpStr = isDone
53
53
  ? `{green-fg}${bcp} BCP{/green-fg}`
54
- : `{dim}${bcp} BCP{/dim}`;
55
- lines.push(` ${dot(story.id)} {dim}${story.id}{/dim} ${story.title || '—'} ${bcpStr}`);
54
+ : `{gray-fg}${bcp} BCP{/gray-fg}`;
55
+ lines.push(` ${dot(story.id)} {gray-fg}${story.id}{/gray-fg} ${story.title || '—'} ${bcpStr}`);
56
56
  });
57
57
 
58
58
  perEpicBcp.push({ id: epic.id, bcps: epicBcpTotal });
59
59
 
60
60
  const bcpProgressStr = epicBcpDone > 0
61
- ? `{green-fg}${epicBcpDone}{/green-fg}{dim}/${epicBcpTotal} BCP{/dim}`
62
- : `{dim}0/${epicBcpTotal} BCP{/dim}`;
63
- lines.push(` {dim}${epicStoriesDone}/${stories.length} done · ${bcpProgressStr}`);
61
+ ? `{green-fg}${epicBcpDone}{/green-fg}{gray-fg}/${epicBcpTotal} BCP{/gray-fg}`
62
+ : `{gray-fg}0/${epicBcpTotal} BCP{/gray-fg}`;
63
+ lines.push(` {gray-fg}${epicStoriesDone}/${stories.length} done · ${bcpProgressStr}`);
64
64
  lines.push('');
65
65
  });
66
66
 
67
67
  // Release baseline footer
68
- lines.push('{dim}─────────────────────────{/dim}');
69
- lines.push(`{dim}release baseline{/dim}`);
70
- lines.push(`{dim}total: {/dim}{yellow-fg}${grandTotalBcps} BCPs{/yellow-fg}`);
71
- lines.push(perEpicBcp.map(e => `{dim}${e.id}: ${e.bcps} BCP{/dim}`).join(' '));
72
- lines.push(`{dim}target: {/dim}{bold}v2.0.0{/bold}`);
68
+ lines.push('{gray-fg}─────────────────────────{/gray-fg}');
69
+ lines.push(`{gray-fg}release baseline{/gray-fg}`);
70
+ lines.push(`{gray-fg}total: {/gray-fg}{yellow-fg}${grandTotalBcps} BCPs{/yellow-fg}`);
71
+ lines.push(perEpicBcp.map(e => `{gray-fg}${e.id}: ${e.bcps} BCP{/gray-fg}`).join(' '));
72
+ lines.push(`{gray-fg}target: {/gray-fg}{bold}v2.0.0{/bold}`);
73
73
 
74
74
  box.setContent(lines.join('\n'));
75
75
  }
@@ -9,7 +9,7 @@ function renderFilesystem(box, projectRoot) {
9
9
  const specsPath = path.join(projectRoot, 'specs');
10
10
 
11
11
  if (!fs.existsSync(specsPath)) {
12
- box.setContent('{center}{dim}specs/ not found{/dim}{/center}');
12
+ box.setContent('{center}{gray-fg}specs/ not found{/gray-fg}{/center}');
13
13
  return;
14
14
  }
15
15
 
@@ -39,7 +39,7 @@ function renderFilesystem(box, projectRoot) {
39
39
  }
40
40
 
41
41
  fileCount = countFiles(specsPath);
42
- lines.push(`{dim}[${fileCount} files]{/dim}`);
42
+ lines.push(`{gray-fg}[${fileCount} files]{/gray-fg}`);
43
43
  lines.push('');
44
44
 
45
45
  // Build tree
@@ -83,7 +83,7 @@ function renderFilesystem(box, projectRoot) {
83
83
 
84
84
  buildTree(specsPath);
85
85
  } catch (err) {
86
- lines.push('{dim}Error reading specs/{/dim}');
86
+ lines.push('{gray-fg}Error reading specs/{/gray-fg}');
87
87
  }
88
88
 
89
89
  box.setContent(lines.join('\n'));
@@ -154,7 +154,7 @@ function start(projectRoot) {
154
154
  const metrics = computeProjectMetrics(cycleTimes);
155
155
 
156
156
  // Title bar: fixed project identity
157
- titleBar.setContent(' {bold}{cyan-fg}⚙ bigpowers factory{/cyan-fg}{/bold} {dim}v2 — seed {cyan-fg}→{/cyan-fg} epics {cyan-fg}→{/cyan-fg} mvp{/dim}');
157
+ titleBar.setContent(' {bold}{cyan-fg}⚙ bigpowers factory{/cyan-fg}{/bold} {gray-fg}v2 — seed {cyan-fg}→{/cyan-fg} epics {cyan-fg}→{/cyan-fg} mvp{/gray-fg}');
158
158
 
159
159
  renderMetricsBar(metricsBar, metrics, stateData, epics, cycleTimes);
160
160
  renderPipeline(pipeline, stateData);
@@ -9,7 +9,7 @@ function renderLedger(box, cycleTimes) {
9
9
  }
10
10
 
11
11
  if (!cycleTimes || cycleTimes.length === 0) {
12
- box.setContent('{dim}stories complete here as they land{/dim}');
12
+ box.setContent('{gray-fg}stories complete here as they land{/gray-fg}');
13
13
  return;
14
14
  }
15
15
 
@@ -22,7 +22,7 @@ function renderLedger(box, cycleTimes) {
22
22
  lines.push(
23
23
  `{cyan-fg}{bold}${pad('Story ID', W.sid)}${pad('Epic', W.epic)}${pad('BCPs', W.bcps)}${pad('Minutes', W.min)}${pad('BCP/hr', W.bhr)}{/bold}{/cyan-fg}`
24
24
  );
25
- lines.push('{dim}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/dim}');
25
+ lines.push('{gray-fg}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/gray-fg}');
26
26
 
27
27
  let totalBCPs = 0;
28
28
  let totalMinutes = 0;
@@ -39,14 +39,14 @@ function renderLedger(box, cycleTimes) {
39
39
 
40
40
  lines.push(
41
41
  `{white-fg}${pad(storyId, W.sid)}{/white-fg}` +
42
- `{dim}${pad(epicId, W.epic)}{/dim}` +
42
+ `{gray-fg}${pad(epicId, W.epic)}{/gray-fg}` +
43
43
  `{yellow-fg}${pad(bcps, W.bcps)}{/yellow-fg}` +
44
- `{dim}${pad(minutes, W.min)}{/dim}` +
44
+ `{gray-fg}${pad(minutes, W.min)}{/gray-fg}` +
45
45
  `{green-fg}${pad(bph, W.bhr)}{/green-fg}`
46
46
  );
47
47
  });
48
48
 
49
- lines.push('{dim}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/dim}');
49
+ lines.push('{gray-fg}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/gray-fg}');
50
50
 
51
51
  const avgBph = totalMinutes > 0 ? ((totalBCPs * 60) / totalMinutes).toFixed(1) : '—';
52
52
  lines.push(
@@ -41,11 +41,11 @@ function renderMetricsBar(box, projectMetrics, stateData, epics, cycleTimes) {
41
41
 
42
42
  // ── Line 1: status bar ──
43
43
  const statusLine =
44
- `{dim}BCPs:{/dim} {yellow-fg}${deliveredBcps}{/yellow-fg}{dim}/${targetBcps}{/dim}` +
45
- ` {dim}│{/dim} {dim}Cycle:{/dim} ${cycleDisplay}` +
46
- ` {dim}│{/dim} {dim}BCP/hr:{/dim} {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}` +
47
- ` {dim}│{/dim} {dim}v{/dim}{green-fg}${version}{/green-fg}` +
48
- `{|} {dim}branch:{/dim} {green-fg}${branch}{/green-fg} `;
44
+ `{gray-fg}BCPs:{/gray-fg} {yellow-fg}${deliveredBcps}{/yellow-fg}{gray-fg}/${targetBcps}{/gray-fg}` +
45
+ ` {gray-fg}│{/gray-fg} {gray-fg}Cycle:{/gray-fg} ${cycleDisplay}` +
46
+ ` {gray-fg}│{/gray-fg} {gray-fg}BCP/hr:{/gray-fg} {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}` +
47
+ ` {gray-fg}│{/gray-fg} {gray-fg}v{/gray-fg}{green-fg}${version}{/green-fg}` +
48
+ ` {gray-fg}branch:{/gray-fg} {green-fg}${branch}{/green-fg}`;
49
49
 
50
50
  // ── Line 2: step info ──
51
51
  const currentStep = stateData?.epicCycle?.current_step;
@@ -60,21 +60,21 @@ function renderMetricsBar(box, projectMetrics, stateData, epics, cycleTimes) {
60
60
  ? `{yellow-fg}running{/yellow-fg}`
61
61
  : `{green-fg}ready{/green-fg}`;
62
62
  const storyDesc = isActive
63
- ? `{dim}${activeEpic}{/dim} {dim}›{/dim} {cyan-fg}${activeStory}{/cyan-fg} {dim}—{/dim} ${stepName}`
64
- : `{dim}flow:{/dim} ${activeFlow} {dim}next:{/dim} {cyan-fg}${stateData?.epicCycle?.next_skill ?? 'survey-context'}{/cyan-fg}`;
63
+ ? `{gray-fg}${activeEpic}{/gray-fg} {gray-fg}›{/gray-fg} {cyan-fg}${activeStory}{/cyan-fg} {gray-fg}—{/gray-fg} ${stepName}`
64
+ : `{gray-fg}flow:{/gray-fg} ${activeFlow} {gray-fg}next:{/gray-fg} {cyan-fg}${stateData?.epicCycle?.next_skill ?? 'survey-context'}{/cyan-fg}`;
65
65
  const stepLine =
66
- `{dim}step {/dim}{cyan-fg}${doneStories}{/cyan-fg}{dim}/${totalStories}{/dim}` +
67
- ` {dim}—{/dim} ${statusWord}` +
68
- ` {dim}—{/dim} ${storyDesc}`;
66
+ `{gray-fg}step {/gray-fg}{cyan-fg}${doneStories}{/cyan-fg}{gray-fg}/${totalStories}{/gray-fg}` +
67
+ ` {gray-fg}—{/gray-fg} ${statusWord}` +
68
+ ` {gray-fg}—{/gray-fg} ${storyDesc}`;
69
69
 
70
70
  // ── Line 3: stats row ──
71
71
  const statsLine =
72
- `{dim}[{/dim} epics {green-fg}${doneEpics}${doneEpics > 0 ? '' : ''}{/green-fg}{dim}/${totalEpics} ]{/dim}` +
73
- ` {dim}[{/dim} stories {cyan-fg}${doneStories}{/cyan-fg}{dim}/${totalStories} ]{/dim}` +
74
- ` {dim}[{/dim} BCPs {yellow-fg}${deliveredBcps}{/yellow-fg}{dim}/${targetBcps} ]{/dim}` +
75
- ` {dim}[{/dim} cycle ${cycleDisplay}{dim} ]{/dim}` +
76
- ` {dim}[{/dim} BCP/hr {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}{dim} ]{/dim}` +
77
- ` {dim}[{/dim} v{green-fg}${version}{/green-fg}{dim} ]{/dim}`;
72
+ `{gray-fg}[{/gray-fg} epics {green-fg}${doneEpics}${doneEpics > 0 ? '' : ''}{/green-fg}{gray-fg}/${totalEpics} ]{/gray-fg}` +
73
+ ` {gray-fg}[{/gray-fg} stories {cyan-fg}${doneStories}{/cyan-fg}{gray-fg}/${totalStories} ]{/gray-fg}` +
74
+ ` {gray-fg}[{/gray-fg} BCPs {yellow-fg}${deliveredBcps}{/yellow-fg}{gray-fg}/${targetBcps} ]{/gray-fg}` +
75
+ ` {gray-fg}[{/gray-fg} cycle ${cycleDisplay}{gray-fg} ]{/gray-fg}` +
76
+ ` {gray-fg}[{/gray-fg} BCP/hr {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}{gray-fg} ]{/gray-fg}` +
77
+ ` {gray-fg}[{/gray-fg} v{green-fg}${version}{/green-fg}{gray-fg} ]{/gray-fg}`;
78
78
 
79
79
  box.setContent([statusLine, stepLine, statsLine].join('\n'));
80
80
  }
@@ -8,7 +8,7 @@ function renderPipeline(box, stateData) {
8
8
  }
9
9
 
10
10
  if (!stateData) {
11
- box.setContent('{dim}state.yaml not loaded{/dim}');
11
+ box.setContent('{gray-fg}state.yaml not loaded{/gray-fg}');
12
12
  return;
13
13
  }
14
14
 
@@ -24,13 +24,13 @@ function renderPipeline(box, stateData) {
24
24
  } else if (i === currentIdx) {
25
25
  return `{reverse}{cyan-fg}${i + 1} ${step}{/cyan-fg}{/reverse}`;
26
26
  } else {
27
- return `{dim}${i + 1} ${step}{/dim}`;
27
+ return `{gray-fg}${i + 1} ${step}{/gray-fg}`;
28
28
  }
29
- }).join(' {dim}›{/dim} ');
29
+ }).join(' {gray-fg}›{/gray-fg} ');
30
30
 
31
31
  const stepLabel = currentIdx >= 0 && currentIdx < STEPS.length
32
- ? `{dim}step{/dim} {cyan-fg}${currentIdx + 1}{/cyan-fg}{dim}/${STEPS.length}{/dim} {dim}—{/dim} ${STEPS[currentIdx]}`
33
- : '{dim}no active step{/dim}';
32
+ ? `{gray-fg}step{/gray-fg} {cyan-fg}${currentIdx + 1}{/cyan-fg}{gray-fg}/${STEPS.length}{/gray-fg} {gray-fg}—{/gray-fg} ${STEPS[currentIdx]}`
33
+ : '{gray-fg}no active step{/gray-fg}';
34
34
 
35
35
  box.setContent(stepLabel + '\n\n' + strip);
36
36
  }
@@ -10,7 +10,7 @@ function renderStateYaml(box, stateData) {
10
10
  }
11
11
 
12
12
  if (!stateData) {
13
- box.setContent('{center}{dim}state.yaml not found{/dim}{/center}');
13
+ box.setContent('{center}{gray-fg}state.yaml not found{/gray-fg}{/center}');
14
14
  return;
15
15
  }
16
16
 
@@ -34,7 +34,7 @@ function renderStateYaml(box, stateData) {
34
34
  const isDash = v === '—';
35
35
  let colored;
36
36
  if (isDash) {
37
- colored = `{dim}${v}{/dim}`;
37
+ colored = `{gray-fg}${v}{/gray-fg}`;
38
38
  } else if (fields.find(f => f.key === key)?.color === 'cyan') {
39
39
  colored = `{cyan-fg}${v}{/cyan-fg}`;
40
40
  } else if (fields.find(f => f.key === key)?.color === 'green') {
@@ -42,19 +42,19 @@ function renderStateYaml(box, stateData) {
42
42
  } else if (fields.find(f => f.key === key)?.color === 'yellow') {
43
43
  colored = `{yellow-fg}${v}{/yellow-fg}`;
44
44
  } else if (fields.find(f => f.key === key)?.color === 'dim') {
45
- colored = `{dim}${v}{/dim}`;
45
+ colored = `{gray-fg}${v}{/gray-fg}`;
46
46
  } else {
47
47
  colored = v;
48
48
  }
49
- return `{dim}${key}:{/dim} ${colored}`;
49
+ return `{gray-fg}${key}:{/gray-fg} ${colored}`;
50
50
  });
51
51
 
52
52
  // Stage tracker strip
53
53
  const tracker = STEP_ABBR.map((abbr, i) => {
54
54
  if (i < currentIdx) return `{green-fg}${abbr}{/green-fg}`;
55
55
  if (i === currentIdx) return `{cyan-fg}{bold}${abbr}{/bold}{/cyan-fg}`;
56
- return `{dim}${abbr}{/dim}`;
57
- }).join(' {dim}·{/dim} ');
56
+ return `{gray-fg}${abbr}{/gray-fg}`;
57
+ }).join(' {gray-fg}·{/gray-fg} ');
58
58
 
59
59
  lines.push('');
60
60
  lines.push(tracker);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "61 agent skills for spec-driven, test-first software development by solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -41,7 +41,7 @@ function parseEpicsFromReleasePlan(text) {
41
41
  const blocks = text.split(/\n\s*-\s+id:\s+/).slice(1);
42
42
  for (const block of blocks) {
43
43
  const id = block.match(/^(\S+)/)?.[1];
44
- const title = block.match(/title:\s*"?([^"\n]+)"?/)?.[1];
44
+ const title = block.match(/(?:title|name):\s*"?([^"\n]+)"?/)?.[1];
45
45
  const wsjf = parseFloat(block.match(/wsjf:\s*([\d.]+)/)?.[1] || '0');
46
46
  const file = block.match(/file:\s*(\S+)/)?.[1];
47
47
  if (id) epics.push({ id, title: title || id, wsjf, file });
@@ -50,7 +50,7 @@ function parseEpicsFromReleasePlan(text) {
50
50
  }
51
51
 
52
52
  function parseSimpleEpic(text) {
53
- const title = text.match(/^title:\s*"?([^"\n]+)"?/m)?.[1];
53
+ const title = text.match(/^(?:title|name):\s*"?([^"\n]+)"?/m)?.[1];
54
54
  const stories = [];
55
55
  const storyBlocks = text.split(/\n\s*-\s+id:\s+/).slice(1);
56
56
  for (const sb of storyBlocks) {