bigpowers 2.3.0 → 2.4.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 (85) hide show
  1. package/.pi/package.json +1 -1
  2. package/.pi/prompts/compose-workflow.md +1 -1
  3. package/.pi/prompts/diagnose-root.md +1 -1
  4. package/.pi/prompts/evolve-skill.md +1 -1
  5. package/.pi/prompts/grill-with-docs.md +1 -1
  6. package/.pi/prompts/research-first.md +1 -1
  7. package/.pi/prompts/reset-baseline.md +1 -1
  8. package/.pi/prompts/run-evals.md +1 -1
  9. package/.pi/prompts/scope-work.md +1 -1
  10. package/.pi/prompts/search-skills.md +1 -1
  11. package/.pi/prompts/setup-environment.md +1 -1
  12. package/.pi/prompts/simulate-agents.md +1 -1
  13. package/.pi/prompts/slice-tasks.md +1 -1
  14. package/.pi/prompts/stocktake-skills.md +1 -1
  15. package/.pi/prompts/verify-work.md +1 -1
  16. package/.pi/skills/assess-impact/SKILL.md +2 -1
  17. package/.pi/skills/audit-code/SKILL.md +1 -0
  18. package/.pi/skills/build-epic/SKILL.md +1 -0
  19. package/.pi/skills/change-request/SKILL.md +1 -0
  20. package/.pi/skills/commit-message/SKILL.md +1 -0
  21. package/.pi/skills/compose-workflow/SKILL.md +2 -1
  22. package/.pi/skills/craft-skill/SKILL.md +1 -0
  23. package/.pi/skills/deepen-architecture/SKILL.md +1 -0
  24. package/.pi/skills/define-language/SKILL.md +2 -1
  25. package/.pi/skills/define-success/SKILL.md +2 -1
  26. package/.pi/skills/delegate-task/SKILL.md +1 -0
  27. package/.pi/skills/design-interface/SKILL.md +2 -1
  28. package/.pi/skills/develop-tdd/SKILL.md +1 -0
  29. package/.pi/skills/diagnose-root/SKILL.md +2 -1
  30. package/.pi/skills/dispatch-agents/SKILL.md +1 -0
  31. package/.pi/skills/edit-document/SKILL.md +1 -0
  32. package/.pi/skills/elaborate-spec/SKILL.md +2 -1
  33. package/.pi/skills/enforce-first/SKILL.md +2 -1
  34. package/.pi/skills/evolve-skill/SKILL.md +2 -1
  35. package/.pi/skills/execute-plan/SKILL.md +1 -0
  36. package/.pi/skills/fix-bug/SKILL.md +1 -0
  37. package/.pi/skills/grill-me/SKILL.md +2 -1
  38. package/.pi/skills/grill-with-docs/SKILL.md +2 -1
  39. package/.pi/skills/guard-git/SKILL.md +1 -0
  40. package/.pi/skills/hook-commits/SKILL.md +1 -0
  41. package/.pi/skills/inspect-quality/SKILL.md +2 -1
  42. package/.pi/skills/investigate-bug/SKILL.md +2 -1
  43. package/.pi/skills/kickoff-branch/SKILL.md +2 -1
  44. package/.pi/skills/map-codebase/SKILL.md +2 -1
  45. package/.pi/skills/migrate-spec/SKILL.md +1 -0
  46. package/.pi/skills/model-domain/SKILL.md +1 -0
  47. package/.pi/skills/orchestrate-project/SKILL.md +1 -0
  48. package/.pi/skills/organize-workspace/SKILL.md +2 -1
  49. package/.pi/skills/plan-refactor/SKILL.md +1 -0
  50. package/.pi/skills/plan-release/SKILL.md +2 -1
  51. package/.pi/skills/plan-work/SKILL.md +2 -1
  52. package/.pi/skills/release-branch/SKILL.md +2 -1
  53. package/.pi/skills/request-review/SKILL.md +1 -0
  54. package/.pi/skills/research-first/SKILL.md +2 -1
  55. package/.pi/skills/reset-baseline/SKILL.md +2 -1
  56. package/.pi/skills/respond-review/SKILL.md +1 -0
  57. package/.pi/skills/run-evals/SKILL.md +2 -1
  58. package/.pi/skills/run-planning/SKILL.md +2 -1
  59. package/.pi/skills/scope-work/SKILL.md +2 -1
  60. package/.pi/skills/search-skills/SKILL.md +2 -1
  61. package/.pi/skills/seed-conventions/SKILL.md +1 -0
  62. package/.pi/skills/session-state/SKILL.md +1 -0
  63. package/.pi/skills/setup-environment/SKILL.md +2 -1
  64. package/.pi/skills/simulate-agents/SKILL.md +2 -1
  65. package/.pi/skills/slice-tasks/SKILL.md +2 -1
  66. package/.pi/skills/spike-prototype/SKILL.md +2 -1
  67. package/.pi/skills/stocktake-skills/SKILL.md +2 -1
  68. package/.pi/skills/survey-context/SKILL.md +1 -0
  69. package/.pi/skills/terse-mode/SKILL.md +2 -1
  70. package/.pi/skills/trace-requirement/SKILL.md +2 -1
  71. package/.pi/skills/using-bigpowers/SKILL.md +2 -1
  72. package/.pi/skills/validate-fix/SKILL.md +2 -1
  73. package/.pi/skills/verify-work/SKILL.md +2 -1
  74. package/.pi/skills/visual-dashboard/SKILL.md +1 -0
  75. package/.pi/skills/wire-observability/SKILL.md +1 -0
  76. package/.pi/skills/write-document/SKILL.md +1 -0
  77. package/CHANGELOG.md +14 -0
  78. package/SKILL-INDEX.md +34 -33
  79. package/dashboard/src/web/client.html +191 -249
  80. package/package.json +11 -2
  81. package/scripts/generate-reference-tables.sh +1 -1
  82. package/scripts/sync-skills.sh +22 -10
  83. package/scripts/validate-skill-yaml.py +73 -0
  84. package/visual-dashboard/scripts/cockpit.html +123 -16
  85. package/visual-dashboard/scripts/frame-template.html +181 -45
@@ -5,282 +5,221 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>BigPowers Dashboard</title>
7
7
  <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
8
+ /* ===== GRID TOKENS ===== */
9
+ :root{
10
+ --cols:12;
11
+ --bl:8px;
12
+ --lh:24px;
13
+ --gutter:24px;
14
+ --margin:72px;
15
+ --pad:48px;
16
+ --maxw:1296px;
17
+ --paper:#0d1117;
18
+ --ink:#c9d1d9;
19
+ --ink-soft:#8b949e;
20
+ --accent:#58a6ff;
21
+ --accent-hover:#79c0ff;
22
+ --g-col:rgba(88,166,255,.075);
23
+ --g-edge:rgba(88,166,255,.40);
24
+ --g-base:rgba(0,150,140,.34);
25
+ --g-base-min:rgba(0,150,140,.12);
26
+ }
27
+ * { margin: 0; padding: 0; box-sizing: border-box; }
13
28
 
14
29
  body {
15
- background: #0d1117;
16
- color: #c9d1d9;
30
+ background: var(--paper);
31
+ color: var(--ink);
17
32
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
18
33
  font-size: 12px;
19
- line-height: 1.6;
20
- padding: 16px;
21
- }
22
-
23
- .container {
24
- max-width: 1400px;
25
- margin: 0 auto;
26
- }
27
-
34
+ line-height: var(--lh);
35
+ -webkit-font-smoothing: antialiased;
36
+ }
37
+
38
+ /* ---- scaffold ---- */
39
+ .spread{position:relative;width:100%;}
40
+ .wrap{position:relative;max-width:var(--maxw);margin:0 auto;padding:var(--pad) var(--margin);}
41
+ .grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);
42
+ column-gap:var(--gutter);row-gap:var(--lh);}
43
+ .band{grid-column:1 / -1;display:grid;grid-template-columns:subgrid;
44
+ column-gap:var(--gutter);row-gap:var(--lh);align-items:start;}
45
+ @supports not (grid-template-columns:subgrid){
46
+ .band{grid-template-columns:repeat(var(--cols),1fr);}
47
+ }
48
+
49
+ /* ---- toggle & guides ---- */
50
+ .guides{position:absolute;inset:0;pointer-events:none;z-index:60;opacity:0;
51
+ transition:opacity .26s ease;}
52
+ body.grid-on .guides{opacity:1;}
53
+ .guides .cols{position:absolute;top:0;bottom:0;left:var(--margin);right:var(--margin);
54
+ display:grid;grid-template-columns:repeat(var(--cols),1fr);column-gap:var(--gutter);}
55
+ .guides .col{background:var(--g-col);
56
+ box-shadow:inset 1px 0 0 var(--g-edge),inset -1px 0 0 var(--g-edge);position:relative;}
57
+ .guides .col span{position:absolute;top:32px;left:0;right:0;text-align:center;
58
+ font-family:"Space Mono",monospace;font-size:10px;line-height:1;color:var(--accent);}
59
+ .guides .rows{position:absolute;left:var(--margin);right:var(--margin);top:0;bottom:0;
60
+ background-image:
61
+ repeating-linear-gradient(to bottom,var(--g-base) 0 1px,transparent 1px var(--lh)),
62
+ repeating-linear-gradient(to bottom,var(--g-base-min) 0 1px,transparent 1px var(--bl));}
63
+ .guides .mline{position:absolute;top:0;bottom:0;width:1px;background:var(--g-edge);}
64
+ .guides .mline.l{left:var(--margin);} .guides .mline.r{right:var(--margin);}
65
+
66
+ .toggle{position:fixed;top:18px;right:18px;z-index:200;display:flex;align-items:center;gap:10px;
67
+ background:var(--ink);color:var(--paper);border:none;cursor:pointer;font-family:"Space Mono",monospace;
68
+ font-size:12px;letter-spacing:.14em;text-transform:uppercase;padding:11px 14px;}
69
+ .toggle .dot{width:9px;height:9px;border-radius:50%;background:var(--ink-soft);}
70
+ body.grid-on .toggle{background:var(--accent);} body.grid-on .toggle .dot{background:var(--paper);}
71
+
72
+ /* ===== COMPONENT STYLES ===== */
28
73
  h1 {
29
74
  font-size: 20px;
30
- margin-bottom: 24px;
31
- color: #58a6ff;
75
+ line-height: calc(var(--lh) * 2);
76
+ margin-bottom: var(--lh);
77
+ color: var(--accent);
32
78
  border-bottom: 2px solid #30363d;
33
- padding-bottom: 8px;
34
- }
35
-
36
- .grid {
37
- display: grid;
38
- grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
39
- gap: 16px;
40
- margin-bottom: 16px;
79
+ padding-bottom: calc(var(--lh) - 2px);
80
+ grid-column: 1 / -1;
41
81
  }
42
82
 
43
83
  .panel {
44
84
  background: #161b22;
45
85
  border: 1px solid #30363d;
46
86
  border-radius: 6px;
47
- padding: 16px;
87
+ padding: calc(var(--lh) - 1px) 23px; /* account for border */
48
88
  }
49
89
 
50
90
  .panel h2 {
51
91
  font-size: 14px;
52
- margin-bottom: 12px;
53
- color: #79c0ff;
92
+ line-height: var(--lh);
93
+ margin-bottom: var(--lh);
94
+ color: var(--accent-hover);
54
95
  border-bottom: 1px solid #30363d;
55
- padding-bottom: 8px;
96
+ padding-bottom: calc(var(--lh) - 1px);
56
97
  }
57
98
 
99
+ /* Keep all internal paddings/margins as multiples of --bl or --lh */
58
100
  .metric-row {
59
101
  display: flex;
60
102
  justify-content: space-between;
61
- padding: 6px 0;
103
+ padding: calc(var(--bl) - 1px) 0 var(--bl);
62
104
  border-bottom: 1px solid #21262d;
105
+ line-height: var(--lh);
63
106
  }
107
+ .metric-row:last-child { border-bottom: none; }
108
+ .metric-label { color: var(--ink-soft); }
109
+ .metric-value { color: var(--accent-hover); font-weight: bold; }
64
110
 
65
- .metric-row:last-child {
66
- border-bottom: none;
67
- }
68
-
69
- .metric-label {
70
- color: #8b949e;
71
- }
72
-
73
- .metric-value {
74
- color: #79c0ff;
75
- font-weight: bold;
76
- }
77
-
78
- .step-container {
79
- display: flex;
80
- gap: 8px;
81
- flex-wrap: wrap;
82
- }
83
-
111
+ .step-container { display: flex; gap: var(--bl); flex-wrap: wrap; }
84
112
  .step {
85
- flex: 1;
86
- min-width: 80px;
87
- padding: 8px;
88
- background: #21262d;
89
- border: 1px solid #30363d;
90
- border-radius: 4px;
91
- text-align: center;
92
- font-size: 10px;
93
- color: #8b949e;
113
+ flex: 1; min-width: 80px;
114
+ padding: calc(var(--bl) - 1px) calc(var(--bl) - 1px);
115
+ background: #21262d; border: 1px solid #30363d; border-radius: 4px;
116
+ text-align: center; font-size: 10px; line-height: 16px; color: var(--ink-soft);
94
117
  transition: all 0.2s ease;
95
118
  }
96
-
97
119
  .step.active {
98
- background: #238636;
99
- border-color: #2ea043;
100
- color: #aff5b4;
101
- font-weight: bold;
102
- }
103
-
104
- .step-label {
105
- display: block;
106
- margin-bottom: 4px;
107
- word-break: break-word;
120
+ background: #238636; border-color: #2ea043; color: #aff5b4; font-weight: bold;
108
121
  }
122
+ .step-label { display: block; word-break: break-word; }
109
123
 
110
- .state-pairs {
111
- display: flex;
112
- flex-direction: column;
113
- gap: 8px;
114
- }
115
-
116
- .state-pair {
117
- display: flex;
118
- flex-direction: column;
119
- }
120
-
121
- .state-key {
122
- color: #8b949e;
123
- font-size: 11px;
124
- }
125
-
126
- .state-value {
127
- color: #79c0ff;
128
- word-break: break-all;
129
- margin-top: 2px;
130
- }
124
+ .state-pairs { display: flex; flex-direction: column; gap: var(--bl); }
125
+ .state-pair { display: flex; flex-direction: column; line-height: 16px; }
126
+ .state-key { color: var(--ink-soft); font-size: 11px; }
127
+ .state-value { color: var(--accent-hover); word-break: break-all; margin-top: 0; }
131
128
 
132
129
  .ledger-row {
133
- padding: 6px 0;
134
- border-bottom: 1px solid #21262d;
135
- display: flex;
136
- justify-content: space-between;
137
- }
138
-
139
- .ledger-row:last-child {
140
- border-bottom: none;
141
- }
142
-
143
- .ledger-id {
144
- color: #8b949e;
145
- min-width: 60px;
146
- }
147
-
148
- .ledger-value {
149
- color: #79c0ff;
150
- font-weight: bold;
151
- }
152
-
153
- .ledger-meta {
154
- color: #6e7681;
155
- font-size: 10px;
156
- margin-top: 2px;
130
+ padding: calc(var(--bl) - 1px) 0 var(--bl);
131
+ border-bottom: 1px solid #21262d; display: flex; justify-content: space-between;
132
+ line-height: 16px;
157
133
  }
134
+ .ledger-row:last-child { border-bottom: none; }
135
+ .ledger-id { color: var(--ink-soft); min-width: 60px; }
136
+ .ledger-value { color: var(--accent-hover); font-weight: bold; }
137
+ .ledger-meta { color: #6e7681; font-size: 10px; margin-top: 0; }
158
138
 
159
139
  .epic-item {
160
- padding: 8px;
161
- background: #21262d;
162
- border-left: 3px solid #30363d;
163
- margin-bottom: 6px;
164
- border-radius: 2px;
140
+ padding: calc(var(--bl) - 1px) calc(var(--bl) - 1px);
141
+ background: #21262d; border-left: 3px solid #30363d;
142
+ margin-bottom: var(--bl); border-radius: 2px;
143
+ line-height: 16px;
165
144
  }
145
+ .epic-title { color: var(--accent-hover); font-weight: bold; margin-bottom: 0; }
146
+ .epic-stories { margin-left: var(--bl); color: var(--ink-soft); font-size: 11px; }
166
147
 
167
- .epic-title {
168
- color: #79c0ff;
169
- font-weight: bold;
170
- margin-bottom: 4px;
171
- }
172
-
173
- .epic-stories {
174
- margin-left: 8px;
175
- color: #8b949e;
176
- font-size: 11px;
177
- }
178
-
179
- .status-badge {
180
- display: inline-block;
181
- padding: 2px 6px;
182
- background: #21262d;
183
- border: 1px solid #30363d;
184
- border-radius: 3px;
185
- font-size: 10px;
186
- color: #8b949e;
187
- margin-right: 4px;
188
- margin-bottom: 2px;
189
- }
190
-
191
- .status-active {
192
- background: #238636;
193
- border-color: #2ea043;
194
- color: #aff5b4;
195
- }
196
-
197
- .status-complete {
198
- background: #1a414b;
199
- border-color: #238636;
200
- color: #7ee787;
201
- }
202
-
203
- .empty {
204
- color: #6e7681;
205
- font-style: italic;
206
- }
148
+ .empty { color: #6e7681; font-style: italic; }
207
149
 
208
150
  .connection-status {
209
- position: fixed;
210
- bottom: 16px;
211
- right: 16px;
212
- padding: 8px 12px;
213
- background: #238636;
214
- color: #aff5b4;
215
- border-radius: 4px;
216
- font-size: 11px;
217
- border: 1px solid #2ea043;
151
+ position: fixed; bottom: 16px; right: 16px;
152
+ padding: 7px 11px; /* 16px tall total inner approx */
153
+ background: #238636; color: #aff5b4; border-radius: 4px;
154
+ font-size: 11px; border: 1px solid #2ea043; line-height: 16px;
218
155
  }
219
-
220
156
  .connection-status.disconnected {
221
- background: #da3633;
222
- border-color: #f85149;
223
- color: #f5a8a8;
157
+ background: #da3633; border-color: #f85149; color: #f5a8a8;
224
158
  }
225
159
  </style>
226
160
  </head>
227
161
  <body>
228
- <div class="container">
229
- <h1>BigPowers Dashboard</h1>
230
-
231
- <div class="grid">
232
- <!-- Pipeline Panel -->
233
- <div class="panel">
234
- <h2>Pipeline</h2>
235
- <div class="step-container" id="pipeline"></div>
236
- </div>
162
+ <div class="spread">
163
+ <div class="wrap">
164
+ <div class="grid">
165
+ <div class="band">
166
+ <h1 class="opt-align">BigPowers Dashboard</h1>
167
+ </div>
168
+
169
+ <div class="band">
170
+ <!-- Pipeline Panel -->
171
+ <div class="panel" style="grid-column: 1 / -1;">
172
+ <h2>Pipeline</h2>
173
+ <div class="step-container" id="pipeline"></div>
174
+ </div>
175
+ </div>
237
176
 
238
- <!-- Metrics Panel -->
239
- <div class="panel">
240
- <h2>Metrics</h2>
241
- <div id="metrics"></div>
242
- </div>
177
+ <div class="band">
178
+ <!-- Metrics Panel -->
179
+ <div class="panel" style="grid-column: 1 / 4;">
180
+ <h2>Metrics</h2>
181
+ <div id="metrics"></div>
182
+ </div>
243
183
 
244
- <!-- State Panel -->
245
- <div class="panel">
246
- <h2>State</h2>
247
- <div class="state-pairs" id="state"></div>
248
- </div>
184
+ <!-- State Panel -->
185
+ <div class="panel" style="grid-column: 4 / 7;">
186
+ <h2>State</h2>
187
+ <div class="state-pairs" id="state"></div>
188
+ </div>
249
189
 
250
- <!-- Epic Queue Panel -->
251
- <div class="panel">
252
- <h2>Epic Queue</h2>
253
- <div id="epics"></div>
254
- </div>
190
+ <!-- Epic Queue Panel -->
191
+ <div class="panel" style="grid-column: 7 / 10;">
192
+ <h2>Epic Queue</h2>
193
+ <div id="epics"></div>
194
+ </div>
255
195
 
256
- <!-- Cycle-Time Ledger Panel -->
257
- <div class="panel">
258
- <h2>Cycle-Time Ledger</h2>
259
- <div id="ledger"></div>
196
+ <!-- Cycle-Time Ledger Panel -->
197
+ <div class="panel" style="grid-column: 10 / 13;">
198
+ <h2>Cycle-Time Ledger</h2>
199
+ <div id="ledger"></div>
200
+ </div>
201
+ </div>
202
+ <!-- GRID GUIDES -->
203
+ <div class="guides" aria-hidden="true">
204
+ <div class="cols"></div><div class="rows"></div>
205
+ <div class="mline l"></div><div class="mline r"></div>
260
206
  </div>
261
207
  </div>
262
208
  </div>
263
209
 
210
+ <button id="gridToggle" class="toggle"><span class="dot"></span> <span class="lbl">Show grid</span> <kbd>G</kbd></button>
264
211
  <div class="connection-status" id="status">Live</div>
265
212
 
266
213
  <script>
267
214
  const PIPELINE_STEPS = [
268
- 'survey-context',
269
- 'plan-work',
270
- 'kickoff-branch',
271
- 'develop-tdd',
272
- 'verify-work',
273
- 'audit-code',
274
- 'commit-message',
275
- 'release-branch'
215
+ 'survey-context', 'plan-work', 'kickoff-branch', 'develop-tdd',
216
+ 'verify-work', 'audit-code', 'commit-message', 'release-branch'
276
217
  ];
277
-
278
218
  let eventSource = null;
279
219
  let pollInterval = null;
280
220
 
281
221
  function render(data) {
282
222
  if (!data) return;
283
-
284
223
  renderPipeline(data);
285
224
  renderMetrics(data);
286
225
  renderState(data);
@@ -292,7 +231,6 @@
292
231
  const container = document.getElementById('pipeline');
293
232
  const activeFlow = data.state?.activeFlow;
294
233
  const activeIndex = activeFlow ? PIPELINE_STEPS.indexOf(activeFlow) : -1;
295
-
296
234
  container.innerHTML = PIPELINE_STEPS.map((step, idx) => {
297
235
  const isActive = idx === activeIndex;
298
236
  const className = isActive ? 'step active' : 'step';
@@ -308,32 +246,17 @@
308
246
 
309
247
  let html = '';
310
248
  if (metrics) {
311
- html += `<div class="metric-row">
312
- <span class="metric-label">Total BCPs</span>
313
- <span class="metric-value">${metrics.totalBcps}</span>
314
- </div>`;
315
- html += `<div class="metric-row">
316
- <span class="metric-label">Avg BCP/Hour</span>
317
- <span class="metric-value">${metrics.avgBcpPerHour.toFixed(2)}</span>
318
- </div>`;
249
+ html += `<div class="metric-row"><span class="metric-label">Total BCPs</span><span class="metric-value">${metrics.totalBcps}</span></div>`;
250
+ html += `<div class="metric-row"><span class="metric-label">Avg BCP/Hour</span><span class="metric-value">${metrics.avgBcpPerHour.toFixed(2)}</span></div>`;
319
251
  }
320
252
  if (velocity) {
321
- html += `<div class="metric-row">
322
- <span class="metric-label">Current Velocity</span>
323
- <span class="metric-value">${velocity.avgBcpPerHour.toFixed(2)} BCP/h</span>
324
- </div>`;
253
+ html += `<div class="metric-row"><span class="metric-label">Current Velocity</span><span class="metric-value">${velocity.avgBcpPerHour.toFixed(2)} BCP/h</span></div>`;
325
254
  }
326
255
  if (data.state?.release?.version) {
327
- html += `<div class="metric-row">
328
- <span class="metric-label">Version</span>
329
- <span class="metric-value">${data.state.release.version}</span>
330
- </div>`;
256
+ html += `<div class="metric-row"><span class="metric-label">Version</span><span class="metric-value">${data.state.release.version}</span></div>`;
331
257
  }
332
258
  if (timestamp) {
333
- html += `<div class="metric-row">
334
- <span class="metric-label">Updated</span>
335
- <span class="metric-value" style="font-size: 10px;">${new Date(timestamp).toLocaleTimeString()}</span>
336
- </div>`;
259
+ html += `<div class="metric-row"><span class="metric-label">Updated</span><span class="metric-value" style="font-size: 10px;">${new Date(timestamp).toLocaleTimeString()}</span></div>`;
337
260
  }
338
261
  container.innerHTML = html || '<p class="empty">No metrics available</p>';
339
262
  }
@@ -341,41 +264,32 @@
341
264
  function renderState(data) {
342
265
  const container = document.getElementById('state');
343
266
  const state = data.state || {};
344
-
345
267
  const pairs = [
346
268
  { key: 'Active Flow', value: state.activeFlow },
347
269
  { key: 'Active Epic', value: state.activeEpic },
348
270
  { key: 'Active Story', value: state.activeStory },
349
271
  { key: 'Git Branch', value: state.gitBranch }
350
272
  ];
351
-
352
273
  container.innerHTML = pairs.map(pair => {
353
274
  const val = pair.value || '(none)';
354
- return `
355
- <div class="state-pair">
356
- <span class="state-key">${pair.key}</span>
357
- <span class="state-value">${val}</span>
358
- </div>
359
- `;
275
+ return `<div class="state-pair"><span class="state-key">${pair.key}</span><span class="state-value">${val}</span></div>`;
360
276
  }).join('');
361
277
  }
362
278
 
363
279
  function renderEpics(data) {
364
280
  const container = document.getElementById('epics');
365
281
  const epics = data.epics || [];
366
-
367
282
  if (epics.length === 0) {
368
283
  container.innerHTML = '<p class="empty">No epics available</p>';
369
284
  return;
370
285
  }
371
-
372
286
  container.innerHTML = epics.map(epic => {
373
287
  const stories = epic.stories || [];
374
288
  const storyCount = stories.length;
375
289
  return `
376
290
  <div class="epic-item">
377
291
  <div class="epic-title">${epic.id || 'Unknown'}</div>
378
- <div style="color: #8b949e; font-size: 11px; margin-bottom: 4px;">${epic.title || 'Untitled'}</div>
292
+ <div style="color: var(--ink-soft); font-size: 11px; margin-bottom: 0;">${epic.title || 'Untitled'}</div>
379
293
  <div class="epic-stories">${storyCount} stories</div>
380
294
  </div>
381
295
  `;
@@ -385,12 +299,10 @@
385
299
  function renderLedger(data) {
386
300
  const container = document.getElementById('ledger');
387
301
  const cycleTimes = data.cycleTimes || [];
388
-
389
302
  if (cycleTimes.length === 0) {
390
303
  container.innerHTML = '<p class="empty">No cycle-time data available</p>';
391
304
  return;
392
305
  }
393
-
394
306
  const recentRows = cycleTimes.slice(-8);
395
307
  container.innerHTML = recentRows.map(row => {
396
308
  const end = row.end ? new Date(row.end).toLocaleDateString() : '(active)';
@@ -420,7 +332,6 @@
420
332
  function startEventSource() {
421
333
  eventSource = new EventSource('/events');
422
334
  setConnectionStatus(true);
423
-
424
335
  eventSource.onmessage = (event) => {
425
336
  try {
426
337
  const data = JSON.parse(event.data);
@@ -429,7 +340,6 @@
429
340
  console.error('Failed to parse SSE data:', err);
430
341
  }
431
342
  };
432
-
433
343
  eventSource.onerror = (err) => {
434
344
  console.warn('SSE connection lost, falling back to polling');
435
345
  eventSource.close();
@@ -440,7 +350,6 @@
440
350
 
441
351
  function startPolling() {
442
352
  if (pollInterval) return;
443
-
444
353
  pollInterval = setInterval(() => {
445
354
  fetch('/api/state')
446
355
  .then(res => res.json())
@@ -459,7 +368,6 @@
459
368
  }
460
369
  }
461
370
 
462
- // Initial load
463
371
  window.addEventListener('load', () => {
464
372
  fetch('/api/state')
465
373
  .then(res => res.json())
@@ -472,6 +380,40 @@
472
380
  startPolling();
473
381
  });
474
382
  });
383
+
384
+ /* ===== GRID SCRIPT ===== */
385
+ var btn=document.getElementById('gridToggle');
386
+ function setGrid(on){document.body.classList.toggle('grid-on',on);
387
+ if(btn){btn.setAttribute('aria-pressed',on?'true':'false');
388
+ var l=btn.querySelector('.lbl'); if(l) l.textContent=on?'Hide grid':'Show grid';}}
389
+ if(btn) btn.addEventListener('click',function(){setGrid(!document.body.classList.contains('grid-on'));});
390
+ document.addEventListener('keydown',function(e){
391
+ if((e.key==='g'||e.key==='G')&&!e.metaKey&&!e.ctrlKey&&!e.altKey){
392
+ setGrid(!document.body.classList.contains('grid-on'));}});
393
+
394
+ document.querySelectorAll('.guides .cols').forEach(function(h){
395
+ var n=getComputedStyle(document.documentElement).getPropertyValue('--cols').trim()||'12';
396
+ for(var i=1;i<=parseInt(n,10);i++){var c=document.createElement('div');c.className='col';
397
+ var s=document.createElement('span');s.textContent=i;c.appendChild(s);h.appendChild(c);}});
398
+
399
+ (function(){
400
+ var cvs=document.createElement('canvas'),ctx=cvs.getContext('2d');
401
+ var sel='.opt-align';
402
+ function align(){
403
+ document.querySelectorAll(sel).forEach(function(el){
404
+ el.style.marginLeft='0px';
405
+ var cs=getComputedStyle(el),ch=(el.textContent||'').trim().charAt(0); if(!ch) return;
406
+ if(cs.textTransform==='uppercase') ch=ch.toUpperCase();
407
+ ctx.font=cs.fontStyle+' '+cs.fontWeight+' '+cs.fontSize+' '+cs.fontFamily;
408
+ ctx.textAlign='left';
409
+ var abl=ctx.measureText(ch).actualBoundingBoxLeft;
410
+ if(isFinite(abl)) el.style.marginLeft=abl.toFixed(2)+'px';
411
+ });
412
+ }
413
+ if(document.fonts&&document.fonts.ready){document.fonts.ready.then(align);}
414
+ align();
415
+ var t;window.addEventListener('resize',function(){clearTimeout(t);t=setTimeout(align,120);});
416
+ })();
475
417
  </script>
476
418
  </body>
477
419
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "61 agent skills for spec-driven, test-first software development by solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,8 +22,17 @@
22
22
  "claude",
23
23
  "development",
24
24
  "spec-driven",
25
- "test-first"
25
+ "test-first",
26
+ "pi-package"
26
27
  ],
28
+ "pi": {
29
+ "skills": [
30
+ "./.pi/skills"
31
+ ],
32
+ "prompts": [
33
+ "./.pi/prompts"
34
+ ]
35
+ },
27
36
  "author": "Daniel",
28
37
  "license": "MIT",
29
38
  "homepage": "https://github.com/danielvm-git/bigpowers#readme",
@@ -16,7 +16,7 @@ while IFS= read -r line; do
16
16
  model_raw="${line##*model: }"
17
17
  model="${model_raw// /}"
18
18
  ROWS+=("$skill_dir $model")
19
- done < <(grep -r "^model:" */SKILL.md | sed 's|/SKILL.md:model:||' | sort)
19
+ done < <(grep -r "^model:" */SKILL.md | sort)
20
20
 
21
21
  TOTAL=${#ROWS[@]}
22
22