roboport 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +25 -0
  2. package/core/agent.d.ts +35 -0
  3. package/core/index.d.ts +9 -0
  4. package/core/mcp.d.ts +6 -0
  5. package/core/message.d.ts +36 -0
  6. package/core/model.d.ts +10 -0
  7. package/core/session.d.ts +75 -0
  8. package/core/skill.d.ts +11 -0
  9. package/core/stream.d.ts +33 -0
  10. package/core/tool.d.ts +77 -0
  11. package/env.d.ts +8 -0
  12. package/harness/claudeCode.d.ts +3 -0
  13. package/harness/codex.d.ts +3 -0
  14. package/harness/core.d.ts +7 -0
  15. package/harness/index.d.ts +5 -0
  16. package/harness/index.js +1512 -0
  17. package/harness/pi.d.ts +3 -0
  18. package/harness/shared.d.ts +33 -0
  19. package/harness/tools.d.ts +33 -0
  20. package/index.d.ts +2 -0
  21. package/index.js +537 -0
  22. package/mcp/auth.d.ts +40 -0
  23. package/mcp/clients/grafana.d.ts +17 -0
  24. package/mcp/clients/linear.d.ts +9 -0
  25. package/mcp/clients/tenderly.d.ts +11 -0
  26. package/mcp/core.d.ts +30 -0
  27. package/mcp/index.d.ts +4 -0
  28. package/mcp/index.js +1356 -0
  29. package/mcp/oauth.d.ts +36 -0
  30. package/mcp/servers/calculator.d.ts +1 -0
  31. package/mcp/storage.d.ts +29 -0
  32. package/models/anthropic.d.ts +15 -0
  33. package/models/google.d.ts +14 -0
  34. package/models/index.d.ts +6 -0
  35. package/models/index.js +2039 -0
  36. package/models/moonshot.d.ts +16 -0
  37. package/models/openai-codex-auth.d.ts +17 -0
  38. package/models/openai-compatible.d.ts +41 -0
  39. package/models/openai.d.ts +29 -0
  40. package/package.json +60 -0
  41. package/skills/index.d.ts +7 -0
  42. package/skills/index.js +1007 -0
  43. package/triggers/bus.d.ts +8 -0
  44. package/triggers/core.d.ts +14 -0
  45. package/triggers/index.d.ts +7 -0
  46. package/triggers/index.js +588 -0
  47. package/triggers/sources/cron.d.ts +29 -0
  48. package/triggers/sources/github.d.ts +148 -0
  49. package/triggers/sources/grafana.d.ts +37 -0
  50. package/triggers/sources/linear.d.ts +39 -0
  51. package/triggers/sources/telegram.d.ts +85 -0
@@ -0,0 +1,1007 @@
1
+ // @bun
2
+ // src/core/agent.ts
3
+ import { z } from "zod";
4
+
5
+ // src/core/session.ts
6
+ class Turn {
7
+ queue = [];
8
+ waiters = [];
9
+ ended = false;
10
+ iterated = false;
11
+ resultPromise;
12
+ abortController = new AbortController;
13
+ constructor(runner) {
14
+ this.resultPromise = runner({
15
+ emit: (event) => this.emit(event),
16
+ signal: this.abortController.signal
17
+ }).then((messages) => {
18
+ this.close();
19
+ return messages;
20
+ }, (error) => {
21
+ this.close();
22
+ throw error;
23
+ });
24
+ this.resultPromise.catch(() => {});
25
+ }
26
+ emit(event) {
27
+ const waiter = this.waiters.shift();
28
+ if (waiter) {
29
+ waiter({ value: event, done: false });
30
+ } else {
31
+ this.queue.push(event);
32
+ }
33
+ }
34
+ close() {
35
+ this.ended = true;
36
+ while (this.waiters.length > 0) {
37
+ const waiter = this.waiters.shift();
38
+ waiter?.({ value: undefined, done: true });
39
+ }
40
+ }
41
+ abort(reason) {
42
+ this.abortController.abort(reason);
43
+ }
44
+ [Symbol.asyncIterator]() {
45
+ if (this.iterated) {
46
+ throw new Error("Turn can only be iterated once.");
47
+ }
48
+ this.iterated = true;
49
+ return {
50
+ next: () => {
51
+ if (this.queue.length > 0) {
52
+ const value = this.queue.shift();
53
+ return Promise.resolve({ value, done: false });
54
+ }
55
+ if (this.ended) {
56
+ return Promise.resolve({
57
+ value: undefined,
58
+ done: true
59
+ });
60
+ }
61
+ return new Promise((resolve) => this.waiters.push(resolve));
62
+ },
63
+ return: async () => {
64
+ this.abort("iteration ended");
65
+ await this.resultPromise.catch(() => {});
66
+ return {
67
+ value: undefined,
68
+ done: true
69
+ };
70
+ }
71
+ };
72
+ }
73
+ then(onfulfilled, onrejected) {
74
+ return this.resultPromise.then(onfulfilled, onrejected);
75
+ }
76
+ }
77
+
78
+ class Session {
79
+ internals;
80
+ state;
81
+ constructor(internals, state) {
82
+ this.internals = internals;
83
+ this.state = state;
84
+ }
85
+ get messages() {
86
+ return this.state.messages;
87
+ }
88
+ send(prompt) {
89
+ return this.internals.send(prompt);
90
+ }
91
+ async close() {
92
+ await this.internals.close();
93
+ }
94
+ async[Symbol.asyncDispose]() {
95
+ await this.close();
96
+ }
97
+ }
98
+
99
+ // src/core/tool.ts
100
+ import * as z4 from "zod/v4/core";
101
+ function hasParseMethod(schema) {
102
+ return typeof schema === "object" && schema !== null && "parse" in schema && typeof schema.parse === "function";
103
+ }
104
+
105
+ class Tool {
106
+ name;
107
+ description;
108
+ inputSchema;
109
+ jsonSchema;
110
+ execute;
111
+ deferred;
112
+ constructor(init) {
113
+ this.name = init.name;
114
+ this.description = init.description;
115
+ this.deferred = init.deferred ?? false;
116
+ if ("inputSchema" in init) {
117
+ this.inputSchema = init.inputSchema;
118
+ this.execute = init.execute;
119
+ } else {
120
+ this.jsonSchema = init.jsonSchema;
121
+ this.execute = init.execute;
122
+ }
123
+ }
124
+ toJsonSchema() {
125
+ if (this.jsonSchema !== undefined)
126
+ return this.jsonSchema;
127
+ if (this.inputSchema === undefined) {
128
+ throw new Error(`Tool "${this.name}" has neither inputSchema nor jsonSchema.`);
129
+ }
130
+ return z4.toJSONSchema(this.inputSchema);
131
+ }
132
+ parse(input) {
133
+ const schema = this.inputSchema;
134
+ if (!schema)
135
+ return input;
136
+ if (hasParseMethod(schema))
137
+ return schema.parse(input);
138
+ return z4.parse(schema, input);
139
+ }
140
+ }
141
+ function createRegistry(tools) {
142
+ const byName = new Map(tools.map((tool) => [tool.name, tool]));
143
+ const loadedNames = new Set(tools.filter((tool) => !tool.deferred).map((tool) => tool.name));
144
+ return {
145
+ loaded: () => tools.filter((tool) => loadedNames.has(tool.name)),
146
+ deferred: () => tools.filter((tool) => tool.deferred && !loadedNames.has(tool.name)),
147
+ load: (names) => {
148
+ const loaded = [];
149
+ const missing = [];
150
+ for (const name of names) {
151
+ const tool = byName.get(name);
152
+ if (!tool) {
153
+ missing.push(name);
154
+ continue;
155
+ }
156
+ loadedNames.add(name);
157
+ loaded.push(tool);
158
+ }
159
+ return { loaded, missing };
160
+ }
161
+ };
162
+ }
163
+
164
+ // src/core/agent.ts
165
+ class Agent {
166
+ model;
167
+ system;
168
+ tools;
169
+ skills;
170
+ mcp;
171
+ cwd;
172
+ registrations = [];
173
+ unsubs = [];
174
+ constructor({
175
+ model,
176
+ system,
177
+ tools,
178
+ skills,
179
+ mcp,
180
+ cwd
181
+ }) {
182
+ this.model = model;
183
+ this.system = system;
184
+ this.tools = tools;
185
+ this.skills = skills;
186
+ this.mcp = mcp ?? [];
187
+ this.cwd = cwd;
188
+ }
189
+ on(trigger, handler) {
190
+ this.registrations.push({ trigger, handler });
191
+ }
192
+ async start() {
193
+ for (const { trigger, handler } of this.registrations) {
194
+ const unsub = await trigger.start((event) => {
195
+ Promise.resolve().then(() => handler(event)).catch((error) => {
196
+ const message = error instanceof Error ? error.message : String(error);
197
+ console.error(`[roboport] trigger "${trigger.name}" handler failed: ${message}`);
198
+ });
199
+ });
200
+ this.unsubs.push(unsub);
201
+ }
202
+ }
203
+ async stop() {
204
+ const unsubs = this.unsubs;
205
+ this.unsubs = [];
206
+ await Promise.all(unsubs.map((u) => u()));
207
+ }
208
+ buildSystem(allTools) {
209
+ let system = this.system;
210
+ if (this.skills.length > 0) {
211
+ const skillsList = this.skills.map((skill) => `- ${skill.name}: ${skill.description}`).join(`
212
+ `);
213
+ system = `${system}
214
+
215
+ # Skills
216
+ The following skills are available. When a task matches one, call the \`Skill\` tool with that skill's name to load its full content before proceeding.
217
+
218
+ ${skillsList}`;
219
+ }
220
+ const deferred = allTools.filter((tool) => tool.deferred);
221
+ if (deferred.length > 0) {
222
+ const list = deferred.map((tool) => `- ${tool.name}`).join(`
223
+ `);
224
+ system = `${system}
225
+
226
+ # Deferred tools
227
+ These tools are available but their schemas are not loaded. Use ToolSearch to load them before calling.
228
+ ${list}`;
229
+ }
230
+ return system;
231
+ }
232
+ buildSkillTool() {
233
+ const byName = new Map(this.skills.map((skill) => [skill.name, skill]));
234
+ return new Tool({
235
+ name: "Skill",
236
+ description: 'Load the full content of a skill listed under "# Skills" in the system prompt. Call this when you decide a listed skill applies to the current task; the returned content extends your instructions for the rest of the session.',
237
+ inputSchema: z.object({
238
+ skill: z.string().describe("Name of the skill to load (must match a listed skill).")
239
+ }),
240
+ execute: ({ skill }) => {
241
+ const found = byName.get(skill);
242
+ if (!found) {
243
+ const available = [...byName.keys()].join(", ");
244
+ throw new Error(`Skill "${skill}" not found. Available: ${available}`);
245
+ }
246
+ return `<skill name="${found.name}">
247
+ ${found.content}
248
+ </skill>`;
249
+ }
250
+ });
251
+ }
252
+ session(init) {
253
+ const initialMessages = init?.messages ? [...init.messages] : [];
254
+ const sessionCwd = init?.cwd ?? this.cwd ?? process.cwd();
255
+ const state = {
256
+ messages: initialMessages,
257
+ store: new Map
258
+ };
259
+ let activeTurn = null;
260
+ let mcpConnected = false;
261
+ let allTools = null;
262
+ let registry = null;
263
+ let ctx = null;
264
+ const ensureReady = async () => {
265
+ if (!allTools || !registry || !ctx) {
266
+ const mcpToolGroups = await Promise.all(this.mcp.map((mcp) => mcp.connect()));
267
+ mcpConnected = true;
268
+ allTools = [
269
+ ...this.tools,
270
+ ...mcpToolGroups.flat(),
271
+ ...this.skills.length > 0 ? [this.buildSkillTool()] : []
272
+ ];
273
+ registry = createRegistry(allTools);
274
+ ctx = {
275
+ complete: async (p) => {
276
+ const response = await this.model.createMessage({
277
+ messages: [{ role: "user", content: p }]
278
+ });
279
+ return response.content.filter((block) => block.type === "text").map((block) => block.text).join(`
280
+ `);
281
+ },
282
+ searchWeb: (query, opts) => this.model.searchWeb(query, opts),
283
+ session: state,
284
+ tools: registry,
285
+ cwd: sessionCwd
286
+ };
287
+ if (state.messages.length === 0 || state.messages[0]?.role !== "system") {
288
+ state.messages.unshift({
289
+ role: "system",
290
+ content: this.buildSystem(allTools)
291
+ });
292
+ }
293
+ }
294
+ return { tools: allTools, registry, ctx };
295
+ };
296
+ const internals = {
297
+ send: (prompt) => {
298
+ if (activeTurn !== null) {
299
+ throw new Error("Session.send() called while another turn is in flight.");
300
+ }
301
+ const turn = new Turn(async (turnCtx) => {
302
+ try {
303
+ const ready = await ensureReady();
304
+ state.messages.push(toUserMessage(prompt));
305
+ await runAgentLoop({
306
+ model: this.model,
307
+ state,
308
+ registry: ready.registry,
309
+ ctx: ready.ctx,
310
+ emit: turnCtx.emit,
311
+ signal: turnCtx.signal
312
+ });
313
+ return [...state.messages];
314
+ } finally {
315
+ activeTurn = null;
316
+ }
317
+ });
318
+ activeTurn = turn;
319
+ return turn;
320
+ },
321
+ close: async () => {
322
+ const pending = activeTurn;
323
+ if (pending) {
324
+ pending.abort("session closed");
325
+ await Promise.resolve(pending).catch(() => {});
326
+ }
327
+ if (mcpConnected) {
328
+ await Promise.all(this.mcp.map((mcp) => mcp.disconnect()));
329
+ mcpConnected = false;
330
+ }
331
+ }
332
+ };
333
+ return new Session(internals, state);
334
+ }
335
+ }
336
+ function toUserMessage(prompt) {
337
+ if (typeof prompt === "string")
338
+ return { role: "user", content: prompt };
339
+ return { role: "user", content: prompt };
340
+ }
341
+ async function runAgentLoop({
342
+ model,
343
+ state,
344
+ registry,
345
+ ctx,
346
+ emit,
347
+ signal
348
+ }) {
349
+ while (true) {
350
+ if (signal.aborted)
351
+ break;
352
+ const active = registry.loaded();
353
+ const toolByName = new Map(active.map((tool) => [tool.name, tool]));
354
+ emit({ type: "message-start" });
355
+ const assistantContent = [];
356
+ let stopReason = "end_turn";
357
+ let usage = { inputTokens: 0, outputTokens: 0 };
358
+ try {
359
+ for await (const event of model.streamMessage({
360
+ messages: state.messages,
361
+ tools: active,
362
+ signal
363
+ })) {
364
+ switch (event.type) {
365
+ case "text-delta":
366
+ emit({ type: "text-delta", text: event.text });
367
+ break;
368
+ case "text-end":
369
+ assistantContent.push({ type: "text", text: event.text });
370
+ emit({ type: "text", text: event.text });
371
+ break;
372
+ case "thinking-delta":
373
+ emit({ type: "thinking-delta", text: event.text });
374
+ break;
375
+ case "thinking-end":
376
+ assistantContent.push({
377
+ type: "thinking",
378
+ text: event.text,
379
+ ...event.signature !== undefined ? { signature: event.signature } : {},
380
+ ...event.redactedData !== undefined ? { redactedData: event.redactedData } : {}
381
+ });
382
+ emit({
383
+ type: "thinking",
384
+ text: event.text,
385
+ ...event.signature !== undefined ? { signature: event.signature } : {}
386
+ });
387
+ break;
388
+ case "tool-call":
389
+ assistantContent.push({
390
+ type: "tool-call",
391
+ toolCallId: event.toolCallId,
392
+ toolName: event.toolName,
393
+ input: event.input
394
+ });
395
+ emit({
396
+ type: "tool-call",
397
+ toolCallId: event.toolCallId,
398
+ toolName: event.toolName,
399
+ input: event.input
400
+ });
401
+ break;
402
+ case "message-end":
403
+ stopReason = event.stopReason;
404
+ usage = event.usage;
405
+ break;
406
+ default:
407
+ break;
408
+ }
409
+ }
410
+ } catch (error) {
411
+ if (signal.aborted) {
412
+ state.messages.push({ role: "assistant", content: assistantContent });
413
+ break;
414
+ }
415
+ const err = error instanceof Error ? error : new Error(String(error));
416
+ emit({ type: "error", error: err });
417
+ throw err;
418
+ }
419
+ state.messages.push({ role: "assistant", content: assistantContent });
420
+ emit({ type: "message-end", usage });
421
+ if (stopReason !== "tool_use") {
422
+ emit({ type: "turn-end" });
423
+ break;
424
+ }
425
+ const toolCalls = assistantContent.filter((block) => block.type === "tool-call");
426
+ const results = [];
427
+ for (const call of toolCalls) {
428
+ if (signal.aborted)
429
+ break;
430
+ const tool = toolByName.get(call.toolName);
431
+ const result = await runTool(tool, call, ctx);
432
+ results.push(result);
433
+ emit({
434
+ type: "tool-result",
435
+ toolCallId: result.toolCallId,
436
+ toolName: result.toolName,
437
+ output: result.output,
438
+ isError: typeof result.output === "string" ? result.output.startsWith("Error:") : false
439
+ });
440
+ }
441
+ state.messages.push({ role: "tool", content: results });
442
+ if (signal.aborted)
443
+ break;
444
+ }
445
+ }
446
+ async function runTool(tool, call, ctx) {
447
+ if (!tool) {
448
+ return {
449
+ type: "tool-result",
450
+ toolCallId: call.toolCallId,
451
+ toolName: call.toolName,
452
+ output: `Error: tool "${call.toolName}" not found`
453
+ };
454
+ }
455
+ try {
456
+ const parsed = tool.parse(call.input);
457
+ const output = await tool.execute(parsed, ctx);
458
+ return {
459
+ type: "tool-result",
460
+ toolCallId: call.toolCallId,
461
+ toolName: call.toolName,
462
+ output
463
+ };
464
+ } catch (error) {
465
+ return {
466
+ type: "tool-result",
467
+ toolCallId: call.toolCallId,
468
+ toolName: call.toolName,
469
+ output: `Error: ${error instanceof Error ? error.message : String(error)}`
470
+ };
471
+ }
472
+ }
473
+
474
+ // src/core/model.ts
475
+ class Model {
476
+ async createMessage(params) {
477
+ const content = [];
478
+ let id = "";
479
+ let stopReason = "end_turn";
480
+ let usage = { inputTokens: 0, outputTokens: 0 };
481
+ for await (const event of this.streamMessage(params)) {
482
+ switch (event.type) {
483
+ case "text-end":
484
+ content.push({ type: "text", text: event.text });
485
+ break;
486
+ case "thinking-end":
487
+ content.push({
488
+ type: "thinking",
489
+ text: event.text,
490
+ ...event.signature !== undefined ? { signature: event.signature } : {},
491
+ ...event.redactedData !== undefined ? { redactedData: event.redactedData } : {}
492
+ });
493
+ break;
494
+ case "tool-call":
495
+ content.push({
496
+ type: "tool-call",
497
+ toolCallId: event.toolCallId,
498
+ toolName: event.toolName,
499
+ input: event.input
500
+ });
501
+ break;
502
+ case "message-end":
503
+ id = event.id;
504
+ stopReason = event.stopReason;
505
+ usage = event.usage;
506
+ break;
507
+ default:
508
+ break;
509
+ }
510
+ }
511
+ return { id, content, stopReason, usage };
512
+ }
513
+ }
514
+
515
+ // src/core/skill.ts
516
+ class Skill {
517
+ name;
518
+ description;
519
+ content;
520
+ constructor({
521
+ name,
522
+ description,
523
+ content
524
+ }) {
525
+ this.name = name;
526
+ this.description = description;
527
+ this.content = content;
528
+ }
529
+ }
530
+
531
+ // src/skills/codeSimplifier/SKILL.md
532
+ var SKILL_default = `---
533
+ name: code-simplifier
534
+ description: Simplifies and refines recently-modified code for clarity, consistency, and naming without changing behaviour. Applies edits in place. Use when the user asks to simplify, tidy, refine, or clean up code they just wrote or changed \u2014 not when they want a review report or a whole-repo audit.
535
+ ---
536
+
537
+ # Code simplifier
538
+
539
+ Refine recently-modified code for clarity, consistency, and maintainability *without changing behaviour*. Apply edits in place.
540
+
541
+ Out of scope: producing a severity-tiered review report, auditing a whole repo for ergonomics, refactors that change observable behaviour.
542
+
543
+ ## Scope
544
+
545
+ Operate only on code the user just wrote or changed in this session, unless they explicitly point you at older code. Don't fish across the repo.
546
+
547
+ If you're not sure which code is in scope, ask once before editing.
548
+
549
+ ## Preserve behaviour
550
+
551
+ Non-negotiable. After any edit:
552
+
553
+ - Public API shapes are unchanged.
554
+ - Outputs, side effects, and error semantics are unchanged.
555
+ - Tests that passed before still pass.
556
+
557
+ If a simplification would alter behaviour \u2014 even marginally \u2014 surface it as a *suggestion* with the tradeoff stated. Do not apply.
558
+
559
+ ## What to simplify
560
+
561
+ In rough order of payoff:
562
+
563
+ 1. **Reduce nesting.** Early returns, guard clauses, flat control flow.
564
+ 2. **Remove dead or redundant code.** Unreachable branches, duplicate logic, unused exports.
565
+ 3. **Inline single-use indirection.** A helper called once with no reuse value is just noise.
566
+ 4. **Improve naming.** Vague names (\`data\`, \`temp\`, \`handle\`) \u2192 concrete ones grounded in the domain.
567
+ 5. **Untangle ternaries.** Nested ternaries \u2192 \`if\`/\`else\` chains or named intermediate variables.
568
+ 6. **Drop redundant comments.** Anything that just restates the next line.
569
+ 7. **Match local style.** Mirror the conventions of the surrounding file before defaulting to "what I'd write."
570
+
571
+ ## What not to do
572
+
573
+ - Don't extract abstractions to "future-proof". Three similar lines are fine.
574
+ - Don't golf \u2014 fewer lines isn't the goal, fewer concepts is.
575
+ - Don't rewrite paragraphs of working code to suit your taste.
576
+ - Don't widen the scope to the rest of the file unless the user said so.
577
+
578
+ ## Output
579
+
580
+ Apply edits with \`Edit\`. For each edit, state in one line *what* and *why* \u2014 e.g. "\`src/foo.ts:42\` \u2014 extracted guard clause, drops 2 levels of nesting."
581
+
582
+ If you can't apply an edit (would change behaviour, or you're unsure), surface it as a one-line suggestion instead of applying.
583
+ `;
584
+
585
+ // src/skills/developerExperience/SKILL.md
586
+ var SKILL_default2 = `---
587
+ name: developer-experience
588
+ description: Reviews a software surface \u2014 a whole repo, an SDK/API, a diff or PR, a single commit, a single file, or a design proposal \u2014 as a linter for developer experience (DX) and agent experience (AX). Surfaces papercuts: friction, inconsistent naming, unidiomatic REST, weak errors, untyped boundaries, and patterns that make the surface hard for LLM-driven agents to consume. Use when the user asks to audit DX, AX, ergonomics, or how a surface feels to use, at any scope.
589
+ ---
590
+
591
+ # Developer experience
592
+
593
+ Review a software surface as a *linter for developer experience*. Surface papercuts \u2014 friction (the user had to know something undocumented to succeed) and inconsistency (the same concept named, shaped, or behaved-with two different ways). Equally evaluates **agent experience** (AX): how easily an LLM-driven agent can use the surface.
594
+
595
+ Out of scope: refactoring the code, writing user-facing docs.
596
+
597
+ ## Scope
598
+
599
+ This skill adapts to the *surface area* under review. Decide it from what you were asked:
600
+
601
+ - **Whole repo / SDK / API** \u2014 the full public surface.
602
+ - **A diff or PR** \u2014 only the surface the change adds, removes, or alters, plus its immediate blast radius.
603
+ - **A single commit** \u2014 the surface that commit touches.
604
+ - **A single file or module** \u2014 the public surface that file exposes.
605
+ - **A proposal / RFC / design doc** \u2014 the surface as *described*; there is no code to read yet.
606
+ - Ambiguous \u2192 ask which.
607
+
608
+ The dimensions, the citation rule, and the what-not-to-flag list below are identical at every scope. Only *what you read* and *how you bound the review* change.
609
+
610
+ ## How to make the review tractable
611
+
612
+ Match the method to the scope. Three passes keep any scope from drowning:
613
+
614
+ 1. **Map the surface.** Enumerate the public surface only \u2014 skip internals.
615
+ - Repo / SDK / API: exported endpoints (router files, OpenAPI), exported symbols (\`index.ts\`, \`package.json#exports\`), error classes, the README quickstart.
616
+ - Diff / commit: only the public symbols the change adds, removes, or alters.
617
+ - File: the symbols that file exports.
618
+ - Proposal: the surface the document describes.
619
+ 2. **Sample.** For a large surface, pick 3\u20135 representative resources / endpoints / tools and review them deeply against the dimensions below. The goal is *patterns*, not coverage. For a diff-sized or single-file surface, review all of it.
620
+ 3. **Place each finding.** Ask: is this **local** (this one symbol) or **systemic** (a pattern across the surface, or drift the change introduces against an existing convention)? Systemic outranks local.
621
+
622
+ Result: review cost is O(surface under review), not O(LOC).
623
+
624
+ ## The citation rule
625
+
626
+ Every finding must cite either:
627
+
628
+ - A public reference \u2014 Stripe error docs, Google AIP, Microsoft REST guidelines, JSON:API \xA7, Anthropic's "Writing tools for agents", RFC 9457, etc. *or*
629
+ - The surface's own internal inconsistency \u2014 endpoint A does X, endpoint B does Y.
630
+
631
+ If neither applies, drop it. "I would have\u2026" is not a citation. This is the single biggest guardrail against taste-creep.
632
+
633
+ ## DX dimensions
634
+
635
+ Roughly in priority order. Lenses, not a checklist:
636
+
637
+ 1. **Consistency of shapes & naming** \u2014 case (snake vs camel), plurality, ID format, response envelope. Compounds across every endpoint.
638
+ 2. **Error design** \u2014 structured object with stable \`code\`, human \`message\`, \`documentation_url\`, optional \`param\` / \`pointer\`. No leaking stack traces.
639
+ 3. **Authentication & onboarding** \u2014 time-to-first-200, key creation friction, scopes, rotation.
640
+ 4. **Resource modeling** \u2014 nouns, plural collections, hierarchy depth \u2264 1, list vs item endpoints.
641
+ 5. **Pagination, filtering, sorting** \u2014 one mechanism repo-wide; cursors stable and opaque; documented defaults and caps.
642
+ 6. **Versioning & deprecation** \u2014 explicit policy; deprecation visible in *both* the response and the docs.
643
+ 7. **Idempotency & safe retries** \u2014 keys on writes, documented replay window, retriable-vs-not made explicit.
644
+ 8. **Type safety on the SDK boundary** \u2014 no \`any\`, generics that infer, discriminated result types, branded nominal IDs, no stringly-typed enums.
645
+ 9. **Docs you can copy-paste** \u2014 runnable examples, env-var conventions, every parameter documented incl. enum values.
646
+ 10. **Observability hooks** \u2014 request IDs in responses and errors, webhook signatures.
647
+ 11. **Defaults & footguns** \u2014 sane defaults, no silent truncation or coercion.
648
+ 12. **Long-running operations** \u2014 one documented pattern (operation polling, webhook, async job), not three.
649
+
650
+ ## AX dimensions
651
+
652
+ Where DX and AX diverge. These are the concrete checks defensible today \u2014 skip speculation about "what agents would prefer".
653
+
654
+ 1. **Machine-readable spec exists and is current.** OpenAPI / TypeSpec / GraphQL SDL / Smithy committed; generated SDKs match.
655
+ 2. **Errors are programmatically branchable.** \`code\` is a stable enum, not free-text. \`is_retriable\` / \`retry_after\` present where meaningful.
656
+ 3. **Non-interactive auth path.** API key or client-credentials works headlessly \u2014 no mandatory browser, CAPTCHA, or MFA on the agent path.
657
+ 4. **Token-efficient responses.** Lists paginate. Field selection or \`expand\` supported. Default payload doesn't dump internal UUIDs and base64 blobs.
658
+ 5. **Self-contained tool/endpoint descriptions.** Every parameter described, enum values listed, "what happens when omitted" stated.
659
+ 6. **Predictable schemas.** Same request, same response shape. No flags that silently change return type.
660
+ 7. **Stable docs URL or \`llms.txt\`.** Deprecation flagged in the *spec*, not only in HTML.
661
+ 8. **Namespacing & discoverability.** Tools/endpoints grouped by resource with consistent prefixes.
662
+
663
+ ## What not to flag
664
+
665
+ - Stylistic taste with no concrete cost ("would be more elegant as\u2026").
666
+ - Linter / formatter territory (semicolons, import order, prettier output).
667
+ - Performance speculation without a measurement.
668
+ - Protocol religion (REST vs GraphQL vs gRPC).
669
+ - Choices a framework forces.
670
+ - Single-occurrence naming nits when the convention is otherwise consistent.
671
+ - When the scope is a diff or commit: pre-existing papercuts outside the surface the change touches \u2014 note systemic drift the change *introduces*, not debt it merely sits next to.
672
+
673
+ ## Output
674
+
675
+ Markdown, sections optional \u2014 omit any that's empty:
676
+
677
+ \`\`\`
678
+ ## Papercuts (blocking)
679
+ - <statement>. \`src/path.ts:42\` \u2014 <why it hurts, citing rule or surface inconsistency>
680
+
681
+ ## Worth addressing
682
+ - ...
683
+
684
+ ## Nits / consistency drift
685
+ - ...
686
+
687
+ ## Agent-experience notes
688
+ - <AX-specific findings, separated because the fix audience may differ>
689
+ \`\`\`
690
+
691
+ End with one line \u2014 a verdict on the surface under review. One of: \`Idiomatic\`, \`Idiomatic with rough edges\`, \`Needs work\`, \`Hostile to agents\`. When reviewing a proposal, frame each finding against the surface it *would* create and phrase the verdict accordingly.
692
+
693
+ No numeric scores. No praise section. Every finding cites a file/line (or, for a proposal, the section it refers to) or a public reference.
694
+ `;
695
+
696
+ // src/skills/docsUpdate/SKILL.md
697
+ var SKILL_default3 = `---
698
+ name: docs-update
699
+ description: Keeps a repo's internal docs (AGENTS.md / CLAUDE.md, docs/**/*.md) in sync with a code change. Routes each fact to one canonical home, preserves the existing voice, and skips trivial changes. Use after a PR-sized change set when public API, commands, structure, or stack claims may have shifted.
700
+ ---
701
+
702
+ # Docs update
703
+
704
+ Keep a repo's *internal* docs (\`AGENTS.md\` / \`CLAUDE.md\`, \`docs/**/*.md\`) in sync with a code change. Surgical edits, one canonical home per fact, matched voice.
705
+
706
+ Out of scope: \`README.md\`, public/user-facing docs sites (Mintlify, Docusaurus, API reference), changelogs, release notes.
707
+
708
+ ## When this runs
709
+
710
+ After a PR-sized change set lands or is about to. Skip for:
711
+
712
+ - Typo / formatting / comment-only changes.
713
+ - Internal refactors with no change to public API, CLI, config, env vars, scripts, or directory layout.
714
+ - Dependency bumps that don't change usage.
715
+ - Test-only changes.
716
+
717
+ If unsure whether a change is doc-worthy, default to *no edit*. Drift is cheaper than slop.
718
+
719
+ ## Inputs
720
+
721
+ Read once, hold in memory:
722
+
723
+ - The diff: \`git diff <base>..HEAD --name-only\` and \`git diff <base>..HEAD\` (or the staged diff if pre-commit).
724
+ - The docs in scope: \`AGENTS.md\` / \`CLAUDE.md\` and \`docs/**/*.md\`. Skip \`README.md\`.
725
+
726
+ Do not pre-read source files. Open them only to verify a specific claim.
727
+
728
+ ## Routing \u2014 one fact, one home
729
+
730
+ | Fact type | Lives in |
731
+ |---|---|
732
+ | Build/test/lint commands, conventions, gotchas, project structure | \`AGENTS.md\` |
733
+ | Multi-page how-to, architecture, ADRs | \`docs/<topic>.md\` |
734
+
735
+ When a fact lands in the diff:
736
+
737
+ 1. Look for an existing doc that already covers the topic \u2014 edit there.
738
+ 2. If none, place it in the *narrowest* home: a bullet in \`AGENTS.md\` before a new \`docs/\` page.
739
+ 3. Never duplicate. If a fact must be referenced from multiple docs, write it once and link.
740
+
741
+ ## Staleness detection
742
+
743
+ Cheap pass: scan each doc for code-shaped tokens, verify they still resolve.
744
+
745
+ - Backticked commands \u2192 still in \`package.json\` scripts / \`Makefile\` / on \`PATH\`?
746
+ - \`file:line\` and \`path/to/x\` references \u2192 file still exists, line still relevant?
747
+ - Named exports, types, CLI flags, env vars, config keys \u2192 still present?
748
+ - Stack / runtime / version claims \u2192 match \`package.json\`, lockfile, \`engines\`?
749
+ - Module-tree descriptions \u2192 match the actual \`src/\` layout?
750
+
751
+ Anything quoted in a doc that the diff invalidates is a *must-fix*. Add new facts only when the diff introduces something a reader would expect to find documented and currently can't.
752
+
753
+ ## Voice preservation
754
+
755
+ Before editing a doc, sample its existing tone \u2014 sentence length, hedging, capitalization, list style. Match it. Edit with \`Edit\` (string replacement), not full rewrites; a one-line change should touch one line.
756
+
757
+ Words to avoid introducing: *comprehensive*, *robust*, *seamlessly*, *leverage*, *in order to*, *cutting-edge*. If a phrase reads like marketing or like an LLM wrote it, rewrite it in the doc's own voice or leave it out.
758
+
759
+ ## AGENTS.md specifically
760
+
761
+ - Keep under ~50 lines. If a topic outgrows that, move to \`docs/<topic>.md\` and link.
762
+ - Prefer \`file:line\` references over inline code snippets \u2014 snippets rot.
763
+ - Omit anything a linter or formatter already enforces.
764
+ - No badges, no ToC, no "Why this project?".
765
+
766
+ ## Output
767
+
768
+ For each proposed change, state:
769
+
770
+ - The file.
771
+ - A one-line reason tied to the diff (e.g. "renamed \`Tool.handler\` \u2192 \`Tool.execute\` (src/core.ts:100)").
772
+ - The edit, applied via \`Edit\`.
773
+
774
+ After applying edits, run the repo's check command (often \`bun run check\` or equivalent) to catch broken refs in fenced code blocks.
775
+
776
+ If no edits are warranted, say so in one line and stop.
777
+ `;
778
+
779
+ // src/skills/prReview/SKILL.md
780
+ var SKILL_default4 = `---
781
+ name: pr-review
782
+ description: Reviews a pull request or branch diff and produces a short, severity-tiered report (must-fix / should-consider / nits) with a final verdict. Use when the user asks to review a PR, review their branch, look at someone else's PR, or mentions a PR number / URL.
783
+ ---
784
+
785
+ # PR review
786
+
787
+ Review a pull request or branch diff for blocking issues before it merges. The output is a short, severity-tiered report \u2014 not a checklist run.
788
+
789
+ ## Scope
790
+
791
+ Three sources. Pick by what the user said:
792
+
793
+ - A PR number, PR URL, or \`#NNN\` \u2192 remote PR. Use \`gh pr view <id> --json title,body,baseRefName,headRefName,author\` for context and \`gh pr diff <id>\` for the patch.
794
+ - "review my branch", "review this branch" \u2192 local diff. Base is \`git merge-base HEAD origin/<default-branch>\`. Use \`git diff <base>...HEAD\` for the patch and \`git log <base>..HEAD --oneline\` for intent.
795
+ - Ambiguous \u2192 ask which one.
796
+
797
+ Skip drafts, lockfiles, and generated files unless explicitly asked.
798
+
799
+ ## Context
800
+
801
+ Load just enough to ground judgement:
802
+
803
+ - Any \`AGENTS.md\` / \`CLAUDE.md\` in the changed files' directory ancestry. These are the source of truth for project conventions.
804
+ - PR title + description, or branch commit messages \u2014 to know the *intent* before reading the patch.
805
+
806
+ Do not pre-read the whole repo. Open files as questions arise during the scan.
807
+
808
+ Do not run the project's lint, typecheck, or test commands. CI already runs them on the same diff, and their output is not more informative than the diff itself.
809
+
810
+ ## Reading the diff
811
+
812
+ Three passes. Single-pass review misses things and over-flags.
813
+
814
+ 1. **Skim.** Summarise the change in one line. Decide if review is warranted. Drafts, pure renames, dep bumps without lockfile changes, mechanical refactors: say so and stop.
815
+ 2. **Scan.** Walk the diff hunk by hunk. Weigh each hunk against the dimensions below. Note *candidate* findings; do not yet decide what to report.
816
+ 3. **Validate.** For every candidate, ask: can I quote the rule it breaks, the input that makes it fail, or the convention in \`AGENTS.md\` it violates? If not, drop it. False positives erode trust faster than missed positives.
817
+
818
+ ## Dimensions
819
+
820
+ In rough priority order \u2014 these are lenses, not a checklist:
821
+
822
+ 1. **Correctness** \u2014 logic, off-by-one, null/empty, error paths, async/race.
823
+ 2. **Security** \u2014 injection, authz, secrets, deserialization, data exposure. Diff-scoped only.
824
+ 3. **Blast radius / design fit** \u2014 does the change belong here? Does it cross boundaries it shouldn't?
825
+ 4. **Tests** \u2014 present where risk warrants, exercising behaviour not implementation.
826
+ 5. **Convention adherence** \u2014 what \`AGENTS.md\` and adjacent code say.
827
+ 6. **API & contract** \u2014 backward-compat, error semantics, public types.
828
+ 7. **Naming & readability** \u2014 only when genuinely confusing.
829
+
830
+ ## What not to flag
831
+
832
+ These destroy signal-to-noise:
833
+
834
+ - Pre-existing issues in unchanged code (diff-scoped only).
835
+ - Style and formatting a linter catches.
836
+ - "Could be more idiomatic" without a concrete reason.
837
+ - Theoretical performance concerns without measurement.
838
+ - Missing tests for trivial or mechanical changes.
839
+ - Documentation for self-evident code.
840
+
841
+ ## Output
842
+
843
+ Markdown. Lead with one line summarising the change (the same summary from the skim pass) \u2014 always, even when there are no findings. Then up to three sections, all optional \u2014 omit any that's empty:
844
+
845
+ \`\`\`
846
+ <one-line summary of the change>
847
+
848
+ ## Must fix
849
+ - <statement>. \`path/to/file.ts:42\` \u2014 <suggested replacement *or* a question>
850
+
851
+ ## Should consider
852
+ - ...
853
+
854
+ ## Nits
855
+ - ...
856
+ \`\`\`
857
+
858
+ End with one line \u2014 a verdict. One of: \`Approve\`, \`Approve with must-fixes\`, \`Request changes\`.
859
+
860
+ No praise section. No confidence scores. Include a committable suggestion only when it fully fixes the issue with no follow-up.
861
+
862
+ ## Posting (remote PRs only)
863
+
864
+ If the user asks to post findings: submit the verdict with \`gh pr review\`, choosing the action that matches it \u2014 \`Approve\` \u2192 \`--approve\`, \`Request changes\` \u2192 \`--request-changes\`, \`Approve with must-fixes\` \u2192 \`--comment\` (advisory) or \`--request-changes\` (gating), caller's choice \u2014 and \`gh api\` for inline comments anchored to \`path\` + \`line\`. Never auto-post.
865
+ `;
866
+
867
+ // src/skills/publicDocs/SKILL.md
868
+ var SKILL_default5 = `---
869
+ name: public-docs
870
+ description: Writes and maintains external, user-facing docs for an SDK / API / product, run from a docs repo with upstream source repos as context. Classifies each page as quickstart / concept / how-to / reference / recipe, prefers generated shape with hand-written meaning, and avoids duplication with the SDK README. Use when working in a docs repo (Mintlify, Docusaurus, or plain markdown) or when the user asks to write or refresh public/user-facing docs.
871
+ ---
872
+
873
+ # Public docs
874
+
875
+ Write and maintain external, user-facing docs for an SDK, API, or product. This skill runs from a docs repo and treats upstream code repos (SDK source, backend, OpenAPI spec) as context.
876
+
877
+ Out of scope: the docs repo's own internal README / \`AGENTS.md\`, changelogs, marketing copy.
878
+
879
+ ## First, orient
880
+
881
+ Three things to discover before writing anything:
882
+
883
+ 1. **Output format.** Inspect the repo root:
884
+ - \`docs.json\` / \`mint.json\` \u2192 Mintlify (MDX).
885
+ - \`docusaurus.config.*\` \u2192 Docusaurus (MDX).
886
+ - Otherwise \u2192 plain Markdown.
887
+ 2. **Upstream source.** Where does the truth live? Look for \`.upstream-refs.json\`, a \`package.json\` \`repository\` field, or a CONTRIBUTING note. If none, ask once and record the answer.
888
+ 3. **Existing pages on the same topic.** Grep the docs tree before drafting. Update > duplicate.
889
+
890
+ ## Doc types \u2014 classify before drafting
891
+
892
+ Each page is *one* of these. Mixing modes is the most common cause of bad docs.
893
+
894
+ | Type | Reader posture | Contains | Does not contain |
895
+ |---|---|---|---|
896
+ | Quickstart | Skim & copy | Prereqs, install, auth, one runnable request, expected response, next-step links | Concepts, full param tables, alternatives |
897
+ | Concept | Sit & read | Mental model, terms defined once, "how it fits" | Code, step-by-step, exhaustive params |
898
+ | How-to | Scan for step | Goal, prereqs, numbered steps, verification, troubleshooting | Theory, tangential alternatives |
899
+ | Reference (API / SDK) | Ctrl-F | Signature or method+path, every param (type, required, default, description), example, response shapes, errors | Tutorials, opinion |
900
+ | Recipe | Steal & adapt | Full runnable file, what to change | Production caveats stacked at the end |
901
+
902
+ Heading is the question the reader asked, not the noun. "How to rotate keys", not "Key rotation".
903
+
904
+ ## Voice
905
+
906
+ - Second person, active, present tense. *"Create a key"*, not *"A key is created"*.
907
+ - One term per concept, used everywhere. Pick "API key" or "API token", not both.
908
+ - Open with the verb. No "Welcome to\u2026", "We're excited to\u2026", "Simply\u2026", "Note that\u2026".
909
+ - Banned adjectives: *powerful, robust, seamless, magical, blazing-fast, simply*. If a sentence reads like marketing, cut it.
910
+
911
+ ## Mintlify MDX components
912
+
913
+ Use sparingly. Plain markdown beats a component that adds no information.
914
+
915
+ | Component | Use for | Avoid for |
916
+ |---|---|---|
917
+ | \`<CodeGroup>\` | Same task in multiple languages | A single snippet |
918
+ | \`<Steps>\` | Ordered, irreversible procedures | Tips, "best practices" |
919
+ | \`<Tabs>\` | Same code across runtimes (Node / Deno / Bun) | Different concepts side-by-side |
920
+ | \`<Cards>\` | Hub or landing pages | Mid-flow navigation |
921
+ | \`<ParamField>\` / \`<ResponseField>\` | Reference param tables | Concept pages |
922
+ | \`<Note>\` / \`<Warning>\` | Genuine constraint or footgun, at most one per page | Praise, sales tone, padding |
923
+ | \`<Accordion>\` | Genuinely optional depth | Hiding required reading |
924
+
925
+ ## Generate the shape, hand-write the meaning
926
+
927
+ | Surface | Approach |
928
+ |---|---|
929
+ | REST endpoints | Generate the reference from OpenAPI. Hand-write the intro paragraph and examples. |
930
+ | TypeScript SDK | Generate signatures from \`tsc --declaration\` or TypeDoc. Hand-write usage and concept links. |
931
+ | Webhooks, errors | Hand-write, cross-checked against a source-of-truth enum or schema. |
932
+ | Quickstart, concept, how-to | Always hand-write. |
933
+
934
+ Generated content with no prose around it reads like \`--help\`. Don't ship it.
935
+
936
+ ## Cross-links to upstream code
937
+
938
+ - Default: link to the SDK's *own published docs page*, not its source.
939
+ - When pointing at an implementation detail, link a permalink at a commit SHA \u2014 never \`main\`. \`main\` links rot silently.
940
+ - Never paste upstream source into a docs page. Snapshot drift > linking cost.
941
+
942
+ ## Avoid duplication with the SDK README
943
+
944
+ The README owns: *what is it*, one runnable example, link to the docs. Nothing else.
945
+
946
+ The docs own: quickstart, concepts, references, how-tos.
947
+
948
+ If a section appears in both, the docs are canonical and the README links to them. Propose collapsing duplicates when you find them.
949
+
950
+ ## Detecting staleness
951
+
952
+ Cheap pass before writing:
953
+
954
+ - For pages with \`<ParamField>\` / \`<ResponseField>\` / fenced code calling SDK symbols \u2192 fetch the upstream file at the latest tagged release and check the symbols still exist.
955
+ - For OpenAPI-driven references \u2192 compare the spec hash in the docs repo to the upstream spec. If they diverge, the reference is stale.
956
+ - Backticked commands and config keys \u2192 verify they still appear in the upstream README or source.
957
+
958
+ Anything quoted in the docs that the upstream invalidates is a must-fix.
959
+
960
+ ## Output
961
+
962
+ For each page touched:
963
+
964
+ - State the doc type and whether it's a new page or an edit.
965
+ - One-line reason tied to the upstream change (or to the user's request).
966
+ - Apply via \`Edit\` for changes, \`Write\` for new pages.
967
+
968
+ After edits, run the docs repo's check (\`mintlify dev\`, \`docusaurus build\`, or repo-specific) to catch broken MDX and link rot.
969
+ `;
970
+
971
+ // src/skills/index.ts
972
+ function parseSkill(raw) {
973
+ const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(raw);
974
+ if (!match) {
975
+ throw new Error("Skill is missing YAML frontmatter.");
976
+ }
977
+ const [, frontmatter, body] = match;
978
+ if (frontmatter === undefined || body === undefined) {
979
+ throw new Error("Skill frontmatter could not be parsed.");
980
+ }
981
+ const fields = {};
982
+ for (const line of frontmatter.split(`
983
+ `)) {
984
+ const kv = /^([A-Za-z][\w-]*):\s*(.+)$/.exec(line);
985
+ if (kv?.[1] && kv[2])
986
+ fields[kv[1]] = kv[2].trim();
987
+ }
988
+ const name = fields.name;
989
+ const description = fields.description;
990
+ if (!name)
991
+ throw new Error('Skill frontmatter is missing "name".');
992
+ if (!description)
993
+ throw new Error('Skill frontmatter is missing "description".');
994
+ return new Skill({ name, description, content: body.trimStart() });
995
+ }
996
+ var prReview = parseSkill(SKILL_default4);
997
+ var docsUpdate = parseSkill(SKILL_default3);
998
+ var publicDocs = parseSkill(SKILL_default5);
999
+ var developerExperience = parseSkill(SKILL_default2);
1000
+ var codeSimplifier = parseSkill(SKILL_default);
1001
+ export {
1002
+ publicDocs,
1003
+ prReview,
1004
+ docsUpdate,
1005
+ developerExperience,
1006
+ codeSimplifier
1007
+ };