@colmbus72/yeehaw 0.5.0 → 0.6.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 (45) hide show
  1. package/claude-plugin/skills/yeehaw-development/SKILL.md +70 -0
  2. package/dist/app.js +166 -15
  3. package/dist/components/CritterHeader.d.ts +7 -0
  4. package/dist/components/CritterHeader.js +81 -0
  5. package/dist/components/List.d.ts +2 -0
  6. package/dist/components/List.js +1 -1
  7. package/dist/components/Panel.js +1 -1
  8. package/dist/components/ScrollableMarkdown.js +1 -1
  9. package/dist/lib/auth/index.d.ts +2 -0
  10. package/dist/lib/auth/index.js +3 -0
  11. package/dist/lib/auth/linear.d.ts +20 -0
  12. package/dist/lib/auth/linear.js +79 -0
  13. package/dist/lib/auth/storage.d.ts +12 -0
  14. package/dist/lib/auth/storage.js +53 -0
  15. package/dist/lib/context.d.ts +10 -0
  16. package/dist/lib/context.js +63 -0
  17. package/dist/lib/critters.d.ts +33 -0
  18. package/dist/lib/critters.js +164 -0
  19. package/dist/lib/hotkeys.d.ts +1 -1
  20. package/dist/lib/hotkeys.js +6 -2
  21. package/dist/lib/issues/github.d.ts +11 -0
  22. package/dist/lib/issues/github.js +154 -0
  23. package/dist/lib/issues/index.d.ts +14 -0
  24. package/dist/lib/issues/index.js +27 -0
  25. package/dist/lib/issues/linear.d.ts +24 -0
  26. package/dist/lib/issues/linear.js +345 -0
  27. package/dist/lib/issues/types.d.ts +82 -0
  28. package/dist/lib/issues/types.js +2 -0
  29. package/dist/lib/paths.d.ts +1 -0
  30. package/dist/lib/paths.js +1 -0
  31. package/dist/lib/tmux.d.ts +1 -0
  32. package/dist/lib/tmux.js +50 -1
  33. package/dist/types.d.ts +19 -0
  34. package/dist/views/BarnContext.d.ts +2 -1
  35. package/dist/views/BarnContext.js +136 -14
  36. package/dist/views/CritterDetailView.d.ts +10 -0
  37. package/dist/views/CritterDetailView.js +117 -0
  38. package/dist/views/CritterLogsView.d.ts +8 -0
  39. package/dist/views/CritterLogsView.js +100 -0
  40. package/dist/views/IssuesView.d.ts +2 -1
  41. package/dist/views/IssuesView.js +775 -103
  42. package/dist/views/LivestockDetailView.d.ts +2 -1
  43. package/dist/views/LivestockDetailView.js +8 -1
  44. package/dist/views/ProjectContext.js +35 -1
  45. package/package.json +1 -1
@@ -0,0 +1,345 @@
1
+ import { isLinearAuthenticated, linearGraphQL } from '../auth/index.js';
2
+ // GraphQL queries
3
+ const TEAMS_QUERY = `
4
+ query {
5
+ teams {
6
+ nodes {
7
+ id
8
+ name
9
+ key
10
+ }
11
+ }
12
+ }
13
+ `;
14
+ const VIEWER_QUERY = `
15
+ query {
16
+ viewer {
17
+ id
18
+ name
19
+ displayName
20
+ }
21
+ }
22
+ `;
23
+ const CYCLES_QUERY = `
24
+ query($teamId: String!) {
25
+ team(id: $teamId) {
26
+ cycles(orderBy: startsAt, first: 20) {
27
+ nodes {
28
+ id
29
+ name
30
+ number
31
+ startsAt
32
+ endsAt
33
+ }
34
+ }
35
+ activeCycle {
36
+ id
37
+ name
38
+ number
39
+ }
40
+ }
41
+ }
42
+ `;
43
+ const ASSIGNEES_QUERY = `
44
+ query($teamId: String!) {
45
+ team(id: $teamId) {
46
+ members {
47
+ nodes {
48
+ id
49
+ name
50
+ displayName
51
+ }
52
+ }
53
+ }
54
+ }
55
+ `;
56
+ const ISSUES_QUERY = `
57
+ query($teamId: String!, $first: Int, $filter: IssueFilter) {
58
+ team(id: $teamId) {
59
+ issues(first: $first, filter: $filter, orderBy: updatedAt) {
60
+ nodes {
61
+ id
62
+ identifier
63
+ title
64
+ description
65
+ url
66
+ createdAt
67
+ updatedAt
68
+ priority
69
+ estimate
70
+ state {
71
+ name
72
+ type
73
+ }
74
+ creator {
75
+ name
76
+ }
77
+ assignee {
78
+ id
79
+ name
80
+ displayName
81
+ }
82
+ cycle {
83
+ id
84
+ name
85
+ number
86
+ }
87
+ labels {
88
+ nodes {
89
+ name
90
+ }
91
+ }
92
+ comments {
93
+ nodes {
94
+ id
95
+ body
96
+ createdAt
97
+ user {
98
+ name
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ `;
107
+ const ISSUE_QUERY = `
108
+ query($id: String!) {
109
+ issue(id: $id) {
110
+ id
111
+ identifier
112
+ title
113
+ description
114
+ url
115
+ createdAt
116
+ updatedAt
117
+ priority
118
+ estimate
119
+ state {
120
+ name
121
+ type
122
+ }
123
+ creator {
124
+ name
125
+ }
126
+ assignee {
127
+ id
128
+ name
129
+ displayName
130
+ }
131
+ cycle {
132
+ id
133
+ name
134
+ number
135
+ }
136
+ labels {
137
+ nodes {
138
+ name
139
+ }
140
+ }
141
+ comments {
142
+ nodes {
143
+ id
144
+ body
145
+ createdAt
146
+ user {
147
+ name
148
+ }
149
+ }
150
+ }
151
+ team {
152
+ name
153
+ }
154
+ }
155
+ }
156
+ `;
157
+ export class LinearProvider {
158
+ type = 'linear';
159
+ teamId;
160
+ teamName;
161
+ cachedUserId;
162
+ cachedActiveCycleId;
163
+ constructor(teamId, teamName) {
164
+ this.teamId = teamId;
165
+ this.teamName = teamName;
166
+ }
167
+ async isAuthenticated() {
168
+ return isLinearAuthenticated();
169
+ }
170
+ async authenticate() {
171
+ throw new Error('Use saveLinearApiKey() to authenticate with a Personal API Key');
172
+ }
173
+ needsTeamSelection() {
174
+ return !this.teamId;
175
+ }
176
+ async fetchTeams() {
177
+ const data = await linearGraphQL(TEAMS_QUERY);
178
+ return data.teams.nodes;
179
+ }
180
+ setTeamId(teamId) {
181
+ this.teamId = teamId;
182
+ }
183
+ setTeamName(teamName) {
184
+ this.teamName = teamName;
185
+ }
186
+ getTeamName() {
187
+ return this.teamName;
188
+ }
189
+ async fetchTeamName() {
190
+ if (this.teamName)
191
+ return this.teamName;
192
+ if (!this.teamId)
193
+ return undefined;
194
+ try {
195
+ const data = await linearGraphQL(`query($teamId: String!) { team(id: $teamId) { name } }`, { teamId: this.teamId });
196
+ this.teamName = data.team.name;
197
+ return this.teamName;
198
+ }
199
+ catch {
200
+ return undefined;
201
+ }
202
+ }
203
+ async getCurrentUserId() {
204
+ if (this.cachedUserId !== undefined) {
205
+ return this.cachedUserId;
206
+ }
207
+ try {
208
+ const data = await linearGraphQL(VIEWER_QUERY);
209
+ this.cachedUserId = data.viewer.id;
210
+ return this.cachedUserId;
211
+ }
212
+ catch {
213
+ this.cachedUserId = null;
214
+ return null;
215
+ }
216
+ }
217
+ async fetchCycles() {
218
+ if (!this.teamId) {
219
+ throw new Error('Team not selected');
220
+ }
221
+ const data = await linearGraphQL(CYCLES_QUERY, { teamId: this.teamId });
222
+ // Cache the active cycle ID
223
+ if (data.team.activeCycle) {
224
+ this.cachedActiveCycleId = data.team.activeCycle.id;
225
+ }
226
+ return data.team.cycles.nodes.map((c) => ({
227
+ id: c.id,
228
+ name: c.name || `Cycle ${c.number}`,
229
+ number: c.number,
230
+ }));
231
+ }
232
+ getActiveCycleId() {
233
+ return this.cachedActiveCycleId;
234
+ }
235
+ async fetchAssignees() {
236
+ if (!this.teamId) {
237
+ throw new Error('Team not selected');
238
+ }
239
+ const data = await linearGraphQL(ASSIGNEES_QUERY, { teamId: this.teamId });
240
+ return data.team.members.nodes;
241
+ }
242
+ async fetchIssues(options = {}) {
243
+ if (!this.teamId) {
244
+ throw new Error('Team not selected');
245
+ }
246
+ const { state = 'open', limit = 50, linearFilter } = options;
247
+ // Build filter object
248
+ const filter = {};
249
+ // State filter
250
+ if (state === 'open') {
251
+ filter.state = { type: { in: ['backlog', 'unstarted', 'started'] } };
252
+ }
253
+ else if (state === 'closed') {
254
+ filter.state = { type: { in: ['completed', 'canceled'] } };
255
+ }
256
+ // Linear-specific filters
257
+ if (linearFilter) {
258
+ // Assignee filter
259
+ if (linearFilter.assigneeId !== undefined) {
260
+ if (linearFilter.assigneeId === null) {
261
+ filter.assignee = { null: true };
262
+ }
263
+ else {
264
+ filter.assignee = { id: { eq: linearFilter.assigneeId } };
265
+ }
266
+ }
267
+ // Cycle filter
268
+ if (linearFilter.cycleId) {
269
+ filter.cycle = { id: { eq: linearFilter.cycleId } };
270
+ }
271
+ // State type filter (overrides basic state filter)
272
+ if (linearFilter.stateType) {
273
+ const types = Array.isArray(linearFilter.stateType)
274
+ ? linearFilter.stateType
275
+ : [linearFilter.stateType];
276
+ filter.state = { type: { in: types } };
277
+ }
278
+ }
279
+ const data = await linearGraphQL(ISSUES_QUERY, { teamId: this.teamId, first: limit, filter: Object.keys(filter).length > 0 ? filter : undefined });
280
+ let issues = data.team.issues.nodes.map((issue) => this.normalizeIssue(issue));
281
+ // Client-side sorting
282
+ if (linearFilter?.sortBy === 'priority') {
283
+ // Priority: 1 = urgent (highest), 4 = low, 0 = no priority (lowest)
284
+ issues = issues.sort((a, b) => {
285
+ const aPri = a.priority === 0 ? 5 : (a.priority ?? 5);
286
+ const bPri = b.priority === 0 ? 5 : (b.priority ?? 5);
287
+ return linearFilter.sortDirection === 'desc' ? aPri - bPri : bPri - aPri;
288
+ });
289
+ }
290
+ else if (linearFilter?.sortBy === 'createdAt') {
291
+ issues = issues.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
292
+ }
293
+ // Default: already sorted by updatedAt from API
294
+ return issues;
295
+ }
296
+ async getIssue(id) {
297
+ const data = await linearGraphQL(ISSUE_QUERY, { id });
298
+ return this.normalizeIssue(data.issue);
299
+ }
300
+ normalizeIssue(issue) {
301
+ const openStateTypes = ['backlog', 'unstarted', 'started'];
302
+ const isOpen = openStateTypes.includes(issue.state.type);
303
+ const comments = issue.comments.nodes.map((c) => ({
304
+ id: c.id,
305
+ author: c.user?.name || 'Unknown',
306
+ body: c.body,
307
+ createdAt: c.createdAt,
308
+ }));
309
+ return {
310
+ id: issue.id,
311
+ identifier: issue.identifier,
312
+ title: issue.title,
313
+ state: issue.state.name,
314
+ stateType: issue.state.type,
315
+ isOpen,
316
+ author: issue.creator?.name || 'Unknown',
317
+ body: issue.description || '',
318
+ labels: issue.labels.nodes.map((l) => l.name),
319
+ url: issue.url,
320
+ createdAt: issue.createdAt,
321
+ updatedAt: issue.updatedAt,
322
+ comments,
323
+ source: {
324
+ type: 'linear',
325
+ team: issue.team?.name || this.teamName || 'Unknown',
326
+ },
327
+ priority: issue.priority,
328
+ estimate: issue.estimate ?? undefined,
329
+ assignee: issue.assignee
330
+ ? {
331
+ id: issue.assignee.id,
332
+ name: issue.assignee.name,
333
+ displayName: issue.assignee.displayName,
334
+ }
335
+ : undefined,
336
+ cycle: issue.cycle
337
+ ? {
338
+ id: issue.cycle.id,
339
+ name: issue.cycle.name || `Cycle ${issue.cycle.number}`,
340
+ number: issue.cycle.number,
341
+ }
342
+ : undefined,
343
+ };
344
+ }
345
+ }
@@ -0,0 +1,82 @@
1
+ export interface IssueComment {
2
+ id: string;
3
+ author: string;
4
+ body: string;
5
+ createdAt: string;
6
+ }
7
+ export type LinearPriority = 0 | 1 | 2 | 3 | 4;
8
+ export type LinearStateType = 'backlog' | 'unstarted' | 'started' | 'completed' | 'canceled' | 'triage';
9
+ export interface LinearAssignee {
10
+ id: string;
11
+ name: string;
12
+ displayName: string;
13
+ }
14
+ export interface LinearCycle {
15
+ id: string;
16
+ name: string;
17
+ number: number;
18
+ }
19
+ export interface Issue {
20
+ id: string;
21
+ identifier: string;
22
+ title: string;
23
+ state: string;
24
+ stateType?: LinearStateType;
25
+ isOpen: boolean;
26
+ author: string;
27
+ body: string;
28
+ labels: string[];
29
+ url: string;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ comments: IssueComment[];
33
+ source: IssueSource;
34
+ priority?: LinearPriority;
35
+ estimate?: number;
36
+ assignee?: LinearAssignee;
37
+ cycle?: LinearCycle;
38
+ }
39
+ export type IssueSource = {
40
+ type: 'github';
41
+ repo: string;
42
+ livestockName: string;
43
+ livestockPath: string;
44
+ } | {
45
+ type: 'linear';
46
+ team: string;
47
+ };
48
+ export interface LinearIssueFilter {
49
+ assigneeId?: string | null;
50
+ cycleId?: string;
51
+ stateType?: LinearStateType | LinearStateType[];
52
+ sortBy?: 'priority' | 'updatedAt' | 'createdAt';
53
+ sortDirection?: 'asc' | 'desc';
54
+ }
55
+ export interface FetchIssuesOptions {
56
+ state?: 'open' | 'closed' | 'all';
57
+ limit?: number;
58
+ linearFilter?: LinearIssueFilter;
59
+ }
60
+ export interface IssueProvider {
61
+ readonly type: 'github' | 'linear';
62
+ isAuthenticated(): Promise<boolean>;
63
+ authenticate(): Promise<void>;
64
+ fetchIssues(options?: FetchIssuesOptions): Promise<Issue[]>;
65
+ getIssue(id: string): Promise<Issue>;
66
+ }
67
+ export interface LinearProviderInterface extends IssueProvider {
68
+ readonly type: 'linear';
69
+ needsTeamSelection(): boolean;
70
+ fetchTeams(): Promise<LinearTeam[]>;
71
+ setTeamId(teamId: string): void;
72
+ setTeamName(teamName: string): void;
73
+ getTeamName(): string | undefined;
74
+ fetchCycles(): Promise<LinearCycle[]>;
75
+ fetchAssignees(): Promise<LinearAssignee[]>;
76
+ getCurrentUserId(): Promise<string | null>;
77
+ }
78
+ export interface LinearTeam {
79
+ id: string;
80
+ name: string;
81
+ key: string;
82
+ }
@@ -0,0 +1,2 @@
1
+ // src/lib/issues/types.ts
2
+ export {};
@@ -1,5 +1,6 @@
1
1
  export declare const YEEHAW_DIR: string;
2
2
  export declare const CONFIG_FILE: string;
3
+ export declare const AUTH_FILE: string;
3
4
  export declare const PROJECTS_DIR: string;
4
5
  export declare const BARNS_DIR: string;
5
6
  export declare const SESSIONS_DIR: string;
package/dist/lib/paths.js CHANGED
@@ -2,6 +2,7 @@ import { homedir } from 'os';
2
2
  import { join } from 'path';
3
3
  export const YEEHAW_DIR = join(homedir(), '.yeehaw');
4
4
  export const CONFIG_FILE = join(YEEHAW_DIR, 'config.yaml');
5
+ export const AUTH_FILE = join(YEEHAW_DIR, 'auth.yaml');
5
6
  export const PROJECTS_DIR = join(YEEHAW_DIR, 'projects');
6
7
  export const BARNS_DIR = join(YEEHAW_DIR, 'barns');
7
8
  export const SESSIONS_DIR = join(YEEHAW_DIR, 'sessions');
@@ -24,6 +24,7 @@ export declare function setupStatusBarHooks(): void;
24
24
  export declare function ensureCorrectStatusBar(): void;
25
25
  export declare function attachToYeehaw(): void;
26
26
  export declare function createClaudeWindow(workingDir: string, windowName: string): number;
27
+ export declare function createClaudeWindowWithPrompt(workingDir: string, windowName: string, systemPrompt: string): number;
27
28
  export declare function createShellWindow(workingDir: string, windowName: string, shell?: string): number;
28
29
  export declare function createSshWindow(windowName: string, host: string, user: string, port: number, identityFile: string, remotePath: string): number;
29
30
  export declare function detachFromSession(): void;
package/dist/lib/tmux.js CHANGED
@@ -131,19 +131,31 @@ export function attachToYeehaw() {
131
131
  }
132
132
  // All yeehaw MCP tools that should be auto-approved
133
133
  const YEEHAW_MCP_TOOLS = [
134
+ // Project management
134
135
  'mcp__yeehaw__list_projects',
135
136
  'mcp__yeehaw__get_project',
136
137
  'mcp__yeehaw__create_project',
137
138
  'mcp__yeehaw__update_project',
138
139
  'mcp__yeehaw__delete_project',
140
+ // Livestock management
139
141
  'mcp__yeehaw__add_livestock',
140
142
  'mcp__yeehaw__remove_livestock',
143
+ 'mcp__yeehaw__read_livestock_logs',
144
+ 'mcp__yeehaw__read_livestock_env',
145
+ // Barn management
141
146
  'mcp__yeehaw__list_barns',
142
147
  'mcp__yeehaw__get_barn',
143
148
  'mcp__yeehaw__create_barn',
144
149
  'mcp__yeehaw__update_barn',
145
150
  'mcp__yeehaw__delete_barn',
151
+ // Critter management
152
+ 'mcp__yeehaw__add_critter',
153
+ 'mcp__yeehaw__remove_critter',
154
+ 'mcp__yeehaw__read_critter_logs',
155
+ 'mcp__yeehaw__discover_critters',
156
+ // Wiki management
146
157
  'mcp__yeehaw__get_wiki',
158
+ 'mcp__yeehaw__get_wiki_section',
147
159
  'mcp__yeehaw__add_wiki_section',
148
160
  'mcp__yeehaw__update_wiki_section',
149
161
  'mcp__yeehaw__delete_wiki_section',
@@ -158,6 +170,8 @@ function setWindowType(windowIndex, type) {
158
170
  ]);
159
171
  }
160
172
  export function createClaudeWindow(workingDir, windowName) {
173
+ // Ensure workingDir is valid, fallback to current working directory
174
+ const effectiveWorkingDir = workingDir || process.cwd();
161
175
  // Build MCP config for yeehaw server
162
176
  const mcpConfig = JSON.stringify({
163
177
  mcpServers: {
@@ -178,7 +192,7 @@ export function createClaudeWindow(workingDir, windowName) {
178
192
  '-a',
179
193
  '-t', YEEHAW_SESSION,
180
194
  '-n', windowName,
181
- '-c', workingDir,
195
+ '-c', effectiveWorkingDir,
182
196
  claudeCmd,
183
197
  ]);
184
198
  // Get the window index we just created (new window is now current)
@@ -190,6 +204,41 @@ export function createClaudeWindow(workingDir, windowName) {
190
204
  setWindowType(windowIndex, 'claude');
191
205
  return windowIndex;
192
206
  }
207
+ export function createClaudeWindowWithPrompt(workingDir, windowName, systemPrompt) {
208
+ // Ensure workingDir is valid, fallback to current working directory
209
+ const effectiveWorkingDir = workingDir || process.cwd();
210
+ // Build MCP config for yeehaw server
211
+ const mcpConfig = JSON.stringify({
212
+ mcpServers: {
213
+ yeehaw: {
214
+ command: 'node',
215
+ args: [MCP_SERVER_PATH],
216
+ },
217
+ },
218
+ });
219
+ // Build allowed tools list for auto-approval
220
+ const allowedTools = YEEHAW_MCP_TOOLS.join(',');
221
+ // Escape the system prompt for shell - use single quotes and escape any single quotes in content
222
+ const escapedPrompt = systemPrompt.replace(/'/g, "'\\''");
223
+ // Create new window running claude with yeehaw MCP server and system prompt
224
+ const claudeCmd = `claude --mcp-config ${shellEscape(mcpConfig)} --allowedTools ${shellEscape(allowedTools)} --plugin-dir ${shellEscape(CLAUDE_PLUGIN_PATH)} --system-prompt '${escapedPrompt}'`;
225
+ execaSync('tmux', [
226
+ 'new-window',
227
+ '-a',
228
+ '-t', YEEHAW_SESSION,
229
+ '-n', windowName,
230
+ '-c', effectiveWorkingDir,
231
+ claudeCmd,
232
+ ]);
233
+ // Get the window index we just created
234
+ const result = execaSync('tmux', [
235
+ 'display-message', '-p', '#{window_index}'
236
+ ]);
237
+ const windowIndex = parseInt(result.stdout.trim(), 10);
238
+ // Mark this window as a Claude session
239
+ setWindowType(windowIndex, 'claude');
240
+ return windowIndex;
241
+ }
193
242
  export function createShellWindow(workingDir, windowName, shell) {
194
243
  // Use the user's configured shell from $SHELL, fallback to /bin/bash
195
244
  const userShell = shell || process.env.SHELL || '/bin/bash';
package/dist/types.d.ts CHANGED
@@ -24,11 +24,21 @@ export interface Project {
24
24
  gradientInverted?: boolean;
25
25
  livestock?: Livestock[];
26
26
  wiki?: WikiSection[];
27
+ issueProvider?: IssueProviderConfig;
27
28
  }
28
29
  export interface WikiSection {
29
30
  title: string;
30
31
  content: string;
31
32
  }
33
+ export type IssueProviderConfig = {
34
+ type: 'github';
35
+ } | {
36
+ type: 'linear';
37
+ teamId?: string;
38
+ teamName?: string;
39
+ } | {
40
+ type: 'none';
41
+ };
32
42
  export interface Livestock {
33
43
  name: string;
34
44
  path: string;
@@ -41,6 +51,7 @@ export interface Livestock {
41
51
  export interface Critter {
42
52
  name: string;
43
53
  service: string;
54
+ service_path?: string;
44
55
  config_path?: string;
45
56
  log_path?: string;
46
57
  use_journald?: boolean;
@@ -92,6 +103,14 @@ export type AppView = {
92
103
  livestock: Livestock;
93
104
  source: 'project' | 'barn';
94
105
  sourceBarn?: Barn;
106
+ } | {
107
+ type: 'critter';
108
+ barn: Barn;
109
+ critter: Critter;
110
+ } | {
111
+ type: 'critter-logs';
112
+ barn: Barn;
113
+ critter: Critter;
95
114
  } | {
96
115
  type: 'night-sky';
97
116
  };
@@ -19,6 +19,7 @@ interface BarnContextProps {
19
19
  onRemoveLivestock: (project: Project, livestockName: string) => void;
20
20
  onAddCritter: (critter: Critter) => void;
21
21
  onRemoveCritter: (critterName: string) => void;
22
+ onSelectCritter: (critter: Critter) => void;
22
23
  }
23
- export declare function BarnContext({ barn, livestock, projects, windows, onBack, onSshToBarn, onSelectLivestock, onOpenLivestockSession, onUpdateBarn, onDeleteBarn, onAddLivestock, onRemoveLivestock, onAddCritter, onRemoveCritter, }: BarnContextProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function BarnContext({ barn, livestock, projects, windows, onBack, onSshToBarn, onSelectLivestock, onOpenLivestockSession, onUpdateBarn, onDeleteBarn, onAddLivestock, onRemoveLivestock, onAddCritter, onRemoveCritter, onSelectCritter, }: BarnContextProps): import("react/jsx-runtime").JSX.Element;
24
25
  export {};