@beastmode-develeap/beastmode 0.1.298 → 0.1.300

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.
@@ -15,7 +15,7 @@
15
15
  }
16
16
  </script>
17
17
  <!--BOARD_DATA-->
18
- <script>window.__BUILD_STAMP__ = "20260517-110542-fd318ea";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260519-132227-d6f9484";</script>
19
19
  <link rel="preconnect" href="https://fonts.googleapis.com">
20
20
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
21
21
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -2762,6 +2762,21 @@ input[type="range"]::-webkit-slider-thumb {
2762
2762
  .badge-info { background: var(--info-subtle); color: var(--info); }
2763
2763
  .badge-muted { background: var(--surface-elevated); color: var(--text-muted); }
2764
2764
  .badge-accent { background: var(--accent-subtle); color: var(--accent); }
2765
+ .badge-view-only, .view-only-badge {
2766
+ display: inline-block;
2767
+ padding: 2px 6px;
2768
+ border-radius: 3px;
2769
+ font-size: 10px;
2770
+ font-weight: 600;
2771
+ background: rgba(99, 102, 241, 0.15);
2772
+ color: #818cf8;
2773
+ border: 1px solid rgba(99, 102, 241, 0.3);
2774
+ margin-left: 6px;
2775
+ cursor: help;
2776
+ vertical-align: middle;
2777
+ text-transform: uppercase;
2778
+ letter-spacing: 0.03em;
2779
+ }
2765
2780
  .badge-restart {
2766
2781
  display: inline-block;
2767
2782
  padding: 2px 6px;
@@ -8595,6 +8610,7 @@ function EnvironmentPanel({ projectName }) {
8595
8610
 
8596
8611
  function CodebaseGuidePanel({ projectName }) {
8597
8612
  const [status, setStatus] = useState('idle');
8613
+ const [failureReason, setFailureReason] = useState(null);
8598
8614
  const [guideContent, setGuideContent] = useState(null);
8599
8615
  const [expanded, setExpanded] = useState(false);
8600
8616
  const [error, setError] = useState(null);
@@ -8604,9 +8620,11 @@ function CodebaseGuidePanel({ projectName }) {
8604
8620
  const data = await api('GET', '/api/projects/' + projectName + '/analyze/status');
8605
8621
  const next = (data && data.status) ? data.status : 'idle';
8606
8622
  setStatus(next);
8623
+ setFailureReason((data && data.failure_reason) || null);
8607
8624
  return next;
8608
8625
  } catch (e) {
8609
8626
  setStatus('idle');
8627
+ setFailureReason(null);
8610
8628
  return 'idle';
8611
8629
  }
8612
8630
  }, [projectName]);
@@ -8649,6 +8667,7 @@ function CodebaseGuidePanel({ projectName }) {
8649
8667
  setError(null);
8650
8668
  setGuideContent(null);
8651
8669
  setExpanded(false);
8670
+ setFailureReason(null);
8652
8671
  await api('POST', '/api/projects/' + projectName + '/analyze?force=true', {});
8653
8672
  setStatus('running');
8654
8673
  } catch (e) {
@@ -8660,6 +8679,8 @@ function CodebaseGuidePanel({ projectName }) {
8660
8679
  status === 'complete' ? '✓ Analyzed'
8661
8680
  : status === 'cached' ? '✓ Analyzed'
8662
8681
  : status === 'running' ? '⏳ Analyzing…'
8682
+ : status === 'failed' && failureReason === 'timeout' ? '⚠ Timed out'
8683
+ : status === 'failed' && failureReason === 'heartbeat_timeout' ? '⚠ Lost contact'
8663
8684
  : status === 'failed' ? '✗ Failed'
8664
8685
  : '—';
8665
8686
  const pillBg =
@@ -8679,7 +8700,7 @@ function CodebaseGuidePanel({ projectName }) {
8679
8700
  <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;">
8680
8701
  <div style="display:flex;align-items:center;gap:8px;">
8681
8702
  <span style="font-size:12px;color:var(--text-secondary);">Codebase Guide</span>
8682
- <span data-testid=${'guide-status-pill-' + projectName} style=${pillStyle}>${pillLabel}</span>
8703
+ <span data-testid=${'guide-status-pill-' + projectName} style=${pillStyle} title=${status === 'failed' && failureReason ? ('Failure: ' + failureReason) : ''}>${pillLabel}</span>
8683
8704
  </div>
8684
8705
  <div style="display:flex;gap:6px;">
8685
8706
  ${canShowContent ? html`
@@ -9723,6 +9744,10 @@ function RestartBadge({field, restartFields}) {
9723
9744
  return html`<span class="badge-restart" title="Changing this field requires a daemon restart to take effect">restart required</span>`;
9724
9745
  }
9725
9746
 
9747
+ function ViewOnlyBadge() {
9748
+ return html`<span class="view-only-badge badge-view-only" title="This section is read-only in the UI — configure via beastmode init or config/beastmode.daemon.json">view only</span>`;
9749
+ }
9750
+
9726
9751
  // Gap 15: opt-in anonymous telemetry status surface. Reads
9727
9752
  // /api/telemetry/status (proxied to the board, which reads the daemon's
9728
9753
  // last heartbeat). Always renders — shows "Disabled" with a red dot
@@ -9744,7 +9769,7 @@ function TelemetrySettings({restartFields}) {
9744
9769
 
9745
9770
  return html`
9746
9771
  <div class="settings-section">
9747
- <h3 style="display:flex;align-items:center;gap:8px">Telemetry <${RestartBadge} field="telemetry" restartFields=${restartFields} /></h3>
9772
+ <h3 style="display:flex;align-items:center;gap:8px">Telemetry <${RestartBadge} field="telemetry" restartFields=${restartFields} /><${ViewOnlyBadge} /></h3>
9748
9773
  ${loading ? html`<p style="font-size:13px;color:var(--text-muted);">Loading telemetry status...</p>` : null}
9749
9774
  ${loadError ? html`<p style="font-size:13px;color:var(--danger);">Telemetry status unavailable: ${loadError}</p>` : null}
9750
9775
  ${!loading && !loadError ? html`
@@ -9753,16 +9778,21 @@ function TelemetrySettings({restartFields}) {
9753
9778
  <div class="setting-label">Status</div>
9754
9779
  <div class="setting-desc">Anonymous pipeline metrics (no code, no PII)</div>
9755
9780
  </div>
9756
- <div class="setting-control">
9781
+ <div class="setting-control" style="display:flex;align-items:center;gap:8px;">
9757
9782
  <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${dotColor};" />
9758
- <span class="mono" style="font-weight:600;color:${enabled ? 'var(--success)' : 'var(--danger)'};">${enabled ? 'Enabled' : 'Disabled'}</span>
9783
+ <select class="form-input" disabled value=${enabled ? 'enabled' : 'disabled'}>
9784
+ <option value="enabled">Enabled</option>
9785
+ <option value="disabled">Disabled</option>
9786
+ </select>
9759
9787
  </div>
9760
9788
  </div>
9761
- ${enabled ? html`
9762
- <div class="setting-row">
9763
- <div><div class="setting-label">Anonymous ID</div></div>
9764
- <div class="setting-control"><span class="mono">${(status && status.anonymous_id_prefix) || ''}</span></div>
9789
+ <div class="setting-row">
9790
+ <div><div class="setting-label">Anonymous ID</div></div>
9791
+ <div class="setting-control">
9792
+ <input type="text" class="form-input" disabled value=${(status && status.anonymous_id_prefix) || ''} placeholder="(unset)" />
9765
9793
  </div>
9794
+ </div>
9795
+ ${enabled ? html`
9766
9796
  <div class="setting-row">
9767
9797
  <div>
9768
9798
  <div class="setting-label">Sentry</div>
@@ -9936,7 +9966,7 @@ function SettingsPage() {
9936
9966
 
9937
9967
  <!-- Pipeline Section -->
9938
9968
  <div class="settings-section">
9939
- <h3>Pipeline</h3>
9969
+ <h3 style="display:flex;align-items:center;gap:8px">Pipeline <${RestartBadge} field="pipeline" restartFields=${restartFields} /></h3>
9940
9970
  <div class="setting-row">
9941
9971
  <div>
9942
9972
  <div class="setting-label">Preset</div>
@@ -9944,7 +9974,25 @@ function SettingsPage() {
9944
9974
  </div>
9945
9975
  <div class="setting-control">
9946
9976
  <select class="form-input" style="width:160px" value=${pipeline.preset || 'full'}
9947
- onChange=${e => updateField('pipeline.preset', e.target.value)}>
9977
+ onChange=${e => {
9978
+ const newPreset = e.target.value;
9979
+ const currentPreset = pipeline.preset || 'full';
9980
+ if (newPreset === currentPreset) return;
9981
+ const ok = window.confirm(
9982
+ `Switch preset from "${currentPreset}" to "${newPreset}"?\n\n` +
9983
+ `Fields you have explicitly edited keep your values. ` +
9984
+ `Other pipeline defaults (threshold, max iterations) will be ` +
9985
+ `updated to match the "${newPreset}" preset.`
9986
+ );
9987
+ if (!ok) {
9988
+ e.target.value = currentPreset;
9989
+ return;
9990
+ }
9991
+ updateField('pipeline.preset', newPreset);
9992
+ api('PATCH', '/api/config', { pipeline: { preset: newPreset } })
9993
+ .then(result => { if (result && !result.confirm_required) setConfig(result); })
9994
+ .catch(() => {});
9995
+ }}>
9948
9996
  ${PRESET_OPTIONS.map(p => html`<option key=${p} value=${p}>${p}</option>`)}
9949
9997
  </select>
9950
9998
  </div>
@@ -9990,7 +10038,7 @@ function SettingsPage() {
9990
10038
 
9991
10039
  <!-- Concurrency Section -->
9992
10040
  <div class="settings-section">
9993
- <h3>Concurrency</h3>
10041
+ <h3 style="display:flex;align-items:center;gap:8px">Concurrency <${RestartBadge} field="max_slots" restartFields=${restartFields} /></h3>
9994
10042
  <div class="setting-row">
9995
10043
  <div>
9996
10044
  <div class="setting-label">Max Slots</div>
@@ -10017,7 +10065,7 @@ function SettingsPage() {
10017
10065
 
10018
10066
  <!-- Models Section -->
10019
10067
  <div class="settings-section">
10020
- <h3>Models</h3>
10068
+ <h3 style="display:flex;align-items:center;gap:8px">Models <${RestartBadge} field="models" restartFields=${restartFields} /></h3>
10021
10069
  ${['spec', 'planner', 'coder', 'coder_iteration', 'verifier', 'reviewer', 'healing', 'precheck'].map(stage => html`
10022
10070
  <div class="setting-row" key=${stage}>
10023
10071
  <div><div class="setting-label" style="text-transform:capitalize">${stage.replace(/_/g, ' ')}</div></div>
@@ -10034,7 +10082,7 @@ function SettingsPage() {
10034
10082
 
10035
10083
  <!-- Resilience Section -->
10036
10084
  <div class="settings-section">
10037
- <h3>Resilience</h3>
10085
+ <h3 style="display:flex;align-items:center;gap:8px">Resilience <${RestartBadge} field="retry" restartFields=${restartFields} /></h3>
10038
10086
  <div class="setting-row">
10039
10087
  <div>
10040
10088
  <div class="setting-label">Max Failures</div>
@@ -10074,7 +10122,7 @@ function SettingsPage() {
10074
10122
 
10075
10123
  <!-- Cost Section -->
10076
10124
  <div class="settings-section">
10077
- <h3>Cost Optimization</h3>
10125
+ <h3 style="display:flex;align-items:center;gap:8px">Cost Optimization <${RestartBadge} field="cost" restartFields=${restartFields} /></h3>
10078
10126
  <div class="setting-row">
10079
10127
  <div>
10080
10128
  <div class="setting-label">Model Tiering</div>
@@ -10118,16 +10166,29 @@ function SettingsPage() {
10118
10166
 
10119
10167
  <!-- Human Gates Section -->
10120
10168
  <div class="settings-section">
10121
- <h3>Human Gates</h3>
10169
+ <h3 style="display:flex;align-items:center;gap:8px">Human Gates <${RestartBadge} field="human_gates" restartFields=${restartFields} /></h3>
10170
+ <div class="setting-row">
10171
+ <div>
10172
+ <div class="setting-label">Standalone Spec Approval</div>
10173
+ <div class="setting-desc">Require human approval for non-epic-child specs after Phase 1</div>
10174
+ </div>
10175
+ <div class="setting-control">
10176
+ <label class="toggle-switch">
10177
+ <input type="checkbox" checked=${humanGates.standalone_spec_approval !== false}
10178
+ onChange=${e => updateField('human_gates.standalone_spec_approval', e.target.checked)} />
10179
+ <span class="toggle-slider"></span>
10180
+ </label>
10181
+ </div>
10182
+ </div>
10122
10183
  <div class="setting-row">
10123
10184
  <div>
10124
- <div class="setting-label">Spec Approval</div>
10125
- <div class="setting-desc">Require human approval for specs</div>
10185
+ <div class="setting-label">Epic Child Spec Approval</div>
10186
+ <div class="setting-desc">Require human approval for epic-child story specs (default off)</div>
10126
10187
  </div>
10127
10188
  <div class="setting-control">
10128
10189
  <label class="toggle-switch">
10129
- <input type="checkbox" checked=${humanGates.spec_approval !== false}
10130
- onChange=${e => updateField('human_gates.spec_approval', e.target.checked)} />
10190
+ <input type="checkbox" checked=${!!humanGates.epic_child_spec_approval}
10191
+ onChange=${e => updateField('human_gates.epic_child_spec_approval', e.target.checked)} />
10131
10192
  <span class="toggle-slider"></span>
10132
10193
  </label>
10133
10194
  </div>
@@ -10161,7 +10222,7 @@ function SettingsPage() {
10161
10222
  </div>
10162
10223
  <!-- Storage Section -->
10163
10224
  <div class="settings-section">
10164
- <h3>Storage</h3>
10225
+ <h3 style="display:flex;align-items:center;gap:8px">Storage <${RestartBadge} field="runs_path" restartFields=${restartFields} /></h3>
10165
10226
  <div class="setting-row">
10166
10227
  <div>
10167
10228
  <div class="setting-label">Runs Path</div>
@@ -10185,22 +10246,10 @@ function SettingsPage() {
10185
10246
  onInput=${e => updateField('archive_after_days', parseInt(e.target.value) || 0)} />
10186
10247
  </div>
10187
10248
  </div>
10188
- <div class="setting-row">
10189
- <div>
10190
- <div class="setting-label">Archive To (S3)</div>
10191
- <div class="setting-desc">S3 bucket for archived runs (empty = local archive only)</div>
10192
- </div>
10193
- <div class="setting-control">
10194
- <input type="text" class="form-input" style="width:280px"
10195
- value=${config.archive_s3_bucket || ''}
10196
- placeholder="s3://bucket-name/prefix (v2)"
10197
- onInput=${e => updateField('archive_s3_bucket', e.target.value)} />
10198
- </div>
10199
- </div>
10200
10249
  </div>
10201
10250
  <!-- Migration Safety Section -->
10202
10251
  <div class="settings-section">
10203
- <h3>Migration Safety</h3>
10252
+ <h3 style="display:flex;align-items:center;gap:8px">Migration Safety <${RestartBadge} field="migration_safety" restartFields=${restartFields} /></h3>
10204
10253
  <div class="setting-row">
10205
10254
  <div>
10206
10255
  <div class="setting-label">Enabled</div>
@@ -10244,7 +10293,7 @@ function SettingsPage() {
10244
10293
 
10245
10294
  <!-- Alerts Section -->
10246
10295
  <div class="settings-section">
10247
- <h3>Alerts</h3>
10296
+ <h3 style="display:flex;align-items:center;gap:8px">Alerts <${RestartBadge} field="alerts" restartFields=${restartFields} /></h3>
10248
10297
  <div class="setting-row">
10249
10298
  <div>
10250
10299
  <div class="setting-label">Webhook URL</div>
@@ -1 +1 @@
1
- fd318eaec11e30ebe601cbd90b2b8c3f7cb3b7cd
1
+ d6f948461dac646344ad82bf131b34770c530704
@@ -1 +1 @@
1
- 20260517-110542-fd318ea
1
+ 20260519-132227-d6f9484
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.298",
3
+ "version": "0.1.300",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "test": "vitest run",
24
24
  "test:watch": "vitest",
25
25
  "lint": "tsc --noEmit",
26
+ "audit:settings": "tsx ../scripts/audit-settings.ts",
26
27
  "prepublishOnly": "npm run build"
27
28
  },
28
29
  "keywords": [
@@ -56,6 +57,7 @@
56
57
  "@types/node": "^22.19.15",
57
58
  "@types/semver": "^7.7.1",
58
59
  "tsup": "^8.5.1",
60
+ "tsx": "^4.20.0",
59
61
  "typescript": "^5.9.3",
60
62
  "vitest": "^3.2.4"
61
63
  }