@colmbus72/yeehaw 0.5.0 → 0.6.0
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/claude-plugin/skills/yeehaw-development/SKILL.md +70 -0
- package/dist/app.js +166 -15
- package/dist/components/CritterHeader.d.ts +7 -0
- package/dist/components/CritterHeader.js +81 -0
- package/dist/components/List.d.ts +2 -0
- package/dist/components/List.js +1 -1
- package/dist/lib/auth/index.d.ts +2 -0
- package/dist/lib/auth/index.js +3 -0
- package/dist/lib/auth/linear.d.ts +20 -0
- package/dist/lib/auth/linear.js +79 -0
- package/dist/lib/auth/storage.d.ts +12 -0
- package/dist/lib/auth/storage.js +53 -0
- package/dist/lib/context.d.ts +10 -0
- package/dist/lib/context.js +63 -0
- package/dist/lib/critters.d.ts +33 -0
- package/dist/lib/critters.js +164 -0
- package/dist/lib/hotkeys.d.ts +1 -1
- package/dist/lib/hotkeys.js +6 -2
- package/dist/lib/issues/github.d.ts +11 -0
- package/dist/lib/issues/github.js +154 -0
- package/dist/lib/issues/index.d.ts +14 -0
- package/dist/lib/issues/index.js +27 -0
- package/dist/lib/issues/linear.d.ts +24 -0
- package/dist/lib/issues/linear.js +345 -0
- package/dist/lib/issues/types.d.ts +82 -0
- package/dist/lib/issues/types.js +2 -0
- package/dist/lib/paths.d.ts +1 -0
- package/dist/lib/paths.js +1 -0
- package/dist/lib/tmux.d.ts +1 -0
- package/dist/lib/tmux.js +45 -0
- package/dist/types.d.ts +19 -0
- package/dist/views/BarnContext.d.ts +2 -1
- package/dist/views/BarnContext.js +136 -14
- package/dist/views/CritterDetailView.d.ts +10 -0
- package/dist/views/CritterDetailView.js +117 -0
- package/dist/views/CritterLogsView.d.ts +8 -0
- package/dist/views/CritterLogsView.js +100 -0
- package/dist/views/IssuesView.d.ts +2 -1
- package/dist/views/IssuesView.js +661 -98
- package/dist/views/LivestockDetailView.d.ts +2 -1
- package/dist/views/LivestockDetailView.js +8 -1
- package/dist/views/ProjectContext.js +35 -1
- 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
|
+
}
|
package/dist/lib/paths.d.ts
CHANGED
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');
|
package/dist/lib/tmux.d.ts
CHANGED
|
@@ -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',
|
|
@@ -190,6 +202,39 @@ export function createClaudeWindow(workingDir, windowName) {
|
|
|
190
202
|
setWindowType(windowIndex, 'claude');
|
|
191
203
|
return windowIndex;
|
|
192
204
|
}
|
|
205
|
+
export function createClaudeWindowWithPrompt(workingDir, windowName, systemPrompt) {
|
|
206
|
+
// Build MCP config for yeehaw server
|
|
207
|
+
const mcpConfig = JSON.stringify({
|
|
208
|
+
mcpServers: {
|
|
209
|
+
yeehaw: {
|
|
210
|
+
command: 'node',
|
|
211
|
+
args: [MCP_SERVER_PATH],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
// Build allowed tools list for auto-approval
|
|
216
|
+
const allowedTools = YEEHAW_MCP_TOOLS.join(',');
|
|
217
|
+
// Escape the system prompt for shell - use single quotes and escape any single quotes in content
|
|
218
|
+
const escapedPrompt = systemPrompt.replace(/'/g, "'\\''");
|
|
219
|
+
// Create new window running claude with yeehaw MCP server and system prompt
|
|
220
|
+
const claudeCmd = `claude --mcp-config ${shellEscape(mcpConfig)} --allowedTools ${shellEscape(allowedTools)} --plugin-dir ${shellEscape(CLAUDE_PLUGIN_PATH)} --system-prompt '${escapedPrompt}'`;
|
|
221
|
+
execaSync('tmux', [
|
|
222
|
+
'new-window',
|
|
223
|
+
'-a',
|
|
224
|
+
'-t', YEEHAW_SESSION,
|
|
225
|
+
'-n', windowName,
|
|
226
|
+
'-c', workingDir,
|
|
227
|
+
claudeCmd,
|
|
228
|
+
]);
|
|
229
|
+
// Get the window index we just created
|
|
230
|
+
const result = execaSync('tmux', [
|
|
231
|
+
'display-message', '-p', '#{window_index}'
|
|
232
|
+
]);
|
|
233
|
+
const windowIndex = parseInt(result.stdout.trim(), 10);
|
|
234
|
+
// Mark this window as a Claude session
|
|
235
|
+
setWindowType(windowIndex, 'claude');
|
|
236
|
+
return windowIndex;
|
|
237
|
+
}
|
|
193
238
|
export function createShellWindow(workingDir, windowName, shell) {
|
|
194
239
|
// Use the user's configured shell from $SHELL, fallback to /bin/bash
|
|
195
240
|
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 {};
|