yourskills 0.4.3 → 0.5.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/dist/main.js +2164 -1718
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -61,136 +61,647 @@ function buildHelp(lines) {
|
|
|
61
61
|
return `${PREAMBLE}${block}${REST}`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// src/auth/
|
|
65
|
-
import {
|
|
64
|
+
// src/auth/screens/gate.tsx
|
|
65
|
+
import { Box as Box6, Text as Text8, useApp as useApp2, useInput as useInput3 } from "ink";
|
|
66
|
+
import {
|
|
67
|
+
useCallback,
|
|
68
|
+
useEffect as useEffect3,
|
|
69
|
+
useMemo,
|
|
70
|
+
useState as useState4
|
|
71
|
+
} from "react";
|
|
66
72
|
|
|
67
|
-
// src/
|
|
68
|
-
import {
|
|
69
|
-
import { homedir as homedir2 } from "node:os";
|
|
70
|
-
import { join as join2 } from "node:path";
|
|
73
|
+
// src/ui/back.tsx
|
|
74
|
+
import { useInput } from "ink";
|
|
71
75
|
|
|
72
|
-
// src/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
// ../../packages/ui/src/lib/ascii.ts
|
|
77
|
+
var DEFAULT_ASCII_CHARS = {
|
|
78
|
+
top: "-",
|
|
79
|
+
bottom: "-",
|
|
80
|
+
left: "|",
|
|
81
|
+
right: "|",
|
|
82
|
+
divider: "-",
|
|
83
|
+
junction: "+"
|
|
84
|
+
};
|
|
85
|
+
function fill(n, seq = "-") {
|
|
86
|
+
const count = Math.max(n, 0);
|
|
87
|
+
if (count === 0) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
const unit = seq.length > 0 ? seq : " ";
|
|
91
|
+
return unit.repeat(Math.ceil(count / unit.length)).slice(0, count);
|
|
81
92
|
}
|
|
82
|
-
function
|
|
83
|
-
return
|
|
93
|
+
function junctionGlyph(chars) {
|
|
94
|
+
return chars.junction.length > 0 ? chars.junction[0] : DEFAULT_ASCII_CHARS.junction;
|
|
84
95
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return {};
|
|
96
|
+
function topBorder(width, title, chars = DEFAULT_ASCII_CHARS) {
|
|
97
|
+
const j = junctionGlyph(chars);
|
|
98
|
+
if (!title) {
|
|
99
|
+
return `${j}${fill(width - 2, chars.top)}${j}`;
|
|
90
100
|
}
|
|
101
|
+
const label = ` ${title} `;
|
|
102
|
+
const left = 2;
|
|
103
|
+
const right = Math.max(width - 2 - left - label.length, 1);
|
|
104
|
+
return `${j}${fill(left, chars.top)}${label}${fill(right, chars.top)}${j}`;
|
|
91
105
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
|
|
107
|
+
// src/ui/frame.tsx
|
|
108
|
+
import { Box, Text, useWindowSize } from "ink";
|
|
109
|
+
|
|
110
|
+
// src/ui/theme.ts
|
|
111
|
+
var tone = {
|
|
112
|
+
primary: "cyan",
|
|
113
|
+
notice: "yellow",
|
|
114
|
+
danger: "red"
|
|
115
|
+
};
|
|
116
|
+
var stateTone = {
|
|
117
|
+
"state-active": undefined,
|
|
118
|
+
"state-experimental": "yellow",
|
|
119
|
+
"state-deprecated": "red"
|
|
120
|
+
};
|
|
121
|
+
var mark = {
|
|
122
|
+
ok: "+",
|
|
123
|
+
warn: "!",
|
|
124
|
+
fail: "x",
|
|
125
|
+
pending: ".",
|
|
126
|
+
none: "",
|
|
127
|
+
cursor: ">",
|
|
128
|
+
selected: "*",
|
|
129
|
+
unselected: " "
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/ui/frame.tsx
|
|
133
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
134
|
+
var MAX_WIDTH = 96;
|
|
135
|
+
var MIN_WIDTH = 44;
|
|
136
|
+
var FRAME_CHROME = 4;
|
|
137
|
+
function useFrameWidth() {
|
|
138
|
+
const { columns } = useWindowSize();
|
|
139
|
+
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, columns - 2));
|
|
103
140
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
141
|
+
var j = DEFAULT_ASCII_CHARS.junction;
|
|
142
|
+
var BORDER = {
|
|
143
|
+
topLeft: j,
|
|
144
|
+
top: DEFAULT_ASCII_CHARS.top,
|
|
145
|
+
topRight: j,
|
|
146
|
+
right: DEFAULT_ASCII_CHARS.right,
|
|
147
|
+
bottomRight: j,
|
|
148
|
+
bottom: DEFAULT_ASCII_CHARS.bottom,
|
|
149
|
+
bottomLeft: j,
|
|
150
|
+
left: DEFAULT_ASCII_CHARS.left
|
|
151
|
+
};
|
|
152
|
+
function Frame({
|
|
153
|
+
title,
|
|
154
|
+
width,
|
|
155
|
+
children
|
|
156
|
+
}) {
|
|
157
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
158
|
+
flexDirection: "column",
|
|
159
|
+
width,
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsx(Text, {
|
|
162
|
+
color: tone.primary,
|
|
163
|
+
children: topBorder(width, title)
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ jsx(Box, {
|
|
166
|
+
flexDirection: "column",
|
|
167
|
+
width,
|
|
168
|
+
paddingLeft: 1,
|
|
169
|
+
paddingRight: 1,
|
|
170
|
+
borderStyle: BORDER,
|
|
171
|
+
borderColor: tone.primary,
|
|
172
|
+
borderTop: false,
|
|
173
|
+
children
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
});
|
|
109
177
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
178
|
+
function Divider({ width }) {
|
|
179
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
180
|
+
marginLeft: -1,
|
|
181
|
+
marginRight: -1,
|
|
182
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
183
|
+
color: tone.primary,
|
|
184
|
+
children: fill(width - 2, DEFAULT_ASCII_CHARS.divider)
|
|
185
|
+
})
|
|
186
|
+
});
|
|
112
187
|
}
|
|
113
|
-
function
|
|
114
|
-
return
|
|
188
|
+
function Blank() {
|
|
189
|
+
return /* @__PURE__ */ jsx(Text, {
|
|
190
|
+
children: " "
|
|
191
|
+
});
|
|
115
192
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const home = process.env.HOME;
|
|
123
|
-
return join2(home && home.length > 0 ? home : homedir2(), ".agents", ".skill-lock.json");
|
|
193
|
+
function clip(text, width) {
|
|
194
|
+
if (width <= 0)
|
|
195
|
+
return "";
|
|
196
|
+
if (text.length <= width)
|
|
197
|
+
return text;
|
|
198
|
+
return width <= 1 ? text.slice(0, width) : `${text.slice(0, width - 1)}~`;
|
|
124
199
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
200
|
+
|
|
201
|
+
// ../../packages/skills/src/lifecycle.ts
|
|
202
|
+
var LIFECYCLES = [
|
|
203
|
+
{ status: "active", word: "active", glyph: "#", tone: "state-active" },
|
|
204
|
+
{
|
|
205
|
+
status: "experimental",
|
|
206
|
+
word: "experimental",
|
|
207
|
+
glyph: "?",
|
|
208
|
+
tone: "state-experimental"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
status: "deprecated",
|
|
212
|
+
word: "deprecated",
|
|
213
|
+
glyph: "x",
|
|
214
|
+
tone: "state-deprecated"
|
|
132
215
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
140
|
-
|
|
216
|
+
];
|
|
217
|
+
var LIFECYCLE_ORDER = LIFECYCLES.map((lifecycle) => lifecycle.status);
|
|
218
|
+
var BY_STATUS = new Map(LIFECYCLES.map((lifecycle) => [lifecycle.status, lifecycle]));
|
|
219
|
+
function lifecycleOf(status) {
|
|
220
|
+
const lifecycle = BY_STATUS.get(status);
|
|
221
|
+
if (!lifecycle) {
|
|
222
|
+
throw new Error(`No lifecycle vocabulary for "${status}". Add it to LIFECYCLES.`);
|
|
223
|
+
}
|
|
224
|
+
return lifecycle;
|
|
141
225
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
return
|
|
148
|
-
return
|
|
226
|
+
// src/ui/rows.tsx
|
|
227
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
228
|
+
import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
|
|
229
|
+
function lifecycleTag(status) {
|
|
230
|
+
if (!status || status === "active")
|
|
231
|
+
return "";
|
|
232
|
+
return `[${lifecycleOf(status).word}]`;
|
|
149
233
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const value = process.env.YOURSKILLS_NO_TRACK;
|
|
153
|
-
return value !== undefined && value !== "" && value !== "0";
|
|
234
|
+
function lifecycleColor(status) {
|
|
235
|
+
return stateTone[lifecycleOf(status ?? "active").tone];
|
|
154
236
|
}
|
|
155
|
-
|
|
156
|
-
|
|
237
|
+
var GUTTER = 4;
|
|
238
|
+
var GAP = 2;
|
|
239
|
+
function nameWidth(rows, max = 28) {
|
|
240
|
+
return Math.min(max, Math.max(8, ...rows.map((r) => r.name.length)));
|
|
157
241
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
242
|
+
function SkillRow({
|
|
243
|
+
row,
|
|
244
|
+
width,
|
|
245
|
+
names,
|
|
246
|
+
cursor,
|
|
247
|
+
selected,
|
|
248
|
+
selectable = false
|
|
249
|
+
}) {
|
|
250
|
+
const tag = lifecycleTag(row.status);
|
|
251
|
+
const meta = row.count !== undefined ? `${row.count} skills` : tag;
|
|
252
|
+
const gutter = selectable ? GUTTER : 0;
|
|
253
|
+
const rest = width - gutter - names - GAP - (meta ? meta.length + GAP : 0);
|
|
254
|
+
return /* @__PURE__ */ jsxs2(Box2, {
|
|
255
|
+
children: [
|
|
256
|
+
selectable ? /* @__PURE__ */ jsx2(Text2, {
|
|
257
|
+
color: cursor ? "cyan" : undefined,
|
|
258
|
+
children: `${cursor ? mark.cursor : " "} ${selected ? mark.selected : mark.unselected} `
|
|
259
|
+
}) : null,
|
|
260
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
261
|
+
bold: cursor,
|
|
262
|
+
color: lifecycleColor(row.status),
|
|
263
|
+
children: clip(row.name, names).padEnd(names)
|
|
264
|
+
}),
|
|
265
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
266
|
+
children: " ".repeat(GAP)
|
|
267
|
+
}),
|
|
268
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
269
|
+
dimColor: true,
|
|
270
|
+
children: clip(row.description ?? "", Math.max(rest, 0))
|
|
271
|
+
}),
|
|
272
|
+
meta ? /* @__PURE__ */ jsxs2(Fragment, {
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
275
|
+
children: " ".repeat(GAP)
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
278
|
+
color: lifecycleColor(row.status),
|
|
279
|
+
dimColor: !tag,
|
|
280
|
+
children: meta
|
|
281
|
+
})
|
|
282
|
+
]
|
|
283
|
+
}) : null
|
|
284
|
+
]
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function Hints({ hints }) {
|
|
288
|
+
return /* @__PURE__ */ jsx2(Box2, {
|
|
289
|
+
children: hints.map(([key, label], i) => /* @__PURE__ */ jsxs2(Text2, {
|
|
290
|
+
children: [
|
|
291
|
+
i > 0 ? /* @__PURE__ */ jsx2(Text2, {
|
|
292
|
+
dimColor: true,
|
|
293
|
+
children: " "
|
|
294
|
+
}) : null,
|
|
295
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
296
|
+
color: "cyan",
|
|
297
|
+
children: key
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
300
|
+
dimColor: true,
|
|
301
|
+
children: ` ${label}`
|
|
302
|
+
})
|
|
303
|
+
]
|
|
304
|
+
}, key))
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/ui/back.tsx
|
|
309
|
+
import { jsx as jsx3, jsxs as jsxs3, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
310
|
+
function Back({
|
|
311
|
+
width,
|
|
312
|
+
onBack,
|
|
313
|
+
extra = []
|
|
314
|
+
}) {
|
|
315
|
+
useInput((input, key) => {
|
|
316
|
+
if (key.escape || input === "x")
|
|
317
|
+
onBack?.();
|
|
318
|
+
}, { isActive: Boolean(onBack) });
|
|
319
|
+
if (!onBack)
|
|
320
|
+
return null;
|
|
321
|
+
return /* @__PURE__ */ jsxs3(Fragment2, {
|
|
322
|
+
children: [
|
|
323
|
+
/* @__PURE__ */ jsx3(Divider, {
|
|
324
|
+
width
|
|
325
|
+
}),
|
|
326
|
+
/* @__PURE__ */ jsx3(Hints, {
|
|
327
|
+
hints: [...extra, ["esc/x", "back"]]
|
|
328
|
+
})
|
|
329
|
+
]
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// src/ui/command.tsx
|
|
333
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
334
|
+
|
|
335
|
+
// src/ui/text-input.tsx
|
|
336
|
+
import { Text as Text3 } from "ink";
|
|
337
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
338
|
+
function SearchInput({
|
|
339
|
+
value,
|
|
340
|
+
placeholder = "",
|
|
341
|
+
focus = true
|
|
342
|
+
}) {
|
|
343
|
+
return /* @__PURE__ */ jsxs4(Text3, {
|
|
344
|
+
children: [
|
|
345
|
+
value.length > 0 ? /* @__PURE__ */ jsx4(Text3, {
|
|
346
|
+
children: value
|
|
347
|
+
}) : /* @__PURE__ */ jsx4(Text3, {
|
|
348
|
+
dimColor: true,
|
|
349
|
+
children: placeholder
|
|
350
|
+
}),
|
|
351
|
+
focus ? /* @__PURE__ */ jsx4(Text3, {
|
|
352
|
+
color: tone.primary,
|
|
353
|
+
children: "_"
|
|
354
|
+
}) : null
|
|
355
|
+
]
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/ui/viewport.ts
|
|
360
|
+
import { useWindowSize as useWindowSize2 } from "ink";
|
|
361
|
+
var MIN_ROWS = 3;
|
|
362
|
+
var MAX_ROWS = 20;
|
|
363
|
+
function useViewport({
|
|
364
|
+
cursor,
|
|
365
|
+
total,
|
|
366
|
+
chrome
|
|
367
|
+
}) {
|
|
368
|
+
const { rows } = useWindowSize2();
|
|
369
|
+
const size = Math.max(MIN_ROWS, Math.min(MAX_ROWS, rows - chrome));
|
|
370
|
+
const first = Math.max(0, Math.min(cursor - Math.floor(size / 2), total - size));
|
|
371
|
+
return { first, size };
|
|
372
|
+
}
|
|
373
|
+
function position(cursor, total) {
|
|
374
|
+
return `${total > 0 ? cursor + 1 : 0}/${total}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/ui/command.tsx
|
|
378
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
379
|
+
function filterCommands(groups, query) {
|
|
380
|
+
if (query.length === 0)
|
|
381
|
+
return groups;
|
|
382
|
+
const q = query.toLowerCase();
|
|
383
|
+
return groups.map((group) => ({
|
|
384
|
+
group: group.group,
|
|
385
|
+
items: group.items.filter((item) => item.label.toLowerCase().includes(q) || (item.hint ?? "").toLowerCase().includes(q))
|
|
386
|
+
})).filter((group) => group.items.length > 0);
|
|
387
|
+
}
|
|
388
|
+
function flattenCommands(groups) {
|
|
389
|
+
return groups.flatMap((group) => group.items);
|
|
390
|
+
}
|
|
391
|
+
function windowGroups(groups, first, count) {
|
|
392
|
+
const out = [];
|
|
393
|
+
let index = 0;
|
|
394
|
+
for (const group of groups) {
|
|
395
|
+
const items = group.items.filter((_, i) => {
|
|
396
|
+
const row = index + i;
|
|
397
|
+
return row >= first && row < first + count;
|
|
398
|
+
});
|
|
399
|
+
index += group.items.length;
|
|
400
|
+
if (items.length > 0)
|
|
401
|
+
out.push({ group: group.group, items });
|
|
402
|
+
}
|
|
403
|
+
return out;
|
|
404
|
+
}
|
|
405
|
+
var POINTER = 2;
|
|
406
|
+
var GAP2 = 2;
|
|
407
|
+
var LABEL_CAP = 22;
|
|
408
|
+
function valueColumn(items) {
|
|
409
|
+
return Math.min(items.reduce((wide, item) => Math.max(wide, item.label.length), 0), LABEL_CAP);
|
|
410
|
+
}
|
|
411
|
+
function CommandRow({
|
|
412
|
+
item,
|
|
413
|
+
width,
|
|
414
|
+
column,
|
|
415
|
+
active
|
|
416
|
+
}) {
|
|
417
|
+
const hint = item.hint ?? "";
|
|
418
|
+
const room = width - POINTER - (hint ? hint.length + GAP2 : 0);
|
|
419
|
+
const label = clip(item.label, Math.max(room, 0));
|
|
420
|
+
const shows = item.shows ?? "";
|
|
421
|
+
const pad = Math.max(column - label.length, 0) + GAP2;
|
|
422
|
+
const value = shows && label.length + pad + shows.length <= room ? shows : "";
|
|
423
|
+
const left = label.length + (value ? pad + value.length : 0);
|
|
424
|
+
const gap = Math.max(width - POINTER - left - hint.length, hint ? 1 : 0);
|
|
425
|
+
return /* @__PURE__ */ jsxs5(Text4, {
|
|
426
|
+
children: [
|
|
427
|
+
/* @__PURE__ */ jsx5(Text4, {
|
|
428
|
+
color: tone.primary,
|
|
429
|
+
children: `${active ? mark.cursor : " "} `
|
|
430
|
+
}),
|
|
431
|
+
/* @__PURE__ */ jsx5(Text4, {
|
|
432
|
+
bold: active,
|
|
433
|
+
color: active ? tone.primary : undefined,
|
|
434
|
+
children: label
|
|
435
|
+
}),
|
|
436
|
+
value ? /* @__PURE__ */ jsx5(Text4, {
|
|
437
|
+
children: `${" ".repeat(pad)}${value}`
|
|
438
|
+
}) : null,
|
|
439
|
+
/* @__PURE__ */ jsx5(Text4, {
|
|
440
|
+
dimColor: true,
|
|
441
|
+
children: `${" ".repeat(gap)}${hint}`
|
|
442
|
+
})
|
|
443
|
+
]
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
function CommandPalette({
|
|
447
|
+
groups,
|
|
448
|
+
query,
|
|
449
|
+
cursor,
|
|
450
|
+
width,
|
|
451
|
+
chrome = 11,
|
|
452
|
+
placeholder = "type a command or search",
|
|
453
|
+
emptyMessage = "No matching commands."
|
|
454
|
+
}) {
|
|
455
|
+
const visible = filterCommands(groups, query);
|
|
456
|
+
const rows = flattenCommands(visible);
|
|
457
|
+
const current = rows[cursor];
|
|
458
|
+
const interior = width - FRAME_CHROME;
|
|
459
|
+
const column = valueColumn(rows);
|
|
460
|
+
const { first, size } = useViewport({
|
|
461
|
+
cursor,
|
|
462
|
+
total: rows.length,
|
|
463
|
+
chrome: chrome + Math.max(0, visible.length * 2 - 1)
|
|
464
|
+
});
|
|
465
|
+
const paged = windowGroups(visible, first, size);
|
|
466
|
+
return /* @__PURE__ */ jsxs5(Box3, {
|
|
467
|
+
flexDirection: "column",
|
|
468
|
+
children: [
|
|
469
|
+
/* @__PURE__ */ jsxs5(Box3, {
|
|
470
|
+
children: [
|
|
471
|
+
/* @__PURE__ */ jsx5(Text4, {
|
|
472
|
+
color: tone.primary,
|
|
473
|
+
children: "> "
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ jsx5(SearchInput, {
|
|
476
|
+
value: query,
|
|
477
|
+
placeholder
|
|
478
|
+
})
|
|
479
|
+
]
|
|
480
|
+
}),
|
|
481
|
+
/* @__PURE__ */ jsx5(Divider, {
|
|
482
|
+
width
|
|
483
|
+
}),
|
|
484
|
+
rows.length === 0 ? /* @__PURE__ */ jsx5(Text4, {
|
|
485
|
+
dimColor: true,
|
|
486
|
+
children: emptyMessage
|
|
487
|
+
}) : paged.map((group, i) => /* @__PURE__ */ jsxs5(Box3, {
|
|
488
|
+
flexDirection: "column",
|
|
489
|
+
children: [
|
|
490
|
+
i > 0 ? /* @__PURE__ */ jsx5(Divider, {
|
|
491
|
+
width
|
|
492
|
+
}) : null,
|
|
493
|
+
/* @__PURE__ */ jsx5(Text4, {
|
|
494
|
+
dimColor: true,
|
|
495
|
+
children: group.group.toUpperCase()
|
|
496
|
+
}),
|
|
497
|
+
group.items.map((item) => /* @__PURE__ */ jsx5(CommandRow, {
|
|
498
|
+
item,
|
|
499
|
+
width: interior,
|
|
500
|
+
column,
|
|
501
|
+
active: item.value === current?.value
|
|
502
|
+
}, item.value))
|
|
503
|
+
]
|
|
504
|
+
}, group.group))
|
|
505
|
+
]
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
// src/ui/confirm.tsx
|
|
509
|
+
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
510
|
+
import { useState } from "react";
|
|
511
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
512
|
+
function ConfirmScreen({
|
|
513
|
+
title,
|
|
514
|
+
headline,
|
|
515
|
+
lines,
|
|
516
|
+
notes = [],
|
|
517
|
+
yes,
|
|
518
|
+
no,
|
|
519
|
+
argv,
|
|
520
|
+
onYes,
|
|
521
|
+
onNo
|
|
522
|
+
}) {
|
|
523
|
+
const width = useFrameWidth();
|
|
524
|
+
const [choice, setChoice] = useState("yes");
|
|
525
|
+
useInput2((input, key) => {
|
|
526
|
+
if (key.upArrow || key.downArrow) {
|
|
527
|
+
setChoice((c) => c === "yes" ? "no" : "yes");
|
|
528
|
+
} else if (key.return) {
|
|
529
|
+
if (choice === "yes")
|
|
530
|
+
onYes();
|
|
531
|
+
else
|
|
532
|
+
onNo();
|
|
533
|
+
} else if (key.escape || input === "x") {
|
|
534
|
+
onNo();
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
const interior = width - FRAME_CHROME;
|
|
538
|
+
const row = (label, active, hint) => {
|
|
539
|
+
const room = interior - 2 - (hint ? hint.length + 2 : 0);
|
|
540
|
+
const text = clip(label, Math.max(room, 0));
|
|
541
|
+
const gap = Math.max(interior - 2 - text.length - (hint?.length ?? 0), 1);
|
|
542
|
+
return /* @__PURE__ */ jsxs6(Text5, {
|
|
543
|
+
children: [
|
|
544
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
545
|
+
color: tone.primary,
|
|
546
|
+
children: `${active ? mark.cursor : " "} `
|
|
547
|
+
}),
|
|
548
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
549
|
+
bold: active,
|
|
550
|
+
color: active ? tone.primary : undefined,
|
|
551
|
+
children: text
|
|
552
|
+
}),
|
|
553
|
+
hint ? /* @__PURE__ */ jsx6(Text5, {
|
|
554
|
+
dimColor: true,
|
|
555
|
+
children: `${" ".repeat(gap)}${hint}`
|
|
556
|
+
}) : null
|
|
557
|
+
]
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
return /* @__PURE__ */ jsxs6(Frame, {
|
|
561
|
+
title,
|
|
562
|
+
width,
|
|
563
|
+
children: [
|
|
564
|
+
/* @__PURE__ */ jsxs6(Box4, {
|
|
565
|
+
flexDirection: "column",
|
|
566
|
+
children: [
|
|
567
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
568
|
+
color: tone.notice,
|
|
569
|
+
children: clip(headline, interior)
|
|
570
|
+
}),
|
|
571
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
572
|
+
children: " "
|
|
573
|
+
}),
|
|
574
|
+
lines.map((line) => /* @__PURE__ */ jsxs6(Text5, {
|
|
575
|
+
children: [
|
|
576
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
577
|
+
color: line.mark === "fail" ? tone.danger : undefined,
|
|
578
|
+
children: `${mark[line.mark] || " "} `
|
|
579
|
+
}),
|
|
580
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
581
|
+
children: clip(line.text, interior - 2)
|
|
582
|
+
}),
|
|
583
|
+
line.note ? /* @__PURE__ */ jsx6(Text5, {
|
|
584
|
+
dimColor: true,
|
|
585
|
+
children: clip(` ${line.note}`, Math.max(interior - 2 - line.text.length, 0))
|
|
586
|
+
}) : null
|
|
587
|
+
]
|
|
588
|
+
}, `${line.mark}:${line.text}`)),
|
|
589
|
+
notes.length > 0 ? /* @__PURE__ */ jsx6(Text5, {
|
|
590
|
+
children: " "
|
|
591
|
+
}) : null,
|
|
592
|
+
notes.map((note) => /* @__PURE__ */ jsx6(Text5, {
|
|
593
|
+
dimColor: true,
|
|
594
|
+
children: clip(note, interior)
|
|
595
|
+
}, note)),
|
|
596
|
+
/* @__PURE__ */ jsx6(Text5, {
|
|
597
|
+
children: " "
|
|
598
|
+
}),
|
|
599
|
+
row(yes, choice === "yes", argv),
|
|
600
|
+
row(no, choice === "no")
|
|
601
|
+
]
|
|
602
|
+
}),
|
|
603
|
+
/* @__PURE__ */ jsx6(Divider, {
|
|
604
|
+
width
|
|
605
|
+
}),
|
|
606
|
+
/* @__PURE__ */ jsx6(Hints, {
|
|
607
|
+
hints: [
|
|
608
|
+
["^v", "choose"],
|
|
609
|
+
["enter", choice === "yes" ? yes : no],
|
|
610
|
+
["esc", no]
|
|
611
|
+
]
|
|
612
|
+
})
|
|
613
|
+
]
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
// src/ui/spinner.tsx
|
|
617
|
+
import { Text as Text6 } from "ink";
|
|
618
|
+
import { useEffect, useState as useState2 } from "react";
|
|
619
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
620
|
+
var FRAMES = ["-", "\\", "|", "/"];
|
|
621
|
+
var INTERVAL_MS = 90;
|
|
622
|
+
function Spinner() {
|
|
623
|
+
const [frame, setFrame] = useState2(0);
|
|
624
|
+
useEffect(() => {
|
|
625
|
+
const timer = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), INTERVAL_MS);
|
|
626
|
+
return () => clearInterval(timer);
|
|
627
|
+
}, []);
|
|
628
|
+
return /* @__PURE__ */ jsx7(Text6, {
|
|
629
|
+
color: tone.primary,
|
|
630
|
+
children: FRAMES[frame]
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
// src/auth/session.ts
|
|
634
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
635
|
+
|
|
636
|
+
// src/settings/agents.ts
|
|
637
|
+
import { readFileSync } from "node:fs";
|
|
638
|
+
import { homedir as homedir2 } from "node:os";
|
|
639
|
+
import { join as join2 } from "node:path";
|
|
640
|
+
|
|
641
|
+
// src/settings/answer.ts
|
|
642
|
+
import { dirname } from "node:path";
|
|
643
|
+
|
|
644
|
+
// src/tracking/answer.ts
|
|
645
|
+
function trackingFrom(layer, configPath) {
|
|
646
|
+
switch (layer) {
|
|
647
|
+
case "env":
|
|
648
|
+
return "YOURSKILLS_NO_TRACK in the environment";
|
|
649
|
+
case "config":
|
|
650
|
+
return `track in ${configPath}`;
|
|
651
|
+
case "organization":
|
|
652
|
+
return "usage tracking in your organization's settings";
|
|
653
|
+
case "default":
|
|
654
|
+
return "built-in default: turn off with `config set track false`";
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function trackingDisabledByEnv() {
|
|
658
|
+
const value = process.env.YOURSKILLS_NO_TRACK;
|
|
659
|
+
return value !== undefined && value !== "" && value !== "0";
|
|
660
|
+
}
|
|
661
|
+
function trackingOn(config) {
|
|
662
|
+
return !trackingDisabledByEnv() && config.track !== false;
|
|
663
|
+
}
|
|
664
|
+
var ON_NOTE = "on; turn off with `yourskills config set track false`";
|
|
665
|
+
function trackingAnswer(question) {
|
|
666
|
+
const { track, configPath, organization } = question;
|
|
667
|
+
if (trackingDisabledByEnv()) {
|
|
668
|
+
return {
|
|
669
|
+
state: "off-here",
|
|
670
|
+
sending: false,
|
|
671
|
+
note: "off (YOURSKILLS_NO_TRACK is set)",
|
|
672
|
+
value: "off",
|
|
673
|
+
layer: "env",
|
|
674
|
+
from: trackingFrom("env", configPath)
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
if (track === false) {
|
|
678
|
+
return {
|
|
679
|
+
state: "off-here",
|
|
680
|
+
sending: false,
|
|
681
|
+
note: "off; turn on with `yourskills config set track true`",
|
|
682
|
+
value: "off",
|
|
683
|
+
layer: "config",
|
|
684
|
+
from: trackingFrom("config", configPath)
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (organization?.enabled === false) {
|
|
688
|
+
return {
|
|
689
|
+
state: "off-for-organization",
|
|
690
|
+
sending: true,
|
|
691
|
+
note: "off; your organization turned usage tracking off for everyone",
|
|
184
692
|
value: "off",
|
|
185
|
-
|
|
693
|
+
layer: "organization",
|
|
694
|
+
from: trackingFrom("organization", configPath)
|
|
186
695
|
};
|
|
187
696
|
}
|
|
697
|
+
const layer = track === true ? "config" : "default";
|
|
188
698
|
return {
|
|
189
699
|
state: "on",
|
|
190
700
|
sending: true,
|
|
191
701
|
note: ON_NOTE,
|
|
192
702
|
value: "on",
|
|
193
|
-
|
|
703
|
+
layer,
|
|
704
|
+
from: trackingFrom(layer, configPath)
|
|
194
705
|
};
|
|
195
706
|
}
|
|
196
707
|
async function trackingNow(question) {
|
|
@@ -249,20 +760,6 @@ function near(a, b) {
|
|
|
249
760
|
return true;
|
|
250
761
|
}
|
|
251
762
|
var UNSET = "(unset)";
|
|
252
|
-
function configValue(config, key) {
|
|
253
|
-
switch (key) {
|
|
254
|
-
case "scope":
|
|
255
|
-
return config.defaultScope ?? UNSET;
|
|
256
|
-
case "agents":
|
|
257
|
-
return config.agents?.length ? config.agents.join(",") : UNSET;
|
|
258
|
-
case "server":
|
|
259
|
-
return config.serverUrl ?? UNSET;
|
|
260
|
-
case "track":
|
|
261
|
-
return String(trackingOn(config));
|
|
262
|
-
case "autoUpdate":
|
|
263
|
-
return String(config.autoUpdate !== false);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
763
|
function setConfigValue(config, key, value) {
|
|
267
764
|
if (!isConfigKey(key)) {
|
|
268
765
|
return {
|
|
@@ -314,875 +811,686 @@ function setConfigValue(config, key, value) {
|
|
|
314
811
|
const serverUrl = url.toString().replace(/\/$/, "");
|
|
315
812
|
return { ok: true, config: { ...config, serverUrl }, shown: serverUrl };
|
|
316
813
|
}
|
|
317
|
-
case "track": {
|
|
318
|
-
if (value !== "true" && value !== "false") {
|
|
319
|
-
return { ok: false, message: "track must be `true` or `false`." };
|
|
320
|
-
}
|
|
321
|
-
const track = value === "true";
|
|
322
|
-
return { ok: true, config: { ...config, track }, shown: String(track) };
|
|
323
|
-
}
|
|
324
|
-
case "autoUpdate": {
|
|
325
|
-
if (value !== "true" && value !== "false") {
|
|
326
|
-
return { ok: false, message: "autoUpdate must be `true` or `false`." };
|
|
327
|
-
}
|
|
328
|
-
const autoUpdate = value === "true";
|
|
329
|
-
return {
|
|
330
|
-
ok: true,
|
|
331
|
-
config: { ...config, autoUpdate },
|
|
332
|
-
shown: String(autoUpdate)
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function configLines(config) {
|
|
338
|
-
const width = Math.max(...CONFIG_KEYS.map((k) => k.length), "user".length);
|
|
339
|
-
const lines = CONFIG_KEYS.map((key) => ({
|
|
340
|
-
mark: "none",
|
|
341
|
-
text: `${key.padEnd(width)} ${configValue(config, key)}`
|
|
342
|
-
}));
|
|
343
|
-
lines.push({
|
|
344
|
-
mark: "none",
|
|
345
|
-
text: `${"user".padEnd(width)} ${config.userEmail ?? UNSET}`
|
|
346
|
-
});
|
|
347
|
-
lines.push({
|
|
348
|
-
mark: "none",
|
|
349
|
-
text: `${"token".padEnd(width)} ${config.token ? REDACTED : UNSET}`
|
|
350
|
-
});
|
|
351
|
-
return lines;
|
|
352
|
-
}
|
|
353
|
-
function configGetReport(config, key, path) {
|
|
354
|
-
if (key === undefined) {
|
|
355
|
-
return {
|
|
356
|
-
title: "config",
|
|
357
|
-
lines: configLines(config),
|
|
358
|
-
json: {
|
|
359
|
-
path,
|
|
360
|
-
...Object.fromEntries(CONFIG_KEYS.map((k) => [k, configValue(config, k)])),
|
|
361
|
-
user: config.userEmail ?? null,
|
|
362
|
-
token: config.token ? REDACTED : null
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
if (!isConfigKey(key)) {
|
|
367
|
-
return {
|
|
368
|
-
title: "config",
|
|
369
|
-
lines: [
|
|
370
|
-
{
|
|
371
|
-
mark: "fail",
|
|
372
|
-
text: `Unknown config key: ${key}`,
|
|
373
|
-
note: `Known keys: ${CONFIG_KEYS.join(", ")}`
|
|
374
|
-
}
|
|
375
|
-
],
|
|
376
|
-
json: { path, error: `unknown key: ${key}`, keys: CONFIG_KEYS },
|
|
377
|
-
failed: true
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
const value = configValue(config, key);
|
|
381
|
-
return {
|
|
382
|
-
title: "config",
|
|
383
|
-
lines: [{ mark: "none", text: value }],
|
|
384
|
-
json: { path, key, value }
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// src/settings/command.ts
|
|
389
|
-
async function configGet(key) {
|
|
390
|
-
return configGetReport(await readConfig(), key, configPath());
|
|
391
|
-
}
|
|
392
|
-
async function configSet(key, value, known) {
|
|
393
|
-
const path = configPath();
|
|
394
|
-
const config = await readConfig();
|
|
395
|
-
const result = setConfigValue(config, key, value);
|
|
396
|
-
if (!result.ok) {
|
|
397
|
-
return {
|
|
398
|
-
title: "config",
|
|
399
|
-
lines: [{ mark: "fail", text: result.message }],
|
|
400
|
-
json: { path, error: result.message },
|
|
401
|
-
failed: true
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
if (key === "agents") {
|
|
405
|
-
const valid = await known();
|
|
406
|
-
const refusal = valid && refuse(result.config.agents ?? [], valid, path);
|
|
407
|
-
if (refusal)
|
|
408
|
-
return refusal;
|
|
409
|
-
}
|
|
410
|
-
await writeConfig(result.config);
|
|
411
|
-
return {
|
|
412
|
-
title: "config",
|
|
413
|
-
lines: [{ mark: "ok", text: `${key} = ${result.shown}`, note: path }],
|
|
414
|
-
json: { path, key, value: result.shown }
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function refuse(asked, valid, path) {
|
|
418
|
-
const { unknown, suggest } = unknownAgents(asked, valid);
|
|
419
|
-
if (unknown.length === 0)
|
|
420
|
-
return;
|
|
421
|
-
const named = unknown.join(", ");
|
|
422
|
-
return {
|
|
423
|
-
title: "config",
|
|
424
|
-
lines: [
|
|
425
|
-
{
|
|
426
|
-
mark: "fail",
|
|
427
|
-
text: `No agent is called ${named}.`,
|
|
428
|
-
note: suggest.length > 0 ? `did you mean ${suggest.join(", ")}?` : ""
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
mark: "none",
|
|
432
|
-
text: "The stock `skills` CLI keeps the list of agent names, and it does not have this one, so every install would stop at it."
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
mark: "none",
|
|
436
|
-
text: "Run `yourskills doctor` to see the names this machine accepts."
|
|
437
|
-
}
|
|
438
|
-
],
|
|
439
|
-
json: { path, error: `unknown agents: ${named}`, suggest },
|
|
440
|
-
failed: true
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
// src/settings/scope.ts
|
|
444
|
-
import { existsSync } from "node:fs";
|
|
445
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
446
|
-
import { dirname, join as join3, resolve } from "node:path";
|
|
447
|
-
var PROJECT_MANIFEST = "yourskills.json";
|
|
448
|
-
function findProjectManifest(from) {
|
|
449
|
-
let dir = resolve(from ?? process.cwd());
|
|
450
|
-
for (;; ) {
|
|
451
|
-
const candidate = join3(dir, PROJECT_MANIFEST);
|
|
452
|
-
if (existsSync(candidate))
|
|
453
|
-
return candidate;
|
|
454
|
-
const parent = dirname(dir);
|
|
455
|
-
if (parent === dir)
|
|
456
|
-
return null;
|
|
457
|
-
dir = parent;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
async function scopeInManifest(path) {
|
|
461
|
-
try {
|
|
462
|
-
const parsed = JSON.parse(await readFile2(path, "utf8"));
|
|
463
|
-
return parsed.scope === "global" || parsed.scope === "project" ? parsed.scope : null;
|
|
464
|
-
} catch {
|
|
465
|
-
return null;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
async function resolveScope(opts = {}) {
|
|
469
|
-
const manifestPath = findProjectManifest(opts.cwd);
|
|
470
|
-
if (opts.flag)
|
|
471
|
-
return { scope: opts.flag, source: "flag", manifestPath };
|
|
472
|
-
if (manifestPath) {
|
|
473
|
-
const declared = await scopeInManifest(manifestPath);
|
|
474
|
-
if (declared)
|
|
475
|
-
return { scope: declared, source: "project", manifestPath };
|
|
476
|
-
}
|
|
477
|
-
const config = await readConfig();
|
|
478
|
-
if (config.defaultScope)
|
|
479
|
-
return { scope: config.defaultScope, source: "config", manifestPath };
|
|
480
|
-
return { scope: "global", source: "default", manifestPath };
|
|
481
|
-
}
|
|
482
|
-
// src/auth/auth.ts
|
|
483
|
-
var CLIENT_ID = "yourskills-cli";
|
|
484
|
-
var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
485
|
-
var SLOW_DOWN_STEP_MS = 5000;
|
|
486
|
-
var authBase = (base) => `${base.replace(/\/$/, "")}/api/auth`;
|
|
487
|
-
async function post(url, body) {
|
|
488
|
-
const res = await fetch(url, {
|
|
489
|
-
method: "POST",
|
|
490
|
-
headers: { "content-type": "application/json" },
|
|
491
|
-
body: JSON.stringify(body)
|
|
492
|
-
});
|
|
493
|
-
const text = await res.text();
|
|
494
|
-
let parsed;
|
|
495
|
-
try {
|
|
496
|
-
parsed = JSON.parse(text);
|
|
497
|
-
} catch {
|
|
498
|
-
return {
|
|
499
|
-
error: "invalid_response",
|
|
500
|
-
error_description: text.slice(0, 200) || res.statusText
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
if (res.ok)
|
|
504
|
-
return { ok: true, data: parsed };
|
|
505
|
-
const err = parsed;
|
|
506
|
-
return {
|
|
507
|
-
error: err.error ?? `http_${res.status}`,
|
|
508
|
-
error_description: err.error_description
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
async function* login() {
|
|
512
|
-
const config = await readConfig();
|
|
513
|
-
const base = serverUrl(config);
|
|
514
|
-
const start = await post(`${authBase(base)}/device/code`, {
|
|
515
|
-
client_id: CLIENT_ID
|
|
516
|
-
});
|
|
517
|
-
if (!("ok" in start)) {
|
|
518
|
-
throw new Error(`Could not start device login: ${start.error_description ?? start.error}`);
|
|
519
|
-
}
|
|
520
|
-
const device = start.data;
|
|
521
|
-
const deadline = Date.now() + device.expires_in * 1000;
|
|
522
|
-
yield {
|
|
523
|
-
kind: "prompt",
|
|
524
|
-
userCode: device.user_code,
|
|
525
|
-
verificationUri: device.verification_uri_complete ?? device.verification_uri,
|
|
526
|
-
expiresAt: deadline
|
|
527
|
-
};
|
|
528
|
-
let intervalMs = Math.max(device.interval, 1) * 1000;
|
|
529
|
-
while (Date.now() < deadline) {
|
|
530
|
-
await sleep(intervalMs);
|
|
531
|
-
const poll = await post(`${authBase(base)}/device/token`, {
|
|
532
|
-
grant_type: GRANT_TYPE,
|
|
533
|
-
device_code: device.device_code,
|
|
534
|
-
client_id: CLIENT_ID
|
|
535
|
-
});
|
|
536
|
-
if ("ok" in poll) {
|
|
537
|
-
const token = poll.data.access_token;
|
|
538
|
-
const who = await fetchSession(base, token);
|
|
539
|
-
await writeConfig({
|
|
540
|
-
...config,
|
|
541
|
-
token,
|
|
542
|
-
serverUrl: base,
|
|
543
|
-
userEmail: who?.user?.email
|
|
544
|
-
});
|
|
545
|
-
yield { kind: "done", email: who?.user?.email };
|
|
546
|
-
return;
|
|
814
|
+
case "track": {
|
|
815
|
+
if (value !== "true" && value !== "false") {
|
|
816
|
+
return { ok: false, message: "track must be `true` or `false`." };
|
|
817
|
+
}
|
|
818
|
+
const track = value === "true";
|
|
819
|
+
return { ok: true, config: { ...config, track }, shown: String(track) };
|
|
547
820
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
case "expired_token":
|
|
559
|
-
throw new Error("The device code expired. Run `yourskills login` again.");
|
|
560
|
-
default:
|
|
561
|
-
throw new Error(`Login failed: ${poll.error_description ?? poll.error}`);
|
|
821
|
+
case "autoUpdate": {
|
|
822
|
+
if (value !== "true" && value !== "false") {
|
|
823
|
+
return { ok: false, message: "autoUpdate must be `true` or `false`." };
|
|
824
|
+
}
|
|
825
|
+
const autoUpdate = value === "true";
|
|
826
|
+
return {
|
|
827
|
+
ok: true,
|
|
828
|
+
config: { ...config, autoUpdate },
|
|
829
|
+
shown: String(autoUpdate)
|
|
830
|
+
};
|
|
562
831
|
}
|
|
563
832
|
}
|
|
564
|
-
throw new Error("The device code expired. Run `yourskills login` again.");
|
|
565
833
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
clearConfig();
|
|
834
|
+
|
|
835
|
+
// src/settings/store.ts
|
|
836
|
+
import { chmodSync, mkdirSync, rmSync } from "node:fs";
|
|
837
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
838
|
+
import { homedir } from "node:os";
|
|
839
|
+
import { join } from "node:path";
|
|
840
|
+
function configDir() {
|
|
841
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
842
|
+
return join(xdg && xdg.length > 0 ? xdg : join(homedir(), ".config"), "yourskills");
|
|
577
843
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
headers: { authorization: `Bearer ${token}` }
|
|
581
|
-
});
|
|
582
|
-
if (!res.ok)
|
|
583
|
-
return null;
|
|
584
|
-
return await res.json();
|
|
844
|
+
function configPath() {
|
|
845
|
+
return join(configDir(), "config.json");
|
|
585
846
|
}
|
|
586
|
-
async function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (!session?.user)
|
|
593
|
-
throw new Error("Session is no longer valid. Run `yourskills login`.");
|
|
594
|
-
return {
|
|
595
|
-
user: session.user.email ?? session.user.id,
|
|
596
|
-
org: session.session?.activeOrganizationId ?? null,
|
|
597
|
-
server: base
|
|
598
|
-
};
|
|
847
|
+
async function readConfig() {
|
|
848
|
+
try {
|
|
849
|
+
return JSON.parse(await readFile(configPath(), "utf8"));
|
|
850
|
+
} catch {
|
|
851
|
+
return {};
|
|
852
|
+
}
|
|
599
853
|
}
|
|
600
|
-
async function
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
854
|
+
async function updateConfig(change) {
|
|
855
|
+
let current = {};
|
|
856
|
+
try {
|
|
857
|
+
current = JSON.parse(await readFile(configPath(), "utf8"));
|
|
858
|
+
} catch (error) {
|
|
859
|
+
if (error?.code !== "ENOENT")
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
const next = change(current);
|
|
863
|
+
await writeConfig(next);
|
|
864
|
+
return next;
|
|
605
865
|
}
|
|
606
|
-
async function
|
|
607
|
-
|
|
866
|
+
async function writeConfig(config) {
|
|
867
|
+
mkdirSync(configDir(), { recursive: true, mode: 448 });
|
|
868
|
+
await writeFile(configPath(), `${JSON.stringify(config, null, 2)}
|
|
869
|
+
`);
|
|
870
|
+
chmodSync(configPath(), 384);
|
|
608
871
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return {
|
|
612
|
-
signedIn: Boolean(config.token),
|
|
613
|
-
email: config.userEmail ?? null,
|
|
614
|
-
organization: config.orgName ?? null,
|
|
615
|
-
server: serverUrl(config, server)
|
|
616
|
-
};
|
|
872
|
+
function clearConfig() {
|
|
873
|
+
rmSync(configPath(), { force: true });
|
|
617
874
|
}
|
|
618
|
-
// src/auth/screens/gate.tsx
|
|
619
|
-
import { Box as Box6, Text as Text8, useApp as useApp2, useInput as useInput3 } from "ink";
|
|
620
|
-
import {
|
|
621
|
-
useCallback,
|
|
622
|
-
useEffect as useEffect3,
|
|
623
|
-
useMemo,
|
|
624
|
-
useState as useState4
|
|
625
|
-
} from "react";
|
|
626
|
-
|
|
627
|
-
// src/ui/back.tsx
|
|
628
|
-
import { useInput } from "ink";
|
|
629
875
|
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
return "";
|
|
876
|
+
// src/settings/answer.ts
|
|
877
|
+
function decide(consulted, last) {
|
|
878
|
+
const layers = [...consulted, last];
|
|
879
|
+
const spoke = consulted.find((layer) => layer.value !== null);
|
|
880
|
+
if (spoke && spoke.value !== null) {
|
|
881
|
+
return {
|
|
882
|
+
value: spoke.value,
|
|
883
|
+
layer: spoke.layer,
|
|
884
|
+
from: spoke.from,
|
|
885
|
+
layers,
|
|
886
|
+
decided: true
|
|
887
|
+
};
|
|
643
888
|
}
|
|
644
|
-
|
|
645
|
-
|
|
889
|
+
return {
|
|
890
|
+
value: last.value,
|
|
891
|
+
layer: last.layer,
|
|
892
|
+
from: last.from,
|
|
893
|
+
layers,
|
|
894
|
+
decided: false
|
|
895
|
+
};
|
|
646
896
|
}
|
|
647
|
-
function
|
|
648
|
-
|
|
897
|
+
function scopeAnswer(held) {
|
|
898
|
+
const { project } = held;
|
|
899
|
+
return decide([
|
|
900
|
+
{
|
|
901
|
+
layer: "flag",
|
|
902
|
+
value: held.flags?.scope ?? null,
|
|
903
|
+
from: "-g or -p on the command line"
|
|
904
|
+
},
|
|
905
|
+
project ? {
|
|
906
|
+
layer: "project",
|
|
907
|
+
value: project.scope,
|
|
908
|
+
from: `yourskills.json at ${dirname(project.path)}${project.scope ? "" : " (declares no scope)"}`
|
|
909
|
+
} : {
|
|
910
|
+
layer: "project",
|
|
911
|
+
value: null,
|
|
912
|
+
from: "no yourskills.json above here"
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
layer: "config",
|
|
916
|
+
value: held.config.defaultScope ?? null,
|
|
917
|
+
from: `defaultScope in ${held.configPath}`
|
|
918
|
+
}
|
|
919
|
+
], { layer: "default", value: "global", from: "built-in default" });
|
|
649
920
|
}
|
|
650
|
-
function
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
921
|
+
function agentsAnswer(held) {
|
|
922
|
+
const some = (agents) => agents && agents.length > 0 ? agents : null;
|
|
923
|
+
return decide([
|
|
924
|
+
{
|
|
925
|
+
layer: "flag",
|
|
926
|
+
value: some(held.flags?.agents),
|
|
927
|
+
from: "-a on the command line"
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
layer: "config",
|
|
931
|
+
value: some(held.config.agents),
|
|
932
|
+
from: `agents in ${held.configPath}`
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
layer: "stock",
|
|
936
|
+
value: some(held.stock?.agents),
|
|
937
|
+
from: held.stock ? `lastSelectedAgents in ${held.stock.path}` : "what the stock `skills` CLI remembers (not read)"
|
|
938
|
+
}
|
|
939
|
+
], {
|
|
940
|
+
layer: "default",
|
|
941
|
+
value: [],
|
|
942
|
+
from: "nothing pinned; the stock `skills` CLI looks at what is installed"
|
|
943
|
+
});
|
|
659
944
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
none: "",
|
|
680
|
-
cursor: ">",
|
|
681
|
-
selected: "*",
|
|
682
|
-
unselected: " "
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
// src/ui/frame.tsx
|
|
686
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
687
|
-
var MAX_WIDTH = 96;
|
|
688
|
-
var MIN_WIDTH = 44;
|
|
689
|
-
var FRAME_CHROME = 4;
|
|
690
|
-
function useFrameWidth() {
|
|
691
|
-
const { columns } = useWindowSize();
|
|
692
|
-
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, columns - 2));
|
|
945
|
+
var DEFAULT_URL = "https://api.yourskills.store";
|
|
946
|
+
function serverAnswer(held) {
|
|
947
|
+
return decide([
|
|
948
|
+
{
|
|
949
|
+
layer: "flag",
|
|
950
|
+
value: held.flags?.server || null,
|
|
951
|
+
from: "--server on the command line"
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
layer: "env",
|
|
955
|
+
value: process.env.YOURSKILLS_URL || null,
|
|
956
|
+
from: "YOURSKILLS_URL in the environment"
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
layer: "config",
|
|
960
|
+
value: held.config.serverUrl || null,
|
|
961
|
+
from: `server in ${held.configPath}`
|
|
962
|
+
}
|
|
963
|
+
], { layer: "default", value: DEFAULT_URL, from: "built-in default" });
|
|
693
964
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
bottomRight: j,
|
|
701
|
-
bottom: DEFAULT_ASCII_CHARS.bottom,
|
|
702
|
-
bottomLeft: j,
|
|
703
|
-
left: DEFAULT_ASCII_CHARS.left
|
|
704
|
-
};
|
|
705
|
-
function Frame({
|
|
706
|
-
title,
|
|
707
|
-
width,
|
|
708
|
-
children
|
|
709
|
-
}) {
|
|
710
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
711
|
-
flexDirection: "column",
|
|
712
|
-
width,
|
|
713
|
-
children: [
|
|
714
|
-
/* @__PURE__ */ jsx(Text, {
|
|
715
|
-
color: tone.primary,
|
|
716
|
-
children: topBorder(width, title)
|
|
717
|
-
}),
|
|
718
|
-
/* @__PURE__ */ jsx(Box, {
|
|
719
|
-
flexDirection: "column",
|
|
720
|
-
width,
|
|
721
|
-
paddingLeft: 1,
|
|
722
|
-
paddingRight: 1,
|
|
723
|
-
borderStyle: BORDER,
|
|
724
|
-
borderColor: tone.primary,
|
|
725
|
-
borderTop: false,
|
|
726
|
-
children
|
|
727
|
-
})
|
|
728
|
-
]
|
|
729
|
-
});
|
|
965
|
+
function serverUrl(config = {}, override) {
|
|
966
|
+
return serverAnswer({
|
|
967
|
+
config,
|
|
968
|
+
configPath: configPath(),
|
|
969
|
+
flags: { server: override }
|
|
970
|
+
}).value;
|
|
730
971
|
}
|
|
731
|
-
function
|
|
732
|
-
return
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
color: tone.primary,
|
|
737
|
-
children: fill(width - 2, DEFAULT_ASCII_CHARS.divider)
|
|
738
|
-
})
|
|
972
|
+
function trackingOf(held) {
|
|
973
|
+
return trackingAnswer({
|
|
974
|
+
track: held.config.track,
|
|
975
|
+
configPath: held.configPath,
|
|
976
|
+
organization: held.organization
|
|
739
977
|
});
|
|
740
978
|
}
|
|
741
|
-
function
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
979
|
+
function trackAnswer(held) {
|
|
980
|
+
const tracking = trackingOf(held);
|
|
981
|
+
const { configPath, organization } = held;
|
|
982
|
+
const layers = [
|
|
983
|
+
{
|
|
984
|
+
layer: "env",
|
|
985
|
+
value: trackingDisabledByEnv() ? false : null,
|
|
986
|
+
from: trackingFrom("env", configPath)
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
layer: "config",
|
|
990
|
+
value: held.config.track ?? null,
|
|
991
|
+
from: trackingFrom("config", configPath)
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
layer: "organization",
|
|
995
|
+
value: organization ? organization.enabled : null,
|
|
996
|
+
from: trackingFrom("organization", configPath)
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
layer: "default",
|
|
1000
|
+
value: true,
|
|
1001
|
+
from: trackingFrom("default", configPath)
|
|
1002
|
+
}
|
|
1003
|
+
];
|
|
1004
|
+
return {
|
|
1005
|
+
value: tracking.value === "on",
|
|
1006
|
+
layer: tracking.layer,
|
|
1007
|
+
from: tracking.from,
|
|
1008
|
+
layers,
|
|
1009
|
+
decided: tracking.layer !== "default"
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function autoUpdateAnswer(held) {
|
|
1013
|
+
return decide([
|
|
1014
|
+
{
|
|
1015
|
+
layer: "config",
|
|
1016
|
+
value: held.config.autoUpdate ?? null,
|
|
1017
|
+
from: `autoUpdate in ${held.configPath}`
|
|
1018
|
+
}
|
|
1019
|
+
], { layer: "default", value: true, from: "built-in default" });
|
|
1020
|
+
}
|
|
1021
|
+
function spell(key, answer, as) {
|
|
1022
|
+
return {
|
|
1023
|
+
key,
|
|
1024
|
+
json: answer.value,
|
|
1025
|
+
value: as(answer.value),
|
|
1026
|
+
layer: answer.layer,
|
|
1027
|
+
from: answer.from,
|
|
1028
|
+
decided: answer.decided,
|
|
1029
|
+
layers: answer.layers.map((layer) => ({
|
|
1030
|
+
layer: layer.layer,
|
|
1031
|
+
from: layer.from,
|
|
1032
|
+
value: layer.value === null ? null : as(layer.value)
|
|
1033
|
+
}))
|
|
1034
|
+
};
|
|
745
1035
|
}
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1036
|
+
function shown(key, held) {
|
|
1037
|
+
switch (key) {
|
|
1038
|
+
case "scope":
|
|
1039
|
+
return spell(key, scopeAnswer(held), (scope) => scope);
|
|
1040
|
+
case "agents":
|
|
1041
|
+
return spell(key, agentsAnswer(held), (agents) => agents.length > 0 ? agents.join(",") : UNSET);
|
|
1042
|
+
case "server":
|
|
1043
|
+
return spell(key, serverAnswer(held), (server) => server);
|
|
1044
|
+
case "track":
|
|
1045
|
+
return spell(key, trackAnswer(held), String);
|
|
1046
|
+
case "autoUpdate":
|
|
1047
|
+
return spell(key, autoUpdateAnswer(held), String);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function settingsShown(held) {
|
|
1051
|
+
return CONFIG_KEYS.map((key) => shown(key, held));
|
|
752
1052
|
}
|
|
753
1053
|
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
];
|
|
770
|
-
var LIFECYCLE_ORDER = LIFECYCLES.map((lifecycle) => lifecycle.status);
|
|
771
|
-
var BY_STATUS = new Map(LIFECYCLES.map((lifecycle) => [lifecycle.status, lifecycle]));
|
|
772
|
-
function lifecycleOf(status) {
|
|
773
|
-
const lifecycle = BY_STATUS.get(status);
|
|
774
|
-
if (!lifecycle) {
|
|
775
|
-
throw new Error(`No lifecycle vocabulary for "${status}". Add it to LIFECYCLES.`);
|
|
1054
|
+
// src/settings/agents.ts
|
|
1055
|
+
function stockLockPath() {
|
|
1056
|
+
const state = process.env.XDG_STATE_HOME;
|
|
1057
|
+
if (state && state.length > 0)
|
|
1058
|
+
return join2(state, "skills", ".skill-lock.json");
|
|
1059
|
+
const home = process.env.HOME;
|
|
1060
|
+
return join2(home && home.length > 0 ? home : homedir2(), ".agents", ".skill-lock.json");
|
|
1061
|
+
}
|
|
1062
|
+
var LOCK_VERSION = 3;
|
|
1063
|
+
function rememberedAgents() {
|
|
1064
|
+
let lock;
|
|
1065
|
+
try {
|
|
1066
|
+
lock = JSON.parse(readFileSync(stockLockPath(), "utf8"));
|
|
1067
|
+
} catch {
|
|
1068
|
+
return [];
|
|
776
1069
|
}
|
|
777
|
-
|
|
1070
|
+
if (!lock || typeof lock !== "object")
|
|
1071
|
+
return [];
|
|
1072
|
+
const raw = lock;
|
|
1073
|
+
if (typeof raw.version !== "number" || raw.version < LOCK_VERSION)
|
|
1074
|
+
return [];
|
|
1075
|
+
if (!raw.skills)
|
|
1076
|
+
return [];
|
|
1077
|
+
return Array.isArray(raw.lastSelectedAgents) ? raw.lastSelectedAgents.filter((a) => typeof a === "string") : [];
|
|
778
1078
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1079
|
+
async function resolveAgents(flagAgents) {
|
|
1080
|
+
const config = await readConfig();
|
|
1081
|
+
return agentsAnswer({
|
|
1082
|
+
config,
|
|
1083
|
+
configPath: configPath(),
|
|
1084
|
+
flags: { agents: flagAgents },
|
|
1085
|
+
stock: { path: stockLockPath(), agents: rememberedAgents() }
|
|
1086
|
+
}).value;
|
|
786
1087
|
}
|
|
787
|
-
|
|
788
|
-
|
|
1088
|
+
// src/settings/scope.ts
|
|
1089
|
+
import { existsSync } from "node:fs";
|
|
1090
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
1091
|
+
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
1092
|
+
var PROJECT_MANIFEST = "yourskills.json";
|
|
1093
|
+
function findProjectManifest(from) {
|
|
1094
|
+
let dir = resolve(from ?? process.cwd());
|
|
1095
|
+
for (;; ) {
|
|
1096
|
+
const candidate = join3(dir, PROJECT_MANIFEST);
|
|
1097
|
+
if (existsSync(candidate))
|
|
1098
|
+
return candidate;
|
|
1099
|
+
const parent = dirname2(dir);
|
|
1100
|
+
if (parent === dir)
|
|
1101
|
+
return null;
|
|
1102
|
+
dir = parent;
|
|
1103
|
+
}
|
|
789
1104
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1105
|
+
async function scopeInManifest(path) {
|
|
1106
|
+
try {
|
|
1107
|
+
const parsed = JSON.parse(await readFile2(path, "utf8"));
|
|
1108
|
+
return parsed.scope === "global" || parsed.scope === "project" ? parsed.scope : null;
|
|
1109
|
+
} catch {
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
794
1112
|
}
|
|
795
|
-
function
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
names,
|
|
799
|
-
cursor,
|
|
800
|
-
selected,
|
|
801
|
-
selectable = false
|
|
802
|
-
}) {
|
|
803
|
-
const tag = lifecycleTag(row.status);
|
|
804
|
-
const meta = row.count !== undefined ? `${row.count} skills` : tag;
|
|
805
|
-
const gutter = selectable ? GUTTER : 0;
|
|
806
|
-
const rest = width - gutter - names - GAP - (meta ? meta.length + GAP : 0);
|
|
807
|
-
return /* @__PURE__ */ jsxs2(Box2, {
|
|
808
|
-
children: [
|
|
809
|
-
selectable ? /* @__PURE__ */ jsx2(Text2, {
|
|
810
|
-
color: cursor ? "cyan" : undefined,
|
|
811
|
-
children: `${cursor ? mark.cursor : " "} ${selected ? mark.selected : mark.unselected} `
|
|
812
|
-
}) : null,
|
|
813
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
814
|
-
bold: cursor,
|
|
815
|
-
color: lifecycleColor(row.status),
|
|
816
|
-
children: clip(row.name, names).padEnd(names)
|
|
817
|
-
}),
|
|
818
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
819
|
-
children: " ".repeat(GAP)
|
|
820
|
-
}),
|
|
821
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
822
|
-
dimColor: true,
|
|
823
|
-
children: clip(row.description ?? "", Math.max(rest, 0))
|
|
824
|
-
}),
|
|
825
|
-
meta ? /* @__PURE__ */ jsxs2(Fragment, {
|
|
826
|
-
children: [
|
|
827
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
828
|
-
children: " ".repeat(GAP)
|
|
829
|
-
}),
|
|
830
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
831
|
-
color: lifecycleColor(row.status),
|
|
832
|
-
dimColor: !tag,
|
|
833
|
-
children: meta
|
|
834
|
-
})
|
|
835
|
-
]
|
|
836
|
-
}) : null
|
|
837
|
-
]
|
|
838
|
-
});
|
|
1113
|
+
async function nearestProject(cwd) {
|
|
1114
|
+
const path = findProjectManifest(cwd);
|
|
1115
|
+
return path ? { path, scope: await scopeInManifest(path) } : null;
|
|
839
1116
|
}
|
|
840
|
-
function
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
children: key
|
|
851
|
-
}),
|
|
852
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
853
|
-
dimColor: true,
|
|
854
|
-
children: ` ${label}`
|
|
855
|
-
})
|
|
856
|
-
]
|
|
857
|
-
}, key))
|
|
1117
|
+
async function resolveScope(opts = {}) {
|
|
1118
|
+
const [config, project] = await Promise.all([
|
|
1119
|
+
readConfig(),
|
|
1120
|
+
nearestProject(opts.cwd)
|
|
1121
|
+
]);
|
|
1122
|
+
const answer = scopeAnswer({
|
|
1123
|
+
config,
|
|
1124
|
+
configPath: configPath(),
|
|
1125
|
+
flags: { scope: opts.flag },
|
|
1126
|
+
project
|
|
858
1127
|
});
|
|
1128
|
+
return {
|
|
1129
|
+
scope: answer.value,
|
|
1130
|
+
source: answer.layer,
|
|
1131
|
+
manifestPath: project?.path ?? null
|
|
1132
|
+
};
|
|
859
1133
|
}
|
|
860
1134
|
|
|
861
|
-
// src/
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
/* @__PURE__ */ jsx3(Divider, {
|
|
877
|
-
width
|
|
878
|
-
}),
|
|
879
|
-
/* @__PURE__ */ jsx3(Hints, {
|
|
880
|
-
hints: [...extra, ["esc/x", "back"]]
|
|
881
|
-
})
|
|
882
|
-
]
|
|
883
|
-
});
|
|
1135
|
+
// src/settings/held.ts
|
|
1136
|
+
async function holdSettings(ask = {}) {
|
|
1137
|
+
const [config, project] = await Promise.all([
|
|
1138
|
+
readConfig(),
|
|
1139
|
+
nearestProject(ask.cwd)
|
|
1140
|
+
]);
|
|
1141
|
+
const organization = ask.askOrganization && trackingOn(config) ? await ask.askOrganization() : undefined;
|
|
1142
|
+
return {
|
|
1143
|
+
config,
|
|
1144
|
+
configPath: configPath(),
|
|
1145
|
+
flags: ask.flags,
|
|
1146
|
+
project,
|
|
1147
|
+
stock: { path: stockLockPath(), agents: rememberedAgents() },
|
|
1148
|
+
organization
|
|
1149
|
+
};
|
|
884
1150
|
}
|
|
885
|
-
// src/ui/command.tsx
|
|
886
|
-
import { Box as Box3, Text as Text4 } from "ink";
|
|
887
1151
|
|
|
888
|
-
// src/
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
color: tone.primary,
|
|
906
|
-
children: "_"
|
|
907
|
-
}) : null
|
|
908
|
-
]
|
|
1152
|
+
// src/settings/command.ts
|
|
1153
|
+
async function configGet(key) {
|
|
1154
|
+
return configGetReport(await holdSettings(), key);
|
|
1155
|
+
}
|
|
1156
|
+
function configLines(held) {
|
|
1157
|
+
const width = Math.max(...CONFIG_KEYS.map((k) => k.length), "user".length);
|
|
1158
|
+
const lines = settingsShown(held).map((setting) => ({
|
|
1159
|
+
mark: "none",
|
|
1160
|
+
text: `${setting.key.padEnd(width)} ${setting.value}`
|
|
1161
|
+
}));
|
|
1162
|
+
lines.push({
|
|
1163
|
+
mark: "none",
|
|
1164
|
+
text: `${"user".padEnd(width)} ${held.config.userEmail ?? UNSET}`
|
|
1165
|
+
});
|
|
1166
|
+
lines.push({
|
|
1167
|
+
mark: "none",
|
|
1168
|
+
text: `${"token".padEnd(width)} ${held.config.token ? REDACTED : UNSET}`
|
|
909
1169
|
});
|
|
1170
|
+
return lines;
|
|
1171
|
+
}
|
|
1172
|
+
function configGetReport(held, key) {
|
|
1173
|
+
const path = held.configPath;
|
|
1174
|
+
if (key === undefined) {
|
|
1175
|
+
return {
|
|
1176
|
+
title: "config",
|
|
1177
|
+
lines: configLines(held),
|
|
1178
|
+
json: {
|
|
1179
|
+
path,
|
|
1180
|
+
...Object.fromEntries(settingsShown(held).map((setting) => [setting.key, setting.json])),
|
|
1181
|
+
user: held.config.userEmail ?? null,
|
|
1182
|
+
token: held.config.token ? REDACTED : null
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
if (!isConfigKey(key)) {
|
|
1187
|
+
return {
|
|
1188
|
+
title: "config",
|
|
1189
|
+
lines: [
|
|
1190
|
+
{
|
|
1191
|
+
mark: "fail",
|
|
1192
|
+
text: `Unknown config key: ${key}`,
|
|
1193
|
+
note: `Known keys: ${CONFIG_KEYS.join(", ")}`
|
|
1194
|
+
}
|
|
1195
|
+
],
|
|
1196
|
+
json: { path, error: `unknown key: ${key}`, keys: CONFIG_KEYS },
|
|
1197
|
+
failed: true
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
const setting = shown(key, held);
|
|
1201
|
+
return {
|
|
1202
|
+
title: "config",
|
|
1203
|
+
lines: [{ mark: "none", text: setting.value }],
|
|
1204
|
+
json: { path, key, value: setting.json }
|
|
1205
|
+
};
|
|
910
1206
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1207
|
+
async function configSet(key, value, known) {
|
|
1208
|
+
const path = configPath();
|
|
1209
|
+
const config = await readConfig();
|
|
1210
|
+
const result = setConfigValue(config, key, value);
|
|
1211
|
+
if (!result.ok) {
|
|
1212
|
+
return {
|
|
1213
|
+
title: "config",
|
|
1214
|
+
lines: [{ mark: "fail", text: result.message }],
|
|
1215
|
+
json: { path, error: result.message },
|
|
1216
|
+
failed: true
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
if (key === "agents") {
|
|
1220
|
+
const valid = await known();
|
|
1221
|
+
const refusal = valid && refuse(result.config.agents ?? [], valid, path);
|
|
1222
|
+
if (refusal)
|
|
1223
|
+
return refusal;
|
|
1224
|
+
}
|
|
1225
|
+
await writeConfig(result.config);
|
|
1226
|
+
return {
|
|
1227
|
+
title: "config",
|
|
1228
|
+
lines: [{ mark: "ok", text: `${key} = ${result.shown}`, note: path }],
|
|
1229
|
+
json: { path, key, value: result.shown }
|
|
1230
|
+
};
|
|
925
1231
|
}
|
|
926
|
-
function
|
|
927
|
-
|
|
1232
|
+
function refuse(asked, valid, path) {
|
|
1233
|
+
const { unknown, suggest } = unknownAgents(asked, valid);
|
|
1234
|
+
if (unknown.length === 0)
|
|
1235
|
+
return;
|
|
1236
|
+
const named = unknown.join(", ");
|
|
1237
|
+
return {
|
|
1238
|
+
title: "config",
|
|
1239
|
+
lines: [
|
|
1240
|
+
{
|
|
1241
|
+
mark: "fail",
|
|
1242
|
+
text: `No agent is called ${named}.`,
|
|
1243
|
+
note: suggest.length > 0 ? `did you mean ${suggest.join(", ")}?` : ""
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
mark: "none",
|
|
1247
|
+
text: "The stock `skills` CLI keeps the list of agent names, and it does not have this one, so every install would stop at it."
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
mark: "none",
|
|
1251
|
+
text: "Run `yourskills doctor` to see the names this machine accepts."
|
|
1252
|
+
}
|
|
1253
|
+
],
|
|
1254
|
+
json: { path, error: `unknown agents: ${named}`, suggest },
|
|
1255
|
+
failed: true
|
|
1256
|
+
};
|
|
928
1257
|
}
|
|
1258
|
+
// src/auth/session.ts
|
|
1259
|
+
var CLIENT_ID = "yourskills-cli";
|
|
1260
|
+
var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
1261
|
+
var SLOW_DOWN_STEP_MS = 5000;
|
|
1262
|
+
var LOGIN = "yourskills login";
|
|
1263
|
+
var EXPIRED = `The device code expired. Run \`${LOGIN}\` again.`;
|
|
1264
|
+
var authBase = (base) => `${base.replace(/\/$/, "")}/api/auth`;
|
|
1265
|
+
var bearer = (token) => ({ authorization: `Bearer ${token}` });
|
|
1266
|
+
var REFUSED = {
|
|
1267
|
+
"no-token": { said: "Not logged in.", fact: "no token on this machine" },
|
|
1268
|
+
stale: {
|
|
1269
|
+
said: "Session is no longer valid.",
|
|
1270
|
+
fact: "the registry no longer accepts the token on this machine"
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
929
1273
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
1274
|
+
class SessionRefusal extends Error {
|
|
1275
|
+
reason;
|
|
1276
|
+
fact;
|
|
1277
|
+
next = LOGIN;
|
|
1278
|
+
constructor(reason) {
|
|
1279
|
+
super(`${REFUSED[reason].said} Run \`${LOGIN}\`.`);
|
|
1280
|
+
this.name = "SessionRefusal";
|
|
1281
|
+
this.reason = reason;
|
|
1282
|
+
this.fact = REFUSED[reason].fact;
|
|
1283
|
+
}
|
|
940
1284
|
}
|
|
941
|
-
function
|
|
942
|
-
return
|
|
1285
|
+
function isSessionRefusal(error) {
|
|
1286
|
+
return error instanceof Error && error.name === "SessionRefusal";
|
|
943
1287
|
}
|
|
944
|
-
function
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const row = index + i;
|
|
950
|
-
return row >= first && row < first + count;
|
|
951
|
-
});
|
|
952
|
-
index += group.items.length;
|
|
953
|
-
if (items.length > 0)
|
|
954
|
-
out.push({ group: group.group, items });
|
|
955
|
-
}
|
|
956
|
-
return out;
|
|
1288
|
+
async function requireToken(server) {
|
|
1289
|
+
const config = await readConfig();
|
|
1290
|
+
if (!config.token)
|
|
1291
|
+
throw new SessionRefusal("no-token");
|
|
1292
|
+
return { token: config.token, base: serverUrl(config, server) };
|
|
957
1293
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
var LABEL_CAP = 22;
|
|
961
|
-
function valueColumn(items) {
|
|
962
|
-
return Math.min(items.reduce((wide, item) => Math.max(wide, item.label.length), 0), LABEL_CAP);
|
|
1294
|
+
async function hasSession() {
|
|
1295
|
+
return Boolean((await readConfig()).token);
|
|
963
1296
|
}
|
|
964
|
-
function
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
const label = clip(item.label, Math.max(room, 0));
|
|
973
|
-
const shows = item.shows ?? "";
|
|
974
|
-
const pad = Math.max(column - label.length, 0) + GAP2;
|
|
975
|
-
const value = shows && label.length + pad + shows.length <= room ? shows : "";
|
|
976
|
-
const left = label.length + (value ? pad + value.length : 0);
|
|
977
|
-
const gap = Math.max(width - POINTER - left - hint.length, hint ? 1 : 0);
|
|
978
|
-
return /* @__PURE__ */ jsxs5(Text4, {
|
|
979
|
-
children: [
|
|
980
|
-
/* @__PURE__ */ jsx5(Text4, {
|
|
981
|
-
color: tone.primary,
|
|
982
|
-
children: `${active ? mark.cursor : " "} `
|
|
983
|
-
}),
|
|
984
|
-
/* @__PURE__ */ jsx5(Text4, {
|
|
985
|
-
bold: active,
|
|
986
|
-
color: active ? tone.primary : undefined,
|
|
987
|
-
children: label
|
|
988
|
-
}),
|
|
989
|
-
value ? /* @__PURE__ */ jsx5(Text4, {
|
|
990
|
-
children: `${" ".repeat(pad)}${value}`
|
|
991
|
-
}) : null,
|
|
992
|
-
/* @__PURE__ */ jsx5(Text4, {
|
|
993
|
-
dimColor: true,
|
|
994
|
-
children: `${" ".repeat(gap)}${hint}`
|
|
995
|
-
})
|
|
996
|
-
]
|
|
997
|
-
});
|
|
1297
|
+
async function localAccount(server) {
|
|
1298
|
+
const config = await readConfig();
|
|
1299
|
+
return {
|
|
1300
|
+
signedIn: Boolean(config.token),
|
|
1301
|
+
email: config.userEmail ?? null,
|
|
1302
|
+
organization: config.orgName ?? null,
|
|
1303
|
+
server: serverUrl(config, server)
|
|
1304
|
+
};
|
|
998
1305
|
}
|
|
999
|
-
function
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
chrome = 11,
|
|
1005
|
-
placeholder = "type a command or search",
|
|
1006
|
-
emptyMessage = "No matching commands."
|
|
1007
|
-
}) {
|
|
1008
|
-
const visible = filterCommands(groups, query);
|
|
1009
|
-
const rows = flattenCommands(visible);
|
|
1010
|
-
const current = rows[cursor];
|
|
1011
|
-
const interior = width - FRAME_CHROME;
|
|
1012
|
-
const column = valueColumn(rows);
|
|
1013
|
-
const { first, size } = useViewport({
|
|
1014
|
-
cursor,
|
|
1015
|
-
total: rows.length,
|
|
1016
|
-
chrome: chrome + Math.max(0, visible.length * 2 - 1)
|
|
1017
|
-
});
|
|
1018
|
-
const paged = windowGroups(visible, first, size);
|
|
1019
|
-
return /* @__PURE__ */ jsxs5(Box3, {
|
|
1020
|
-
flexDirection: "column",
|
|
1021
|
-
children: [
|
|
1022
|
-
/* @__PURE__ */ jsxs5(Box3, {
|
|
1023
|
-
children: [
|
|
1024
|
-
/* @__PURE__ */ jsx5(Text4, {
|
|
1025
|
-
color: tone.primary,
|
|
1026
|
-
children: "> "
|
|
1027
|
-
}),
|
|
1028
|
-
/* @__PURE__ */ jsx5(SearchInput, {
|
|
1029
|
-
value: query,
|
|
1030
|
-
placeholder
|
|
1031
|
-
})
|
|
1032
|
-
]
|
|
1033
|
-
}),
|
|
1034
|
-
/* @__PURE__ */ jsx5(Divider, {
|
|
1035
|
-
width
|
|
1036
|
-
}),
|
|
1037
|
-
rows.length === 0 ? /* @__PURE__ */ jsx5(Text4, {
|
|
1038
|
-
dimColor: true,
|
|
1039
|
-
children: emptyMessage
|
|
1040
|
-
}) : paged.map((group, i) => /* @__PURE__ */ jsxs5(Box3, {
|
|
1041
|
-
flexDirection: "column",
|
|
1042
|
-
children: [
|
|
1043
|
-
i > 0 ? /* @__PURE__ */ jsx5(Divider, {
|
|
1044
|
-
width
|
|
1045
|
-
}) : null,
|
|
1046
|
-
/* @__PURE__ */ jsx5(Text4, {
|
|
1047
|
-
dimColor: true,
|
|
1048
|
-
children: group.group.toUpperCase()
|
|
1049
|
-
}),
|
|
1050
|
-
group.items.map((item) => /* @__PURE__ */ jsx5(CommandRow, {
|
|
1051
|
-
item,
|
|
1052
|
-
width: interior,
|
|
1053
|
-
column,
|
|
1054
|
-
active: item.value === current?.value
|
|
1055
|
-
}, item.value))
|
|
1056
|
-
]
|
|
1057
|
-
}, group.group))
|
|
1058
|
-
]
|
|
1306
|
+
async function post(talk, url, body) {
|
|
1307
|
+
const res = await talk(url, {
|
|
1308
|
+
method: "POST",
|
|
1309
|
+
headers: { "content-type": "application/json" },
|
|
1310
|
+
body: JSON.stringify(body)
|
|
1059
1311
|
});
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
} else if (key.return) {
|
|
1082
|
-
if (choice === "yes")
|
|
1083
|
-
onYes();
|
|
1084
|
-
else
|
|
1085
|
-
onNo();
|
|
1086
|
-
} else if (key.escape || input === "x") {
|
|
1087
|
-
onNo();
|
|
1088
|
-
}
|
|
1312
|
+
const text = await res.text();
|
|
1313
|
+
let parsed;
|
|
1314
|
+
try {
|
|
1315
|
+
parsed = JSON.parse(text);
|
|
1316
|
+
} catch {
|
|
1317
|
+
return {
|
|
1318
|
+
error: "invalid_response",
|
|
1319
|
+
error_description: text.slice(0, 200) || res.statusText
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
if (res.ok)
|
|
1323
|
+
return { ok: true, data: parsed };
|
|
1324
|
+
const err = parsed;
|
|
1325
|
+
return {
|
|
1326
|
+
error: err.error ?? `http_${res.status}`,
|
|
1327
|
+
error_description: err.error_description
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
async function getSession(talk, base, token) {
|
|
1331
|
+
const res = await talk(`${authBase(base)}/get-session`, {
|
|
1332
|
+
headers: bearer(token)
|
|
1089
1333
|
});
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
}) : null
|
|
1110
|
-
]
|
|
1334
|
+
if (!res.ok)
|
|
1335
|
+
return null;
|
|
1336
|
+
return await res.json();
|
|
1337
|
+
}
|
|
1338
|
+
async function sessionOf(talk, base, token) {
|
|
1339
|
+
const answer = await getSession(talk, base, token);
|
|
1340
|
+
if (!answer?.user)
|
|
1341
|
+
throw new SessionRefusal("stale");
|
|
1342
|
+
return {
|
|
1343
|
+
user: answer.user.email ?? answer.user.id,
|
|
1344
|
+
organizationId: answer.session?.activeOrganizationId ?? null,
|
|
1345
|
+
server: base
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
async function listOrganizations(talk, base, token) {
|
|
1349
|
+
let res;
|
|
1350
|
+
try {
|
|
1351
|
+
res = await talk(`${authBase(base)}/organization/list`, {
|
|
1352
|
+
headers: bearer(token)
|
|
1111
1353
|
});
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
throw new Error(`Could not reach ${base}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1356
|
+
}
|
|
1357
|
+
if (!res.ok) {
|
|
1358
|
+
throw new Error(`Could not list organizations: ${res.status} ${res.statusText}`);
|
|
1359
|
+
}
|
|
1360
|
+
return await res.json();
|
|
1361
|
+
}
|
|
1362
|
+
async function* login(reach = {}) {
|
|
1363
|
+
const talk = reach.talk ?? fetch;
|
|
1364
|
+
const wait = reach.wait ?? ((ms) => sleep(ms));
|
|
1365
|
+
const config = await readConfig();
|
|
1366
|
+
const base = serverUrl(config, reach.server);
|
|
1367
|
+
const start = await post(talk, `${authBase(base)}/device/code`, { client_id: CLIENT_ID });
|
|
1368
|
+
if (!("ok" in start)) {
|
|
1369
|
+
throw new Error(`Could not start device login: ${start.error_description ?? start.error}`);
|
|
1370
|
+
}
|
|
1371
|
+
const device = start.data;
|
|
1372
|
+
const deadline = Date.now() + device.expires_in * 1000;
|
|
1373
|
+
yield {
|
|
1374
|
+
kind: "prompt",
|
|
1375
|
+
userCode: device.user_code,
|
|
1376
|
+
verificationUri: device.verification_uri_complete ?? device.verification_uri,
|
|
1377
|
+
expiresAt: deadline
|
|
1112
1378
|
};
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
children: " "
|
|
1151
|
-
}),
|
|
1152
|
-
row(yes, choice === "yes", argv),
|
|
1153
|
-
row(no, choice === "no")
|
|
1154
|
-
]
|
|
1155
|
-
}),
|
|
1156
|
-
/* @__PURE__ */ jsx6(Divider, {
|
|
1157
|
-
width
|
|
1158
|
-
}),
|
|
1159
|
-
/* @__PURE__ */ jsx6(Hints, {
|
|
1160
|
-
hints: [
|
|
1161
|
-
["^v", "choose"],
|
|
1162
|
-
["enter", choice === "yes" ? yes : no],
|
|
1163
|
-
["esc", no]
|
|
1164
|
-
]
|
|
1165
|
-
})
|
|
1166
|
-
]
|
|
1167
|
-
});
|
|
1379
|
+
let intervalMs = Math.max(device.interval, 1) * 1000;
|
|
1380
|
+
while (Date.now() < deadline) {
|
|
1381
|
+
await wait(intervalMs);
|
|
1382
|
+
const poll = await post(talk, `${authBase(base)}/device/token`, {
|
|
1383
|
+
grant_type: GRANT_TYPE,
|
|
1384
|
+
device_code: device.device_code,
|
|
1385
|
+
client_id: CLIENT_ID
|
|
1386
|
+
});
|
|
1387
|
+
if ("ok" in poll) {
|
|
1388
|
+
const token = poll.data.access_token;
|
|
1389
|
+
const who = await getSession(talk, base, token);
|
|
1390
|
+
await writeConfig({
|
|
1391
|
+
...config,
|
|
1392
|
+
token,
|
|
1393
|
+
serverUrl: base,
|
|
1394
|
+
userEmail: who?.user?.email
|
|
1395
|
+
});
|
|
1396
|
+
yield { kind: "done", email: who?.user?.email };
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
switch (poll.error) {
|
|
1400
|
+
case "authorization_pending":
|
|
1401
|
+
yield { kind: "waiting" };
|
|
1402
|
+
break;
|
|
1403
|
+
case "slow_down":
|
|
1404
|
+
intervalMs += SLOW_DOWN_STEP_MS;
|
|
1405
|
+
yield { kind: "waiting" };
|
|
1406
|
+
break;
|
|
1407
|
+
case "access_denied":
|
|
1408
|
+
throw new Error("Login was denied.");
|
|
1409
|
+
case "expired_token":
|
|
1410
|
+
throw new Error(EXPIRED);
|
|
1411
|
+
default:
|
|
1412
|
+
throw new Error(`Login failed: ${poll.error_description ?? poll.error}`);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
throw new Error(EXPIRED);
|
|
1168
1416
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1417
|
+
async function logout(reach = {}) {
|
|
1418
|
+
const config = await readConfig();
|
|
1419
|
+
if (config.token) {
|
|
1420
|
+
await (reach.talk ?? fetch)(`${authBase(serverUrl(config, reach.server))}/sign-out`, { method: "POST", headers: bearer(config.token) }).catch(() => {
|
|
1421
|
+
return;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
clearConfig();
|
|
1425
|
+
}
|
|
1426
|
+
async function readSession(reach = {}) {
|
|
1427
|
+
const { token, base } = await requireToken(reach.server);
|
|
1428
|
+
return sessionOf(reach.talk ?? fetch, base, token);
|
|
1429
|
+
}
|
|
1430
|
+
var REACH_TIMEOUT_MS = 5000;
|
|
1431
|
+
async function reachable(reach = {}, timeoutMs = REACH_TIMEOUT_MS) {
|
|
1432
|
+
const base = serverUrl(await readConfig(), reach.server);
|
|
1433
|
+
try {
|
|
1434
|
+
const res = await (reach.talk ?? fetch)(`${authBase(base)}/ok`, {
|
|
1435
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1436
|
+
});
|
|
1437
|
+
return { base, status: res.status };
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
return {
|
|
1440
|
+
base,
|
|
1441
|
+
status: null,
|
|
1442
|
+
said: error instanceof Error ? error.message : String(error)
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
async function rememberOrganization(name) {
|
|
1447
|
+
try {
|
|
1448
|
+
const config = await readConfig();
|
|
1449
|
+
if ((config.orgName ?? null) === name)
|
|
1450
|
+
return;
|
|
1451
|
+
await writeConfig(name === null ? { ...config, orgName: undefined } : { ...config, orgName: name });
|
|
1452
|
+
} catch {}
|
|
1453
|
+
}
|
|
1454
|
+
async function organizations(reach = {}) {
|
|
1455
|
+
const { token, base } = await requireToken(reach.server);
|
|
1456
|
+
const talk = reach.talk ?? fetch;
|
|
1457
|
+
const [who, list] = await Promise.allSettled([
|
|
1458
|
+
sessionOf(talk, base, token),
|
|
1459
|
+
listOrganizations(talk, base, token)
|
|
1460
|
+
]);
|
|
1461
|
+
if (who.status === "rejected")
|
|
1462
|
+
throw who.reason;
|
|
1463
|
+
if (list.status === "rejected")
|
|
1464
|
+
throw list.reason;
|
|
1465
|
+
const active = list.value.find((o) => o.id === who.value.organizationId) ?? null;
|
|
1466
|
+
await rememberOrganization(active?.name ?? null);
|
|
1467
|
+
return { all: list.value, active };
|
|
1468
|
+
}
|
|
1469
|
+
async function switchOrganization(name, reach = {}) {
|
|
1470
|
+
const { token, base } = await requireToken(reach.server);
|
|
1471
|
+
const talk = reach.talk ?? fetch;
|
|
1472
|
+
const all = await listOrganizations(talk, base, token);
|
|
1473
|
+
const wanted = name.toLowerCase();
|
|
1474
|
+
const match = all.find((o) => o.name.toLowerCase() === wanted || o.slug?.toLowerCase() === wanted);
|
|
1475
|
+
if (!match)
|
|
1476
|
+
return { kind: "unknown", offered: all };
|
|
1477
|
+
const res = await talk(`${authBase(base)}/organization/set-active`, {
|
|
1478
|
+
method: "POST",
|
|
1479
|
+
headers: { "content-type": "application/json", ...bearer(token) },
|
|
1480
|
+
body: JSON.stringify({ organizationId: match.id })
|
|
1184
1481
|
});
|
|
1482
|
+
if (!res.ok) {
|
|
1483
|
+
const detail = (await res.text()).slice(0, 200);
|
|
1484
|
+
return {
|
|
1485
|
+
kind: "refused",
|
|
1486
|
+
organization: match,
|
|
1487
|
+
detail: detail || `${res.status} ${res.statusText}`
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
await rememberOrganization(match.name);
|
|
1491
|
+
return { kind: "switched", organization: match };
|
|
1185
1492
|
}
|
|
1493
|
+
|
|
1186
1494
|
// src/auth/screens/login.tsx
|
|
1187
1495
|
import { Box as Box5, Text as Text7, useApp } from "ink";
|
|
1188
1496
|
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
@@ -1533,7 +1841,7 @@ import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMem
|
|
|
1533
1841
|
|
|
1534
1842
|
// src/manifest/manifest.ts
|
|
1535
1843
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
1536
|
-
import { dirname as
|
|
1844
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
|
|
1537
1845
|
function emptyManifest(scope) {
|
|
1538
1846
|
return { version: 1, scope, agents: [], bundles: {}, skills: {} };
|
|
1539
1847
|
}
|
|
@@ -1561,7 +1869,7 @@ function readManifest(path) {
|
|
|
1561
1869
|
};
|
|
1562
1870
|
}
|
|
1563
1871
|
function writeManifest(path, m) {
|
|
1564
|
-
mkdirSync2(
|
|
1872
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
1565
1873
|
writeFileSync(path, `${JSON.stringify(m, null, 2)}
|
|
1566
1874
|
`);
|
|
1567
1875
|
}
|
|
@@ -1637,7 +1945,7 @@ async function nearestManifest(flag, cwd) {
|
|
|
1637
1945
|
// src/install/install.ts
|
|
1638
1946
|
import { spawn } from "node:child_process";
|
|
1639
1947
|
import { createRequire } from "node:module";
|
|
1640
|
-
import { dirname as
|
|
1948
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
1641
1949
|
function skillsAddArgv(location, opts = {}) {
|
|
1642
1950
|
const path = location.path.replace(/^\/+|\/+$/g, "");
|
|
1643
1951
|
const at = location.ref ? `#${location.ref}` : "";
|
|
@@ -1671,7 +1979,7 @@ function skillsBin() {
|
|
|
1671
1979
|
if (!bin) {
|
|
1672
1980
|
throw new Error("The bundled `skills` package declares no `skills` binary. Re-install yourskills.");
|
|
1673
1981
|
}
|
|
1674
|
-
return join5(
|
|
1982
|
+
return join5(dirname4(pkgPath), bin);
|
|
1675
1983
|
}
|
|
1676
1984
|
function runSkills(argv) {
|
|
1677
1985
|
const bin = skillsBin();
|
|
@@ -1703,9 +2011,9 @@ function runSkills(argv) {
|
|
|
1703
2011
|
}
|
|
1704
2012
|
function pendingMessage(pending) {
|
|
1705
2013
|
const where = `${pending.repo}/${pending.path}`;
|
|
1706
|
-
return
|
|
2014
|
+
return `${pending.skillName} not in your catalog yet — opened a pull request against ${where}
|
|
1707
2015
|
` + ` ${pending.prUrl}
|
|
1708
|
-
` + " Merge it, then run this command again."
|
|
2016
|
+
` + " Merge it, then run this command again.";
|
|
1709
2017
|
}
|
|
1710
2018
|
function licenceQuestionMessage(name, licence) {
|
|
1711
2019
|
const repos = [...new Set(licence.upstreams.map((one) => one.repo))];
|
|
@@ -2185,7 +2493,7 @@ function InstallLine({ line, width }) {
|
|
|
2185
2493
|
})
|
|
2186
2494
|
]
|
|
2187
2495
|
}),
|
|
2188
|
-
|
|
2496
|
+
/* @__PURE__ */ jsxs9(Text9, {
|
|
2189
2497
|
children: [
|
|
2190
2498
|
/* @__PURE__ */ jsx10(Text9, {
|
|
2191
2499
|
children: " "
|
|
@@ -2195,7 +2503,7 @@ function InstallLine({ line, width }) {
|
|
|
2195
2503
|
children: line.vendor.prUrl
|
|
2196
2504
|
})
|
|
2197
2505
|
]
|
|
2198
|
-
})
|
|
2506
|
+
})
|
|
2199
2507
|
]
|
|
2200
2508
|
});
|
|
2201
2509
|
case "question":
|
|
@@ -2371,64 +2679,13 @@ function Installer({
|
|
|
2371
2679
|
]
|
|
2372
2680
|
}) : null
|
|
2373
2681
|
]
|
|
2374
|
-
}),
|
|
2375
|
-
done || error !== null ? /* @__PURE__ */ jsx10(Back, {
|
|
2376
|
-
width,
|
|
2377
|
-
onBack
|
|
2378
|
-
}) : null
|
|
2379
|
-
]
|
|
2380
|
-
});
|
|
2381
|
-
}
|
|
2382
|
-
function plainLine(line) {
|
|
2383
|
-
switch (line.state) {
|
|
2384
|
-
case "installing":
|
|
2385
|
-
return `installing ${line.name} ${line.where}`;
|
|
2386
|
-
case "installed":
|
|
2387
|
-
return `${line.name} ${line.where}${line.deprecated ? ` ${lifecycleTag("deprecated")}` : ""}`;
|
|
2388
|
-
case "pending":
|
|
2389
|
-
return pendingMessage(line.vendor);
|
|
2390
|
-
case "question":
|
|
2391
|
-
return licenceQuestionMessage(line.name, line.licence);
|
|
2392
|
-
case "failed":
|
|
2393
|
-
return `error: ${line.name}: ${line.message}`;
|
|
2394
|
-
default:
|
|
2395
|
-
return null;
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
async function installPlain(stream) {
|
|
2399
|
-
let lines = [];
|
|
2400
|
-
const printed = new Set;
|
|
2401
|
-
const started = new Set;
|
|
2402
|
-
for await (const event of stream) {
|
|
2403
|
-
lines = reduce(lines, event);
|
|
2404
|
-
for (const line of lines) {
|
|
2405
|
-
if (line.state === "installing" && !started.has(line.name)) {
|
|
2406
|
-
started.add(line.name);
|
|
2407
|
-
const text = plainLine(line);
|
|
2408
|
-
if (text !== null)
|
|
2409
|
-
console.log(text);
|
|
2410
|
-
continue;
|
|
2411
|
-
}
|
|
2412
|
-
if (printed.has(line.name) || !settled(line))
|
|
2413
|
-
continue;
|
|
2414
|
-
const text = plainLine(line);
|
|
2415
|
-
if (text === null)
|
|
2416
|
-
continue;
|
|
2417
|
-
printed.add(line.name);
|
|
2418
|
-
if (line.state === "failed") {
|
|
2419
|
-
console.error(text);
|
|
2420
|
-
if (line.detail)
|
|
2421
|
-
console.error(line.detail);
|
|
2422
|
-
} else {
|
|
2423
|
-
console.log(text);
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
const counts = receipt(lines);
|
|
2428
|
-
if (counts) {
|
|
2429
|
-
console.log(`${counts.installed} installed, ${counts.pending} pending, ${counts.failed} failed` + (counts.questions > 0 ? `, ${counts.questions} unanswered` : ""));
|
|
2430
|
-
}
|
|
2431
|
-
return summarise(lines);
|
|
2682
|
+
}),
|
|
2683
|
+
done || error !== null ? /* @__PURE__ */ jsx10(Back, {
|
|
2684
|
+
width,
|
|
2685
|
+
onBack
|
|
2686
|
+
}) : null
|
|
2687
|
+
]
|
|
2688
|
+
});
|
|
2432
2689
|
}
|
|
2433
2690
|
// src/screens.tsx
|
|
2434
2691
|
import { Box as Box8, Text as Text10, useApp as useApp4, useInput as useInput4 } from "ink";
|
|
@@ -2437,29 +2694,41 @@ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
|
2437
2694
|
var HELP_COLUMN = 26;
|
|
2438
2695
|
function NoticeScreen({
|
|
2439
2696
|
title,
|
|
2440
|
-
message
|
|
2697
|
+
message,
|
|
2698
|
+
exitWhenDone = true,
|
|
2699
|
+
onBack
|
|
2441
2700
|
}) {
|
|
2442
2701
|
const { exit } = useApp4();
|
|
2443
2702
|
const width = useFrameWidth();
|
|
2444
|
-
useEffect5(
|
|
2445
|
-
|
|
2703
|
+
useEffect5(() => {
|
|
2704
|
+
if (exitWhenDone)
|
|
2705
|
+
exit();
|
|
2706
|
+
}, [exit, exitWhenDone]);
|
|
2707
|
+
return /* @__PURE__ */ jsxs10(Frame, {
|
|
2446
2708
|
title,
|
|
2447
2709
|
width,
|
|
2448
|
-
children:
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2710
|
+
children: [
|
|
2711
|
+
/* @__PURE__ */ jsxs10(Text10, {
|
|
2712
|
+
children: [
|
|
2713
|
+
/* @__PURE__ */ jsx11(Text10, {
|
|
2714
|
+
color: "green",
|
|
2715
|
+
children: mark.ok
|
|
2716
|
+
}),
|
|
2717
|
+
/* @__PURE__ */ jsx11(Text10, {
|
|
2718
|
+
children: ` ${message}`
|
|
2719
|
+
})
|
|
2720
|
+
]
|
|
2721
|
+
}),
|
|
2722
|
+
/* @__PURE__ */ jsx11(Back, {
|
|
2723
|
+
width,
|
|
2724
|
+
onBack
|
|
2725
|
+
})
|
|
2726
|
+
]
|
|
2459
2727
|
});
|
|
2460
2728
|
}
|
|
2461
2729
|
var COLOURS = {
|
|
2462
2730
|
ok: "green",
|
|
2731
|
+
warn: tone.notice,
|
|
2463
2732
|
fail: tone.danger,
|
|
2464
2733
|
pending: tone.notice,
|
|
2465
2734
|
none: undefined
|
|
@@ -2571,39 +2840,105 @@ function numberedLines(text) {
|
|
|
2571
2840
|
return text.split(`
|
|
2572
2841
|
`).map((line, i) => ({ id: `${i}:${line}`, line }));
|
|
2573
2842
|
}
|
|
2574
|
-
function HelpScreen({
|
|
2843
|
+
function HelpScreen({
|
|
2844
|
+
text,
|
|
2845
|
+
exitWhenDone = true,
|
|
2846
|
+
onBack
|
|
2847
|
+
}) {
|
|
2575
2848
|
const { exit } = useApp4();
|
|
2576
2849
|
const width = useFrameWidth();
|
|
2577
|
-
useEffect5(
|
|
2578
|
-
|
|
2850
|
+
useEffect5(() => {
|
|
2851
|
+
if (exitWhenDone)
|
|
2852
|
+
exit();
|
|
2853
|
+
}, [exit, exitWhenDone]);
|
|
2854
|
+
return /* @__PURE__ */ jsxs10(Frame, {
|
|
2579
2855
|
title: "yourskills",
|
|
2580
2856
|
width,
|
|
2581
|
-
children:
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2857
|
+
children: [
|
|
2858
|
+
/* @__PURE__ */ jsx11(Box8, {
|
|
2859
|
+
flexDirection: "column",
|
|
2860
|
+
children: numberedLines(text).map(({ id, line }) => {
|
|
2861
|
+
if (line.length === 0)
|
|
2862
|
+
return /* @__PURE__ */ jsx11(Blank, {}, id);
|
|
2863
|
+
if (!line.startsWith(" "))
|
|
2864
|
+
return /* @__PURE__ */ jsx11(Text10, {
|
|
2865
|
+
children: clip(line, width - FRAME_CHROME)
|
|
2866
|
+
}, id);
|
|
2867
|
+
return /* @__PURE__ */ jsxs10(Text10, {
|
|
2868
|
+
children: [
|
|
2869
|
+
/* @__PURE__ */ jsx11(Text10, {
|
|
2870
|
+
color: tone.primary,
|
|
2871
|
+
children: line.slice(0, HELP_COLUMN)
|
|
2872
|
+
}),
|
|
2873
|
+
/* @__PURE__ */ jsx11(Text10, {
|
|
2874
|
+
dimColor: true,
|
|
2875
|
+
children: clip(line.slice(HELP_COLUMN), width - FRAME_CHROME - HELP_COLUMN)
|
|
2876
|
+
})
|
|
2877
|
+
]
|
|
2878
|
+
}, line);
|
|
2879
|
+
})
|
|
2880
|
+
}),
|
|
2881
|
+
/* @__PURE__ */ jsx11(Back, {
|
|
2882
|
+
width,
|
|
2883
|
+
onBack
|
|
2602
2884
|
})
|
|
2603
|
-
|
|
2885
|
+
]
|
|
2604
2886
|
});
|
|
2605
2887
|
}
|
|
2606
2888
|
|
|
2889
|
+
// src/install/plain.ts
|
|
2890
|
+
function plainLine(line) {
|
|
2891
|
+
switch (line.state) {
|
|
2892
|
+
case "installing":
|
|
2893
|
+
return `installing ${line.name} ${line.where}`;
|
|
2894
|
+
case "installed":
|
|
2895
|
+
return `${line.name} ${line.where}${line.deprecated ? ` ${lifecycleTag("deprecated")}` : ""}`;
|
|
2896
|
+
case "pending":
|
|
2897
|
+
return pendingMessage(line.vendor);
|
|
2898
|
+
case "question":
|
|
2899
|
+
return licenceQuestionMessage(line.name, line.licence);
|
|
2900
|
+
case "failed":
|
|
2901
|
+
return `error: ${line.name}: ${line.message}`;
|
|
2902
|
+
default:
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
async function installPlain(stream, io) {
|
|
2907
|
+
let lines = [];
|
|
2908
|
+
const printed = new Set;
|
|
2909
|
+
const started = new Set;
|
|
2910
|
+
for await (const event of stream) {
|
|
2911
|
+
lines = reduce(lines, event);
|
|
2912
|
+
for (const line of lines) {
|
|
2913
|
+
if (line.state === "installing" && !started.has(line.name)) {
|
|
2914
|
+
started.add(line.name);
|
|
2915
|
+
const text = plainLine(line);
|
|
2916
|
+
if (text !== null)
|
|
2917
|
+
io.out(text);
|
|
2918
|
+
continue;
|
|
2919
|
+
}
|
|
2920
|
+
if (printed.has(line.name) || !settled(line))
|
|
2921
|
+
continue;
|
|
2922
|
+
const text = plainLine(line);
|
|
2923
|
+
if (text === null)
|
|
2924
|
+
continue;
|
|
2925
|
+
printed.add(line.name);
|
|
2926
|
+
if (line.state === "failed") {
|
|
2927
|
+
io.err(text);
|
|
2928
|
+
if (line.detail)
|
|
2929
|
+
io.err(line.detail);
|
|
2930
|
+
} else {
|
|
2931
|
+
io.out(text);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
const counts = receipt(lines);
|
|
2936
|
+
if (counts) {
|
|
2937
|
+
io.out(`${counts.installed} installed, ${counts.pending} pending, ${counts.failed} failed` + (counts.questions > 0 ? `, ${counts.questions} unanswered` : ""));
|
|
2938
|
+
}
|
|
2939
|
+
return summarise(lines);
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2607
2942
|
// src/install/screens/run.tsx
|
|
2608
2943
|
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
2609
2944
|
function RunScreen({
|
|
@@ -2636,13 +2971,13 @@ function RunScreen({
|
|
|
2636
2971
|
...onNext ? { onNext } : {}
|
|
2637
2972
|
});
|
|
2638
2973
|
}
|
|
2639
|
-
async function plainRun(run, emit) {
|
|
2974
|
+
async function plainRun(run, emit, io) {
|
|
2640
2975
|
if (run.kind === "report") {
|
|
2641
2976
|
const done = await run.report();
|
|
2642
2977
|
emit(done);
|
|
2643
2978
|
return done;
|
|
2644
2979
|
}
|
|
2645
|
-
return installPlain(run.stream());
|
|
2980
|
+
return installPlain(run.stream(), io);
|
|
2646
2981
|
}
|
|
2647
2982
|
// src/install/empty.ts
|
|
2648
2983
|
function nothingTracked(nearest, kind, all = false) {
|
|
@@ -3272,7 +3607,7 @@ function shellFromEnv(shell) {
|
|
|
3272
3607
|
// src/doctor/doctor.ts
|
|
3273
3608
|
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
3274
3609
|
import { homedir as homedir4 } from "node:os";
|
|
3275
|
-
import { dirname as
|
|
3610
|
+
import { dirname as dirname6, join as join8 } from "node:path";
|
|
3276
3611
|
|
|
3277
3612
|
// ../../node_modules/.bun/@trpc+client@11.18.0+acc6907a906fa511/node_modules/@trpc/client/dist/objectSpread2-BvkFp-_Y.mjs
|
|
3278
3613
|
var __create = Object.create;
|
|
@@ -4702,7 +5037,7 @@ var EXIT_CODES = [
|
|
|
4702
5037
|
[EXIT.usage, "the command line was wrong; the last line says the spelling"],
|
|
4703
5038
|
[EXIT.session, "needs a session: run `yourskills login`"]
|
|
4704
5039
|
];
|
|
4705
|
-
var
|
|
5040
|
+
var LOGIN2 = "yourskills login";
|
|
4706
5041
|
function needsTerminal(kind) {
|
|
4707
5042
|
if (kind === "login")
|
|
4708
5043
|
return new CliError("login needs a terminal", {
|
|
@@ -4726,7 +5061,7 @@ function describeError(error) {
|
|
|
4726
5061
|
const message = error instanceof Error ? error.message : String(error);
|
|
4727
5062
|
if (isUnauthorized(error)) {
|
|
4728
5063
|
return {
|
|
4729
|
-
lines: shape("you are not signed in", message.startsWith("Session") ? "the registry no longer accepts the token on this machine" : "the catalog needs a session, and there is no token on this machine",
|
|
5064
|
+
lines: shape("you are not signed in", message.startsWith("Session") ? "the registry no longer accepts the token on this machine" : "the catalog needs a session, and there is no token on this machine", LOGIN2),
|
|
4730
5065
|
code: EXIT.session
|
|
4731
5066
|
};
|
|
4732
5067
|
}
|
|
@@ -4845,7 +5180,7 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
4845
5180
|
// src/hooks/settings.ts
|
|
4846
5181
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
4847
5182
|
import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
4848
|
-
import { dirname as
|
|
5183
|
+
import { dirname as dirname5 } from "node:path";
|
|
4849
5184
|
function binaryName(command) {
|
|
4850
5185
|
const parts = command.split(/[\\/]/);
|
|
4851
5186
|
return (parts[parts.length - 1] ?? "").replace(/\.(js|mjs|cjs|exe)$/i, "");
|
|
@@ -4967,7 +5302,7 @@ function serialiseSettings(settings, indent) {
|
|
|
4967
5302
|
`;
|
|
4968
5303
|
}
|
|
4969
5304
|
async function writeSettingsFile(path, settings, indent) {
|
|
4970
|
-
mkdirSync3(
|
|
5305
|
+
mkdirSync3(dirname5(path), { recursive: true });
|
|
4971
5306
|
await writeFile2(path, serialiseSettings(settings, indent));
|
|
4972
5307
|
}
|
|
4973
5308
|
async function applyTo(path, change) {
|
|
@@ -5752,7 +6087,7 @@ async function hooksStatus() {
|
|
|
5752
6087
|
{ mark: "none", text: "tracking", note: tracking.note }
|
|
5753
6088
|
];
|
|
5754
6089
|
const agents = [];
|
|
5755
|
-
for (const agent of agentsToReport(
|
|
6090
|
+
for (const agent of agentsToReport(await resolveAgents())) {
|
|
5756
6091
|
const adapter = adapterFor(agent);
|
|
5757
6092
|
if (!adapter) {
|
|
5758
6093
|
lines.push({
|
|
@@ -5817,7 +6152,7 @@ import { realpathSync } from "node:fs";
|
|
|
5817
6152
|
// package.json
|
|
5818
6153
|
var package_default = {
|
|
5819
6154
|
name: "yourskills",
|
|
5820
|
-
version: "0.
|
|
6155
|
+
version: "0.5.0",
|
|
5821
6156
|
description: "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
|
|
5822
6157
|
license: "UNLICENSED",
|
|
5823
6158
|
homepage: "https://yourskills.store",
|
|
@@ -6262,40 +6597,6 @@ async function runSessionStart() {
|
|
|
6262
6597
|
const outcome = await sessionStart();
|
|
6263
6598
|
emit(outcome, await upgradeCheck());
|
|
6264
6599
|
}
|
|
6265
|
-
// src/org/wire.ts
|
|
6266
|
-
var authBase2 = (base) => `${base.replace(/\/$/, "")}/api/auth`;
|
|
6267
|
-
async function activeOrganizationId(base, token) {
|
|
6268
|
-
const res = await fetch(`${authBase2(base)}/get-session`, {
|
|
6269
|
-
headers: { authorization: `Bearer ${token}` }
|
|
6270
|
-
});
|
|
6271
|
-
if (!res.ok)
|
|
6272
|
-
return null;
|
|
6273
|
-
const body = await res.json();
|
|
6274
|
-
return body?.session?.activeOrganizationId ?? null;
|
|
6275
|
-
}
|
|
6276
|
-
async function listOrganizations(base, token) {
|
|
6277
|
-
let res;
|
|
6278
|
-
try {
|
|
6279
|
-
res = await fetch(`${authBase2(base)}/organization/list`, {
|
|
6280
|
-
headers: { authorization: `Bearer ${token}` }
|
|
6281
|
-
});
|
|
6282
|
-
} catch (error) {
|
|
6283
|
-
throw new Error(`Could not reach ${base}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6284
|
-
}
|
|
6285
|
-
if (!res.ok) {
|
|
6286
|
-
throw new Error(`Could not list organizations: ${res.status} ${res.statusText}`);
|
|
6287
|
-
}
|
|
6288
|
-
return await res.json();
|
|
6289
|
-
}
|
|
6290
|
-
async function rememberOrganization(name) {
|
|
6291
|
-
try {
|
|
6292
|
-
const config = await readConfig();
|
|
6293
|
-
if ((config.orgName ?? null) === name)
|
|
6294
|
-
return;
|
|
6295
|
-
await writeConfig(name === null ? { ...config, orgName: undefined } : { ...config, orgName: name });
|
|
6296
|
-
} catch {}
|
|
6297
|
-
}
|
|
6298
|
-
|
|
6299
6600
|
// src/org/org.ts
|
|
6300
6601
|
var NO_ACTIVE_ORGANIZATION = "none: choose one with `yourskills org <name>`, or in the web app";
|
|
6301
6602
|
async function namedOrganization(server) {
|
|
@@ -6304,8 +6605,8 @@ async function namedOrganization(server) {
|
|
|
6304
6605
|
return view.organization;
|
|
6305
6606
|
}
|
|
6306
6607
|
async function whoamiReport(server) {
|
|
6307
|
-
const who = await
|
|
6308
|
-
const organization = who.
|
|
6608
|
+
const who = await readSession({ server });
|
|
6609
|
+
const organization = who.organizationId ? await namedOrganization(server) : null;
|
|
6309
6610
|
await rememberOrganization(organization?.name ?? null);
|
|
6310
6611
|
return drawWhoami({ user: who.user, server: who.server, organization });
|
|
6311
6612
|
}
|
|
@@ -6343,152 +6644,167 @@ function rows(orgs, active) {
|
|
|
6343
6644
|
}
|
|
6344
6645
|
async function learnActiveOrganization(server) {
|
|
6345
6646
|
try {
|
|
6346
|
-
|
|
6347
|
-
const [orgs, active] = await Promise.all([
|
|
6348
|
-
listOrganizations(base, token),
|
|
6349
|
-
activeOrganizationId(base, token)
|
|
6350
|
-
]);
|
|
6351
|
-
const name = orgs.find((o) => o.id === active)?.name ?? null;
|
|
6352
|
-
await rememberOrganization(name);
|
|
6353
|
-
return name;
|
|
6647
|
+
return (await organizations({ server })).active?.name ?? null;
|
|
6354
6648
|
} catch {
|
|
6355
6649
|
return null;
|
|
6356
6650
|
}
|
|
6357
6651
|
}
|
|
6358
6652
|
async function organizationChoices(server) {
|
|
6359
|
-
const {
|
|
6360
|
-
|
|
6361
|
-
listOrganizations(base, token),
|
|
6362
|
-
activeOrganizationId(base, token)
|
|
6363
|
-
]);
|
|
6364
|
-
await rememberOrganization(orgs.find((o) => o.id === active)?.name ?? null);
|
|
6365
|
-
return orgs.map((org) => ({
|
|
6653
|
+
const { all, active } = await organizations({ server });
|
|
6654
|
+
return all.map((org) => ({
|
|
6366
6655
|
name: org.name,
|
|
6367
6656
|
slug: org.slug ?? null,
|
|
6368
|
-
active: org.id === active
|
|
6657
|
+
active: org.id === active?.id
|
|
6369
6658
|
}));
|
|
6370
6659
|
}
|
|
6371
6660
|
async function orgListReport(server) {
|
|
6372
|
-
const {
|
|
6373
|
-
const [orgs, active] = await Promise.all([
|
|
6374
|
-
listOrganizations(base, token),
|
|
6375
|
-
activeOrganizationId(base, token)
|
|
6376
|
-
]);
|
|
6377
|
-
await rememberOrganization(orgs.find((o) => o.id === active)?.name ?? null);
|
|
6661
|
+
const { all, active } = await organizations({ server });
|
|
6378
6662
|
return {
|
|
6379
6663
|
title: "org",
|
|
6380
|
-
lines: rows(
|
|
6664
|
+
lines: rows(all, active?.id ?? null),
|
|
6381
6665
|
json: {
|
|
6382
|
-
active,
|
|
6383
|
-
organizations:
|
|
6666
|
+
active: active?.id ?? null,
|
|
6667
|
+
organizations: all.map((o) => ({
|
|
6384
6668
|
id: o.id,
|
|
6385
6669
|
name: o.name,
|
|
6386
6670
|
slug: o.slug ?? null,
|
|
6387
|
-
active: o.id === active
|
|
6671
|
+
active: o.id === active?.id
|
|
6388
6672
|
}))
|
|
6389
6673
|
}
|
|
6390
6674
|
};
|
|
6391
6675
|
}
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6676
|
+
function drawSwitch(name, outcome) {
|
|
6677
|
+
switch (outcome.kind) {
|
|
6678
|
+
case "unknown":
|
|
6679
|
+
return {
|
|
6680
|
+
title: "org",
|
|
6681
|
+
lines: [
|
|
6682
|
+
{
|
|
6683
|
+
mark: "fail",
|
|
6684
|
+
text: `No organization called ${name}.`,
|
|
6685
|
+
note: outcome.offered.map((o) => o.name).join(", ") || "you are in none"
|
|
6686
|
+
}
|
|
6687
|
+
],
|
|
6688
|
+
json: { error: `unknown organization: ${name}` },
|
|
6689
|
+
failed: true
|
|
6690
|
+
};
|
|
6691
|
+
case "refused":
|
|
6692
|
+
return {
|
|
6693
|
+
title: "org",
|
|
6694
|
+
lines: [
|
|
6695
|
+
{
|
|
6696
|
+
mark: "fail",
|
|
6697
|
+
text: `Could not switch to ${outcome.organization.name}.`,
|
|
6698
|
+
note: outcome.detail
|
|
6699
|
+
}
|
|
6700
|
+
],
|
|
6701
|
+
json: { error: outcome.detail },
|
|
6702
|
+
failed: true
|
|
6703
|
+
};
|
|
6704
|
+
case "switched": {
|
|
6705
|
+
const { organization } = outcome;
|
|
6706
|
+
return {
|
|
6707
|
+
title: "org",
|
|
6708
|
+
lines: [
|
|
6709
|
+
{ mark: "ok", text: `Active organization: ${organization.name}` }
|
|
6710
|
+
],
|
|
6711
|
+
json: {
|
|
6712
|
+
active: organization.id,
|
|
6713
|
+
name: organization.name,
|
|
6714
|
+
slug: organization.slug ?? null
|
|
6405
6715
|
}
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
failed: true
|
|
6409
|
-
};
|
|
6716
|
+
};
|
|
6717
|
+
}
|
|
6410
6718
|
}
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6719
|
+
}
|
|
6720
|
+
async function orgSwitchReport(name, server) {
|
|
6721
|
+
return drawSwitch(name, await switchOrganization(name, { server }));
|
|
6722
|
+
}
|
|
6723
|
+
// src/doctor/check.ts
|
|
6724
|
+
function fixable(checks) {
|
|
6725
|
+
return checks.filter((check) => check.state === "fail" && check.fix?.apply !== undefined);
|
|
6726
|
+
}
|
|
6727
|
+
var MARK = {
|
|
6728
|
+
pass: "ok",
|
|
6729
|
+
warn: "warn",
|
|
6730
|
+
fail: "fail"
|
|
6731
|
+
};
|
|
6732
|
+
function markOf(state) {
|
|
6733
|
+
return MARK[state];
|
|
6734
|
+
}
|
|
6735
|
+
function blocks(checks) {
|
|
6736
|
+
return checks.some((check) => check.state === "fail");
|
|
6737
|
+
}
|
|
6738
|
+
function nextFor2(checks, fixing) {
|
|
6739
|
+
const fixes = fixable(checks);
|
|
6740
|
+
if (!fixing && fixes.length > 0)
|
|
6421
6741
|
return {
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
{
|
|
6425
|
-
mark: "fail",
|
|
6426
|
-
text: `Could not switch to ${match.name}.`,
|
|
6427
|
-
note: detail || `${res.status} ${res.statusText}`
|
|
6428
|
-
}
|
|
6429
|
-
],
|
|
6430
|
-
json: { error: detail || res.statusText },
|
|
6431
|
-
failed: true
|
|
6742
|
+
command: "yourskills doctor --fix",
|
|
6743
|
+
why: `repair ${fixes.length === 1 ? "it" : "them"}`
|
|
6432
6744
|
};
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
};
|
|
6745
|
+
const names = (name) => checks.some((check) => check.fix?.name === name);
|
|
6746
|
+
if (names("login"))
|
|
6747
|
+
return { command: "yourskills login", why: "start a session" };
|
|
6748
|
+
if (names("upgrade"))
|
|
6749
|
+
return { command: "yourskills upgrade", why: "get the newer yourskills" };
|
|
6750
|
+
return;
|
|
6440
6751
|
}
|
|
6752
|
+
|
|
6441
6753
|
// src/doctor/doctor.ts
|
|
6442
|
-
var REACH_TIMEOUT_MS = 5000;
|
|
6443
6754
|
var liveDiagnosis = async (server) => (await createClient(server)).downstream.diagnosis.query();
|
|
6444
|
-
function withLogin(message) {
|
|
6445
|
-
return message.includes("yourskills login") ? message : `${message} — run \`yourskills login\``;
|
|
6446
|
-
}
|
|
6447
6755
|
function home() {
|
|
6448
6756
|
const env = process.env.HOME;
|
|
6449
6757
|
return env && env.length > 0 ? env : homedir4();
|
|
6450
6758
|
}
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
};
|
|
6468
|
-
}
|
|
6759
|
+
function said(error) {
|
|
6760
|
+
return error instanceof Error ? error.message : String(error);
|
|
6761
|
+
}
|
|
6762
|
+
async function reached(server) {
|
|
6763
|
+
const answer = await reachable({ server });
|
|
6764
|
+
return answer.status === null ? {
|
|
6765
|
+
name: "server reachable",
|
|
6766
|
+
state: "fail",
|
|
6767
|
+
summary: `${answer.base}: ${answer.said}`,
|
|
6768
|
+
fix: null
|
|
6769
|
+
} : {
|
|
6770
|
+
name: "server reachable",
|
|
6771
|
+
state: "pass",
|
|
6772
|
+
summary: `${answer.base} (${answer.status})`,
|
|
6773
|
+
fix: null
|
|
6774
|
+
};
|
|
6469
6775
|
}
|
|
6470
6776
|
async function session(ask, server) {
|
|
6471
6777
|
try {
|
|
6472
|
-
const who = await
|
|
6778
|
+
const who = await readSession({ server });
|
|
6779
|
+
const id = who.organizationId;
|
|
6473
6780
|
return [
|
|
6474
|
-
{ name: "session valid",
|
|
6781
|
+
{ name: "session valid", state: "pass", summary: who.user, fix: null },
|
|
6475
6782
|
{
|
|
6476
6783
|
name: "active organization",
|
|
6477
|
-
|
|
6478
|
-
|
|
6784
|
+
state: id ? "pass" : "fail",
|
|
6785
|
+
summary: id ? (await ask(server).catch(() => null))?.name ?? id : NO_ACTIVE_ORGANIZATION,
|
|
6786
|
+
fix: null
|
|
6479
6787
|
}
|
|
6480
6788
|
];
|
|
6481
6789
|
} catch (error) {
|
|
6790
|
+
const refused = isSessionRefusal(error);
|
|
6482
6791
|
return [
|
|
6483
|
-
{
|
|
6792
|
+
refused ? {
|
|
6793
|
+
name: "session valid",
|
|
6794
|
+
state: "fail",
|
|
6795
|
+
summary: error.fact,
|
|
6796
|
+
fix: { name: "login", sentence: `run \`${error.next}\`` }
|
|
6797
|
+
} : {
|
|
6484
6798
|
name: "session valid",
|
|
6485
|
-
|
|
6486
|
-
|
|
6799
|
+
state: "fail",
|
|
6800
|
+
summary: said(error),
|
|
6801
|
+
fix: null
|
|
6487
6802
|
},
|
|
6488
6803
|
{
|
|
6489
6804
|
name: "active organization",
|
|
6490
|
-
|
|
6491
|
-
|
|
6805
|
+
state: "fail",
|
|
6806
|
+
summary: refused ? "unknown until you are logged in" : "not asked: the session could not be read",
|
|
6807
|
+
fix: null
|
|
6492
6808
|
}
|
|
6493
6809
|
];
|
|
6494
6810
|
}
|
|
@@ -6496,27 +6812,42 @@ async function session(ask, server) {
|
|
|
6496
6812
|
function configParses() {
|
|
6497
6813
|
const path = configPath();
|
|
6498
6814
|
if (!existsSync5(path))
|
|
6499
|
-
return {
|
|
6815
|
+
return {
|
|
6816
|
+
name: "config",
|
|
6817
|
+
state: "pass",
|
|
6818
|
+
summary: `${path} (not created yet)`,
|
|
6819
|
+
fix: null
|
|
6820
|
+
};
|
|
6500
6821
|
try {
|
|
6501
6822
|
JSON.parse(readFileSync3(path, "utf8"));
|
|
6502
|
-
return { name: "config",
|
|
6823
|
+
return { name: "config", state: "pass", summary: path, fix: null };
|
|
6503
6824
|
} catch (error) {
|
|
6504
6825
|
return {
|
|
6505
6826
|
name: "config",
|
|
6506
|
-
|
|
6507
|
-
|
|
6827
|
+
state: "fail",
|
|
6828
|
+
summary: `${path}: ${said(error)}`,
|
|
6829
|
+
fix: {
|
|
6830
|
+
name: "by-hand",
|
|
6831
|
+
sentence: "fix it by hand, or move it aside and log in again"
|
|
6832
|
+
}
|
|
6508
6833
|
};
|
|
6509
6834
|
}
|
|
6510
6835
|
}
|
|
6511
6836
|
function skillsResolvable() {
|
|
6512
6837
|
try {
|
|
6513
6838
|
const bin = skillsBin();
|
|
6514
|
-
return existsSync5(bin) ? { name: "skills dependency",
|
|
6839
|
+
return existsSync5(bin) ? { name: "skills dependency", state: "pass", summary: bin, fix: null } : {
|
|
6840
|
+
name: "skills dependency",
|
|
6841
|
+
state: "fail",
|
|
6842
|
+
summary: `missing: ${bin}`,
|
|
6843
|
+
fix: null
|
|
6844
|
+
};
|
|
6515
6845
|
} catch (error) {
|
|
6516
6846
|
return {
|
|
6517
6847
|
name: "skills dependency",
|
|
6518
|
-
|
|
6519
|
-
|
|
6848
|
+
state: "fail",
|
|
6849
|
+
summary: said(error),
|
|
6850
|
+
fix: null
|
|
6520
6851
|
};
|
|
6521
6852
|
}
|
|
6522
6853
|
}
|
|
@@ -6525,7 +6856,7 @@ async function agents(nearest, local) {
|
|
|
6525
6856
|
const found = local ? [] : [
|
|
6526
6857
|
...new Set((await listInstalled(nearest.scope)).flatMap((row) => row.agents))
|
|
6527
6858
|
];
|
|
6528
|
-
const
|
|
6859
|
+
const sentences = [
|
|
6529
6860
|
pinned.length > 0 ? `pinned: ${pinned.join(", ")}` : "",
|
|
6530
6861
|
found.length > 0 ? `found: ${found.join(", ")} (the stock CLI's labels)` : ""
|
|
6531
6862
|
].filter(Boolean);
|
|
@@ -6536,15 +6867,25 @@ async function agents(nearest, local) {
|
|
|
6536
6867
|
const hint = suggest.length > 0 ? `did you mean ${suggest.join(", ")}?` : "run `yourskills config set agents <name>` with a name the stock CLI knows";
|
|
6537
6868
|
return {
|
|
6538
6869
|
name: "agents",
|
|
6539
|
-
|
|
6540
|
-
|
|
6870
|
+
state: "fail",
|
|
6871
|
+
summary: `no agent is called ${unknown.join(", ")}: every install would stop at it`,
|
|
6872
|
+
fix: { name: "pin-agent", sentence: hint }
|
|
6541
6873
|
};
|
|
6542
6874
|
}
|
|
6543
6875
|
}
|
|
6544
|
-
return {
|
|
6876
|
+
return sentences.length > 0 ? {
|
|
6877
|
+
name: "agents",
|
|
6878
|
+
state: "pass",
|
|
6879
|
+
summary: sentences.join(" "),
|
|
6880
|
+
fix: null
|
|
6881
|
+
} : {
|
|
6545
6882
|
name: "agents",
|
|
6546
|
-
|
|
6547
|
-
|
|
6883
|
+
state: "fail",
|
|
6884
|
+
summary: "none",
|
|
6885
|
+
fix: {
|
|
6886
|
+
name: "pin-agent",
|
|
6887
|
+
sentence: "pin one with `yourskills config set agents claude-code`"
|
|
6888
|
+
}
|
|
6548
6889
|
};
|
|
6549
6890
|
}
|
|
6550
6891
|
function hookCheck(gap) {
|
|
@@ -6552,22 +6893,28 @@ function hookCheck(gap) {
|
|
|
6552
6893
|
if (gap.error)
|
|
6553
6894
|
return {
|
|
6554
6895
|
name,
|
|
6555
|
-
|
|
6556
|
-
|
|
6896
|
+
state: "fail",
|
|
6897
|
+
summary: `${gap.path}: ${gap.error}`,
|
|
6898
|
+
fix: {
|
|
6899
|
+
name: "by-hand",
|
|
6900
|
+
sentence: "left untouched; fix the JSON by hand"
|
|
6901
|
+
}
|
|
6557
6902
|
};
|
|
6558
6903
|
if (gap.missing.length === 0)
|
|
6559
6904
|
return {
|
|
6560
6905
|
name,
|
|
6561
|
-
|
|
6562
|
-
|
|
6906
|
+
state: "pass",
|
|
6907
|
+
summary: `${gap.registered.join(" + ")} ${gap.path}`,
|
|
6908
|
+
fix: null
|
|
6563
6909
|
};
|
|
6564
6910
|
const kinds = gap.missing.join(" + ");
|
|
6565
6911
|
return {
|
|
6566
6912
|
name,
|
|
6567
|
-
|
|
6568
|
-
|
|
6913
|
+
state: "fail",
|
|
6914
|
+
summary: `${kinds} not registered`,
|
|
6569
6915
|
fix: {
|
|
6570
|
-
|
|
6916
|
+
name: "register-hooks",
|
|
6917
|
+
sentence: `register ${kinds} in ${gap.path}`,
|
|
6571
6918
|
apply: () => registerMissing(gap)
|
|
6572
6919
|
}
|
|
6573
6920
|
};
|
|
@@ -6576,26 +6923,30 @@ function manifestParses(nearest) {
|
|
|
6576
6923
|
if (!existsSync5(nearest.path)) {
|
|
6577
6924
|
return {
|
|
6578
6925
|
name: "manifest",
|
|
6579
|
-
|
|
6580
|
-
|
|
6926
|
+
state: "pass",
|
|
6927
|
+
summary: `${nearest.path} (not created yet)`,
|
|
6928
|
+
fix: null
|
|
6581
6929
|
};
|
|
6582
6930
|
}
|
|
6583
6931
|
try {
|
|
6584
6932
|
const raw = JSON.parse(readFileSync3(nearest.path, "utf8"));
|
|
6585
6933
|
return raw && typeof raw === "object" ? {
|
|
6586
6934
|
name: "manifest",
|
|
6587
|
-
|
|
6588
|
-
|
|
6935
|
+
state: "pass",
|
|
6936
|
+
summary: `${nearest.path} (${nearest.scope})`,
|
|
6937
|
+
fix: null
|
|
6589
6938
|
} : {
|
|
6590
6939
|
name: "manifest",
|
|
6591
|
-
|
|
6592
|
-
|
|
6940
|
+
state: "fail",
|
|
6941
|
+
summary: `${nearest.path}: not an object`,
|
|
6942
|
+
fix: null
|
|
6593
6943
|
};
|
|
6594
6944
|
} catch (error) {
|
|
6595
6945
|
return {
|
|
6596
6946
|
name: "manifest",
|
|
6597
|
-
|
|
6598
|
-
|
|
6947
|
+
state: "fail",
|
|
6948
|
+
summary: `${nearest.path}: ${said(error)}`,
|
|
6949
|
+
fix: null
|
|
6599
6950
|
};
|
|
6600
6951
|
}
|
|
6601
6952
|
}
|
|
@@ -6603,7 +6954,7 @@ function onDisk(nearest, name) {
|
|
|
6603
6954
|
const entry = nearest.manifest.skills[name];
|
|
6604
6955
|
if (entry?.unpinned)
|
|
6605
6956
|
return existsSync5(entry.path);
|
|
6606
|
-
const root = nearest.scope === "project" ?
|
|
6957
|
+
const root = nearest.scope === "project" ? dirname6(nearest.path) : home();
|
|
6607
6958
|
return existsSync5(join8(root, ".agents", "skills", name));
|
|
6608
6959
|
}
|
|
6609
6960
|
function manifestMatchesDisk(nearest) {
|
|
@@ -6612,16 +6963,18 @@ function manifestMatchesDisk(nearest) {
|
|
|
6612
6963
|
if (missing.length === 0)
|
|
6613
6964
|
return {
|
|
6614
6965
|
name: "manifest matches disk",
|
|
6615
|
-
|
|
6616
|
-
|
|
6966
|
+
state: "pass",
|
|
6967
|
+
summary: `${names.length} skill(s) recorded`,
|
|
6968
|
+
fix: null
|
|
6617
6969
|
};
|
|
6618
6970
|
const list = missing.join(", ");
|
|
6619
6971
|
return {
|
|
6620
6972
|
name: "manifest matches disk",
|
|
6621
|
-
|
|
6622
|
-
|
|
6973
|
+
state: "fail",
|
|
6974
|
+
summary: `missing on disk: ${list}`,
|
|
6623
6975
|
fix: {
|
|
6624
|
-
|
|
6976
|
+
name: "forget",
|
|
6977
|
+
sentence: `forget ${list} in ${nearest.path}, or \`yourskills install\` to restore ${missing.length === 1 ? "it" : "them"}`,
|
|
6625
6978
|
apply: async () => {
|
|
6626
6979
|
openManifest({ path: nearest.path, scope: nearest.scope }).forget(missing);
|
|
6627
6980
|
return `forgot ${list}; \`yourskills add <name>\` brings one back`;
|
|
@@ -6635,8 +6988,9 @@ function unpinned(nearest) {
|
|
|
6635
6988
|
return null;
|
|
6636
6989
|
return {
|
|
6637
6990
|
name: "unpinned",
|
|
6638
|
-
|
|
6639
|
-
|
|
6991
|
+
state: "warn",
|
|
6992
|
+
summary: `${names.join(", ")}: on this machine, not in your catalog; nothing keeps ${names.length === 1 ? "it" : "them"} up to date`,
|
|
6993
|
+
fix: null
|
|
6640
6994
|
};
|
|
6641
6995
|
}
|
|
6642
6996
|
async function downstreamChecks(ask, server) {
|
|
@@ -6647,8 +7001,9 @@ async function downstreamChecks(ask, server) {
|
|
|
6647
7001
|
return [
|
|
6648
7002
|
{
|
|
6649
7003
|
name: "downstream",
|
|
6650
|
-
|
|
6651
|
-
|
|
7004
|
+
state: "fail",
|
|
7005
|
+
summary: `could not ask the registry: ${said(error)}`,
|
|
7006
|
+
fix: null
|
|
6652
7007
|
}
|
|
6653
7008
|
];
|
|
6654
7009
|
}
|
|
@@ -6656,21 +7011,24 @@ async function downstreamChecks(ask, server) {
|
|
|
6656
7011
|
return [
|
|
6657
7012
|
{
|
|
6658
7013
|
name: "downstream",
|
|
6659
|
-
|
|
6660
|
-
|
|
7014
|
+
state: "fail",
|
|
7015
|
+
summary: `${answer.headline}: nothing you install can be vendored until one is set`,
|
|
7016
|
+
fix: null
|
|
6661
7017
|
}
|
|
6662
7018
|
];
|
|
6663
7019
|
}
|
|
6664
7020
|
return [
|
|
6665
7021
|
{
|
|
6666
7022
|
name: "downstream",
|
|
6667
|
-
|
|
6668
|
-
|
|
7023
|
+
state: answer.tone,
|
|
7024
|
+
summary: `${answer.repo.repo}: ${answer.headline}`,
|
|
7025
|
+
fix: null
|
|
6669
7026
|
},
|
|
6670
7027
|
...answer.checks.map((check) => ({
|
|
6671
7028
|
name: `downstream: ${check.key}`,
|
|
6672
|
-
|
|
6673
|
-
|
|
7029
|
+
state: check.state,
|
|
7030
|
+
summary: check.summary,
|
|
7031
|
+
fix: check.fix
|
|
6674
7032
|
}))
|
|
6675
7033
|
];
|
|
6676
7034
|
}
|
|
@@ -6683,16 +7041,18 @@ async function unwatchedCheck(nearest, ask, server) {
|
|
|
6683
7041
|
} catch (error) {
|
|
6684
7042
|
return {
|
|
6685
7043
|
name,
|
|
6686
|
-
|
|
6687
|
-
|
|
7044
|
+
state: "fail",
|
|
7045
|
+
summary: `could not ask the registry: ${said(error)}`,
|
|
7046
|
+
fix: null
|
|
6688
7047
|
};
|
|
6689
7048
|
}
|
|
6690
7049
|
const orphans = drift.unknown.filter((row) => row.reason === "orphaned");
|
|
6691
7050
|
if (orphans.length === 0)
|
|
6692
7051
|
return {
|
|
6693
7052
|
name,
|
|
6694
|
-
|
|
6695
|
-
|
|
7053
|
+
state: "pass",
|
|
7054
|
+
summary: "every installed skill came from a source this organization still connects",
|
|
7055
|
+
fix: null
|
|
6696
7056
|
};
|
|
6697
7057
|
const names = orphans.map((row) => row.name);
|
|
6698
7058
|
const repos = [
|
|
@@ -6701,8 +7061,12 @@ async function unwatchedCheck(nearest, ask, server) {
|
|
|
6701
7061
|
const one = names.length === 1;
|
|
6702
7062
|
return {
|
|
6703
7063
|
name,
|
|
6704
|
-
|
|
6705
|
-
|
|
7064
|
+
state: "warn",
|
|
7065
|
+
summary: `${names.join(", ")}: installed from ${repos.join(", ")}, which this ` + "organization does not connect any more, so nothing is watching " + `${repos.length === 1 ? "it" : "them"} for changes. The ` + `${one ? "copy on this machine goes" : "copies on this machine go"} on ` + "working.",
|
|
7066
|
+
fix: {
|
|
7067
|
+
name: "unwatched",
|
|
7068
|
+
sentence: "A team member can reconnect the source or vendor the skill into your " + "catalog, and then `yourskills update --all` moves this machine onto it."
|
|
7069
|
+
}
|
|
6706
7070
|
};
|
|
6707
7071
|
}
|
|
6708
7072
|
function version(config) {
|
|
@@ -6710,20 +7074,23 @@ function version(config) {
|
|
|
6710
7074
|
if (latest && isNewer(latest, VERSION))
|
|
6711
7075
|
return {
|
|
6712
7076
|
name: "version",
|
|
6713
|
-
|
|
6714
|
-
|
|
7077
|
+
state: "warn",
|
|
7078
|
+
summary: VERSION,
|
|
7079
|
+
fix: {
|
|
7080
|
+
name: "upgrade",
|
|
7081
|
+
sentence: `${latest} is out; run \`yourskills upgrade\``
|
|
7082
|
+
}
|
|
6715
7083
|
};
|
|
6716
|
-
return { name: "version",
|
|
7084
|
+
return { name: "version", state: "pass", summary: VERSION, fix: null };
|
|
6717
7085
|
}
|
|
6718
7086
|
async function diagnose(nearest, options = {}) {
|
|
6719
7087
|
const config = await readConfig();
|
|
6720
|
-
const base = serverUrl(config, options.server);
|
|
6721
7088
|
const local = options.local ?? false;
|
|
6722
7089
|
const checks = [configParses()];
|
|
6723
7090
|
if (!local) {
|
|
6724
7091
|
const account = await session(options.askOrganization ?? namedOrganization, options.server);
|
|
6725
|
-
checks.push(await
|
|
6726
|
-
if (account[0].
|
|
7092
|
+
checks.push(await reached(options.server), ...account);
|
|
7093
|
+
if (account[0].state !== "fail") {
|
|
6727
7094
|
checks.push(...await downstreamChecks(options.ask ?? liveDiagnosis, options.server), await unwatchedCheck(nearest, options.askDrift ?? liveDrift, options.server));
|
|
6728
7095
|
}
|
|
6729
7096
|
}
|
|
@@ -6736,53 +7103,41 @@ async function diagnose(nearest, options = {}) {
|
|
|
6736
7103
|
checks.push(version(config));
|
|
6737
7104
|
return checks;
|
|
6738
7105
|
}
|
|
6739
|
-
function fixable(checks) {
|
|
6740
|
-
return checks.filter((check) => !check.ok && check.fix !== undefined);
|
|
6741
|
-
}
|
|
6742
7106
|
async function applyFixes(checks) {
|
|
6743
7107
|
const out = [];
|
|
6744
7108
|
for (const check of checks) {
|
|
6745
|
-
|
|
7109
|
+
const [own] = fixable([check]);
|
|
7110
|
+
if (!own) {
|
|
6746
7111
|
out.push(check);
|
|
6747
7112
|
continue;
|
|
6748
7113
|
}
|
|
6749
|
-
|
|
6750
|
-
const detail = await check.fix.apply();
|
|
6751
|
-
out.push({ ...check, ok: true, fixed: { ok: true, detail } });
|
|
6752
|
-
} catch (error) {
|
|
6753
|
-
out.push({
|
|
6754
|
-
...check,
|
|
6755
|
-
fixed: {
|
|
6756
|
-
ok: false,
|
|
6757
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
6758
|
-
}
|
|
6759
|
-
});
|
|
6760
|
-
}
|
|
7114
|
+
out.push(await applied(own));
|
|
6761
7115
|
}
|
|
6762
7116
|
return out;
|
|
6763
7117
|
}
|
|
6764
|
-
function
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
};
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
7118
|
+
async function applied(check) {
|
|
7119
|
+
try {
|
|
7120
|
+
const detail = await check.fix.apply();
|
|
7121
|
+
return { ...check, state: "pass", fixed: { ok: true, detail } };
|
|
7122
|
+
} catch (error) {
|
|
7123
|
+
return { ...check, fixed: { ok: false, detail: said(error) } };
|
|
7124
|
+
}
|
|
7125
|
+
}
|
|
7126
|
+
function noteFor(check, fixing) {
|
|
7127
|
+
if (check.fixed)
|
|
7128
|
+
return check.fixed.ok ? `fixed: ${check.fixed.detail}` : `${check.summary} could not fix: ${check.fixed.detail}`;
|
|
7129
|
+
if (!check.fix)
|
|
7130
|
+
return check.summary;
|
|
7131
|
+
const own = check.fix.apply && !fixing ? " [fixable]" : "";
|
|
7132
|
+
return `${check.summary} ${check.fix.sentence}${own}`;
|
|
6775
7133
|
}
|
|
6776
7134
|
function doctorLines(checks, fixing = false) {
|
|
6777
|
-
const width = Math.max(...checks.map((check) => check.name.length));
|
|
6778
|
-
const lines = checks.map((check) => {
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
note
|
|
6784
|
-
};
|
|
6785
|
-
});
|
|
7135
|
+
const width = Math.max(...checks.map((check) => check.name.length));
|
|
7136
|
+
const lines = checks.map((check) => ({
|
|
7137
|
+
mark: markOf(check.state),
|
|
7138
|
+
text: check.name.padEnd(width),
|
|
7139
|
+
note: noteFor(check, fixing)
|
|
7140
|
+
}));
|
|
6786
7141
|
const next = nextFor2(checks, fixing);
|
|
6787
7142
|
return {
|
|
6788
7143
|
title: fixing ? "doctor --fix" : "doctor",
|
|
@@ -6790,11 +7145,12 @@ function doctorLines(checks, fixing = false) {
|
|
|
6790
7145
|
json: {
|
|
6791
7146
|
checks: checks.map(({ fix, ...rest }) => ({
|
|
6792
7147
|
...rest,
|
|
6793
|
-
|
|
7148
|
+
fix: fix ? { name: fix.name, sentence: fix.sentence } : null,
|
|
7149
|
+
fixable: fix?.apply !== undefined
|
|
6794
7150
|
})),
|
|
6795
7151
|
fixed: checks.filter((check) => check.fixed?.ok).map((check) => check.fixed?.detail)
|
|
6796
7152
|
},
|
|
6797
|
-
failed: checks
|
|
7153
|
+
failed: blocks(checks),
|
|
6798
7154
|
...next ? { next } : {}
|
|
6799
7155
|
};
|
|
6800
7156
|
}
|
|
@@ -6806,38 +7162,6 @@ async function doctorReport(nearest, options = {}) {
|
|
|
6806
7162
|
return { ...report, json: { ...report.json, manifest: nearest.path } };
|
|
6807
7163
|
}
|
|
6808
7164
|
// src/doctor/explain.ts
|
|
6809
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
6810
|
-
import { dirname as dirname6 } from "node:path";
|
|
6811
|
-
function scopeLayers(resolved, manifestScope, config) {
|
|
6812
|
-
const project = resolved.manifestPath ? {
|
|
6813
|
-
layer: "project",
|
|
6814
|
-
value: manifestScope,
|
|
6815
|
-
from: `yourskills.json at ${dirname6(resolved.manifestPath)}${manifestScope ? "" : " (declares no scope)"}`
|
|
6816
|
-
} : { layer: "project", value: null, from: "no yourskills.json above here" };
|
|
6817
|
-
return [
|
|
6818
|
-
{
|
|
6819
|
-
layer: "flag",
|
|
6820
|
-
value: resolved.source === "flag" ? "given" : null,
|
|
6821
|
-
from: "-g or -p on the command line"
|
|
6822
|
-
},
|
|
6823
|
-
project,
|
|
6824
|
-
{
|
|
6825
|
-
layer: "config",
|
|
6826
|
-
value: config.defaultScope ?? null,
|
|
6827
|
-
from: `defaultScope in ${configPath()}`
|
|
6828
|
-
},
|
|
6829
|
-
{ layer: "default", value: "global", from: "built-in default" }
|
|
6830
|
-
];
|
|
6831
|
-
}
|
|
6832
|
-
function serverFrom(config, override) {
|
|
6833
|
-
if (override)
|
|
6834
|
-
return "--server on the command line";
|
|
6835
|
-
if (process.env.YOURSKILLS_URL)
|
|
6836
|
-
return "YOURSKILLS_URL in the environment";
|
|
6837
|
-
if (config.serverUrl)
|
|
6838
|
-
return `server in ${configPath()}`;
|
|
6839
|
-
return "built-in default";
|
|
6840
|
-
}
|
|
6841
7165
|
var LABEL = 12;
|
|
6842
7166
|
function row(label, value, from) {
|
|
6843
7167
|
return {
|
|
@@ -6846,36 +7170,32 @@ function row(label, value, from) {
|
|
|
6846
7170
|
note: `<- ${from}`
|
|
6847
7171
|
};
|
|
6848
7172
|
}
|
|
6849
|
-
|
|
6850
|
-
|
|
7173
|
+
var LOSER = LABEL + 22;
|
|
7174
|
+
function losers(setting) {
|
|
7175
|
+
return setting.layers.filter((layer) => layer.layer !== setting.layer).map((layer) => ({
|
|
6851
7176
|
mark: "none",
|
|
6852
|
-
text: `${"".padEnd(LABEL)}(${layer.layer}: ${layer.value ?? "unset"}`.padEnd(
|
|
7177
|
+
text: `${"".padEnd(LABEL)}(${layer.layer}: ${layer.value ?? "unset"}`.padEnd(LOSER),
|
|
6853
7178
|
note: `${layer.from})`
|
|
6854
7179
|
}));
|
|
6855
7180
|
}
|
|
7181
|
+
function settingRows(settings) {
|
|
7182
|
+
return settings.flatMap((setting) => [
|
|
7183
|
+
row(setting.key, setting.value, setting.from),
|
|
7184
|
+
...losers(setting)
|
|
7185
|
+
]);
|
|
7186
|
+
}
|
|
6856
7187
|
async function explainReport(server) {
|
|
6857
|
-
const [
|
|
6858
|
-
|
|
6859
|
-
|
|
7188
|
+
const [held, nearest, queue] = await Promise.all([
|
|
7189
|
+
holdSettings({
|
|
7190
|
+
flags: { server },
|
|
7191
|
+
askOrganization: () => organizationTracking(server)
|
|
7192
|
+
}),
|
|
6860
7193
|
nearestManifest(),
|
|
6861
7194
|
readQueue()
|
|
6862
7195
|
]);
|
|
6863
|
-
const
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
askOrganization: () => organizationTracking(server)
|
|
6867
|
-
});
|
|
6868
|
-
const manifestScope = resolved.manifestPath && existsSync6(resolved.manifestPath) ? resolved.source === "project" ? resolved.scope : null : null;
|
|
6869
|
-
const layers = scopeLayers(resolved, manifestScope, config);
|
|
6870
|
-
const winner = layers.find((layer) => layer.layer === resolved.source);
|
|
6871
|
-
const lines = [
|
|
6872
|
-
row("scope", resolved.scope, winner?.from ?? resolved.source),
|
|
6873
|
-
...losers(layers, resolved.source),
|
|
6874
|
-
row("agents", config.agents?.length ? config.agents.join(", ") : "(the stock CLI detects)", config.agents?.length ? `agents in ${configPath()}` : "nothing pinned; `skills` looks at what is installed"),
|
|
6875
|
-
row("server", serverUrl(config, server), serverFrom(config, server)),
|
|
6876
|
-
row("track", track.value, track.from),
|
|
6877
|
-
row("autoUpdate", config.autoUpdate === false ? "off" : "on", config.autoUpdate === undefined ? "built-in default" : `autoUpdate in ${configPath()}`)
|
|
6878
|
-
];
|
|
7196
|
+
const settings = settingsShown(held);
|
|
7197
|
+
const tracking = trackingOf(held);
|
|
7198
|
+
const lines = settingRows(settings);
|
|
6879
7199
|
const skills = Object.keys(nearest.manifest.skills).length;
|
|
6880
7200
|
const bundles = Object.keys(nearest.manifest.bundles).length;
|
|
6881
7201
|
const loose = looseSkills(nearest.manifest).length;
|
|
@@ -6912,16 +7232,19 @@ async function explainReport(server) {
|
|
|
6912
7232
|
title: "config --explain",
|
|
6913
7233
|
lines,
|
|
6914
7234
|
json: {
|
|
6915
|
-
path: configPath
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
7235
|
+
path: held.configPath,
|
|
7236
|
+
settings: Object.fromEntries(settings.map((setting) => [
|
|
7237
|
+
setting.key,
|
|
7238
|
+
{
|
|
7239
|
+
value: setting.json,
|
|
7240
|
+
layer: setting.layer,
|
|
7241
|
+
from: setting.from,
|
|
7242
|
+
layers: setting.layers,
|
|
7243
|
+
decided: setting.decided
|
|
7244
|
+
}
|
|
7245
|
+
])),
|
|
7246
|
+
track: tracking.value === "on",
|
|
7247
|
+
trackingState: tracking.state,
|
|
6925
7248
|
manifest: { path: nearest.path, skills, bundles, loose },
|
|
6926
7249
|
queue: { path: queuePath(), events: queue.length },
|
|
6927
7250
|
hooks: hooksJson
|
|
@@ -7199,7 +7522,25 @@ var TABLE = {
|
|
|
7199
7522
|
hint: "org <name>",
|
|
7200
7523
|
shows: (facts) => facts.feed.organizations === undefined ? "" : facts.feed.organizations <= 1 ? "only this one" : count2(facts.feed.organizations - 1, "other"),
|
|
7201
7524
|
when: (facts) => facts.signedIn,
|
|
7202
|
-
says: (facts) => facts.organization ? `switch this machine out of ${facts.organization}` : "choose which organization this machine installs from"
|
|
7525
|
+
says: (facts) => facts.organization ? `switch this machine out of ${facts.organization}` : "choose which organization this machine installs from",
|
|
7526
|
+
asks: {
|
|
7527
|
+
title: "org",
|
|
7528
|
+
verb: "switch",
|
|
7529
|
+
pick: "one",
|
|
7530
|
+
empty: "You are not a member of any organization yet.",
|
|
7531
|
+
choices: async (ctx, _facts, ask = organizationChoices) => {
|
|
7532
|
+
const orgs = await ask(ctx.server);
|
|
7533
|
+
const active = orgs.find((org) => org.active)?.name;
|
|
7534
|
+
return {
|
|
7535
|
+
rows: orgs.map((org) => ({
|
|
7536
|
+
name: org.name,
|
|
7537
|
+
description: org.slug ?? ""
|
|
7538
|
+
})),
|
|
7539
|
+
chosen: active ? [active] : []
|
|
7540
|
+
};
|
|
7541
|
+
},
|
|
7542
|
+
opens: (picked) => ({ kind: "org", name: picked[0] ?? "" })
|
|
7543
|
+
}
|
|
7203
7544
|
}
|
|
7204
7545
|
],
|
|
7205
7546
|
invitesHooks: false,
|
|
@@ -7272,6 +7613,7 @@ var TABLE = {
|
|
|
7272
7613
|
label: "search",
|
|
7273
7614
|
hint: "search <query>",
|
|
7274
7615
|
when: (facts) => facts.signedIn,
|
|
7616
|
+
opens: { kind: "search", query: "" },
|
|
7275
7617
|
says: () => "filter the same catalog by name and description"
|
|
7276
7618
|
}
|
|
7277
7619
|
],
|
|
@@ -7341,7 +7683,21 @@ var TABLE = {
|
|
|
7341
7683
|
label: "remove",
|
|
7342
7684
|
hint: "remove <name...>",
|
|
7343
7685
|
when: (facts) => facts.skills > 0,
|
|
7344
|
-
says: (facts) => `pick which of the ${count2(facts.skills, "skill")} to delete from this machine; it asks before it does
|
|
7686
|
+
says: (facts) => `pick which of the ${count2(facts.skills, "skill")} to delete from this machine; it asks before it does`,
|
|
7687
|
+
asks: {
|
|
7688
|
+
title: "remove",
|
|
7689
|
+
verb: "remove",
|
|
7690
|
+
pick: "many",
|
|
7691
|
+
empty: "Nothing installed here.",
|
|
7692
|
+
choices: async (ctx) => ({
|
|
7693
|
+
rows: installedRows(ctx.where).map((row) => ({
|
|
7694
|
+
name: row.name,
|
|
7695
|
+
description: `${row.bundle ?? "loose"} ${shortRef(row.ref)}`
|
|
7696
|
+
})),
|
|
7697
|
+
chosen: []
|
|
7698
|
+
}),
|
|
7699
|
+
opens: (picked) => ({ kind: "remove", names: [...picked] })
|
|
7700
|
+
}
|
|
7345
7701
|
}
|
|
7346
7702
|
],
|
|
7347
7703
|
invitesHooks: true,
|
|
@@ -7617,7 +7973,7 @@ var TABLE = {
|
|
|
7617
7973
|
lines: fixes.map((check) => ({
|
|
7618
7974
|
mark: "fail",
|
|
7619
7975
|
text: check.name,
|
|
7620
|
-
note: check.fix
|
|
7976
|
+
note: check.fix.sentence
|
|
7621
7977
|
})),
|
|
7622
7978
|
notes: [
|
|
7623
7979
|
"Every other check stays as it is; a session or an upgrade is yours to run."
|
|
@@ -7663,14 +8019,62 @@ var TABLE = {
|
|
|
7663
8019
|
label: "scope",
|
|
7664
8020
|
hint: "config set scope project",
|
|
7665
8021
|
shows: (facts) => facts.scope,
|
|
7666
|
-
says: (facts) => `where installs land: ${facts.scope} right now
|
|
8022
|
+
says: (facts) => `where installs land: ${facts.scope} right now`,
|
|
8023
|
+
asks: {
|
|
8024
|
+
title: "scope",
|
|
8025
|
+
verb: "set",
|
|
8026
|
+
pick: "one",
|
|
8027
|
+
empty: "",
|
|
8028
|
+
choices: async (_ctx, facts) => ({
|
|
8029
|
+
rows: [
|
|
8030
|
+
{
|
|
8031
|
+
name: "global",
|
|
8032
|
+
description: "installs land in your home directory, where every project sees them"
|
|
8033
|
+
},
|
|
8034
|
+
{
|
|
8035
|
+
name: "project",
|
|
8036
|
+
description: "installs land in this project's yourskills.json, and travel with the repo"
|
|
8037
|
+
}
|
|
8038
|
+
],
|
|
8039
|
+
chosen: [facts.scope]
|
|
8040
|
+
}),
|
|
8041
|
+
opens: (picked) => ({
|
|
8042
|
+
kind: "config",
|
|
8043
|
+
action: "set",
|
|
8044
|
+
key: "scope",
|
|
8045
|
+
value: picked[0] ?? ""
|
|
8046
|
+
})
|
|
8047
|
+
}
|
|
7667
8048
|
},
|
|
7668
8049
|
{
|
|
7669
8050
|
value: "setup.agents",
|
|
7670
8051
|
label: "agents",
|
|
7671
8052
|
hint: "config set agents <name>",
|
|
7672
8053
|
shows: (facts) => facts.agents.length > 0 ? count2(facts.agents.length, "agent") : "unset",
|
|
7673
|
-
says: (facts) => facts.agents.length > 0 ? `which agents an install writes for: ${facts.agents.join(", ")}` : "which agents an install writes for; nothing pinned, so `skills` looks"
|
|
8054
|
+
says: (facts) => facts.agents.length > 0 ? `which agents an install writes for: ${facts.agents.join(", ")}` : "which agents an install writes for; nothing pinned, so `skills` looks",
|
|
8055
|
+
asks: {
|
|
8056
|
+
title: "agents",
|
|
8057
|
+
verb: "set",
|
|
8058
|
+
pick: "many",
|
|
8059
|
+
empty: "Could not ask the `skills` CLI which agent names it accepts. Run `yourskills doctor`.",
|
|
8060
|
+
choices: async (_ctx, facts, ask = knownAgents) => {
|
|
8061
|
+
const valid = await ask() ?? [];
|
|
8062
|
+
const onDisk = new Set(facts.agentsOnDisk);
|
|
8063
|
+
return {
|
|
8064
|
+
rows: valid.map((name) => ({
|
|
8065
|
+
name,
|
|
8066
|
+
description: onDisk.has(name) ? "on this machine" : ""
|
|
8067
|
+
})),
|
|
8068
|
+
chosen: facts.agents
|
|
8069
|
+
};
|
|
8070
|
+
},
|
|
8071
|
+
opens: (picked) => ({
|
|
8072
|
+
kind: "config",
|
|
8073
|
+
action: "set",
|
|
8074
|
+
key: "agents",
|
|
8075
|
+
value: picked.join(",")
|
|
8076
|
+
})
|
|
8077
|
+
}
|
|
7674
8078
|
},
|
|
7675
8079
|
{
|
|
7676
8080
|
value: "setup.explain",
|
|
@@ -8173,14 +8577,13 @@ function paletteGroups(facts) {
|
|
|
8173
8577
|
}
|
|
8174
8578
|
return groups;
|
|
8175
8579
|
}
|
|
8176
|
-
// src/shell/
|
|
8177
|
-
import { existsSync as
|
|
8580
|
+
// src/shell/machine.ts
|
|
8581
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
8178
8582
|
async function paletteFacts(scope, server) {
|
|
8179
|
-
const [account, nearest,
|
|
8583
|
+
const [account, nearest, held] = await Promise.all([
|
|
8180
8584
|
localAccount(server),
|
|
8181
8585
|
nearestManifest(scope),
|
|
8182
|
-
|
|
8183
|
-
resolveAgents()
|
|
8586
|
+
holdSettings({ flags: { server } })
|
|
8184
8587
|
]);
|
|
8185
8588
|
const skills = Object.keys(nearest.manifest.skills).length;
|
|
8186
8589
|
return {
|
|
@@ -8188,31 +8591,26 @@ async function paletteFacts(scope, server) {
|
|
|
8188
8591
|
email: account.email,
|
|
8189
8592
|
organization: account.organization,
|
|
8190
8593
|
server: account.server,
|
|
8191
|
-
firstRun: !account.signedIn && skills === 0 && !config.welcomed,
|
|
8192
|
-
agentsOnDisk: HOOK_ADAPTERS.filter((adapter) =>
|
|
8193
|
-
agents,
|
|
8594
|
+
firstRun: !account.signedIn && skills === 0 && !held.config.welcomed,
|
|
8595
|
+
agentsOnDisk: HOOK_ADAPTERS.filter((adapter) => existsSync6(adapter.home())).map((adapter) => adapter.agent),
|
|
8596
|
+
agents: agentsAnswer(held).value,
|
|
8194
8597
|
scope: nearest.scope,
|
|
8195
8598
|
manifest: nearest.path,
|
|
8196
8599
|
skills,
|
|
8197
8600
|
bundles: Object.keys(nearest.manifest.bundles).length,
|
|
8198
8601
|
loose: looseSkills(nearest.manifest).length,
|
|
8199
|
-
settings:
|
|
8602
|
+
settings: settingsShown(held).filter((setting) => setting.decided).length,
|
|
8200
8603
|
feed: NO_FEED
|
|
8201
8604
|
};
|
|
8202
8605
|
}
|
|
8203
|
-
|
|
8204
|
-
import { Box as Box14, Text as Text16, useApp as useApp7, useInput as useInput8 } from "ink";
|
|
8205
|
-
import { useCallback as useCallback3, useEffect as useEffect9, useMemo as useMemo4, useRef, useState as useState11 } from "react";
|
|
8206
|
-
|
|
8207
|
-
// src/shell/feed.ts
|
|
8208
|
-
async function feedFacts(client, where, server) {
|
|
8606
|
+
async function feedFacts(client, where, server, choices = organizationChoices) {
|
|
8209
8607
|
const installed = Object.keys(where.manifest.skills).length > 0;
|
|
8210
8608
|
const [skills, bundles, passes, drift, orgs] = await Promise.allSettled([
|
|
8211
8609
|
client.skills.list.query({}),
|
|
8212
8610
|
client.bundles.list.query({}),
|
|
8213
8611
|
client.mirror.passes.query(),
|
|
8214
8612
|
installed ? driftNow(client, where) : Promise.resolve(null),
|
|
8215
|
-
|
|
8613
|
+
choices(server)
|
|
8216
8614
|
]);
|
|
8217
8615
|
return {
|
|
8218
8616
|
...skills.status === "fulfilled" ? { skills: skills.value.length } : {},
|
|
@@ -8222,8 +8620,6 @@ async function feedFacts(client, where, server) {
|
|
|
8222
8620
|
...orgs.status === "fulfilled" ? { organizations: orgs.value.length } : {}
|
|
8223
8621
|
};
|
|
8224
8622
|
}
|
|
8225
|
-
|
|
8226
|
-
// src/shell/probe.ts
|
|
8227
8623
|
var importProbe = async (where) => {
|
|
8228
8624
|
const found = await listInstalled(where.scope);
|
|
8229
8625
|
const recorded = new Set(Object.keys(where.manifest.skills));
|
|
@@ -8237,7 +8633,7 @@ var doctorProbe = async (where, server) => {
|
|
|
8237
8633
|
local: true,
|
|
8238
8634
|
...server ? { server } : {}
|
|
8239
8635
|
});
|
|
8240
|
-
const bad = checks.filter((check) =>
|
|
8636
|
+
const bad = checks.filter((check) => check.state === "fail");
|
|
8241
8637
|
return bad.length === 0 ? `${count2(checks.length, "local check")}, all passing` : `${bad.length} of ${checks.length} local checks failing: ${bad.map((check) => check.name).join(", ")}`;
|
|
8242
8638
|
};
|
|
8243
8639
|
var PROBES = {
|
|
@@ -8247,6 +8643,259 @@ var PROBES = {
|
|
|
8247
8643
|
function probeFor(value) {
|
|
8248
8644
|
return value ? PROBES[value] : undefined;
|
|
8249
8645
|
}
|
|
8646
|
+
var REST_MS = 250;
|
|
8647
|
+
var noop3 = () => {
|
|
8648
|
+
return;
|
|
8649
|
+
};
|
|
8650
|
+
function knowMachine(from, talkers = {}) {
|
|
8651
|
+
const { server } = from;
|
|
8652
|
+
const ask = {
|
|
8653
|
+
upgrade: talkers.upgrade ?? (() => upgradeCheck()),
|
|
8654
|
+
feed: talkers.feed ?? feedFacts,
|
|
8655
|
+
probes: talkers.probes ?? probeFor,
|
|
8656
|
+
learnOrganization: talkers.learnOrganization ?? learnActiveOrganization
|
|
8657
|
+
};
|
|
8658
|
+
const restMs = talkers.restMs ?? REST_MS;
|
|
8659
|
+
let where = from.where;
|
|
8660
|
+
let local = from.facts;
|
|
8661
|
+
let feed = from.facts.feed;
|
|
8662
|
+
let upgrade;
|
|
8663
|
+
let probes = {};
|
|
8664
|
+
let probing = null;
|
|
8665
|
+
let dead = false;
|
|
8666
|
+
let epoch = 0;
|
|
8667
|
+
const listeners = new Set;
|
|
8668
|
+
const pending = new Set;
|
|
8669
|
+
function fold() {
|
|
8670
|
+
return {
|
|
8671
|
+
where,
|
|
8672
|
+
facts: {
|
|
8673
|
+
...local,
|
|
8674
|
+
feed: {
|
|
8675
|
+
...feed,
|
|
8676
|
+
...upgrade === undefined ? {} : { upgrade: upgrade ? upgrade.latest : null }
|
|
8677
|
+
}
|
|
8678
|
+
},
|
|
8679
|
+
upgrade,
|
|
8680
|
+
probes,
|
|
8681
|
+
probing
|
|
8682
|
+
};
|
|
8683
|
+
}
|
|
8684
|
+
let state = fold();
|
|
8685
|
+
function set() {
|
|
8686
|
+
state = fold();
|
|
8687
|
+
for (const listener of listeners)
|
|
8688
|
+
listener();
|
|
8689
|
+
}
|
|
8690
|
+
function track(work) {
|
|
8691
|
+
const done = work.then(noop3, noop3).then(() => {
|
|
8692
|
+
pending.delete(done);
|
|
8693
|
+
});
|
|
8694
|
+
pending.add(done);
|
|
8695
|
+
return done;
|
|
8696
|
+
}
|
|
8697
|
+
let held = null;
|
|
8698
|
+
async function client() {
|
|
8699
|
+
held ??= await from.client();
|
|
8700
|
+
return held;
|
|
8701
|
+
}
|
|
8702
|
+
function askUpgrade() {
|
|
8703
|
+
track(ask.upgrade().then((found) => {
|
|
8704
|
+
if (dead)
|
|
8705
|
+
return;
|
|
8706
|
+
upgrade = found;
|
|
8707
|
+
set();
|
|
8708
|
+
}));
|
|
8709
|
+
}
|
|
8710
|
+
function askFeed() {
|
|
8711
|
+
if (dead || !local.signedIn)
|
|
8712
|
+
return;
|
|
8713
|
+
const mine = epoch;
|
|
8714
|
+
track(client().then((live) => ask.feed(live, where, server)).then((found) => {
|
|
8715
|
+
if (dead || mine !== epoch)
|
|
8716
|
+
return;
|
|
8717
|
+
feed = found;
|
|
8718
|
+
set();
|
|
8719
|
+
}));
|
|
8720
|
+
}
|
|
8721
|
+
let resting;
|
|
8722
|
+
let timer = null;
|
|
8723
|
+
let wake = null;
|
|
8724
|
+
function unrest() {
|
|
8725
|
+
if (timer)
|
|
8726
|
+
clearTimeout(timer);
|
|
8727
|
+
timer = null;
|
|
8728
|
+
wake?.();
|
|
8729
|
+
wake = null;
|
|
8730
|
+
}
|
|
8731
|
+
function scan(value, probe) {
|
|
8732
|
+
const mine = epoch;
|
|
8733
|
+
probing = value;
|
|
8734
|
+
set();
|
|
8735
|
+
track(probe(where, server).then((text) => {
|
|
8736
|
+
if (dead || mine !== epoch)
|
|
8737
|
+
return;
|
|
8738
|
+
probes = { ...probes, [value]: text };
|
|
8739
|
+
}).catch(noop3).finally(() => {
|
|
8740
|
+
if (dead || mine !== epoch)
|
|
8741
|
+
return;
|
|
8742
|
+
if (probing === value)
|
|
8743
|
+
probing = null;
|
|
8744
|
+
set();
|
|
8745
|
+
}));
|
|
8746
|
+
}
|
|
8747
|
+
function rest(value) {
|
|
8748
|
+
resting = value;
|
|
8749
|
+
unrest();
|
|
8750
|
+
if (dead || !value)
|
|
8751
|
+
return;
|
|
8752
|
+
const probe = ask.probes(value);
|
|
8753
|
+
if (!probe || probes[value] !== undefined)
|
|
8754
|
+
return;
|
|
8755
|
+
let fired = false;
|
|
8756
|
+
track(new Promise((resolve) => {
|
|
8757
|
+
wake = resolve;
|
|
8758
|
+
timer = setTimeout(() => {
|
|
8759
|
+
fired = true;
|
|
8760
|
+
timer = null;
|
|
8761
|
+
wake = null;
|
|
8762
|
+
resolve();
|
|
8763
|
+
}, restMs);
|
|
8764
|
+
}).then(() => {
|
|
8765
|
+
if (fired && !dead)
|
|
8766
|
+
scan(value, probe);
|
|
8767
|
+
}));
|
|
8768
|
+
}
|
|
8769
|
+
async function reread() {
|
|
8770
|
+
const [nextFacts, nextWhere] = await Promise.allSettled([
|
|
8771
|
+
from.reload(),
|
|
8772
|
+
from.resite()
|
|
8773
|
+
]);
|
|
8774
|
+
if (dead)
|
|
8775
|
+
return;
|
|
8776
|
+
if (nextFacts.status === "fulfilled")
|
|
8777
|
+
local = nextFacts.value;
|
|
8778
|
+
if (nextWhere.status === "fulfilled")
|
|
8779
|
+
where = nextWhere.value;
|
|
8780
|
+
if (!local.signedIn)
|
|
8781
|
+
feed = NO_FEED;
|
|
8782
|
+
set();
|
|
8783
|
+
askFeed();
|
|
8784
|
+
rest(resting);
|
|
8785
|
+
}
|
|
8786
|
+
function forgetScans() {
|
|
8787
|
+
epoch += 1;
|
|
8788
|
+
probes = {};
|
|
8789
|
+
probing = null;
|
|
8790
|
+
set();
|
|
8791
|
+
}
|
|
8792
|
+
function back() {
|
|
8793
|
+
forgetScans();
|
|
8794
|
+
track(reread());
|
|
8795
|
+
}
|
|
8796
|
+
function loggedIn() {
|
|
8797
|
+
forgetScans();
|
|
8798
|
+
return track(ask.learnOrganization(server).catch(noop3).then(reread));
|
|
8799
|
+
}
|
|
8800
|
+
function stop() {
|
|
8801
|
+
dead = true;
|
|
8802
|
+
unrest();
|
|
8803
|
+
listeners.clear();
|
|
8804
|
+
}
|
|
8805
|
+
async function settled() {
|
|
8806
|
+
while (pending.size > 0)
|
|
8807
|
+
await Promise.all([...pending]);
|
|
8808
|
+
}
|
|
8809
|
+
askUpgrade();
|
|
8810
|
+
askFeed();
|
|
8811
|
+
return {
|
|
8812
|
+
read: () => state,
|
|
8813
|
+
subscribe: (listener) => {
|
|
8814
|
+
listeners.add(listener);
|
|
8815
|
+
return () => {
|
|
8816
|
+
listeners.delete(listener);
|
|
8817
|
+
};
|
|
8818
|
+
},
|
|
8819
|
+
back,
|
|
8820
|
+
loggedIn,
|
|
8821
|
+
rest,
|
|
8822
|
+
client,
|
|
8823
|
+
forget: () => {
|
|
8824
|
+
held = null;
|
|
8825
|
+
},
|
|
8826
|
+
resite: from.resite,
|
|
8827
|
+
stop,
|
|
8828
|
+
settled
|
|
8829
|
+
};
|
|
8830
|
+
}
|
|
8831
|
+
// src/shell/screens/home.tsx
|
|
8832
|
+
import { Box as Box14, Text as Text16, useApp as useApp7, useInput as useInput8 } from "ink";
|
|
8833
|
+
import {
|
|
8834
|
+
useCallback as useCallback3,
|
|
8835
|
+
useEffect as useEffect9,
|
|
8836
|
+
useMemo as useMemo4,
|
|
8837
|
+
useState as useState11,
|
|
8838
|
+
useSyncExternalStore
|
|
8839
|
+
} from "react";
|
|
8840
|
+
|
|
8841
|
+
// src/draw.tsx
|
|
8842
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
8843
|
+
function drawInk(answer, {
|
|
8844
|
+
onError,
|
|
8845
|
+
onSummary,
|
|
8846
|
+
onReport,
|
|
8847
|
+
exitWhenDone,
|
|
8848
|
+
onBack,
|
|
8849
|
+
onNext,
|
|
8850
|
+
working
|
|
8851
|
+
}) {
|
|
8852
|
+
const leaves = exitWhenDone === undefined ? {} : { exitWhenDone };
|
|
8853
|
+
const back = onBack ? { onBack } : {};
|
|
8854
|
+
const next = onNext ? { onNext } : {};
|
|
8855
|
+
const reported = onReport ? { onReport } : {};
|
|
8856
|
+
switch (answer.kind) {
|
|
8857
|
+
case "text":
|
|
8858
|
+
return answer.style === "document" ? /* @__PURE__ */ jsx15(HelpScreen, {
|
|
8859
|
+
text: answer.body,
|
|
8860
|
+
...leaves,
|
|
8861
|
+
...back
|
|
8862
|
+
}) : /* @__PURE__ */ jsx15(NoticeScreen, {
|
|
8863
|
+
title: answer.title,
|
|
8864
|
+
message: answer.body,
|
|
8865
|
+
...leaves,
|
|
8866
|
+
...back
|
|
8867
|
+
});
|
|
8868
|
+
case "report": {
|
|
8869
|
+
const says = working ?? answer.working;
|
|
8870
|
+
return /* @__PURE__ */ jsx15(ReportScreen, {
|
|
8871
|
+
title: answer.title,
|
|
8872
|
+
load: answer.load,
|
|
8873
|
+
onError,
|
|
8874
|
+
...says ? { working: says } : {},
|
|
8875
|
+
...reported,
|
|
8876
|
+
...leaves,
|
|
8877
|
+
...back,
|
|
8878
|
+
...next
|
|
8879
|
+
});
|
|
8880
|
+
}
|
|
8881
|
+
case "rows":
|
|
8882
|
+
return /* @__PURE__ */ jsx15(CatalogScreen, {
|
|
8883
|
+
title: answer.title,
|
|
8884
|
+
load: async () => toRows(await answer.load()),
|
|
8885
|
+
onError
|
|
8886
|
+
});
|
|
8887
|
+
case "stream":
|
|
8888
|
+
return /* @__PURE__ */ jsx15(RunScreen, {
|
|
8889
|
+
run: answer.run,
|
|
8890
|
+
onError,
|
|
8891
|
+
...onSummary ? { onDone: onSummary } : {},
|
|
8892
|
+
...reported,
|
|
8893
|
+
...leaves,
|
|
8894
|
+
...back,
|
|
8895
|
+
...next
|
|
8896
|
+
});
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8250
8899
|
|
|
8251
8900
|
// src/args.ts
|
|
8252
8901
|
function configCommand2(rest, explain) {
|
|
@@ -8502,7 +9151,7 @@ function runnable(typed) {
|
|
|
8502
9151
|
|
|
8503
9152
|
// src/shell/screens/keys.tsx
|
|
8504
9153
|
import { Box as Box11, Text as Text13 } from "ink";
|
|
8505
|
-
import { jsx as
|
|
9154
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
8506
9155
|
var KEYS = [
|
|
8507
9156
|
["^ v", "move the cursor", "move, or scroll"],
|
|
8508
9157
|
["enter", "run the highlighted row", "confirm the layer's action"],
|
|
@@ -8528,15 +9177,15 @@ function KeysScreen({ onBack }) {
|
|
|
8528
9177
|
children: [
|
|
8529
9178
|
/* @__PURE__ */ jsxs13(Text13, {
|
|
8530
9179
|
children: [
|
|
8531
|
-
/* @__PURE__ */
|
|
9180
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8532
9181
|
dimColor: true,
|
|
8533
9182
|
children: "".padEnd(key)
|
|
8534
9183
|
}),
|
|
8535
|
-
/* @__PURE__ */
|
|
9184
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8536
9185
|
dimColor: true,
|
|
8537
9186
|
children: "at the prompt".padEnd(half)
|
|
8538
9187
|
}),
|
|
8539
|
-
/* @__PURE__ */
|
|
9188
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8540
9189
|
dimColor: true,
|
|
8541
9190
|
children: "in a layer"
|
|
8542
9191
|
})
|
|
@@ -8544,21 +9193,21 @@ function KeysScreen({ onBack }) {
|
|
|
8544
9193
|
}),
|
|
8545
9194
|
KEYS.map(([k, root, layer]) => /* @__PURE__ */ jsxs13(Text13, {
|
|
8546
9195
|
children: [
|
|
8547
|
-
/* @__PURE__ */
|
|
9196
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8548
9197
|
color: tone.primary,
|
|
8549
9198
|
children: k.padEnd(key)
|
|
8550
9199
|
}),
|
|
8551
|
-
/* @__PURE__ */
|
|
9200
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8552
9201
|
children: clip(root, half - 2).padEnd(half)
|
|
8553
9202
|
}),
|
|
8554
|
-
/* @__PURE__ */
|
|
9203
|
+
/* @__PURE__ */ jsx16(Text13, {
|
|
8555
9204
|
children: clip(layer, interior - key - half)
|
|
8556
9205
|
})
|
|
8557
9206
|
]
|
|
8558
9207
|
}, k))
|
|
8559
9208
|
]
|
|
8560
9209
|
}),
|
|
8561
|
-
/* @__PURE__ */
|
|
9210
|
+
/* @__PURE__ */ jsx16(Back, {
|
|
8562
9211
|
width,
|
|
8563
9212
|
onBack
|
|
8564
9213
|
})
|
|
@@ -8569,7 +9218,7 @@ function KeysScreen({ onBack }) {
|
|
|
8569
9218
|
// src/shell/screens/picker.tsx
|
|
8570
9219
|
import { Box as Box12, Text as Text14, useInput as useInput6 } from "ink";
|
|
8571
9220
|
import { useEffect as useEffect8, useMemo as useMemo3, useState as useState9 } from "react";
|
|
8572
|
-
import { jsx as
|
|
9221
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
8573
9222
|
function PickScreen({
|
|
8574
9223
|
title,
|
|
8575
9224
|
rows,
|
|
@@ -8639,7 +9288,7 @@ function PickScreen({
|
|
|
8639
9288
|
});
|
|
8640
9289
|
const names = nameWidth(rows);
|
|
8641
9290
|
const page = visible.slice(first, first + size);
|
|
8642
|
-
return /* @__PURE__ */
|
|
9291
|
+
return /* @__PURE__ */ jsx17(Frame, {
|
|
8643
9292
|
title,
|
|
8644
9293
|
width,
|
|
8645
9294
|
children: /* @__PURE__ */ jsxs14(Box12, {
|
|
@@ -8647,26 +9296,26 @@ function PickScreen({
|
|
|
8647
9296
|
children: [
|
|
8648
9297
|
/* @__PURE__ */ jsxs14(Box12, {
|
|
8649
9298
|
children: [
|
|
8650
|
-
/* @__PURE__ */
|
|
9299
|
+
/* @__PURE__ */ jsx17(Text14, {
|
|
8651
9300
|
color: tone.primary,
|
|
8652
9301
|
children: "> "
|
|
8653
9302
|
}),
|
|
8654
|
-
/* @__PURE__ */
|
|
9303
|
+
/* @__PURE__ */ jsx17(SearchInput, {
|
|
8655
9304
|
value: query,
|
|
8656
9305
|
placeholder: "type to filter"
|
|
8657
9306
|
})
|
|
8658
9307
|
]
|
|
8659
9308
|
}),
|
|
8660
|
-
/* @__PURE__ */
|
|
9309
|
+
/* @__PURE__ */ jsx17(Divider, {
|
|
8661
9310
|
width
|
|
8662
9311
|
}),
|
|
8663
|
-
rows.length === 0 ? /* @__PURE__ */
|
|
9312
|
+
rows.length === 0 ? /* @__PURE__ */ jsx17(Text14, {
|
|
8664
9313
|
dimColor: true,
|
|
8665
9314
|
children: empty
|
|
8666
|
-
}) : visible.length === 0 ? /* @__PURE__ */
|
|
9315
|
+
}) : visible.length === 0 ? /* @__PURE__ */ jsx17(Text14, {
|
|
8667
9316
|
dimColor: true,
|
|
8668
9317
|
children: `Nothing matches "${query}".`
|
|
8669
|
-
}) : page.map((row, i) => /* @__PURE__ */
|
|
9318
|
+
}) : page.map((row, i) => /* @__PURE__ */ jsx17(SkillRow, {
|
|
8670
9319
|
row,
|
|
8671
9320
|
width: width - 2,
|
|
8672
9321
|
names,
|
|
@@ -8674,20 +9323,20 @@ function PickScreen({
|
|
|
8674
9323
|
cursor: first + i === cursor,
|
|
8675
9324
|
selected: single ? row.name === marked : picked.has(row.name)
|
|
8676
9325
|
}, row.name)),
|
|
8677
|
-
/* @__PURE__ */
|
|
9326
|
+
/* @__PURE__ */ jsx17(Divider, {
|
|
8678
9327
|
width
|
|
8679
9328
|
}),
|
|
8680
9329
|
/* @__PURE__ */ jsxs14(Box12, {
|
|
8681
9330
|
children: [
|
|
8682
|
-
/* @__PURE__ */
|
|
9331
|
+
/* @__PURE__ */ jsx17(Text14, {
|
|
8683
9332
|
dimColor: true,
|
|
8684
9333
|
children: `${position(cursor, visible.length)} `
|
|
8685
9334
|
}),
|
|
8686
|
-
picked.size > 0 && !single ? /* @__PURE__ */
|
|
9335
|
+
picked.size > 0 && !single ? /* @__PURE__ */ jsx17(Text14, {
|
|
8687
9336
|
color: tone.notice,
|
|
8688
9337
|
children: `${mark.selected} ${picked.size} marked `
|
|
8689
9338
|
}) : null,
|
|
8690
|
-
/* @__PURE__ */
|
|
9339
|
+
/* @__PURE__ */ jsx17(Hints, {
|
|
8691
9340
|
hints: [
|
|
8692
9341
|
["^v", "move"],
|
|
8693
9342
|
...single ? [] : [["space", "mark"]],
|
|
@@ -8705,7 +9354,7 @@ function PickScreen({
|
|
|
8705
9354
|
// src/shell/screens/welcome.tsx
|
|
8706
9355
|
import { Box as Box13, Text as Text15, useInput as useInput7 } from "ink";
|
|
8707
9356
|
import { useState as useState10 } from "react";
|
|
8708
|
-
import { jsx as
|
|
9357
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
8709
9358
|
function WelcomeScreen({
|
|
8710
9359
|
agents,
|
|
8711
9360
|
onStart,
|
|
@@ -8736,16 +9385,16 @@ function WelcomeScreen({
|
|
|
8736
9385
|
const gap = Math.max(interior - 2 - label.length - hint.length, 1);
|
|
8737
9386
|
return /* @__PURE__ */ jsxs15(Text15, {
|
|
8738
9387
|
children: [
|
|
8739
|
-
/* @__PURE__ */
|
|
9388
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8740
9389
|
color: tone.primary,
|
|
8741
9390
|
children: `${active ? mark.cursor : " "} `
|
|
8742
9391
|
}),
|
|
8743
|
-
/* @__PURE__ */
|
|
9392
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8744
9393
|
bold: active,
|
|
8745
9394
|
color: active ? tone.primary : undefined,
|
|
8746
9395
|
children: label
|
|
8747
9396
|
}),
|
|
8748
|
-
/* @__PURE__ */
|
|
9397
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8749
9398
|
dimColor: true,
|
|
8750
9399
|
children: `${" ".repeat(gap)}${hint}`
|
|
8751
9400
|
})
|
|
@@ -8759,34 +9408,34 @@ function WelcomeScreen({
|
|
|
8759
9408
|
/* @__PURE__ */ jsxs15(Box13, {
|
|
8760
9409
|
flexDirection: "column",
|
|
8761
9410
|
children: [
|
|
8762
|
-
/* @__PURE__ */
|
|
9411
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8763
9412
|
children: "Welcome. Three steps, about a minute."
|
|
8764
9413
|
}),
|
|
8765
|
-
/* @__PURE__ */
|
|
9414
|
+
/* @__PURE__ */ jsx18(Blank, {}),
|
|
8766
9415
|
steps.map(([n, what, how]) => /* @__PURE__ */ jsxs15(Text15, {
|
|
8767
9416
|
children: [
|
|
8768
|
-
/* @__PURE__ */
|
|
9417
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8769
9418
|
color: tone.primary,
|
|
8770
9419
|
children: `${n} `
|
|
8771
9420
|
}),
|
|
8772
|
-
/* @__PURE__ */
|
|
9421
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8773
9422
|
children: what.padEnd(16)
|
|
8774
9423
|
}),
|
|
8775
|
-
/* @__PURE__ */
|
|
9424
|
+
/* @__PURE__ */ jsx18(Text15, {
|
|
8776
9425
|
dimColor: true,
|
|
8777
9426
|
children: clip(how, interior - 19)
|
|
8778
9427
|
})
|
|
8779
9428
|
]
|
|
8780
9429
|
}, n)),
|
|
8781
|
-
/* @__PURE__ */
|
|
9430
|
+
/* @__PURE__ */ jsx18(Blank, {}),
|
|
8782
9431
|
row("start", "", choice === "start"),
|
|
8783
9432
|
row("skip, I know what I am doing", "yourskills login", choice === "skip")
|
|
8784
9433
|
]
|
|
8785
9434
|
}),
|
|
8786
|
-
/* @__PURE__ */
|
|
9435
|
+
/* @__PURE__ */ jsx18(Divider, {
|
|
8787
9436
|
width
|
|
8788
9437
|
}),
|
|
8789
|
-
/* @__PURE__ */
|
|
9438
|
+
/* @__PURE__ */ jsx18(Hints, {
|
|
8790
9439
|
hints: [
|
|
8791
9440
|
["enter", choice === "start" ? "start" : "skip"],
|
|
8792
9441
|
["esc", "skip"]
|
|
@@ -8797,7 +9446,7 @@ function WelcomeScreen({
|
|
|
8797
9446
|
}
|
|
8798
9447
|
|
|
8799
9448
|
// src/shell/screens/home.tsx
|
|
8800
|
-
import { jsx as
|
|
9449
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
8801
9450
|
function failure(title, error) {
|
|
8802
9451
|
const message = error instanceof Error ? error.message : String(error);
|
|
8803
9452
|
return {
|
|
@@ -8841,25 +9490,25 @@ function AccountLine({ facts, width }) {
|
|
|
8841
9490
|
children: [
|
|
8842
9491
|
/* @__PURE__ */ jsxs16(Text16, {
|
|
8843
9492
|
children: [
|
|
8844
|
-
/* @__PURE__ */
|
|
9493
|
+
/* @__PURE__ */ jsx19(Text16, {
|
|
8845
9494
|
color: facts.signedIn ? "green" : tone.notice,
|
|
8846
9495
|
children: facts.signedIn ? mark.ok : mark.fail
|
|
8847
9496
|
}),
|
|
8848
|
-
/* @__PURE__ */
|
|
9497
|
+
/* @__PURE__ */ jsx19(Text16, {
|
|
8849
9498
|
color: facts.signedIn ? undefined : tone.notice,
|
|
8850
9499
|
children: ` ${clip(head, room - 2)}`
|
|
8851
9500
|
}),
|
|
8852
|
-
showOrg ? /* @__PURE__ */
|
|
9501
|
+
showOrg ? /* @__PURE__ */ jsx19(Text16, {
|
|
8853
9502
|
color: tone.primary,
|
|
8854
9503
|
children: org
|
|
8855
9504
|
}) : null,
|
|
8856
|
-
showTail ? /* @__PURE__ */
|
|
9505
|
+
showTail ? /* @__PURE__ */ jsx19(Text16, {
|
|
8857
9506
|
dimColor: true,
|
|
8858
9507
|
children: tail
|
|
8859
9508
|
}) : null
|
|
8860
9509
|
]
|
|
8861
9510
|
}),
|
|
8862
|
-
/* @__PURE__ */
|
|
9511
|
+
/* @__PURE__ */ jsx19(Text16, {
|
|
8863
9512
|
dimColor: true,
|
|
8864
9513
|
children: clip(right, inner - room)
|
|
8865
9514
|
})
|
|
@@ -8868,70 +9517,39 @@ function AccountLine({ facts, width }) {
|
|
|
8868
9517
|
}
|
|
8869
9518
|
function StaleLine({ stale, width }) {
|
|
8870
9519
|
const text = `${mark.ok} ${stale.latest} is out; you have ${stale.current}. type upgrade`;
|
|
8871
|
-
return /* @__PURE__ */
|
|
9520
|
+
return /* @__PURE__ */ jsx19(Box14, {
|
|
8872
9521
|
width: width - FRAME_CHROME,
|
|
8873
|
-
children: /* @__PURE__ */
|
|
9522
|
+
children: /* @__PURE__ */ jsx19(Text16, {
|
|
8874
9523
|
color: tone.notice,
|
|
8875
9524
|
children: clip(text, width - FRAME_CHROME)
|
|
8876
9525
|
})
|
|
8877
9526
|
});
|
|
8878
9527
|
}
|
|
8879
9528
|
var RUN = "run";
|
|
8880
|
-
var REST_MS = 250;
|
|
8881
|
-
var ASK_NPM = () => upgradeCheck();
|
|
8882
|
-
var ASK_REGISTRY = feedFacts;
|
|
8883
9529
|
var NO_EXIT_CODE = () => {
|
|
8884
9530
|
return;
|
|
8885
9531
|
};
|
|
8886
9532
|
function HomeScreen({
|
|
8887
|
-
|
|
8888
|
-
resite,
|
|
8889
|
-
facts: initial,
|
|
8890
|
-
reload,
|
|
9533
|
+
known,
|
|
8891
9534
|
server,
|
|
8892
|
-
openClient,
|
|
8893
9535
|
onError,
|
|
8894
9536
|
onSummary,
|
|
8895
9537
|
onWelcomed = async () => {
|
|
8896
9538
|
return;
|
|
8897
|
-
}
|
|
8898
|
-
checkUpgrade = ASK_NPM,
|
|
8899
|
-
loadFeed = ASK_REGISTRY,
|
|
8900
|
-
probes: probeSet = probeFor
|
|
9539
|
+
}
|
|
8901
9540
|
}) {
|
|
8902
9541
|
const { exit } = useApp7();
|
|
8903
9542
|
const width = useFrameWidth();
|
|
8904
|
-
|
|
8905
|
-
const
|
|
9543
|
+
useEffect9(() => () => known.stop(), [known]);
|
|
9544
|
+
const machine = useSyncExternalStore(known.subscribe, known.read, known.read);
|
|
9545
|
+
const { where, facts, upgrade: stale, probes, probing } = machine;
|
|
9546
|
+
const { resite } = known;
|
|
8906
9547
|
const [query, setQuery] = useState11("");
|
|
8907
9548
|
const [cursor, setCursor] = useState11(0);
|
|
8908
9549
|
const [route, setRoute] = useState11(null);
|
|
8909
|
-
const [welcome, setWelcome] = useState11(
|
|
9550
|
+
const [welcome, setWelcome] = useState11(() => known.read().facts.firstRun);
|
|
8910
9551
|
const [wizard, setWizard] = useState11(false);
|
|
8911
|
-
const
|
|
8912
|
-
useEffect9(() => {
|
|
8913
|
-
let live = true;
|
|
8914
|
-
checkUpgrade().then((found) => {
|
|
8915
|
-
if (live)
|
|
8916
|
-
setStale(found);
|
|
8917
|
-
}).catch(() => {
|
|
8918
|
-
return;
|
|
8919
|
-
});
|
|
8920
|
-
return () => {
|
|
8921
|
-
live = false;
|
|
8922
|
-
};
|
|
8923
|
-
}, [checkUpgrade]);
|
|
8924
|
-
const [feed, setFeed] = useState11(NO_FEED);
|
|
8925
|
-
const [probes, setProbes] = useState11({});
|
|
8926
|
-
const [probing, setProbing] = useState11(null);
|
|
8927
|
-
const shown = useMemo4(() => ({
|
|
8928
|
-
...facts,
|
|
8929
|
-
feed: {
|
|
8930
|
-
...feed,
|
|
8931
|
-
...stale === undefined ? {} : { upgrade: stale ? stale.latest : null }
|
|
8932
|
-
}
|
|
8933
|
-
}), [facts, feed, stale]);
|
|
8934
|
-
const groups = useMemo4(() => paletteGroups(shown), [shown]);
|
|
9552
|
+
const groups = useMemo4(() => paletteGroups(facts), [facts]);
|
|
8935
9553
|
const typed = useMemo4(() => typedCommand(query), [query]);
|
|
8936
9554
|
const visible = useMemo4(() => {
|
|
8937
9555
|
const filtered = filterCommands(groups, query);
|
|
@@ -8949,48 +9567,17 @@ function HomeScreen({
|
|
|
8949
9567
|
useEffect9(() => {
|
|
8950
9568
|
setCursor((c) => Math.max(0, Math.min(c, rows.length - 1)));
|
|
8951
9569
|
}, [rows.length]);
|
|
8952
|
-
const
|
|
8953
|
-
const connect = useCallback3(async () => {
|
|
8954
|
-
held.current ??= await openClient();
|
|
8955
|
-
return held.current;
|
|
8956
|
-
}, [openClient]);
|
|
8957
|
-
useEffect9(() => {
|
|
8958
|
-
if (!facts.signedIn)
|
|
8959
|
-
return;
|
|
8960
|
-
let live = true;
|
|
8961
|
-
connect().then((client) => loadFeed(client, where, server)).then((found) => {
|
|
8962
|
-
if (live)
|
|
8963
|
-
setFeed(found);
|
|
8964
|
-
}).catch(() => {
|
|
8965
|
-
return;
|
|
8966
|
-
});
|
|
8967
|
-
return () => {
|
|
8968
|
-
live = false;
|
|
8969
|
-
};
|
|
8970
|
-
}, [facts.signedIn, connect, loadFeed, where, server]);
|
|
9570
|
+
const connect = known.client;
|
|
8971
9571
|
const checkSession = useCallback3(async () => facts.signedIn, [facts.signedIn]);
|
|
8972
|
-
const
|
|
8973
|
-
kind: "
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
} catch (error) {
|
|
8980
|
-
return failure(title, error);
|
|
8981
|
-
}
|
|
9572
|
+
const failing = useCallback3((title, error) => ({
|
|
9573
|
+
kind: "answer",
|
|
9574
|
+
working: "",
|
|
9575
|
+
answer: {
|
|
9576
|
+
kind: "report",
|
|
9577
|
+
title,
|
|
9578
|
+
load: async () => failure(title, error)
|
|
8982
9579
|
}
|
|
8983
9580
|
}), []);
|
|
8984
|
-
const answeringRun = useCallback3((run) => run.kind === "stream" ? run : {
|
|
8985
|
-
...run,
|
|
8986
|
-
report: async () => {
|
|
8987
|
-
try {
|
|
8988
|
-
return await run.report();
|
|
8989
|
-
} catch (error) {
|
|
8990
|
-
return failure(run.title, error);
|
|
8991
|
-
}
|
|
8992
|
-
}
|
|
8993
|
-
}, []);
|
|
8994
9581
|
const ctx = useMemo4(() => ({
|
|
8995
9582
|
where,
|
|
8996
9583
|
resite,
|
|
@@ -9020,20 +9607,9 @@ function HomeScreen({
|
|
|
9020
9607
|
...answer.tab ? { tab: answer.tab } : {},
|
|
9021
9608
|
...answer.query ? { query: answer.query } : {}
|
|
9022
9609
|
};
|
|
9023
|
-
case "text":
|
|
9024
|
-
return answering(answer.title, plan, async () => ({
|
|
9025
|
-
title: answer.title,
|
|
9026
|
-
lines: [{ mark: "ok", text: answer.body }],
|
|
9027
|
-
json: { ok: true }
|
|
9028
|
-
}));
|
|
9029
|
-
case "report":
|
|
9030
|
-
return answering(answer.title, plan, answer.load);
|
|
9031
|
-
case "rows":
|
|
9032
|
-
return answering(answer.title, plan, async () => answer.report(await answer.load()));
|
|
9033
|
-
case "stream":
|
|
9034
|
-
return { kind: "run", run: answeringRun(answer.run) };
|
|
9035
9610
|
}
|
|
9036
|
-
|
|
9611
|
+
return { kind: "answer", answer, working: plan };
|
|
9612
|
+
}, []);
|
|
9037
9613
|
const routeForCommand = useCallback3(async (command) => {
|
|
9038
9614
|
if (command.kind === "error")
|
|
9039
9615
|
return null;
|
|
@@ -9044,99 +9620,29 @@ function HomeScreen({
|
|
|
9044
9620
|
const asked = await row.confirm?.(command, ctx, facts);
|
|
9045
9621
|
const answer = asked ?? await row.run(command, ctx);
|
|
9046
9622
|
if (command.kind === "logout")
|
|
9047
|
-
|
|
9623
|
+
known.forget();
|
|
9048
9624
|
return routeFrom(answer, say(command, facts));
|
|
9049
|
-
}, [catalogDoor, ctx, facts, routeFrom]);
|
|
9050
|
-
const
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
empty: "Nothing installed here.",
|
|
9063
|
-
verb: "remove",
|
|
9064
|
-
command: (names) => ({ kind: "remove", names })
|
|
9065
|
-
};
|
|
9066
|
-
case "setup.scope":
|
|
9067
|
-
return {
|
|
9068
|
-
kind: "pick",
|
|
9069
|
-
title: "scope",
|
|
9070
|
-
rows: [
|
|
9071
|
-
{
|
|
9072
|
-
name: "global",
|
|
9073
|
-
description: "installs land in your home directory, where every project sees them"
|
|
9074
|
-
},
|
|
9075
|
-
{
|
|
9076
|
-
name: "project",
|
|
9077
|
-
description: "installs land in this project's yourskills.json, and travel with the repo"
|
|
9078
|
-
}
|
|
9079
|
-
],
|
|
9080
|
-
empty: "",
|
|
9081
|
-
verb: "set",
|
|
9082
|
-
single: true,
|
|
9083
|
-
marked: facts.scope,
|
|
9084
|
-
command: (names) => ({
|
|
9085
|
-
kind: "config",
|
|
9086
|
-
action: "set",
|
|
9087
|
-
key: "scope",
|
|
9088
|
-
value: names[0] ?? ""
|
|
9089
|
-
})
|
|
9090
|
-
};
|
|
9091
|
-
case "setup.agents": {
|
|
9092
|
-
const valid = await knownAgents() ?? [];
|
|
9093
|
-
const onDisk = new Set(facts.agentsOnDisk);
|
|
9094
|
-
return {
|
|
9095
|
-
kind: "pick",
|
|
9096
|
-
title: "agents",
|
|
9097
|
-
rows: valid.map((name) => ({
|
|
9098
|
-
name,
|
|
9099
|
-
description: onDisk.has(name) ? "on this machine" : ""
|
|
9100
|
-
})),
|
|
9101
|
-
empty: "Could not ask the `skills` CLI which agent names it accepts. Run `yourskills doctor`.",
|
|
9102
|
-
verb: "set",
|
|
9103
|
-
preset: facts.agents,
|
|
9104
|
-
command: (names) => ({
|
|
9105
|
-
kind: "config",
|
|
9106
|
-
action: "set",
|
|
9107
|
-
key: "agents",
|
|
9108
|
-
value: names.join(",")
|
|
9109
|
-
})
|
|
9110
|
-
};
|
|
9111
|
-
}
|
|
9112
|
-
case "account.org": {
|
|
9113
|
-
const orgs = await organizationChoices(server);
|
|
9114
|
-
const active = orgs.find((org) => org.active)?.name;
|
|
9115
|
-
return {
|
|
9116
|
-
kind: "pick",
|
|
9117
|
-
title: "org",
|
|
9118
|
-
rows: orgs.map((org) => ({
|
|
9119
|
-
name: org.name,
|
|
9120
|
-
description: org.slug ?? ""
|
|
9121
|
-
})),
|
|
9122
|
-
empty: "You are not a member of any organization yet.",
|
|
9123
|
-
verb: "switch",
|
|
9124
|
-
single: true,
|
|
9125
|
-
...active ? { marked: active } : {},
|
|
9126
|
-
command: (names) => ({ kind: "org", name: names[0] ?? "" })
|
|
9127
|
-
};
|
|
9128
|
-
}
|
|
9129
|
-
}
|
|
9130
|
-
}, [where, server, facts]);
|
|
9131
|
-
const routeForDoor = useCallback3(async (door) => door.opens === undefined ? ownRoute(door.value) : routeForCommand(door.opens), [ownRoute, routeForCommand]);
|
|
9625
|
+
}, [catalogDoor, ctx, facts, known, routeFrom]);
|
|
9626
|
+
const routeForDoor = useCallback3(async (door) => {
|
|
9627
|
+
if (door.opens !== undefined)
|
|
9628
|
+
return routeForCommand(door.opens);
|
|
9629
|
+
const { asks } = door;
|
|
9630
|
+
const choices = await asks.choices(ctx, facts);
|
|
9631
|
+
return {
|
|
9632
|
+
kind: "pick",
|
|
9633
|
+
ask: asks,
|
|
9634
|
+
rows: [...choices.rows],
|
|
9635
|
+
chosen: [...choices.chosen]
|
|
9636
|
+
};
|
|
9637
|
+
}, [ctx, facts, routeForCommand]);
|
|
9132
9638
|
const open = useCallback3((pending, title) => {
|
|
9133
9639
|
pending.then((next) => {
|
|
9134
9640
|
if (next)
|
|
9135
9641
|
setRoute(next);
|
|
9136
9642
|
}).catch((error) => {
|
|
9137
|
-
setRoute(
|
|
9643
|
+
setRoute(failing(title, error));
|
|
9138
9644
|
});
|
|
9139
|
-
}, [
|
|
9645
|
+
}, [failing]);
|
|
9140
9646
|
const runText = useCallback3((text) => {
|
|
9141
9647
|
const parsed = typedCommand(text);
|
|
9142
9648
|
if (!runnable(parsed))
|
|
@@ -9144,29 +9650,10 @@ function HomeScreen({
|
|
|
9144
9650
|
open(routeForCommand(parsed.command), parsed.text);
|
|
9145
9651
|
}, [open, routeForCommand]);
|
|
9146
9652
|
const current = rows[cursor];
|
|
9653
|
+
const resting = current?.value;
|
|
9147
9654
|
useEffect9(() => {
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
if (!value || !probe || probes[value] !== undefined)
|
|
9151
|
-
return;
|
|
9152
|
-
let live = true;
|
|
9153
|
-
const timer = setTimeout(() => {
|
|
9154
|
-
setProbing(value);
|
|
9155
|
-
probe(where, server).then((text) => {
|
|
9156
|
-
if (live)
|
|
9157
|
-
setProbes((was) => ({ ...was, [value]: text }));
|
|
9158
|
-
}).catch(() => {
|
|
9159
|
-
return;
|
|
9160
|
-
}).finally(() => {
|
|
9161
|
-
if (live)
|
|
9162
|
-
setProbing(null);
|
|
9163
|
-
});
|
|
9164
|
-
}, REST_MS);
|
|
9165
|
-
return () => {
|
|
9166
|
-
live = false;
|
|
9167
|
-
clearTimeout(timer);
|
|
9168
|
-
};
|
|
9169
|
-
}, [current?.value, probes, probeSet, where, server]);
|
|
9655
|
+
known.rest(resting);
|
|
9656
|
+
}, [known, resting]);
|
|
9170
9657
|
const activate = useCallback3(() => {
|
|
9171
9658
|
const item = rows[cursor];
|
|
9172
9659
|
if (!item)
|
|
@@ -9189,32 +9676,24 @@ function HomeScreen({
|
|
|
9189
9676
|
setQuery(item.hint.includes("<") ? `${bare} ` : bare);
|
|
9190
9677
|
setCursor(0);
|
|
9191
9678
|
}, [rows, cursor]);
|
|
9192
|
-
const
|
|
9679
|
+
const land = useCallback3(() => {
|
|
9193
9680
|
setRoute(null);
|
|
9194
9681
|
setQuery("");
|
|
9195
9682
|
setCursor(0);
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
return;
|
|
9202
|
-
});
|
|
9203
|
-
}, [reload, resite]);
|
|
9683
|
+
}, []);
|
|
9684
|
+
const back = useCallback3(() => {
|
|
9685
|
+
land();
|
|
9686
|
+
known.back();
|
|
9687
|
+
}, [land, known]);
|
|
9204
9688
|
const afterLogin = useCallback3(() => {
|
|
9205
|
-
learnActiveOrganization(server).then(() => reload()).then(setFacts).catch(() => {
|
|
9206
|
-
return;
|
|
9207
|
-
});
|
|
9208
9689
|
if (!wizard) {
|
|
9209
|
-
|
|
9690
|
+
land();
|
|
9691
|
+
known.loggedIn();
|
|
9210
9692
|
return;
|
|
9211
9693
|
}
|
|
9212
9694
|
setWizard(false);
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
setRoute({ kind: "browse" });
|
|
9216
|
-
}).catch(() => setRoute(null));
|
|
9217
|
-
}, [wizard, back, reload, server]);
|
|
9695
|
+
known.loggedIn().then(() => setRoute({ kind: "browse" }));
|
|
9696
|
+
}, [wizard, land, known]);
|
|
9218
9697
|
const leaveWelcome = useCallback3((start) => {
|
|
9219
9698
|
setWelcome(false);
|
|
9220
9699
|
onWelcomed().catch(() => {
|
|
@@ -9227,7 +9706,7 @@ function HomeScreen({
|
|
|
9227
9706
|
}, [onWelcomed]);
|
|
9228
9707
|
const browseTab = route?.kind === "browse" ? route.tab : undefined;
|
|
9229
9708
|
const browseQuery = route?.kind === "browse" ? route.query : undefined;
|
|
9230
|
-
const browser = useCallback3((live) => /* @__PURE__ */
|
|
9709
|
+
const browser = useCallback3((live) => /* @__PURE__ */ jsx19(BrowserScreen, {
|
|
9231
9710
|
client: live,
|
|
9232
9711
|
where,
|
|
9233
9712
|
onError,
|
|
@@ -9236,6 +9715,14 @@ function HomeScreen({
|
|
|
9236
9715
|
...browseTab ? { initialTab: browseTab } : {},
|
|
9237
9716
|
...browseQuery ? { initialQuery: browseQuery } : {}
|
|
9238
9717
|
}), [where, onError, onSummary, back, browseTab, browseQuery]);
|
|
9718
|
+
const drawn = useMemo4(() => route?.kind === "answer" ? drawInk(route.answer, {
|
|
9719
|
+
onError: (error) => setRoute(failing(route.answer.title, error)),
|
|
9720
|
+
onSummary,
|
|
9721
|
+
exitWhenDone: false,
|
|
9722
|
+
onBack: back,
|
|
9723
|
+
onNext: runText,
|
|
9724
|
+
working: route.working
|
|
9725
|
+
}) : null, [route, onSummary, back, runText, failing]);
|
|
9239
9726
|
useInput8((input, key) => {
|
|
9240
9727
|
if (key.upArrow) {
|
|
9241
9728
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -9261,64 +9748,43 @@ function HomeScreen({
|
|
|
9261
9748
|
}
|
|
9262
9749
|
}, { isActive: route === null && !welcome });
|
|
9263
9750
|
if (welcome) {
|
|
9264
|
-
return /* @__PURE__ */
|
|
9751
|
+
return /* @__PURE__ */ jsx19(WelcomeScreen, {
|
|
9265
9752
|
agents: facts.agentsOnDisk,
|
|
9266
9753
|
onStart: () => leaveWelcome(true),
|
|
9267
9754
|
onSkip: () => leaveWelcome(false)
|
|
9268
9755
|
});
|
|
9269
9756
|
}
|
|
9270
9757
|
if (route?.kind === "login") {
|
|
9271
|
-
return /* @__PURE__ */
|
|
9758
|
+
return /* @__PURE__ */ jsx19(LoginScreen, {
|
|
9272
9759
|
onError,
|
|
9273
9760
|
onDone: afterLogin,
|
|
9274
9761
|
onBack: back
|
|
9275
9762
|
});
|
|
9276
9763
|
}
|
|
9277
9764
|
if (route?.kind === "keys") {
|
|
9278
|
-
return /* @__PURE__ */
|
|
9765
|
+
return /* @__PURE__ */ jsx19(KeysScreen, {
|
|
9279
9766
|
onBack: back
|
|
9280
9767
|
});
|
|
9281
9768
|
}
|
|
9282
|
-
if (
|
|
9283
|
-
return
|
|
9284
|
-
title: route.title,
|
|
9285
|
-
load: route.load,
|
|
9286
|
-
working: route.working,
|
|
9287
|
-
onError,
|
|
9288
|
-
exitWhenDone: false,
|
|
9289
|
-
onBack: back,
|
|
9290
|
-
onNext: runText
|
|
9291
|
-
});
|
|
9292
|
-
}
|
|
9293
|
-
if (route?.kind === "run") {
|
|
9294
|
-
return /* @__PURE__ */ jsx18(RunScreen, {
|
|
9295
|
-
run: route.run,
|
|
9296
|
-
exitWhenDone: false,
|
|
9297
|
-
onError,
|
|
9298
|
-
onDone: onSummary,
|
|
9299
|
-
onBack: back,
|
|
9300
|
-
onNext: runText
|
|
9301
|
-
});
|
|
9302
|
-
}
|
|
9769
|
+
if (drawn)
|
|
9770
|
+
return drawn;
|
|
9303
9771
|
if (route?.kind === "pick") {
|
|
9304
|
-
const
|
|
9305
|
-
return /* @__PURE__ */
|
|
9306
|
-
title:
|
|
9307
|
-
rows:
|
|
9308
|
-
empty:
|
|
9309
|
-
verb:
|
|
9310
|
-
...
|
|
9311
|
-
...picked.marked ? { marked: picked.marked } : {},
|
|
9312
|
-
...picked.preset ? { preset: picked.preset } : {},
|
|
9772
|
+
const { ask, rows: choices, chosen } = route;
|
|
9773
|
+
return /* @__PURE__ */ jsx19(PickScreen, {
|
|
9774
|
+
title: ask.title,
|
|
9775
|
+
rows: choices,
|
|
9776
|
+
empty: ask.empty,
|
|
9777
|
+
verb: ask.verb,
|
|
9778
|
+
...ask.pick === "one" ? { single: true, ...chosen[0] ? { marked: chosen[0] } : {} } : { preset: chosen },
|
|
9313
9779
|
onPick: (names) => {
|
|
9314
|
-
open(routeForCommand(
|
|
9780
|
+
open(routeForCommand(ask.opens(names)), ask.title);
|
|
9315
9781
|
},
|
|
9316
9782
|
onBack: back
|
|
9317
9783
|
});
|
|
9318
9784
|
}
|
|
9319
9785
|
if (route?.kind === "ask") {
|
|
9320
9786
|
const { spec } = route;
|
|
9321
|
-
return /* @__PURE__ */
|
|
9787
|
+
return /* @__PURE__ */ jsx19(ConfirmScreen, {
|
|
9322
9788
|
title: spec.title,
|
|
9323
9789
|
headline: spec.headline,
|
|
9324
9790
|
lines: spec.lines,
|
|
@@ -9326,12 +9792,16 @@ function HomeScreen({
|
|
|
9326
9792
|
yes: spec.yes,
|
|
9327
9793
|
no: spec.no,
|
|
9328
9794
|
argv: spec.argv,
|
|
9329
|
-
onYes: () => setRoute(
|
|
9795
|
+
onYes: () => setRoute({
|
|
9796
|
+
kind: "answer",
|
|
9797
|
+
working: spec.working,
|
|
9798
|
+
answer: { kind: "report", title: spec.title, load: spec.run }
|
|
9799
|
+
}),
|
|
9330
9800
|
onNo: back
|
|
9331
9801
|
});
|
|
9332
9802
|
}
|
|
9333
9803
|
if (route?.kind === "browse") {
|
|
9334
|
-
return /* @__PURE__ */
|
|
9804
|
+
return /* @__PURE__ */ jsx19(AuthGate, {
|
|
9335
9805
|
title: "browse",
|
|
9336
9806
|
check: checkSession,
|
|
9337
9807
|
connect,
|
|
@@ -9342,43 +9812,43 @@ function HomeScreen({
|
|
|
9342
9812
|
});
|
|
9343
9813
|
}
|
|
9344
9814
|
const highlighted = current ? doorFor(current.value) : undefined;
|
|
9345
|
-
const description = current?.value === RUN && runnable(typed) ? say(typed.command,
|
|
9815
|
+
const description = current?.value === RUN && runnable(typed) ? say(typed.command, facts) : highlighted ? doorSays(highlighted, facts) : typed ? say(typed.command, facts) : "";
|
|
9346
9816
|
const found = current ? probes[current.value] ?? (probing === current.value ? "looking..." : "") : "";
|
|
9347
9817
|
const empty = typed?.command.kind === "error" && typed.command.next ? `${mark.fail} ${typed.command.message} try: ${typed.command.next.replace(/^yourskills /, "")}` : "No matching commands.";
|
|
9348
9818
|
return /* @__PURE__ */ jsxs16(Frame, {
|
|
9349
9819
|
title: "yourskills",
|
|
9350
9820
|
width,
|
|
9351
9821
|
children: [
|
|
9352
|
-
/* @__PURE__ */
|
|
9353
|
-
facts
|
|
9822
|
+
/* @__PURE__ */ jsx19(AccountLine, {
|
|
9823
|
+
facts,
|
|
9354
9824
|
width
|
|
9355
9825
|
}),
|
|
9356
|
-
stale ? /* @__PURE__ */
|
|
9826
|
+
stale ? /* @__PURE__ */ jsx19(StaleLine, {
|
|
9357
9827
|
stale,
|
|
9358
9828
|
width
|
|
9359
9829
|
}) : null,
|
|
9360
|
-
/* @__PURE__ */
|
|
9830
|
+
/* @__PURE__ */ jsx19(Divider, {
|
|
9361
9831
|
width
|
|
9362
9832
|
}),
|
|
9363
|
-
/* @__PURE__ */
|
|
9833
|
+
/* @__PURE__ */ jsx19(CommandPalette, {
|
|
9364
9834
|
groups: visible,
|
|
9365
9835
|
query,
|
|
9366
9836
|
cursor,
|
|
9367
9837
|
width,
|
|
9368
9838
|
emptyMessage: empty
|
|
9369
9839
|
}),
|
|
9370
|
-
/* @__PURE__ */
|
|
9840
|
+
/* @__PURE__ */ jsx19(Divider, {
|
|
9371
9841
|
width
|
|
9372
9842
|
}),
|
|
9373
|
-
/* @__PURE__ */
|
|
9843
|
+
/* @__PURE__ */ jsx19(Text16, {
|
|
9374
9844
|
dimColor: true,
|
|
9375
9845
|
children: clip(description, width - FRAME_CHROME)
|
|
9376
9846
|
}),
|
|
9377
|
-
/* @__PURE__ */
|
|
9847
|
+
/* @__PURE__ */ jsx19(Text16, {
|
|
9378
9848
|
dimColor: true,
|
|
9379
9849
|
children: found ? clip(` ${found}`, width - FRAME_CHROME) : " "
|
|
9380
9850
|
}),
|
|
9381
|
-
/* @__PURE__ */
|
|
9851
|
+
/* @__PURE__ */ jsx19(Hints, {
|
|
9382
9852
|
hints: [
|
|
9383
9853
|
["^v", "move"],
|
|
9384
9854
|
["tab", "complete"],
|
|
@@ -9391,7 +9861,7 @@ function HomeScreen({
|
|
|
9391
9861
|
});
|
|
9392
9862
|
}
|
|
9393
9863
|
// src/drive.tsx
|
|
9394
|
-
import { jsx as
|
|
9864
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
9395
9865
|
var liveChores = {
|
|
9396
9866
|
sendUsage: flushQuietly,
|
|
9397
9867
|
offerHooks: ensureHooksRegistered,
|
|
@@ -9466,7 +9936,7 @@ async function drawPlain(answer, output, io) {
|
|
|
9466
9936
|
let code = EXIT.ok;
|
|
9467
9937
|
const done = await plainRun(answer.run, (report) => {
|
|
9468
9938
|
code = emit2(report, output, io);
|
|
9469
|
-
});
|
|
9939
|
+
}, io);
|
|
9470
9940
|
return "pending" in done ? landed(done) : code;
|
|
9471
9941
|
}
|
|
9472
9942
|
}
|
|
@@ -9474,7 +9944,7 @@ async function drawPlain(answer, output, io) {
|
|
|
9474
9944
|
async function screenFor(command, ctx, resite, fail, exit) {
|
|
9475
9945
|
const summarise = (summary) => exit.raise(landed(summary));
|
|
9476
9946
|
const report = (result) => exit.raise(result.failed ? EXIT.failed : EXIT.ok);
|
|
9477
|
-
const gate = (title, build) => /* @__PURE__ */
|
|
9947
|
+
const gate = (title, build) => /* @__PURE__ */ jsx20(AuthGate, {
|
|
9478
9948
|
title,
|
|
9479
9949
|
build,
|
|
9480
9950
|
connect: ctx.client,
|
|
@@ -9484,13 +9954,17 @@ async function screenFor(command, ctx, resite, fail, exit) {
|
|
|
9484
9954
|
const { row, answer } = await answerFor(command, ctx);
|
|
9485
9955
|
if (answer.kind === "home") {
|
|
9486
9956
|
const reload = () => paletteFacts(ctx.where.scope, ctx.server);
|
|
9487
|
-
|
|
9957
|
+
const known = knowMachine({
|
|
9488
9958
|
where: ctx.where,
|
|
9489
|
-
resite,
|
|
9490
9959
|
facts: await reload(),
|
|
9960
|
+
...ctx.server ? { server: ctx.server } : {},
|
|
9491
9961
|
reload,
|
|
9962
|
+
resite,
|
|
9963
|
+
client: ctx.client
|
|
9964
|
+
});
|
|
9965
|
+
return /* @__PURE__ */ jsx20(HomeScreen, {
|
|
9966
|
+
known,
|
|
9492
9967
|
...ctx.server ? { server: ctx.server } : {},
|
|
9493
|
-
openClient: ctx.client,
|
|
9494
9968
|
onError: fail,
|
|
9495
9969
|
onSummary: summarise,
|
|
9496
9970
|
onWelcomed: async () => {
|
|
@@ -9503,54 +9977,26 @@ async function screenFor(command, ctx, resite, fail, exit) {
|
|
|
9503
9977
|
});
|
|
9504
9978
|
}
|
|
9505
9979
|
if (answer.kind === "login")
|
|
9506
|
-
return /* @__PURE__ */
|
|
9980
|
+
return /* @__PURE__ */ jsx20(LoginScreen, {
|
|
9507
9981
|
onError: fail
|
|
9508
9982
|
});
|
|
9509
9983
|
if (answer.kind === "browse")
|
|
9510
|
-
return gate("browse", (client) => /* @__PURE__ */
|
|
9984
|
+
return gate("browse", (client) => /* @__PURE__ */ jsx20(BrowserScreen, {
|
|
9511
9985
|
client,
|
|
9512
9986
|
where: ctx.where,
|
|
9513
9987
|
onError: fail,
|
|
9514
9988
|
onSummary: summarise
|
|
9515
9989
|
}));
|
|
9516
|
-
const drawn = drawInk(answer,
|
|
9990
|
+
const drawn = drawInk(answer, {
|
|
9991
|
+
onError: fail,
|
|
9992
|
+
onSummary: summarise,
|
|
9993
|
+
onReport: report
|
|
9994
|
+
});
|
|
9517
9995
|
return sessionFirst(row, answer) ? gate(answer.title, () => drawn) : drawn;
|
|
9518
9996
|
}
|
|
9519
9997
|
function sessionFirst(row, answer) {
|
|
9520
9998
|
return answer.kind === "stream" ? answer.run.needsSession : row.needs === "session";
|
|
9521
9999
|
}
|
|
9522
|
-
function drawInk(answer, fail, summarise, report) {
|
|
9523
|
-
switch (answer.kind) {
|
|
9524
|
-
case "text":
|
|
9525
|
-
return answer.style === "document" ? /* @__PURE__ */ jsx19(HelpScreen, {
|
|
9526
|
-
text: answer.body
|
|
9527
|
-
}) : /* @__PURE__ */ jsx19(NoticeScreen, {
|
|
9528
|
-
title: answer.title,
|
|
9529
|
-
message: answer.body
|
|
9530
|
-
});
|
|
9531
|
-
case "report":
|
|
9532
|
-
return /* @__PURE__ */ jsx19(ReportScreen, {
|
|
9533
|
-
title: answer.title,
|
|
9534
|
-
load: answer.load,
|
|
9535
|
-
onError: fail,
|
|
9536
|
-
onReport: report,
|
|
9537
|
-
...answer.working ? { working: answer.working } : {}
|
|
9538
|
-
});
|
|
9539
|
-
case "rows":
|
|
9540
|
-
return /* @__PURE__ */ jsx19(CatalogScreen, {
|
|
9541
|
-
title: answer.title,
|
|
9542
|
-
load: async () => toRows(await answer.load()),
|
|
9543
|
-
onError: fail
|
|
9544
|
-
});
|
|
9545
|
-
case "stream":
|
|
9546
|
-
return /* @__PURE__ */ jsx19(RunScreen, {
|
|
9547
|
-
run: answer.run,
|
|
9548
|
-
onError: fail,
|
|
9549
|
-
onDone: summarise,
|
|
9550
|
-
onReport: report
|
|
9551
|
-
});
|
|
9552
|
-
}
|
|
9553
|
-
}
|
|
9554
10000
|
async function runInk(command, ctx, resite, io, exit) {
|
|
9555
10001
|
let failure = null;
|
|
9556
10002
|
let frame = null;
|