@colmbus72/yeehaw 0.4.0 → 0.4.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/dist/app.js CHANGED
@@ -83,6 +83,7 @@ export function App() {
83
83
  const [showHelp, setShowHelp] = useState(false);
84
84
  const [error, setError] = useState(null);
85
85
  const [pendingGo, setPendingGo] = useState(false); // For g+number sequence
86
+ const [isChildInputMode, setIsChildInputMode] = useState(false); // Track when child views have text input active
86
87
  // Get terminal height for full-height layout
87
88
  const terminalHeight = stdout?.rows || 24;
88
89
  // Check tmux availability
@@ -95,6 +96,36 @@ export function App() {
95
96
  ensureCorrectStatusBar();
96
97
  }
97
98
  }, [view.type]);
99
+ // Hot-reload: sync view state when projects/barns change (e.g., from MCP server updates)
100
+ useEffect(() => {
101
+ setView((currentView) => {
102
+ // Update project data in views that contain a project
103
+ if (currentView.type === 'project' || currentView.type === 'wiki' || currentView.type === 'issues') {
104
+ const freshProject = projects.find((p) => p.name === currentView.project.name);
105
+ if (freshProject && freshProject !== currentView.project) {
106
+ return { ...currentView, project: freshProject };
107
+ }
108
+ }
109
+ // Update livestock detail view
110
+ if (currentView.type === 'livestock' || currentView.type === 'logs') {
111
+ const freshProject = projects.find((p) => p.name === currentView.project.name);
112
+ if (freshProject && freshProject !== currentView.project) {
113
+ const freshLivestock = (freshProject.livestock || []).find((l) => l.name === currentView.livestock.name);
114
+ if (freshLivestock) {
115
+ return { ...currentView, project: freshProject, livestock: freshLivestock };
116
+ }
117
+ }
118
+ }
119
+ // Update barn data in barn view
120
+ if (currentView.type === 'barn') {
121
+ const freshBarn = barns.find((b) => b.name === currentView.barn.name);
122
+ if (freshBarn && freshBarn !== currentView.barn) {
123
+ return { ...currentView, barn: freshBarn };
124
+ }
125
+ }
126
+ return currentView;
127
+ });
128
+ }, [projects, barns]);
98
129
  const handleSelectProject = useCallback((project) => {
99
130
  setView({ type: 'project', project });
100
131
  updateStatusBar(project.name);
@@ -357,8 +388,8 @@ export function App() {
357
388
  setPendingGo(true);
358
389
  return;
359
390
  }
360
- // v: Enter night sky visualizer (only from global dashboard to avoid text input conflicts)
361
- if (input === 'v' && view.type === 'global') {
391
+ // v: Enter night sky visualizer (only from global dashboard when not in text input mode)
392
+ if (input === 'v' && view.type === 'global' && !isChildInputMode) {
362
393
  handleEnterNightSky();
363
394
  return;
364
395
  }
@@ -370,7 +401,7 @@ export function App() {
370
401
  }
371
402
  switch (view.type) {
372
403
  case 'global':
373
- return (_jsx(GlobalDashboard, { projects: projects, barns: barns, windows: windows, versionInfo: versionInfo, onSelectProject: handleSelectProject, onSelectBarn: handleSelectBarn, onSelectWindow: handleSelectWindow, onNewClaude: handleNewClaude, onCreateProject: handleCreateProject, onCreateBarn: handleCreateBarn, onSshToBarn: handleSshToBarn }));
404
+ return (_jsx(GlobalDashboard, { projects: projects, barns: barns, windows: windows, versionInfo: versionInfo, onSelectProject: handleSelectProject, onSelectBarn: handleSelectBarn, onSelectWindow: handleSelectWindow, onNewClaude: handleNewClaude, onCreateProject: handleCreateProject, onCreateBarn: handleCreateBarn, onSshToBarn: handleSshToBarn, onInputModeChange: setIsChildInputMode }));
374
405
  case 'project':
375
406
  return (_jsx(ProjectContext, { project: view.project, barns: barns, windows: windows, onBack: handleBack, onNewClaude: handleNewClaude, onSelectWindow: handleSelectWindow, onSelectLivestock: (livestock, barn) => handleOpenLivestockDetail(view.project, livestock, 'project'), onOpenLivestockSession: (livestock, barn) => handleOpenLivestockSession(livestock, barn, view.project.name), onUpdateProject: handleUpdateProject, onDeleteProject: handleDeleteProject, onOpenWiki: () => handleOpenWiki(view.project), onOpenIssues: () => handleOpenIssues(view.project) }));
376
407
  case 'barn':
@@ -2,6 +2,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import figlet from 'figlet';
5
+ // Compare semver versions - returns true if latest > current
6
+ function isNewerVersion(latest, current) {
7
+ const parseVersion = (v) => v.split('.').map(n => parseInt(n, 10) || 0);
8
+ const [lMajor, lMinor, lPatch] = parseVersion(latest);
9
+ const [cMajor, cMinor, cPatch] = parseVersion(current);
10
+ if (lMajor > cMajor)
11
+ return true;
12
+ if (lMajor < cMajor)
13
+ return false;
14
+ if (lMinor > cMinor)
15
+ return true;
16
+ if (lMinor < cMinor)
17
+ return false;
18
+ return lPatch > cPatch;
19
+ }
5
20
  // Convert hex to RGB
6
21
  function hexToRgb(hex) {
7
22
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@@ -89,5 +104,5 @@ export function Header({ text, subtitle, summary, color, gradientSpread, gradien
89
104
  const showTumbleweed = text.toLowerCase() === 'yeehaw';
90
105
  // Vertically center tumbleweed next to ASCII art
91
106
  const tumbleweedTopPadding = Math.max(0, Math.floor((lines.length - TUMBLEWEED.length) / 2));
92
- return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, paddingLeft: 2, children: [_jsxs(Box, { flexDirection: "row", children: [showTumbleweed && (_jsxs(Box, { flexDirection: "column", marginRight: 2, children: [Array(tumbleweedTopPadding).fill(null).map((_, i) => (_jsx(Text, { children: " " }, `pad-${i}`))), TUMBLEWEED.map((line, i) => (_jsx(Text, { color: TUMBLEWEED_COLOR, bold: true, children: line }, `tumbleweed-${i}`)))] })), _jsx(Box, { flexDirection: "column", children: lines.map((line, i) => (_jsx(Text, { color: gradientColors[i], children: line }, i))) }), versionInfo && showTumbleweed && (_jsx(Box, { marginLeft: 2, paddingRight: 1, alignItems: "flex-end", flexGrow: 1, justifyContent: "flex-end", children: versionInfo.latest && versionInfo.latest !== versionInfo.current ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: ["v", versionInfo.current] }), _jsx(Text, { dimColor: true, children: " \u2192 " }), _jsxs(Text, { color: "yellow", children: ["v", versionInfo.latest] })] })) : versionInfo.latest ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: ["v", versionInfo.current] }), _jsx(Text, { color: "green", children: " \u2713 latest" })] })) : (_jsxs(Text, { dimColor: true, children: ["v", versionInfo.current] })) }))] }), (subtitle || summary) && (_jsxs(Box, { gap: 2, children: [subtitle && _jsx(Text, { dimColor: true, children: subtitle }), summary && _jsxs(Text, { color: "gray", children: ["- ", summary] })] }))] }));
107
+ return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, paddingLeft: 2, children: [_jsxs(Box, { flexDirection: "row", children: [showTumbleweed && (_jsxs(Box, { flexDirection: "column", marginRight: 2, children: [Array(tumbleweedTopPadding).fill(null).map((_, i) => (_jsx(Text, { children: " " }, `pad-${i}`))), TUMBLEWEED.map((line, i) => (_jsx(Text, { color: TUMBLEWEED_COLOR, bold: true, children: line }, `tumbleweed-${i}`)))] })), _jsx(Box, { flexDirection: "column", children: lines.map((line, i) => (_jsx(Text, { color: gradientColors[i], children: line }, i))) }), versionInfo && showTumbleweed && (_jsx(Box, { marginLeft: 2, paddingRight: 1, alignItems: "flex-end", flexGrow: 1, justifyContent: "flex-end", children: versionInfo.latest && isNewerVersion(versionInfo.latest, versionInfo.current) ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: ["v", versionInfo.current] }), _jsx(Text, { dimColor: true, children: " \u2192 " }), _jsxs(Text, { color: "yellow", children: ["v", versionInfo.latest] })] })) : (_jsxs(Text, { dimColor: true, children: ["v", versionInfo.current] })) }))] }), (subtitle || summary) && (_jsxs(Box, { gap: 2, children: [subtitle && _jsx(Text, { dimColor: true, children: subtitle }), summary && _jsxs(Text, { color: "gray", children: ["- ", summary] })] }))] }));
93
108
  }
@@ -321,7 +321,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
321
321
  // Wiki tools
322
322
  {
323
323
  name: 'get_wiki',
324
- description: 'Get all wiki sections for a project',
324
+ description: 'Get all wiki section titles for a project (use get_wiki_section to fetch content)',
325
325
  inputSchema: {
326
326
  type: 'object',
327
327
  properties: {
@@ -330,6 +330,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
330
330
  required: ['project'],
331
331
  },
332
332
  },
333
+ {
334
+ name: 'get_wiki_section',
335
+ description: 'Get the content of a specific wiki section',
336
+ inputSchema: {
337
+ type: 'object',
338
+ properties: {
339
+ project: { type: 'string', description: 'Project name' },
340
+ title: { type: 'string', description: 'Section title' },
341
+ },
342
+ required: ['project', 'title'],
343
+ },
344
+ },
333
345
  {
334
346
  name: 'add_wiki_section',
335
347
  description: 'Add a new wiki section to a project',
@@ -379,11 +391,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
379
391
  // Project operations
380
392
  case 'list_projects': {
381
393
  const projects = loadProjects();
394
+ // Return simplified project data to reduce context usage
395
+ const simplified = projects.map((p) => ({
396
+ name: p.name,
397
+ path: p.path,
398
+ summary: p.summary,
399
+ color: p.color,
400
+ livestock: (p.livestock || []).map((l) => ({
401
+ name: l.name,
402
+ path: l.path,
403
+ barn: l.barn,
404
+ })),
405
+ }));
382
406
  return {
383
407
  content: [
384
408
  {
385
409
  type: 'text',
386
- text: JSON.stringify(projects, null, 2),
410
+ text: JSON.stringify(simplified, null, 2),
387
411
  },
388
412
  ],
389
413
  };
@@ -397,8 +421,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
397
421
  isError: true,
398
422
  };
399
423
  }
424
+ // Return project with wiki section titles only (not full content)
425
+ // Use get_wiki_section to fetch individual section content
426
+ const projectWithWikiTitles = {
427
+ ...project,
428
+ wiki: (project.wiki || []).map((s) => ({ title: s.title })),
429
+ };
400
430
  return {
401
- content: [{ type: 'text', text: JSON.stringify(project, null, 2) }],
431
+ content: [{ type: 'text', text: JSON.stringify(projectWithWikiTitles, null, 2) }],
402
432
  };
403
433
  }
404
434
  case 'create_project': {
@@ -712,8 +742,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
712
742
  isError: true,
713
743
  };
714
744
  }
745
+ // Return only section titles to reduce context usage
746
+ const titles = (project.wiki || []).map((s) => ({ title: s.title }));
747
+ return {
748
+ content: [{ type: 'text', text: JSON.stringify(titles, null, 2) }],
749
+ };
750
+ }
751
+ case 'get_wiki_section': {
752
+ const projectName = requireString(args, 'project');
753
+ const title = requireString(args, 'title');
754
+ const project = loadProject(projectName);
755
+ if (!project) {
756
+ return {
757
+ content: [{ type: 'text', text: `Project not found: ${projectName}` }],
758
+ isError: true,
759
+ };
760
+ }
761
+ const section = (project.wiki || []).find((s) => s.title === title);
762
+ if (!section) {
763
+ return {
764
+ content: [{ type: 'text', text: `Wiki section not found: ${title}` }],
765
+ isError: true,
766
+ };
767
+ }
715
768
  return {
716
- content: [{ type: 'text', text: JSON.stringify(project.wiki || [], null, 2) }],
769
+ content: [{ type: 'text', text: JSON.stringify(section, null, 2) }],
717
770
  };
718
771
  }
719
772
  case 'add_wiki_section': {
@@ -15,6 +15,7 @@ interface GlobalDashboardProps {
15
15
  onCreateProject: (name: string, path: string) => void;
16
16
  onCreateBarn: (barn: Barn) => void;
17
17
  onSshToBarn: (barn: Barn) => void;
18
+ onInputModeChange?: (isInputMode: boolean) => void;
18
19
  }
19
- export declare function GlobalDashboard({ projects, barns, windows, versionInfo, onSelectProject, onSelectBarn, onSelectWindow, onNewClaude, onCreateProject, onCreateBarn, onSshToBarn, }: GlobalDashboardProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function GlobalDashboard({ projects, barns, windows, versionInfo, onSelectProject, onSelectBarn, onSelectWindow, onNewClaude, onCreateProject, onCreateBarn, onSshToBarn, onInputModeChange, }: GlobalDashboardProps): import("react/jsx-runtime").JSX.Element;
20
21
  export {};
@@ -12,9 +12,14 @@ import { isLocalBarn } from '../lib/config.js';
12
12
  function countSessionsForProject(projectName, windows) {
13
13
  return windows.filter((w) => w.name.startsWith(projectName)).length;
14
14
  }
15
- export function GlobalDashboard({ projects, barns, windows, versionInfo, onSelectProject, onSelectBarn, onSelectWindow, onNewClaude, onCreateProject, onCreateBarn, onSshToBarn, }) {
15
+ export function GlobalDashboard({ projects, barns, windows, versionInfo, onSelectProject, onSelectBarn, onSelectWindow, onNewClaude, onCreateProject, onCreateBarn, onSshToBarn, onInputModeChange, }) {
16
16
  const [focusedPanel, setFocusedPanel] = useState('projects');
17
- const [mode, setMode] = useState('normal');
17
+ const [mode, setModeInternal] = useState('normal');
18
+ // Wrapper to notify parent when input mode changes
19
+ const setMode = (newMode) => {
20
+ setModeInternal(newMode);
21
+ onInputModeChange?.(newMode !== 'normal');
22
+ };
18
23
  // New project form state
19
24
  const [newProjectName, setNewProjectName] = useState('');
20
25
  const [newProjectPath, setNewProjectPath] = useState('');
@@ -273,8 +273,8 @@ export function NightSkyView({ onExit }) {
273
273
  }, FRAME_INTERVAL);
274
274
  return () => clearInterval(interval);
275
275
  }, [isPaused]);
276
- // Rendering helpers
277
- const getWinkingStarDisplay = (star) => {
276
+ // Memoized rendering helpers to ensure stable references
277
+ const getWinkingStarDisplay = useCallback((star) => {
278
278
  const { progress, holdDuration } = star;
279
279
  let brightness;
280
280
  if (progress < 1) {
@@ -288,13 +288,13 @@ export function NightSkyView({ onExit }) {
288
288
  }
289
289
  const chars = [' ', '.', '·', '+', '*'];
290
290
  return { char: chars[Math.round(brightness * 4)], dim: brightness < 0.5 };
291
- };
292
- const getStaticStarDisplay = (star) => {
291
+ }, []);
292
+ const getStaticStarDisplay = useCallback((star) => {
293
293
  const brightness = star.baseBrightness + Math.sin(star.pulsePhase) * star.pulseAmount;
294
294
  return { char: star.char, dim: brightness < 0.4 };
295
- };
295
+ }, []);
296
296
  // Cloud display - simple opacity-based fading
297
- const getCloudOpacity = (cloud) => {
297
+ const getCloudOpacity = useCallback((cloud) => {
298
298
  const { progress } = cloud;
299
299
  if (progress < 0.3) {
300
300
  // Fading in - dim
@@ -308,67 +308,70 @@ export function NightSkyView({ onExit }) {
308
308
  // Fading out - dim
309
309
  return { dim: true, visible: true };
310
310
  }
311
- };
312
- // Render sky
313
- const skyRows = [];
314
- for (let y = 0; y < skyHeight; y++) {
315
- let rowChars = ' '.repeat(width).split('');
316
- let rowDims = new Array(width).fill(false);
317
- let rowColors = new Array(width).fill(undefined);
318
- // Place stars
319
- for (const star of state.stars) {
320
- if (star.y === y && star.x >= 0 && star.x < width) {
321
- const display = star.type === 'winking' ? getWinkingStarDisplay(star) : getStaticStarDisplay(star);
322
- if (display.char !== ' ') {
323
- rowChars[star.x] = display.char;
324
- rowDims[star.x] = display.dim;
311
+ }, []);
312
+ // Memoized sky row rendering to reduce re-render overhead
313
+ const skyRows = useMemo(() => {
314
+ const rows = [];
315
+ for (let y = 0; y < skyHeight; y++) {
316
+ const rowChars = ' '.repeat(width).split('');
317
+ const rowDims = new Array(width).fill(false);
318
+ const rowColors = new Array(width).fill(undefined);
319
+ // Place stars
320
+ for (const star of state.stars) {
321
+ if (star.y === y && star.x >= 0 && star.x < width) {
322
+ const display = star.type === 'winking' ? getWinkingStarDisplay(star) : getStaticStarDisplay(star);
323
+ if (display.char !== ' ') {
324
+ rowChars[star.x] = display.char;
325
+ rowDims[star.x] = display.dim;
326
+ }
325
327
  }
326
328
  }
327
- }
328
- // Place clouds
329
- for (const cloud of state.clouds) {
330
- const display = getCloudOpacity(cloud);
331
- if (!display.visible)
332
- continue;
333
- const cloudY = Math.round(cloud.y);
334
- const cloudX = Math.round(cloud.x);
335
- // Simple box cloud
336
- const textLen = cloud.text.length + 2;
337
- const lines = [
338
- '' + '─'.repeat(textLen) + '',
339
- '' + cloud.text + '',
340
- '╰' + '─'.repeat(textLen) + '╯',
341
- ];
342
- const lineIdx = y - cloudY;
343
- if (lineIdx >= 0 && lineIdx < 3) {
344
- const line = lines[lineIdx];
345
- for (let lx = 0; lx < line.length; lx++) {
346
- const cellX = cloudX + lx;
347
- if (cellX >= 0 && cellX < width) {
348
- rowChars[cellX] = line[lx];
349
- rowDims[cellX] = display.dim;
350
- rowColors[cellX] = '#87CEEB';
329
+ // Place clouds
330
+ for (const cloud of state.clouds) {
331
+ const display = getCloudOpacity(cloud);
332
+ if (!display.visible)
333
+ continue;
334
+ const cloudY = Math.round(cloud.y);
335
+ const cloudX = Math.round(cloud.x);
336
+ // Simple box cloud
337
+ const textLen = cloud.text.length + 2;
338
+ const lines = [
339
+ '╭' + '─'.repeat(textLen) + '╮',
340
+ '' + cloud.text + '',
341
+ '' + '─'.repeat(textLen) + '',
342
+ ];
343
+ const lineIdx = y - cloudY;
344
+ if (lineIdx >= 0 && lineIdx < 3) {
345
+ const line = lines[lineIdx];
346
+ for (let lx = 0; lx < line.length; lx++) {
347
+ const cellX = cloudX + lx;
348
+ if (cellX >= 0 && cellX < width) {
349
+ rowChars[cellX] = line[lx];
350
+ rowDims[cellX] = display.dim;
351
+ rowColors[cellX] = '#87CEEB';
352
+ }
351
353
  }
352
354
  }
353
355
  }
354
- }
355
- // Build segments for efficient rendering
356
- const segments = [];
357
- let seg = { text: rowChars[0], dim: rowDims[0], color: rowColors[0] };
358
- for (let x = 1; x < width; x++) {
359
- if (rowDims[x] === seg.dim && rowColors[x] === seg.color) {
360
- seg.text += rowChars[x];
361
- }
362
- else {
363
- segments.push(seg);
364
- seg = { text: rowChars[x], dim: rowDims[x], color: rowColors[x] };
356
+ // Build segments for efficient rendering - join consecutive same-style chars
357
+ const segments = [];
358
+ let seg = { text: rowChars[0], dim: rowDims[0], color: rowColors[0] };
359
+ for (let x = 1; x < width; x++) {
360
+ if (rowDims[x] === seg.dim && rowColors[x] === seg.color) {
361
+ seg.text += rowChars[x];
362
+ }
363
+ else {
364
+ segments.push(seg);
365
+ seg = { text: rowChars[x], dim: rowDims[x], color: rowColors[x] };
366
+ }
365
367
  }
368
+ segments.push(seg);
369
+ rows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color || 'white', dimColor: s.dim, children: s.text }, `${y}-${i}`))) }, y));
366
370
  }
367
- segments.push(seg);
368
- skyRows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color || 'white', dimColor: s.dim, children: s.text }, i))) }, y));
369
- }
370
- // Render landscape with cacti and ground
371
- const renderLandscape = () => {
371
+ return rows;
372
+ }, [state.stars, state.clouds, width, skyHeight, getWinkingStarDisplay, getStaticStarDisplay, getCloudOpacity]);
373
+ // Memoized landscape rendering - only changes when cacti/ground change
374
+ const landscapeRows = useMemo(() => {
372
375
  const GROUND_LINE_Y = 5; // Ground line position in landscape
373
376
  // Create ground grid
374
377
  const ground = groundBase.map(row => [...row]);
@@ -433,9 +436,9 @@ export function NightSkyView({ onExit }) {
433
436
  }
434
437
  if (currentText)
435
438
  segments.push({ text: currentText, color: currentColor, dim: currentDim });
436
- rows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color, dimColor: s.dim, children: s.text }, i))) }, `land-${y}`));
439
+ rows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color, dimColor: s.dim, children: s.text }, `land-${y}-${i}`))) }, `land-${y}`));
437
440
  }
438
441
  return rows;
439
- };
440
- return (_jsxs(Box, { flexDirection: "column", height: height, children: [_jsx(Box, { flexDirection: "column", children: skyRows }), _jsx(Box, { flexDirection: "column", children: renderLandscape() })] }));
442
+ }, [cacti, groundBase, width]);
443
+ return (_jsxs(Box, { flexDirection: "column", height: height, children: [_jsx(Box, { flexDirection: "column", children: skyRows }), _jsx(Box, { flexDirection: "column", children: landscapeRows })] }));
441
444
  }
@@ -70,17 +70,10 @@ export function WikiView({ project, onBack, onUpdateProject }) {
70
70
  setFocusedPanel((prev) => (prev === 'sections' ? 'content' : 'sections'));
71
71
  return;
72
72
  }
73
- // Only handle section navigation when sections panel is focused
73
+ // Only handle section operations when sections panel is focused
74
+ // Note: j/k navigation is handled by the List component
74
75
  if (focusedPanel !== 'sections')
75
76
  return;
76
- if (input === 'j' || key.downArrow) {
77
- setSelectedIndex((i) => Math.min(i + 1, wiki.length - 1));
78
- return;
79
- }
80
- if (input === 'k' || key.upArrow) {
81
- setSelectedIndex((i) => Math.max(i - 1, 0));
82
- return;
83
- }
84
77
  if (input === 'n') {
85
78
  setEditTitle('');
86
79
  setEditContent('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colmbus72/yeehaw",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Terminal dashboard for managing projects, servers, and deployments",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",