@beastmode-develeap/beastmode 0.1.297 → 0.1.299

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-101149-ce8723f";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260517-112431-6520cd8";</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">
@@ -8589,6 +8589,122 @@ function EnvironmentPanel({ projectName }) {
8589
8589
  `;
8590
8590
  }
8591
8591
 
8592
+ // ================================================================
8593
+ // Codebase Guide Panel — surfaced per project on the Projects page
8594
+ // ================================================================
8595
+
8596
+ function CodebaseGuidePanel({ projectName }) {
8597
+ const [status, setStatus] = useState('idle');
8598
+ const [guideContent, setGuideContent] = useState(null);
8599
+ const [expanded, setExpanded] = useState(false);
8600
+ const [error, setError] = useState(null);
8601
+
8602
+ const fetchStatus = useCallback(async () => {
8603
+ try {
8604
+ const data = await api('GET', '/api/projects/' + projectName + '/analyze/status');
8605
+ const next = (data && data.status) ? data.status : 'idle';
8606
+ setStatus(next);
8607
+ return next;
8608
+ } catch (e) {
8609
+ setStatus('idle');
8610
+ return 'idle';
8611
+ }
8612
+ }, [projectName]);
8613
+
8614
+ const fetchGuide = useCallback(async () => {
8615
+ try {
8616
+ const data = await api('GET', '/api/projects/' + projectName + '/codebase-guide?level=l1');
8617
+ let content = (data && (data.content || data.guide || data.l1)) || '';
8618
+ if (typeof data === 'string') content = data;
8619
+ if (content && content.startsWith('# ')) {
8620
+ const nl = content.indexOf('\n');
8621
+ content = nl >= 0 ? content.slice(nl + 1).replace(/^\s*\n/, '') : '';
8622
+ }
8623
+ setGuideContent(content || '');
8624
+ } catch (e) {
8625
+ if (e && /404/.test(String(e.message || ''))) {
8626
+ setGuideContent('Guide not available');
8627
+ } else {
8628
+ setGuideContent(null);
8629
+ }
8630
+ }
8631
+ }, [projectName]);
8632
+
8633
+ useEffect(() => { fetchStatus(); }, [fetchStatus]);
8634
+
8635
+ useEffect(() => {
8636
+ if (status === 'complete' || status === 'cached') {
8637
+ fetchGuide();
8638
+ }
8639
+ }, [status, fetchGuide]);
8640
+
8641
+ useEffect(() => {
8642
+ if (status !== 'running') return undefined;
8643
+ const id = setInterval(() => { fetchStatus(); }, 3000);
8644
+ return () => clearInterval(id);
8645
+ }, [status, fetchStatus]);
8646
+
8647
+ const reanalyze = async () => {
8648
+ try {
8649
+ setError(null);
8650
+ setGuideContent(null);
8651
+ setExpanded(false);
8652
+ await api('POST', '/api/projects/' + projectName + '/analyze?force=true', {});
8653
+ setStatus('running');
8654
+ } catch (e) {
8655
+ setError(e.message);
8656
+ }
8657
+ };
8658
+
8659
+ const pillLabel =
8660
+ status === 'complete' ? '✓ Analyzed'
8661
+ : status === 'cached' ? '✓ Analyzed'
8662
+ : status === 'running' ? '⏳ Analyzing…'
8663
+ : status === 'failed' ? '✗ Failed'
8664
+ : '—';
8665
+ const pillBg =
8666
+ (status === 'complete' || status === 'cached') ? 'var(--success)'
8667
+ : status === 'running' ? 'var(--warning)'
8668
+ : status === 'failed' ? 'var(--danger)'
8669
+ : 'var(--muted)';
8670
+ const pillStyle = 'display:inline-block;padding:2px 8px;border-radius:10px;background:' + pillBg + ';color:white;font-size:11px;font-weight:600;';
8671
+
8672
+ const canShowContent = (status === 'complete' || status === 'cached');
8673
+ const canReanalyze = (status === 'complete' || status === 'cached' || status === 'failed');
8674
+
8675
+ return html`
8676
+ <div class="ext-item"
8677
+ data-testid=${'codebase-guide-panel-' + projectName}
8678
+ style="flex-basis:100%;flex-direction:column;align-items:stretch;gap:8px;">
8679
+ <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;">
8680
+ <div style="display:flex;align-items:center;gap:8px;">
8681
+ <span style="font-size:12px;color:var(--text-secondary);">Codebase Guide</span>
8682
+ <span data-testid=${'guide-status-pill-' + projectName} style=${pillStyle}>${pillLabel}</span>
8683
+ </div>
8684
+ <div style="display:flex;gap:6px;">
8685
+ ${canShowContent ? html`
8686
+ <button class="btn btn-sm btn-secondary"
8687
+ onClick=${() => setExpanded(e => !e)}>
8688
+ ${expanded ? 'Collapse' : 'Expand'}
8689
+ </button>
8690
+ ` : null}
8691
+ ${canReanalyze ? html`
8692
+ <button class="btn btn-sm btn-secondary" onClick=${reanalyze}>Re-analyze</button>
8693
+ ` : null}
8694
+ </div>
8695
+ </div>
8696
+ ${error ? html`<div class="error-msg" style="margin:0;">${error}</div>` : null}
8697
+ ${(canShowContent && expanded) ? html`
8698
+ <div data-testid=${'guide-content-' + projectName}
8699
+ class="guide-content"
8700
+ style="max-height:300px;overflow-y:auto;font-size:0.85rem;white-space:pre-wrap;background:var(--bg-secondary);padding:8px;border-radius:var(--radius-sm);border:1px solid var(--border);">
8701
+ ${guideContent === null ? 'Loading…' : guideContent}
8702
+ </div>
8703
+ ` : null}
8704
+ </div>
8705
+ `;
8706
+ }
8707
+
8592
8708
  // ================================================================
8593
8709
  // Projects Page
8594
8710
  // ================================================================
@@ -8605,6 +8721,9 @@ function ProjectsPage({ selectedProject, onProjectChange }) {
8605
8721
  const [onboardingTier, setOnboardingTier] = useState('standard');
8606
8722
  const [showPicker, setShowPicker] = useState(false);
8607
8723
  const [adding, setAdding] = useState(false);
8724
+ const [analysisStage, setAnalysisStage] = useState(null);
8725
+ const [fullTierGuide, setFullTierGuide] = useState(null);
8726
+ const [fullTierProject, setFullTierProject] = useState(null);
8608
8727
 
8609
8728
  const fetchProjects = useCallback(() => {
8610
8729
  api('GET', '/api/projects')
@@ -8634,21 +8753,69 @@ function ProjectsPage({ selectedProject, onProjectChange }) {
8634
8753
  setSuccess(isGithub ? `Cloned and added: ${registeredPath}` : 'Project added successfully');
8635
8754
 
8636
8755
  if (onboardingTier === 'standard' || onboardingTier === 'full') {
8637
- setSuccess('Project added running codebase analysis...');
8756
+ const tier = onboardingTier;
8757
+ setAnalysisStage('clone');
8758
+ setSuccess('Cloning repository...');
8638
8759
  try {
8639
8760
  await api('POST', '/api/projects/' + projName + '/analyze', {});
8640
- setSuccess('Project added and analyzed');
8641
- } catch { setSuccess('Project added (analysis failed — can retry later)'); }
8642
- }
8643
-
8644
- if (onboardingTier === 'full') {
8645
- setTimeout(() => {
8646
- if (onProjectChange) onProjectChange(projName);
8647
- navigate('#/new-session');
8648
- }, 1000);
8761
+ } catch {
8762
+ setAnalysisStage(null);
8763
+ setSuccess('Project added (analysis failed — can retry later)');
8764
+ setTimeout(() => setSuccess(null), 5000);
8765
+ fetchProjects();
8766
+ return;
8767
+ }
8768
+ const startedAt = Date.now();
8769
+ const poll = setInterval(async () => {
8770
+ let st = 'running';
8771
+ try {
8772
+ const data = await api('GET', '/api/projects/' + projName + '/analyze/status');
8773
+ st = (data && data.status) ? data.status : 'running';
8774
+ } catch {
8775
+ return;
8776
+ }
8777
+ if (st === 'running') {
8778
+ const elapsed = (Date.now() - startedAt) / 1000;
8779
+ const stage = elapsed < 3 ? 'clone' : (elapsed < 6 ? 'detect' : 'analyze');
8780
+ setAnalysisStage(stage);
8781
+ setSuccess(
8782
+ stage === 'clone' ? 'Cloning repository...'
8783
+ : stage === 'detect' ? 'Detecting stack...'
8784
+ : 'Analyzing codebase...'
8785
+ );
8786
+ } else if (st === 'complete' || st === 'cached') {
8787
+ clearInterval(poll);
8788
+ setAnalysisStage('ready');
8789
+ setSuccess('✓ Analysis complete — codebase guide ready');
8790
+ fetchProjects();
8791
+ if (tier === 'full') {
8792
+ try {
8793
+ const data = await api('GET', '/api/projects/' + projName + '/codebase-guide?level=l1');
8794
+ let content = (data && (data.content || data.guide || data.l1)) || '';
8795
+ if (typeof data === 'string') content = data;
8796
+ if (content && content.startsWith('# ')) {
8797
+ const nl = content.indexOf('\n');
8798
+ content = nl >= 0 ? content.slice(nl + 1).replace(/^\s*\n/, '') : '';
8799
+ }
8800
+ setFullTierGuide(content || '');
8801
+ setFullTierProject(projName);
8802
+ } catch {
8803
+ setFullTierGuide('Guide not available');
8804
+ setFullTierProject(projName);
8805
+ }
8806
+ }
8807
+ setTimeout(() => setSuccess(null), 3000);
8808
+ } else if (st === 'failed') {
8809
+ clearInterval(poll);
8810
+ setAnalysisStage(null);
8811
+ setSuccess('Analysis failed — can retry from project settings');
8812
+ setTimeout(() => setSuccess(null), 5000);
8813
+ }
8814
+ }, 2000);
8815
+ } else {
8816
+ setTimeout(() => setSuccess(null), 5000);
8649
8817
  }
8650
8818
 
8651
- setTimeout(() => setSuccess(null), 5000);
8652
8819
  fetchProjects();
8653
8820
  } catch (e) { setError(e.message); }
8654
8821
  finally { setAdding(false); }
@@ -8673,7 +8840,30 @@ function ProjectsPage({ selectedProject, onProjectChange }) {
8673
8840
  </div>
8674
8841
 
8675
8842
  ${error ? html`<div class="error-msg">${error}</div>` : null}
8676
- ${success ? html`<div class="success-msg">${success}</div>` : null}
8843
+ ${success ? html`
8844
+ <div class="success-msg" data-testid="analysis-toast">
8845
+ <span data-testid="analysis-stage">${success}</span>
8846
+ </div>
8847
+ ` : null}
8848
+ ${fullTierGuide !== null ? html`
8849
+ <div class="card" data-testid="full-tier-guide-panel"
8850
+ style="margin-bottom:16px;border-left:3px solid var(--success);">
8851
+ <div class="card-header">
8852
+ <h3>📋 Codebase Guide: ${fullTierProject}</h3>
8853
+ </div>
8854
+ <div style="max-height:200px;overflow-y:auto;white-space:pre-wrap;font-size:0.85rem;background:var(--bg-secondary);padding:8px;border-radius:var(--radius-sm);border:1px solid var(--border);margin-bottom:12px;">
8855
+ ${fullTierGuide || 'Guide not available'}
8856
+ </div>
8857
+ <button class="btn btn-primary"
8858
+ data-testid="full-tier-start-session"
8859
+ onClick=${() => {
8860
+ if (onProjectChange) onProjectChange(fullTierProject);
8861
+ navigate('#/new-session');
8862
+ }}>
8863
+ Start Session →
8864
+ </button>
8865
+ </div>
8866
+ ` : null}
8677
8867
 
8678
8868
  <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px;">
8679
8869
  <div class="card" style="margin-bottom:0;">
@@ -8754,6 +8944,7 @@ function ProjectsPage({ selectedProject, onProjectChange }) {
8754
8944
  <button class="btn btn-sm btn-danger" onClick=${() => removeProject(proj.name)}>Remove</button>
8755
8945
  </div>
8756
8946
  <${EnvironmentPanel} projectName=${proj.name} />
8947
+ <${CodebaseGuidePanel} projectName=${proj.name} />
8757
8948
  </div>
8758
8949
  `)}
8759
8950
  </div>
@@ -1 +1 @@
1
- ce8723f79a199da44cf36a634aa5f643face5de6
1
+ 6520cd8f4e441d3beffb107fae2ed6c77bb8e23a
@@ -1 +1 @@
1
- 20260517-101149-ce8723f
1
+ 20260517-112431-6520cd8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.297",
3
+ "version": "0.1.299",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {