@zibby/skills 2.0.5 → 2.0.7

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 (44) hide show
  1. package/dist/artifact.js +7 -7
  2. package/dist/chat-notify.js +4 -4
  3. package/dist/chatProgress.js +4 -4
  4. package/dist/datasetStore.js +2 -2
  5. package/dist/discord.js +3 -3
  6. package/dist/figma.js +2 -2
  7. package/dist/git-write.js +9 -9
  8. package/dist/github.js +5 -5
  9. package/dist/gitlab.js +5 -5
  10. package/dist/googleDocs.js +9 -9
  11. package/dist/hubspot.js +1 -1
  12. package/dist/index.js +179 -181
  13. package/dist/jira.d.ts +19 -9
  14. package/dist/jira.js +22 -20
  15. package/dist/kvMemory.js +2 -2
  16. package/dist/lark.js +2 -2
  17. package/dist/larkAttendance.js +1 -1
  18. package/dist/larkDocs.js +3 -3
  19. package/dist/lib/http-deadline.d.ts +117 -0
  20. package/dist/lib/http-deadline.js +1 -0
  21. package/dist/lib/markup.d.ts +158 -0
  22. package/dist/lib/markup.js +22 -0
  23. package/dist/linear.js +15 -15
  24. package/dist/linkedin.js +2 -2
  25. package/dist/llm-billing.js +1 -1
  26. package/dist/notion.js +7 -7
  27. package/dist/opendesign.js +2 -2
  28. package/dist/package.json +6 -5
  29. package/dist/report.d.ts +42 -42
  30. package/dist/review.js +4 -4
  31. package/dist/reviewMemoryIo.js +1 -1
  32. package/dist/sentry.js +2 -2
  33. package/dist/skill-installer.js +3 -3
  34. package/dist/slack.js +2 -2
  35. package/dist/trackers/github-adapter.js +5 -5
  36. package/dist/trackers/index.js +46 -44
  37. package/dist/trackers/jira-adapter.js +24 -22
  38. package/dist/trackers/linear-adapter.js +19 -19
  39. package/dist/trackers/plane-adapter.js +1 -1
  40. package/dist/triggerAgent.js +1 -1
  41. package/dist/vikunja.d.ts +8 -1
  42. package/dist/vikunja.js +22 -7
  43. package/docs/templates/catalog-metadata.md +1 -1
  44. package/package.json +6 -5
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -73,7 +73,8 @@
73
73
  "./review": {
74
74
  "types": "./dist/review.d.ts",
75
75
  "default": "./dist/review.js"
76
- }
76
+ },
77
+ "./markup": {"types": "./dist/lib/markup.d.ts", "default": "./dist/lib/markup.js"}
77
78
  },
78
79
  "scripts": {
79
80
  "build": "node ../scripts/build.mjs && npm run build:types",
@@ -116,10 +117,10 @@
116
117
  "dependencies": {
117
118
  "@modelcontextprotocol/sdk": "^1.29.0",
118
119
  "@resvg/resvg-js": "^2.6.2",
119
- "@zibby/agent-workflow": "2.0.5",
120
+ "@zibby/agent-workflow": "2.0.7",
120
121
  "@zibby/bin-oxlint": "^1.73.0",
121
122
  "@zibby/bin-semgrep": "^1.169.0",
122
- "@zibby/skill-ids": "2.0.5",
123
+ "@zibby/skill-ids": "2.0.7",
123
124
  "echarts": "^6.1.0",
124
125
  "mem0ai": "npm:@zibby/mem0ai@^3.0.5",
125
126
  "zod": "^3.23.0 || ^4.0.0"
@@ -134,7 +135,7 @@
134
135
  }
135
136
  },
136
137
  "optionalDependencies": {
137
- "@zibby/mcp-browser": "2.0.5",
138
+ "@zibby/mcp-browser": "2.0.7",
138
139
  "@zibby/mcp-memory": "*"
139
140
  },
140
141
  "devDependencies": {
package/dist/report.d.ts CHANGED
@@ -72,16 +72,16 @@ export declare const reportObjectSchema: z.ZodObject<{
72
72
  severity: z.ZodOptional<z.ZodEnum<["ok", "info", "warn", "critical"]>>;
73
73
  }, "strip", z.ZodTypeAny, {
74
74
  values?: number[];
75
+ kind?: "trend";
75
76
  title?: string;
76
77
  severity?: "warn" | "info" | "critical" | "ok";
77
- kind?: "trend";
78
78
  labels?: string[];
79
79
  highlight?: "none" | "last" | "max" | "min";
80
80
  }, {
81
81
  values?: number[];
82
+ kind?: "trend";
82
83
  title?: string;
83
84
  severity?: "warn" | "info" | "critical" | "ok";
84
- kind?: "trend";
85
85
  labels?: string[];
86
86
  highlight?: "none" | "last" | "max" | "min";
87
87
  }>, z.ZodObject<{
@@ -90,13 +90,13 @@ export declare const reportObjectSchema: z.ZodObject<{
90
90
  headers: z.ZodArray<z.ZodString, "many">;
91
91
  rows: z.ZodArray<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">, "many">;
92
92
  }, "strip", z.ZodTypeAny, {
93
- title?: string;
94
93
  kind?: "table";
94
+ title?: string;
95
95
  headers?: string[];
96
96
  rows?: (string | number)[][];
97
97
  }, {
98
- title?: string;
99
98
  kind?: "table";
99
+ title?: string;
100
100
  headers?: string[];
101
101
  rows?: (string | number)[][];
102
102
  }>, z.ZodObject<{
@@ -105,15 +105,15 @@ export declare const reportObjectSchema: z.ZodObject<{
105
105
  tone: z.ZodOptional<z.ZodDefault<z.ZodEnum<["ok", "info", "warn", "critical"]>>>;
106
106
  items: z.ZodArray<z.ZodString, "many">;
107
107
  }, "strip", z.ZodTypeAny, {
108
- title?: string;
109
108
  kind?: "callouts";
110
- tone?: "warn" | "info" | "critical" | "ok";
109
+ title?: string;
111
110
  items?: string[];
111
+ tone?: "warn" | "info" | "critical" | "ok";
112
112
  }, {
113
- title?: string;
114
113
  kind?: "callouts";
115
- tone?: "warn" | "info" | "critical" | "ok";
114
+ title?: string;
116
115
  items?: string[];
116
+ tone?: "warn" | "info" | "critical" | "ok";
117
117
  }>, z.ZodObject<{
118
118
  kind: z.ZodLiteral<"breakdown">;
119
119
  title: z.ZodOptional<z.ZodString>;
@@ -134,8 +134,8 @@ export declare const reportObjectSchema: z.ZodObject<{
134
134
  value?: string;
135
135
  }>, "many">;
136
136
  }, "strip", z.ZodTypeAny, {
137
- title?: string;
138
137
  kind?: "breakdown";
138
+ title?: string;
139
139
  rows?: {
140
140
  label?: string;
141
141
  sub?: string;
@@ -143,8 +143,8 @@ export declare const reportObjectSchema: z.ZodObject<{
143
143
  value?: string;
144
144
  }[];
145
145
  }, {
146
- title?: string;
147
146
  kind?: "breakdown";
147
+ title?: string;
148
148
  rows?: {
149
149
  label?: string;
150
150
  sub?: string;
@@ -156,33 +156,33 @@ export declare const reportObjectSchema: z.ZodObject<{
156
156
  title: z.ZodOptional<z.ZodString>;
157
157
  text: z.ZodString;
158
158
  }, "strip", z.ZodTypeAny, {
159
+ kind?: "paragraph";
159
160
  text?: string;
160
161
  title?: string;
161
- kind?: "paragraph";
162
162
  }, {
163
+ kind?: "paragraph";
163
164
  text?: string;
164
165
  title?: string;
165
- kind?: "paragraph";
166
166
  }>]>, "many">>, ({
167
167
  values?: number[];
168
+ kind?: "trend";
168
169
  title?: string;
169
170
  severity?: "warn" | "info" | "critical" | "ok";
170
- kind?: "trend";
171
171
  labels?: string[];
172
172
  highlight?: "none" | "last" | "max" | "min";
173
173
  } | {
174
- title?: string;
175
174
  kind?: "table";
175
+ title?: string;
176
176
  headers?: string[];
177
177
  rows?: (string | number)[][];
178
178
  } | {
179
- title?: string;
180
179
  kind?: "callouts";
181
- tone?: "warn" | "info" | "critical" | "ok";
180
+ title?: string;
182
181
  items?: string[];
182
+ tone?: "warn" | "info" | "critical" | "ok";
183
183
  } | {
184
- title?: string;
185
184
  kind?: "breakdown";
185
+ title?: string;
186
186
  rows?: {
187
187
  label?: string;
188
188
  sub?: string;
@@ -190,29 +190,29 @@ export declare const reportObjectSchema: z.ZodObject<{
190
190
  value?: string;
191
191
  }[];
192
192
  } | {
193
+ kind?: "paragraph";
193
194
  text?: string;
194
195
  title?: string;
195
- kind?: "paragraph";
196
196
  })[], ({
197
197
  values?: number[];
198
+ kind?: "trend";
198
199
  title?: string;
199
200
  severity?: "warn" | "info" | "critical" | "ok";
200
- kind?: "trend";
201
201
  labels?: string[];
202
202
  highlight?: "none" | "last" | "max" | "min";
203
203
  } | {
204
- title?: string;
205
204
  kind?: "table";
205
+ title?: string;
206
206
  headers?: string[];
207
207
  rows?: (string | number)[][];
208
208
  } | {
209
- title?: string;
210
209
  kind?: "callouts";
211
- tone?: "warn" | "info" | "critical" | "ok";
210
+ title?: string;
212
211
  items?: string[];
212
+ tone?: "warn" | "info" | "critical" | "ok";
213
213
  } | {
214
- title?: string;
215
214
  kind?: "breakdown";
215
+ title?: string;
216
216
  rows?: {
217
217
  label?: string;
218
218
  sub?: string;
@@ -220,9 +220,9 @@ export declare const reportObjectSchema: z.ZodObject<{
220
220
  value?: string;
221
221
  }[];
222
222
  } | {
223
+ kind?: "paragraph";
223
224
  text?: string;
224
225
  title?: string;
225
- kind?: "paragraph";
226
226
  })[]>;
227
227
  footer: z.ZodOptional<z.ZodObject<{
228
228
  viewUrl: z.ZodOptional<z.ZodString>;
@@ -236,6 +236,10 @@ export declare const reportObjectSchema: z.ZodObject<{
236
236
  }>>;
237
237
  }, "strip", z.ZodTypeAny, {
238
238
  title?: string;
239
+ footer?: {
240
+ viewUrl?: string;
241
+ rerunUrl?: string;
242
+ };
239
243
  subtitle?: string;
240
244
  headline?: {
241
245
  summary?: string;
@@ -248,24 +252,24 @@ export declare const reportObjectSchema: z.ZodObject<{
248
252
  };
249
253
  sections?: ({
250
254
  values?: number[];
255
+ kind?: "trend";
251
256
  title?: string;
252
257
  severity?: "warn" | "info" | "critical" | "ok";
253
- kind?: "trend";
254
258
  labels?: string[];
255
259
  highlight?: "none" | "last" | "max" | "min";
256
260
  } | {
257
- title?: string;
258
261
  kind?: "table";
262
+ title?: string;
259
263
  headers?: string[];
260
264
  rows?: (string | number)[][];
261
265
  } | {
262
- title?: string;
263
266
  kind?: "callouts";
264
- tone?: "warn" | "info" | "critical" | "ok";
267
+ title?: string;
265
268
  items?: string[];
269
+ tone?: "warn" | "info" | "critical" | "ok";
266
270
  } | {
267
- title?: string;
268
271
  kind?: "breakdown";
272
+ title?: string;
269
273
  rows?: {
270
274
  label?: string;
271
275
  sub?: string;
@@ -273,16 +277,16 @@ export declare const reportObjectSchema: z.ZodObject<{
273
277
  value?: string;
274
278
  }[];
275
279
  } | {
280
+ kind?: "paragraph";
276
281
  text?: string;
277
282
  title?: string;
278
- kind?: "paragraph";
279
283
  })[];
284
+ }, {
285
+ title?: string;
280
286
  footer?: {
281
287
  viewUrl?: string;
282
288
  rerunUrl?: string;
283
289
  };
284
- }, {
285
- title?: string;
286
290
  subtitle?: string;
287
291
  headline?: {
288
292
  summary?: string;
@@ -295,24 +299,24 @@ export declare const reportObjectSchema: z.ZodObject<{
295
299
  };
296
300
  sections?: ({
297
301
  values?: number[];
302
+ kind?: "trend";
298
303
  title?: string;
299
304
  severity?: "warn" | "info" | "critical" | "ok";
300
- kind?: "trend";
301
305
  labels?: string[];
302
306
  highlight?: "none" | "last" | "max" | "min";
303
307
  } | {
304
- title?: string;
305
308
  kind?: "table";
309
+ title?: string;
306
310
  headers?: string[];
307
311
  rows?: (string | number)[][];
308
312
  } | {
309
- title?: string;
310
313
  kind?: "callouts";
311
- tone?: "warn" | "info" | "critical" | "ok";
314
+ title?: string;
312
315
  items?: string[];
316
+ tone?: "warn" | "info" | "critical" | "ok";
313
317
  } | {
314
- title?: string;
315
318
  kind?: "breakdown";
319
+ title?: string;
316
320
  rows?: {
317
321
  label?: string;
318
322
  sub?: string;
@@ -320,14 +324,10 @@ export declare const reportObjectSchema: z.ZodObject<{
320
324
  value?: string;
321
325
  }[];
322
326
  } | {
327
+ kind?: "paragraph";
323
328
  text?: string;
324
329
  title?: string;
325
- kind?: "paragraph";
326
330
  })[];
327
- footer?: {
328
- viewUrl?: string;
329
- rerunUrl?: string;
330
- };
331
331
  }>;
332
332
  /**
333
333
  * Convert a report-object → Slack Block-Kit `blocks` array.
package/dist/review.js CHANGED
@@ -1,4 +1,4 @@
1
- var A=1,l="review-record",O=204800,T=2e3,I=["blocker","should-fix","nit"],_={blocker:0,"should-fix":1,nit:2},g=["open","conceded","held","resolved"];function v(e){let r=String(e??"").trim().toLowerCase();return r?r.includes("\u{1F534}")||r.includes("blocker")||r.includes("critical")||r.includes("high")?"blocker":r.includes("\u{1F7E2}")||r.includes("nit")||r.includes("minor")||r.includes("low")||r.includes("info")?"nit":(r.includes("\u{1F7E1}")||r.includes("should")||r.includes("medium")||r.includes("warn"),"should-fix"):"should-fix"}var c=(e,r)=>{let n=e==null?"":String(e);return n.length>r?n.slice(0,r):n},E=e=>typeof Buffer<"u"&&typeof Buffer.byteLength=="function"?Buffer.byteLength(e,"utf8"):new TextEncoder().encode(e).length;function k({headSha:e=null,verdict:r="COMMENT",objectivesChecked:n=!1,findings:i=[],nowIso:t=null}={}){let s=Array.isArray(i)?i:[];return{schemaVersion:1,kind:l,headSha:e||null,verdict:String(r||"COMMENT"),objectivesChecked:!!n,reviewedAt:t||null,findings:s.map((o,u)=>({id:`f${u+1}`,file:c(o?.file,512),line:o?.line==null?null:o.line,severity:v(o?.severity),category:c(o?.category,64),claim:c(o?.claim,2e3),evidence:c(o?.evidence,2e3),suggestion:c(o?.suggestion,2e3),confidence:typeof o?.confidence=="number"?o.confidence:null,status:"open"}))}}function C(e,{findingId:r,status:n,note:i}={}){if(!e||!Array.isArray(e.findings))return e;let t=g.includes(n)?n:"held";return{...e,findings:e.findings.map(s=>s.id===r?{...s,status:t,replyNote:c(i,2e3)}:s)}}function a(e){if(!e)return"";let r=[...e.findings||[]].sort((s,o)=>{let u=(_[s.severity]??9)-(_[o.severity]??9);if(u!==0)return u;let y=s.file||"",p=o.file||"";if(y!==p)return y<p?-1:1;let m=Number(s.line)||0,R=Number(o.line)||0;return m!==R?m-R:(s.id||"")<(o.id||"")?-1:1}),n={...e,findings:r},i=JSON.stringify(n);if(E(i)<=204800)return i;let t=[...r];for(n={...e,findings:t,truncated:!0};t.length>0&&E(JSON.stringify(n))>204800;)t.pop(),n={...e,findings:t,truncated:!0};return JSON.stringify(n)}var S=e=>e&&typeof e=="object"&&!Array.isArray(e)&&e.kind===l;function d(e){if(e==null)return{kind:"empty"};if(typeof e=="object"){if(!Array.isArray(e)&&typeof e.error=="string")return{kind:"empty"};if(S(e)){let i=Number(e.schemaVersion)>1;return{kind:"record",record:e,future:i}}return{kind:"legacy",legacyNote:h(e)}}if(typeof e!="string")return{kind:"empty"};let r=e.trim();if(!r)return{kind:"empty"};let n;try{n=JSON.parse(r)}catch{return{kind:"legacy",legacyNote:r}}if(n&&typeof n=="object"&&typeof n.error=="string")return{kind:"empty"};if(S(n)){let i=Number(n.schemaVersion)>1;return{kind:"record",record:n,future:i}}return{kind:"legacy",legacyNote:r}}function h(e){try{return JSON.stringify(e)}catch{return String(e)}}function w(e){if(!e||e.kind==="empty")return"";if(e.kind==="legacy")return`Prior review memory (legacy note, advisory only):
2
- ${e.legacyNote}`;let r=e.record||{};if(e.future)return`Prior review memory (newer format ${r.schemaVersion} \u2014 advisory; verify against the live thread):
3
- ${h(r).slice(0,2e3)}`;let n=[`Prior review of this change (verdict: ${r.verdict||"COMMENT"}${r.headSha?`, commit ${r.headSha}`:""}):`],i=(r.findings||[]).map(t=>{let s=t.file?`${t.file}${t.line!=null?`:${t.line}`:""}`:"(general)",o=t.status&&t.status!=="open"?` [${t.status}]`:"";return`- [${t.severity}] ${s} \u2014 ${t.claim||""}${o}`});return r.truncated&&i.push("- (\u2026older/lower-severity findings were truncated to fit memory)"),[...n,...i].join(`
4
- `)}import{existsSync as M,readFileSync as x}from"node:fs";import{homedir as $}from"node:os";import{join as b}from"node:path";function B(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let e=b($(),".zibby","config.json");return M(e)&&JSON.parse(x(e,"utf-8")).sessionToken||null}catch{return null}}function V(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function D(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function f(e){return`${D()}:${e}`}async function N(e,r){let n=B();if(!n)return{__noToken:!0};let i=`${V()}/credits/review-memory`,t=await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({op:e,...r})});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`review-memory ${e} failed (${t.status}): ${s.slice(0,300)}`)}return t.json()}async function F(e,r){try{let n=a(r);if(!n)return{ok:!1,reason:"empty-record"};let i=await N("store",{scope:f(e),content:n});return i&&i.__noToken?{ok:!1,reason:"no-token"}:{ok:!0}}catch(n){return{ok:!1,reason:n?.message||"store-error"}}}async function L(e){try{let r=await N("recall",{scope:f(e)});if(!r||r.__noToken)return{kind:"empty"};let n=r.found&&r.memory?r.memory:null;if(!n)return{kind:"empty"};let i=n.content!=null?n.content:n.metadata!=null?n.metadata:null;return d(i)}catch{return{kind:"empty"}}}export{O as CONTENT_MAX_BYTES,T as FIELD_MAX_CHARS,g as FINDING_STATUSES,l as REVIEW_RECORD_KIND,A as REVIEW_RECORD_SCHEMA_VERSION,I as SEVERITY_TIERS,k as buildReviewRecord,v as normalizeSeverity,d as parseReviewMemory,L as recallReviewRecord,f as scopeForReviewMemory,a as serializeReviewRecord,F as storeReviewRecord,w as summarizeForPrompt,C as upsertReplyOutcome};
1
+ var h=1,d="review-record",v=204800,A=2e3,k=["blocker","should-fix","nit"],S={blocker:0,"should-fix":1,nit:2},I=["open","conceded","held","resolved"];function O(e){let n=String(e??"").trim().toLowerCase();return n?n.includes("\u{1F534}")||n.includes("blocker")||n.includes("critical")||n.includes("high")?"blocker":n.includes("\u{1F7E2}")||n.includes("nit")||n.includes("minor")||n.includes("low")||n.includes("info")?"nit":(n.includes("\u{1F7E1}")||n.includes("should")||n.includes("medium")||n.includes("warn"),"should-fix"):"should-fix"}var a=(e,n)=>{let t=e==null?"":String(e);return t.length>n?t.slice(0,n):t},R=e=>typeof Buffer<"u"&&typeof Buffer.byteLength=="function"?Buffer.byteLength(e,"utf8"):new TextEncoder().encode(e).length;function N({headSha:e=null,verdict:n="COMMENT",objectivesChecked:t=!1,findings:r=[],nowIso:o=null}={}){let s=Array.isArray(r)?r:[];return{schemaVersion:1,kind:d,headSha:e||null,verdict:String(n||"COMMENT"),objectivesChecked:!!t,reviewedAt:o||null,findings:s.map((i,c)=>({id:`f${c+1}`,file:a(i?.file,512),line:i?.line==null?null:i.line,severity:O(i?.severity),category:a(i?.category,64),claim:a(i?.claim,2e3),evidence:a(i?.evidence,2e3),suggestion:a(i?.suggestion,2e3),confidence:typeof i?.confidence=="number"?i.confidence:null,status:"open"}))}}function L(e,{findingId:n,status:t,note:r}={}){if(!e||!Array.isArray(e.findings))return e;let o=I.includes(t)?t:"held";return{...e,findings:e.findings.map(s=>s.id===n?{...s,status:o,replyNote:a(r,2e3)}:s)}}function _(e){if(!e)return"";let n=[...e.findings||[]].sort((s,i)=>{let c=(S[s.severity]??9)-(S[i.severity]??9);if(c!==0)return c;let l=s.file||"",u=i.file||"";if(l!==u)return l<u?-1:1;let y=Number(s.line)||0,E=Number(i.line)||0;return y!==E?y-E:(s.id||"")<(i.id||"")?-1:1}),t={...e,findings:n},r=JSON.stringify(t);if(R(r)<=204800)return r;let o=[...n];for(t={...e,findings:o,truncated:!0};o.length>0&&R(JSON.stringify(t))>204800;)o.pop(),t={...e,findings:o,truncated:!0};return JSON.stringify(t)}var T=e=>e&&typeof e=="object"&&!Array.isArray(e)&&e.kind===d;function m(e){if(e==null)return{kind:"empty"};if(typeof e=="object"){if(!Array.isArray(e)&&typeof e.error=="string")return{kind:"empty"};if(T(e)){let r=Number(e.schemaVersion)>1;return{kind:"record",record:e,future:r}}return{kind:"legacy",legacyNote:g(e)}}if(typeof e!="string")return{kind:"empty"};let n=e.trim();if(!n)return{kind:"empty"};let t;try{t=JSON.parse(n)}catch{return{kind:"legacy",legacyNote:n}}if(t&&typeof t=="object"&&typeof t.error=="string")return{kind:"empty"};if(T(t)){let r=Number(t.schemaVersion)>1;return{kind:"record",record:t,future:r}}return{kind:"legacy",legacyNote:n}}function g(e){try{return JSON.stringify(e)}catch{return String(e)}}function w(e){if(!e||e.kind==="empty")return"";if(e.kind==="legacy")return`Prior review memory (legacy note, advisory only):
2
+ ${e.legacyNote}`;let n=e.record||{};if(e.future)return`Prior review memory (newer format ${n.schemaVersion} \u2014 advisory; verify against the live thread):
3
+ ${g(n).slice(0,2e3)}`;let t=[`Prior review of this change (verdict: ${n.verdict||"COMMENT"}${n.headSha?`, commit ${n.headSha}`:""}):`],r=(n.findings||[]).map(o=>{let s=o.file?`${o.file}${o.line!=null?`:${o.line}`:""}`:"(general)",i=o.status&&o.status!=="open"?` [${o.status}]`:"";return`- [${o.severity}] ${s} \u2014 ${o.claim||""}${i}`});return n.truncated&&r.push("- (\u2026older/lower-severity findings were truncated to fit memory)"),[...t,...r].join(`
4
+ `)}import{existsSync as F,readFileSync as K}from"node:fs";import{homedir as B}from"node:os";import{join as P}from"node:path";var f={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function x(e,n,t=process.env){let r=Number(t?.[e]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):n}function C(e="api",n=process.env){let t=f[e]||f.api;return x(t.knob,t.fallback,n)}function D(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}function $(e){try{return new URL(String(e?.url??e)).host||"unknown host"}catch{return"unknown host"}}function U(e,n){if(!e)return n;let t=new AbortController,r=i=>{t.signal.aborted||t.abort(i)},o=()=>r(e.reason),s=()=>r(n.reason);return t.signal.addEventListener("abort",()=>{e.removeEventListener("abort",o),n.removeEventListener("abort",s)},{once:!0}),e.aborted?r(e.reason):n.aborted?r(n.reason):(e.addEventListener("abort",o,{once:!0}),n.addEventListener("abort",s,{once:!0})),t.signal}async function M(e,n={},t={}){let r=t.kind||"api",o=(f[r]||f.api).knob,s=t.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(t.timeoutMs))):C(r),i=n?.signal;if(i?.aborted)throw i.reason??new DOMException("This operation was aborted","AbortError");let c=AbortSignal.timeout(s),l=U(i,c);try{return await fetch(e,{...n,signal:l})}catch(u){throw c.aborted&&D(u)?new Error(`${t.what||"request"} TIMED OUT after ${s}ms against ${$(e)} (${o})`):u}}function V(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let e=P(B(),".zibby","config.json");return F(e)&&JSON.parse(K(e,"utf-8")).sessionToken||null}catch{return null}}function Y(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function W(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function p(e){return`${W()}:${e}`}async function b(e,n){let t=V();if(!t)return{__noToken:!0};let r=`${Y()}/credits/review-memory`,o=await M(r,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:e,...n})},{kind:"api",what:`review-memory ${e}`});if(!o.ok){let s=await o.text().catch(()=>"");throw new Error(`review-memory ${e} failed (${o.status}): ${s.slice(0,300)}`)}return o.json()}async function H(e,n){try{let t=_(n);if(!t)return{ok:!1,reason:"empty-record"};let r=await b("store",{scope:p(e),content:t});return r&&r.__noToken?{ok:!1,reason:"no-token"}:{ok:!0}}catch(t){return{ok:!1,reason:t?.message||"store-error"}}}async function J(e){try{let n=await b("recall",{scope:p(e)});if(!n||n.__noToken)return{kind:"empty"};let t=n.found&&n.memory?n.memory:null;if(!t)return{kind:"empty"};let r=t.content!=null?t.content:t.metadata!=null?t.metadata:null;return m(r)}catch{return{kind:"empty"}}}export{v as CONTENT_MAX_BYTES,A as FIELD_MAX_CHARS,I as FINDING_STATUSES,d as REVIEW_RECORD_KIND,h as REVIEW_RECORD_SCHEMA_VERSION,k as SEVERITY_TIERS,N as buildReviewRecord,O as normalizeSeverity,m as parseReviewMemory,J as recallReviewRecord,p as scopeForReviewMemory,_ as serializeReviewRecord,H as storeReviewRecord,w as summarizeForPrompt,L as upsertReplyOutcome};
@@ -1 +1 @@
1
- import{existsSync as S,readFileSync as v}from"node:fs";import{homedir as N}from"node:os";import{join as A}from"node:path";var R="review-record";var d={blocker:0,"should-fix":1,nit:2};var y=e=>typeof Buffer<"u"&&typeof Buffer.byteLength=="function"?Buffer.byteLength(e,"utf8"):new TextEncoder().encode(e).length;function m(e){if(!e)return"";let t=[...e.findings||[]].sort((o,s)=>{let c=(d[o.severity]??9)-(d[s.severity]??9);if(c!==0)return c;let u=o.file||"",l=s.file||"";if(u!==l)return u<l?-1:1;let a=Number(o.line)||0,f=Number(s.line)||0;return a!==f?a-f:(o.id||"")<(s.id||"")?-1:1}),n={...e,findings:t},r=JSON.stringify(n);if(y(r)<=204800)return r;let i=[...t];for(n={...e,findings:i,truncated:!0};i.length>0&&y(JSON.stringify(n))>204800;)i.pop(),n={...e,findings:i,truncated:!0};return JSON.stringify(n)}var p=e=>e&&typeof e=="object"&&!Array.isArray(e)&&e.kind===R;function _(e){if(e==null)return{kind:"empty"};if(typeof e=="object"){if(!Array.isArray(e)&&typeof e.error=="string")return{kind:"empty"};if(p(e)){let r=Number(e.schemaVersion)>1;return{kind:"record",record:e,future:r}}return{kind:"legacy",legacyNote:h(e)}}if(typeof e!="string")return{kind:"empty"};let t=e.trim();if(!t)return{kind:"empty"};let n;try{n=JSON.parse(t)}catch{return{kind:"legacy",legacyNote:t}}if(n&&typeof n=="object"&&typeof n.error=="string")return{kind:"empty"};if(p(n)){let r=Number(n.schemaVersion)>1;return{kind:"record",record:n,future:r}}return{kind:"legacy",legacyNote:t}}function h(e){try{return JSON.stringify(e)}catch{return String(e)}}function O(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let e=A(N(),".zibby","config.json");return S(e)&&JSON.parse(v(e,"utf-8")).sessionToken||null}catch{return null}}function k(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function T(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function E(e){return`${T()}:${e}`}async function g(e,t){let n=O();if(!n)return{__noToken:!0};let r=`${k()}/credits/review-memory`,i=await fetch(r,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({op:e,...t})});if(!i.ok){let o=await i.text().catch(()=>"");throw new Error(`review-memory ${e} failed (${i.status}): ${o.slice(0,300)}`)}return i.json()}async function $(e,t){try{let n=m(t);if(!n)return{ok:!1,reason:"empty-record"};let r=await g("store",{scope:E(e),content:n});return r&&r.__noToken?{ok:!1,reason:"no-token"}:{ok:!0}}catch(n){return{ok:!1,reason:n?.message||"store-error"}}}async function b(e){try{let t=await g("recall",{scope:E(e)});if(!t||t.__noToken)return{kind:"empty"};let n=t.found&&t.memory?t.memory:null;if(!n)return{kind:"empty"};let r=n.content!=null?n.content:n.metadata!=null?n.metadata:null;return _(r)}catch{return{kind:"empty"}}}export{b as recallReviewRecord,E as scopeForReviewMemory,$ as storeReviewRecord};
1
+ import{existsSync as v,readFileSync as A}from"node:fs";import{homedir as N}from"node:os";import{join as L}from"node:path";var b="review-record";var p={blocker:0,"should-fix":1,nit:2};var y=e=>typeof Buffer<"u"&&typeof Buffer.byteLength=="function"?Buffer.byteLength(e,"utf8"):new TextEncoder().encode(e).length;function _(e){if(!e)return"";let t=[...e.findings||[]].sort((i,s)=>{let c=(p[i.severity]??9)-(p[s.severity]??9);if(c!==0)return c;let u=i.file||"",a=s.file||"";if(u!==a)return u<a?-1:1;let f=Number(i.line)||0,d=Number(s.line)||0;return f!==d?f-d:(i.id||"")<(s.id||"")?-1:1}),n={...e,findings:t},r=JSON.stringify(n);if(y(r)<=204800)return r;let o=[...t];for(n={...e,findings:o,truncated:!0};o.length>0&&y(JSON.stringify(n))>204800;)o.pop(),n={...e,findings:o,truncated:!0};return JSON.stringify(n)}var m=e=>e&&typeof e=="object"&&!Array.isArray(e)&&e.kind===b;function E(e){if(e==null)return{kind:"empty"};if(typeof e=="object"){if(!Array.isArray(e)&&typeof e.error=="string")return{kind:"empty"};if(m(e)){let r=Number(e.schemaVersion)>1;return{kind:"record",record:e,future:r}}return{kind:"legacy",legacyNote:O(e)}}if(typeof e!="string")return{kind:"empty"};let t=e.trim();if(!t)return{kind:"empty"};let n;try{n=JSON.parse(t)}catch{return{kind:"legacy",legacyNote:t}}if(n&&typeof n=="object"&&typeof n.error=="string")return{kind:"empty"};if(m(n)){let r=Number(n.schemaVersion)>1;return{kind:"record",record:n,future:r}}return{kind:"legacy",legacyNote:t}}function O(e){try{return JSON.stringify(e)}catch{return String(e)}}var l={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function h(e,t,n=process.env){let r=Number(n?.[e]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):t}function I(e="api",t=process.env){let n=l[e]||l.api;return h(n.knob,n.fallback,t)}function M(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}function R(e){try{return new URL(String(e?.url??e)).host||"unknown host"}catch{return"unknown host"}}function k(e,t){if(!e)return t;let n=new AbortController,r=s=>{n.signal.aborted||n.abort(s)},o=()=>r(e.reason),i=()=>r(t.reason);return n.signal.addEventListener("abort",()=>{e.removeEventListener("abort",o),t.removeEventListener("abort",i)},{once:!0}),e.aborted?r(e.reason):t.aborted?r(t.reason):(e.addEventListener("abort",o,{once:!0}),t.addEventListener("abort",i,{once:!0})),n.signal}async function S(e,t={},n={}){let r=n.kind||"api",o=(l[r]||l.api).knob,i=n.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(n.timeoutMs))):I(r),s=t?.signal;if(s?.aborted)throw s.reason??new DOMException("This operation was aborted","AbortError");let c=AbortSignal.timeout(i),u=k(s,c);try{return await fetch(e,{...t,signal:u})}catch(a){throw c.aborted&&M(a)?new Error(`${n.what||"request"} TIMED OUT after ${i}ms against ${R(e)} (${o})`):a}}function w(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let e=L(N(),".zibby","config.json");return v(e)&&JSON.parse(A(e,"utf-8")).sessionToken||null}catch{return null}}function x(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function C(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function T(e){return`${C()}:${e}`}async function g(e,t){let n=w();if(!n)return{__noToken:!0};let r=`${x()}/credits/review-memory`,o=await S(r,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({op:e,...t})},{kind:"api",what:`review-memory ${e}`});if(!o.ok){let i=await o.text().catch(()=>"");throw new Error(`review-memory ${e} failed (${o.status}): ${i.slice(0,300)}`)}return o.json()}async function V(e,t){try{let n=_(t);if(!n)return{ok:!1,reason:"empty-record"};let r=await g("store",{scope:T(e),content:n});return r&&r.__noToken?{ok:!1,reason:"no-token"}:{ok:!0}}catch(n){return{ok:!1,reason:n?.message||"store-error"}}}async function Y(e){try{let t=await g("recall",{scope:T(e)});if(!t||t.__noToken)return{kind:"empty"};let n=t.found&&t.memory?t.memory:null;if(!n)return{kind:"empty"};let r=n.content!=null?n.content:n.metadata!=null?n.metadata:null;return E(r)}catch{return{kind:"empty"}}}export{Y as recallReviewRecord,T as scopeForReviewMemory,V as storeReviewRecord};
package/dist/sentry.js CHANGED
@@ -1,7 +1,7 @@
1
- import{existsSync as h}from"fs";import{fileURLToPath as _}from"url";import{dirname as S,resolve as I}from"path";import{resolveIntegrationToken as c}from"@zibby/core/backend-client.js";var l=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),N=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});function f(){if(process.env.MCP_SENTRY_PATH)return process.env.MCP_SENTRY_PATH;let n=S(_(import.meta.url)),e=I(n,"..","bin","mcp-sentry.mjs");return h(e)?e:null}function d(n){return(n||process.env.SENTRY_URL||"https://sentry.io").trim().replace(/\/+$/,"")}function m(n){let e=n||process.env.SENTRY_ORG;if(!e)throw new Error('Sentry organization not resolved \u2014 reconnect Sentry, or set SENTRY_ORG (self-hosted; the default org slug is "sentry").');return e}async function g(n,e={}){let{token:t,organizationSlug:s,baseUrl:i}=await c("sentry"),r=`${d(i)}/api/0/organizations/${m(s)}${n}`,o={method:e.method||"GET",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"}};e.body!=null&&(o.body=typeof e.body=="string"?e.body:JSON.stringify(e.body));let a=await fetch(r,o);if(!a.ok){let u=await a.text().catch(()=>"");throw new Error(`Sentry API ${a.status}: ${u.slice(0,300)}`)}return a.json()}async function k(){return g("/projects/?per_page=50")}async function w({query:n="is:unresolved",sort:e="date",project:t,limit:s=25}={}){let i=`/issues/?query=${encodeURIComponent(n)}&sort=${e}&per_page=${s}`;return t&&(i+=`&project=${encodeURIComponent(t)}`),g(i)}async function p(n){let e=String(n??"").trim();if(!e)throw new Error("resolveSentryIssueId: issueRef is required");if(/^\d+$/.test(e))return e;let{token:t,organizationSlug:s,baseUrl:i}=await c("sentry"),r=await fetch(`${d(i)}/api/0/organizations/${m(s)}/shortids/${encodeURIComponent(e)}/`,{headers:{Authorization:`Bearer ${t}`}});if(!r.ok){let u=await r.text().catch(()=>"");throw new Error(`Sentry API ${r.status} resolving shortId "${e}": ${u.slice(0,200)}`)}let o=await r.json(),a=o.groupId||o.group&&o.group.id;if(!a)throw new Error(`Sentry shortId "${e}" did not resolve to a numeric issue id`);return String(a)}async function v(n){if(!n)throw new Error("sentryGetIssue: issueId is required");let e=await p(n),{token:t,baseUrl:s}=await c("sentry"),i=await fetch(`${d(s)}/api/0/issues/${e}/`,{headers:{Authorization:`Bearer ${t}`}});if(!i.ok){let r=await i.text().catch(()=>"");throw new Error(`Sentry API ${i.status}: ${r.slice(0,300)}`)}return i.json()}async function b(n,e={}){if(!n)throw new Error("sentryUpdateIssue: issueId is required");let t={};for(let a of["status","statusDetails","assignedTo","isBookmarked","hasSeen"])e[a]!==void 0&&(t[a]=e[a]);if(Object.keys(t).length===0)throw new Error("sentryUpdateIssue: nothing to update (pass status / statusDetails / assignedTo / isBookmarked / hasSeen)");let s=await p(n),{token:i,baseUrl:r}=await c("sentry"),o=await fetch(`${d(r)}/api/0/issues/${s}/`,{method:"PUT",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!o.ok){let a=await o.text().catch(()=>"");throw o.status===403?new Error(`Sentry API 403 updating issue ${n}: ${a.slice(0,200)}. The connected Sentry integration likely lacks the \`event:write\` scope \u2014 reconnect Sentry with write access to let Zibby resolve/comment on issues.`):new Error(`Sentry API ${o.status} updating issue ${n}: ${a.slice(0,300)}`)}return o.json()}async function P(n,e){if(!n)throw new Error("sentryAddComment: issueId is required");if(!e||!String(e).trim())throw new Error("sentryAddComment: text is required");let t=await p(n),{token:s,baseUrl:i}=await c("sentry"),r=await fetch(`${d(i)}/api/0/issues/${t}/comments/`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({text:String(e)})});if(!r.ok){let o=await r.text().catch(()=>"");throw r.status===403?new Error(`Sentry API 403 commenting on issue ${n}: ${o.slice(0,200)}. The connected Sentry integration likely lacks the \`event:write\` scope \u2014 reconnect Sentry with write access.`):new Error(`Sentry API ${r.status} commenting on issue ${n}: ${o.slice(0,300)}`)}return r.json()}var y={id:"sentry",callsBackend:!0,serverName:"sentry",allowedTools:["mcp__sentry__*"],requiresIntegration:l.SENTRY,description:"Sentry error tracking \u2014 projects, issues, events",envKeys:[],tools:[],promptFragment:`## Sentry
1
+ import{existsSync as E}from"fs";import{fileURLToPath as w}from"url";import{dirname as O,resolve as v}from"path";import{resolveIntegrationToken as u}from"@zibby/core/backend-client.js";var _=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),U=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});var p={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function I(n,e,t=process.env){let r=Number(t?.[n]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):e}function f(n="api",e=process.env){let t=p[n]||p.api;return I(t.knob,t.fallback,e)}function b(n){return n?.name==="TimeoutError"||n?.name==="AbortError"}function k(n){try{return new URL(String(n?.url??n)).host||"unknown host"}catch{return"unknown host"}}function T(n,e){if(!n)return e;let t=new AbortController,r=o=>{t.signal.aborted||t.abort(o)},i=()=>r(n.reason),s=()=>r(e.reason);return t.signal.addEventListener("abort",()=>{n.removeEventListener("abort",i),e.removeEventListener("abort",s)},{once:!0}),n.aborted?r(n.reason):e.aborted?r(e.reason):(n.addEventListener("abort",i,{once:!0}),e.addEventListener("abort",s,{once:!0})),t.signal}async function c(n,e={},t={}){let r=t.kind||"api",i=(p[r]||p.api).knob,s=t.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(t.timeoutMs))):f(r),o=e?.signal;if(o?.aborted)throw o.reason??new DOMException("This operation was aborted","AbortError");let a=AbortSignal.timeout(s),d=T(o,a);try{return await fetch(n,{...e,signal:d})}catch(y){throw a.aborted&&b(y)?new Error(`${t.what||"request"} TIMED OUT after ${s}ms against ${k(n)} (${i})`):y}}function A(){if(process.env.MCP_SENTRY_PATH)return process.env.MCP_SENTRY_PATH;let n=O(w(import.meta.url)),e=v(n,"..","bin","mcp-sentry.mjs");return E(e)?e:null}function l(n){return(n||process.env.SENTRY_URL||"https://sentry.io").trim().replace(/\/+$/,"")}function h(n){let e=n||process.env.SENTRY_ORG;if(!e)throw new Error('Sentry organization not resolved \u2014 reconnect Sentry, or set SENTRY_ORG (self-hosted; the default org slug is "sentry").');return e}async function S(n,e={}){let{token:t,organizationSlug:r,baseUrl:i}=await u("sentry"),s=`${l(i)}/api/0/organizations/${h(r)}${n}`,o={method:e.method||"GET",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"}};e.body!=null&&(o.body=typeof e.body=="string"?e.body:JSON.stringify(e.body));let a=await c(s,o,{kind:"api",what:`Sentry ${e.method||"GET"} ${n}`});if(!a.ok){let d=await a.text().catch(()=>"");throw new Error(`Sentry API ${a.status}: ${d.slice(0,300)}`)}return a.json()}async function L(){return S("/projects/?per_page=50")}async function N({query:n="is:unresolved",sort:e="date",project:t,limit:r=25}={}){let i=`/issues/?query=${encodeURIComponent(n)}&sort=${e}&per_page=${r}`;return t&&(i+=`&project=${encodeURIComponent(t)}`),S(i)}async function m(n){let e=String(n??"").trim();if(!e)throw new Error("resolveSentryIssueId: issueRef is required");if(/^\d+$/.test(e))return e;let{token:t,organizationSlug:r,baseUrl:i}=await u("sentry"),s=await c(`${l(i)}/api/0/organizations/${h(r)}/shortids/${encodeURIComponent(e)}/`,{headers:{Authorization:`Bearer ${t}`}},{kind:"api",what:"Sentry GET shortids"});if(!s.ok){let d=await s.text().catch(()=>"");throw new Error(`Sentry API ${s.status} resolving shortId "${e}": ${d.slice(0,200)}`)}let o=await s.json(),a=o.groupId||o.group&&o.group.id;if(!a)throw new Error(`Sentry shortId "${e}" did not resolve to a numeric issue id`);return String(a)}async function P(n){if(!n)throw new Error("sentryGetIssue: issueId is required");let e=await m(n),{token:t,baseUrl:r}=await u("sentry"),i=await c(`${l(r)}/api/0/issues/${e}/`,{headers:{Authorization:`Bearer ${t}`}},{kind:"api",what:`Sentry GET issue ${e}`});if(!i.ok){let s=await i.text().catch(()=>"");throw new Error(`Sentry API ${i.status}: ${s.slice(0,300)}`)}return i.json()}async function R(n,e={}){if(!n)throw new Error("sentryUpdateIssue: issueId is required");let t={};for(let a of["status","statusDetails","assignedTo","isBookmarked","hasSeen"])e[a]!==void 0&&(t[a]=e[a]);if(Object.keys(t).length===0)throw new Error("sentryUpdateIssue: nothing to update (pass status / statusDetails / assignedTo / isBookmarked / hasSeen)");let r=await m(n),{token:i,baseUrl:s}=await u("sentry"),o=await c(`${l(s)}/api/0/issues/${r}/`,{method:"PUT",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},body:JSON.stringify(t)},{kind:"api",what:`Sentry PUT issue ${r}`});if(!o.ok){let a=await o.text().catch(()=>"");throw o.status===403?new Error(`Sentry API 403 updating issue ${n}: ${a.slice(0,200)}. The connected Sentry integration likely lacks the \`event:write\` scope \u2014 reconnect Sentry with write access to let Zibby resolve/comment on issues.`):new Error(`Sentry API ${o.status} updating issue ${n}: ${a.slice(0,300)}`)}return o.json()}async function $(n,e){if(!n)throw new Error("sentryAddComment: issueId is required");if(!e||!String(e).trim())throw new Error("sentryAddComment: text is required");let t=await m(n),{token:r,baseUrl:i}=await u("sentry"),s=await c(`${l(i)}/api/0/issues/${t}/comments/`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify({text:String(e)})},{kind:"api",what:`Sentry POST issue ${t} comment`});if(!s.ok){let o=await s.text().catch(()=>"");throw s.status===403?new Error(`Sentry API 403 commenting on issue ${n}: ${o.slice(0,200)}. The connected Sentry integration likely lacks the \`event:write\` scope \u2014 reconnect Sentry with write access.`):new Error(`Sentry API ${s.status} commenting on issue ${n}: ${o.slice(0,300)}`)}return s.json()}var g={id:"sentry",callsBackend:!0,serverName:"sentry",allowedTools:["mcp__sentry__*"],requiresIntegration:_.SENTRY,description:"Sentry error tracking \u2014 projects, issues, events",envKeys:[],tools:[],promptFragment:`## Sentry
2
2
  You have access to the user's Sentry. Use these tools:
3
3
  - sentry_list_projects: List projects in the organization
4
4
  - sentry_list_issues: List errors/issues (supports Sentry search query, project filter, sort)
5
5
  - sentry_get_issue: Get detailed info about a specific issue (requires issueId)
6
6
  - sentry_update_issue: Change an issue's status (resolved / resolvedInNextRelease / ignored / unresolved / muted), assignment, or bookmark (requires issueId; needs write scope)
7
- - sentry_add_comment: Post a comment/note on an issue (requires issueId + text; needs write scope)`,resolve(){let n=f();if(!n)return null;let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_USER_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","PROGRESS_API_URL","EXECUTION_ID","PROJECT_ID","STAGE","ZIBBY_SELF_HOST","SENTRY_URL","SENTRY_ORG","SENTRY_AUTH_TOKEN"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[n],env:e,alwaysLoad:!0}},async handleToolCall(n,e={}){try{switch(n){case"sentry_list_projects":{let t=await k();return JSON.stringify({projects:t.map(s=>({slug:s.slug,name:s.name,platform:s.platform}))})}case"sentry_list_issues":{let t=await w({query:e.query,sort:e.sort,project:e.project,limit:e.limit});return JSON.stringify({issues:t.map(s=>({id:s.id,title:s.title,culprit:s.culprit,count:s.count,firstSeen:s.firstSeen,lastSeen:s.lastSeen,level:s.level,status:s.status}))})}case"sentry_get_issue":{let t=await v(e.issueId);return JSON.stringify({id:t.id,title:t.title,culprit:t.culprit,metadata:t.metadata,count:t.count,userCount:t.userCount,firstSeen:t.firstSeen,lastSeen:t.lastSeen,level:t.level,status:t.status,project:{slug:t.project?.slug,name:t.project?.name}})}case"sentry_update_issue":{let t=await b(e.issueId,{status:e.status,statusDetails:e.statusDetails,assignedTo:e.assignedTo,isBookmarked:e.isBookmarked,hasSeen:e.hasSeen});return JSON.stringify({ok:!0,id:t.id??e.issueId,status:t.status,assignedTo:t.assignedTo,isBookmarked:t.isBookmarked})}case"sentry_add_comment":{let t=await P(e.issueId,e.text);return JSON.stringify({ok:!0,id:t.id,issueId:e.issueId})}default:return JSON.stringify({error:`Unknown tool: ${n}`})}}catch(t){return JSON.stringify({error:t.message})}},toolsForAssistant:[{name:"sentry_list_projects",description:"List Sentry projects",input_schema:{type:"object",properties:{}}},{name:"sentry_list_issues",description:"List Sentry issues (errors)",input_schema:{type:"object",properties:{project:{type:"string",description:"Project slug (optional)"},query:{type:"string",description:"Sentry search query (default: is:unresolved)"},sort:{type:"string",description:"Sort order: date, new, priority, freq, user (default: date)"},limit:{type:"number",description:"Max issues to return (default 25)"}}}},{name:"sentry_get_issue",description:"Get details of a specific Sentry issue",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"}},required:["issueId"]}},{name:"sentry_update_issue",description:"Update a Sentry issue's status, assignment, or bookmark (needs event:write scope)",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"},status:{type:"string",description:"resolved | resolvedInNextRelease | unresolved | ignored | muted"},statusDetails:{type:"object",description:'Optional status details, e.g. { "inRelease": "latest" }'},assignedTo:{type:"string",description:'Assignee actor id, e.g. "user:123" or "team:456" (optional)'},isBookmarked:{type:"boolean",description:"Bookmark/unbookmark the issue (optional)"},hasSeen:{type:"boolean",description:"Mark the issue seen/unseen (optional)"}},required:["issueId"]}},{name:"sentry_add_comment",description:"Post a comment/note on a Sentry issue (needs event:write scope)",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"},text:{type:"string",description:"Comment body (markdown)"}},required:["issueId","text"]}}]};y.tools=y.toolsForAssistant;export{p as resolveSentryIssueId,P as sentryAddComment,g as sentryFetch,v as sentryGetIssue,w as sentryListIssues,k as sentryListProjects,y as sentrySkill,b as sentryUpdateIssue};
7
+ - sentry_add_comment: Post a comment/note on an issue (requires issueId + text; needs write scope)`,resolve(){let n=A();if(!n)return null;let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_USER_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","PROGRESS_API_URL","EXECUTION_ID","PROJECT_ID","STAGE","ZIBBY_SELF_HOST","SENTRY_URL","SENTRY_ORG","SENTRY_AUTH_TOKEN"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[n],env:e,alwaysLoad:!0}},async handleToolCall(n,e={}){try{switch(n){case"sentry_list_projects":{let t=await L();return JSON.stringify({projects:t.map(r=>({slug:r.slug,name:r.name,platform:r.platform}))})}case"sentry_list_issues":{let t=await N({query:e.query,sort:e.sort,project:e.project,limit:e.limit});return JSON.stringify({issues:t.map(r=>({id:r.id,title:r.title,culprit:r.culprit,count:r.count,firstSeen:r.firstSeen,lastSeen:r.lastSeen,level:r.level,status:r.status}))})}case"sentry_get_issue":{let t=await P(e.issueId);return JSON.stringify({id:t.id,title:t.title,culprit:t.culprit,metadata:t.metadata,count:t.count,userCount:t.userCount,firstSeen:t.firstSeen,lastSeen:t.lastSeen,level:t.level,status:t.status,project:{slug:t.project?.slug,name:t.project?.name}})}case"sentry_update_issue":{let t=await R(e.issueId,{status:e.status,statusDetails:e.statusDetails,assignedTo:e.assignedTo,isBookmarked:e.isBookmarked,hasSeen:e.hasSeen});return JSON.stringify({ok:!0,id:t.id??e.issueId,status:t.status,assignedTo:t.assignedTo,isBookmarked:t.isBookmarked})}case"sentry_add_comment":{let t=await $(e.issueId,e.text);return JSON.stringify({ok:!0,id:t.id,issueId:e.issueId})}default:return JSON.stringify({error:`Unknown tool: ${n}`})}}catch(t){return JSON.stringify({error:t.message})}},toolsForAssistant:[{name:"sentry_list_projects",description:"List Sentry projects",input_schema:{type:"object",properties:{}}},{name:"sentry_list_issues",description:"List Sentry issues (errors)",input_schema:{type:"object",properties:{project:{type:"string",description:"Project slug (optional)"},query:{type:"string",description:"Sentry search query (default: is:unresolved)"},sort:{type:"string",description:"Sort order: date, new, priority, freq, user (default: date)"},limit:{type:"number",description:"Max issues to return (default 25)"}}}},{name:"sentry_get_issue",description:"Get details of a specific Sentry issue",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"}},required:["issueId"]}},{name:"sentry_update_issue",description:"Update a Sentry issue's status, assignment, or bookmark (needs event:write scope)",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"},status:{type:"string",description:"resolved | resolvedInNextRelease | unresolved | ignored | muted"},statusDetails:{type:"object",description:'Optional status details, e.g. { "inRelease": "latest" }'},assignedTo:{type:"string",description:'Assignee actor id, e.g. "user:123" or "team:456" (optional)'},isBookmarked:{type:"boolean",description:"Bookmark/unbookmark the issue (optional)"},hasSeen:{type:"boolean",description:"Mark the issue seen/unseen (optional)"}},required:["issueId"]}},{name:"sentry_add_comment",description:"Post a comment/note on a Sentry issue (needs event:write scope)",input_schema:{type:"object",properties:{issueId:{type:"string",description:"Sentry issue ID"},text:{type:"string",description:"Comment body (markdown)"}},required:["issueId","text"]}}]};g.tools=g.toolsForAssistant;export{m as resolveSentryIssueId,$ as sentryAddComment,S as sentryFetch,P as sentryGetIssue,N as sentryListIssues,L as sentryListProjects,g as sentrySkill,R as sentryUpdateIssue};
@@ -1,4 +1,4 @@
1
- import{existsSync as m,readFileSync as b}from"fs";import{homedir as v}from"os";import{join as I}from"path";import{spawn as w}from"child_process";var r={jira:{description:"Jira issue search, details, comments, transitions",integrationProvider:"jira",envKeys:[],setupInstructions:`To connect Jira:
1
+ import{existsSync as O,readFileSync as w}from"fs";import{homedir as E}from"os";import{join as M}from"path";import{spawn as L}from"child_process";var f={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function I(t,e,n=process.env){let i=Number(n?.[t]);return Number.isFinite(i)&&i>0?Math.min(6e5,Math.max(1e3,Math.floor(i))):e}function _(t="api",e=process.env){let n=f[t]||f.api;return I(n.knob,n.fallback,e)}function S(t){return t?.name==="TimeoutError"||t?.name==="AbortError"}function v(t){try{return new URL(String(t?.url??t)).host||"unknown host"}catch{return"unknown host"}}function T(t,e){if(!t)return e;let n=new AbortController,i=o=>{n.signal.aborted||n.abort(o)},a=()=>i(t.reason),s=()=>i(e.reason);return n.signal.addEventListener("abort",()=>{t.removeEventListener("abort",a),e.removeEventListener("abort",s)},{once:!0}),t.aborted?i(t.reason):e.aborted?i(e.reason):(t.addEventListener("abort",a,{once:!0}),e.addEventListener("abort",s,{once:!0})),n.signal}async function g(t,e={},n={}){let i=n.kind||"api",a=(f[i]||f.api).knob,s=n.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(n.timeoutMs))):_(i),o=e?.signal;if(o?.aborted)throw o.reason??new DOMException("This operation was aborted","AbortError");let r=AbortSignal.timeout(s),p=T(o,r);try{return await fetch(t,{...e,signal:p})}catch(l){throw r.aborted&&S(l)?new Error(`${n.what||"request"} TIMED OUT after ${s}ms against ${v(t)} (${a})`):l}}var c={jira:{description:"Jira issue search, details, comments, transitions",integrationProvider:"jira",envKeys:[],setupInstructions:`To connect Jira:
2
2
  1. Go to Settings \u2192 Integrations (https://studio.zibby.dev/integrations)
3
3
  2. Click "Connect Jira" and authorize via Atlassian OAuth
4
4
  3. After OAuth completes, ask me to install Jira again`},github:{description:"GitHub issues, PRs, repository management",integrationProvider:"github",envKeys:[],setupInstructions:`To connect GitHub:
@@ -10,5 +10,5 @@ import{existsSync as m,readFileSync as b}from"fs";import{homedir as v}from"os";i
10
10
  3. After OAuth completes, ask me to install Slack again`},sentry:{description:"Sentry error tracking \u2014 projects, issues, events",integrationProvider:"sentry",envKeys:[],setupInstructions:`To connect Sentry:
11
11
  1. Go to Settings \u2192 Integrations (https://studio.zibby.dev/integrations)
12
12
  2. Click "Connect Sentry" and authorize
13
- 3. After OAuth completes, ask me to install Sentry again`},runner:{description:"Run zibby test workflows from chat (parallel supported)",envKeys:[],setupInstructions:"Ready to use. Runs zibby test workflows as background processes \u2014 each with its own browser and session."},browser:{description:"Playwright browser automation (navigate, click, fill, screenshot)",envKeys:[],setupInstructions:"Ready to use. Starts a Playwright browser for web automation."},memory:{description:"Test memory database (Dolt) \u2014 history, selectors, insights",envKeys:[],setupInstructions:"Ready to use. Requires Dolt (https://docs.dolthub.com/introduction/installation) and a memory DB via `zibby init --mem`."},"chat-memory":{description:"Persistent chat memory \u2014 remembers facts, decisions, and task history across sessions (Dolt-backed)",envKeys:[],setupInstructions:'Ready to use. Requires Dolt installed. Tables auto-create on first use. Install with: "add chat memory" or "install chat-memory".'},git:{description:"Clone and explore git repositories locally for codebase analysis",envKeys:[],setupInstructions:"Ready to use. Clone repos with git_checkout, explore with git_explore. Auto-authenticates with GitHub/GitLab tokens."}};function S(){let t=["## Available Skills"];for(let[s,n]of Object.entries(r)){let o=n.integrationProvider?`integration: ${n.integrationProvider}`:"ready";t.push(`- ${s}: ${n.description} [${o}]`)}return t.push(""),t.push("Use the install_skill / uninstall_skill / list_available_skills tools to manage skills."),t.push(`Zibby third party Integration settings page: ${p()}`),t.push(""),t.push("## Tool-First Policy (mandatory)"),t.push("CRITICAL RULES \u2014 follow these strictly:"),t.push("1. When user asks to do something and a matching skill is available but not installed, IMMEDIATELY call install_skill. Never ask for credentials or confirmation first."),t.push(`2. If install_skill succeeds, the skill's tools are now available. Use them RIGHT AWAY in the same turn \u2014 don't just say "it's connected", actually call the tools.`),t.push("3. If install_skill reports needsIntegration, tell the user to connect via the integration URL and try again after."),t.push("4. When the relevant skill is already installed, use its tools directly \u2014 don't ask for IDs or keys. Each skill's own instructions explain the workflow."),t.push("5. If a task needs multiple skills (e.g. data from one + execution from another), install all of them, then follow each skill's workflow instructions."),t.join(`
14
- `)}function _(){if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let t=I(v(),".zibby","config.json");return m(t)&&JSON.parse(b(t,"utf-8")).sessionToken||null}catch{return null}}function O(){return(process.env.ZIBBY_API_URL||process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app").replace(/\/$/,"")}function p(){return`${(process.env.ZIBBY_FRONTEND_URL||process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.dev").replace(/\/$/,"")}/integrations`}function P(t){try{let s=process.platform;return w(s==="darwin"?"open":s==="win32"?"cmd":"xdg-open",s==="win32"?["/c","start","",t]:[t],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}async function R(){let t=_();if(!t)return{checked:!1,statuses:null,reason:"no-session-token"};try{let s=await fetch(`${O()}/integrations/status`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});return s.ok?{checked:!0,statuses:await s.json()||{},reason:null}:{checked:!1,statuses:null,reason:`status-${s.status}`}}catch{return{checked:!1,statuses:null,reason:"network-error"}}}function g(t,s){if(!s||!t)return{connected:null};let n=t[s];return!n||typeof n.connected!="boolean"?{connected:null,details:n||null}:{connected:n.connected,details:n}}var j={id:"skill-installer",description:"Live skill installation for chat sessions",envKeys:[],catalog:r,promptFragment:S,tools:[{name:"install_skill",description:"Install a skill into the current chat session so its tools become available",input_schema:{type:"object",properties:{skillId:{type:"string",description:'Skill identifier to install (e.g. "jira", "github", "browser", "memory")'}},required:["skillId"]}},{name:"uninstall_skill",description:"Remove a skill from the current chat session",input_schema:{type:"object",properties:{skillId:{type:"string",description:"Skill identifier to remove"}},required:["skillId"]}},{name:"list_available_skills",description:"List all skills that can be installed, with their env-var readiness status",input_schema:{type:"object",properties:{}}}],async handleToolCall(t,s,n){let{activeSkills:o}=n,d=await R();if(t==="list_available_skills"){let e=Object.entries(r).map(([i,a])=>{let h=o.includes(i),c=g(d.statuses,a.integrationProvider);return{id:i,description:a.description,installed:h,integrationProvider:a.integrationProvider||void 0,integrationConnected:c.connected,setupInstructions:c.connected===!1?a.setupInstructions:void 0}});return JSON.stringify({skills:e})}if(t==="install_skill"){let{skillId:e}=s;if(!e)return JSON.stringify({ok:!1,error:"skillId is required"});if(o.includes(e)){let l=r[e],{getSkill:u}=await import("@zibby/agent-workflow"),k=(u(e)?.tools||[]).map(y=>y.name);return JSON.stringify({ok:!0,alreadyInstalled:!0,skillId:e,description:l?.description,availableTools:k,integrationUrl:l?.integrationProvider?p():void 0,hint:`${e} is already active. Tools available: ${k.join(", ")}. Use them directly.`})}if(!r[e])return JSON.stringify({ok:!1,error:`Unknown skill "${e}". Available: ${Object.keys(r).join(", ")}`});let i=r[e];if(i.integrationProvider){let l=g(d.statuses,i.integrationProvider),u=p();if(d.checked&&l.connected===!1){let f=P(u);return JSON.stringify({ok:!1,error:`${i.integrationProvider} is not connected for this Zibby account yet`,needsIntegration:!0,integrationUrl:u,openedBrowser:f,setupInstructions:`Please connect ${i.integrationProvider} first at ${u}. After you finish OAuth, ask me to install ${e} again.`})}}o.push(e);let{getSkill:a}=await import("@zibby/agent-workflow"),c=(a(e)?.tools||[]).map(l=>l.name);return JSON.stringify({ok:!0,installed:e,description:i.description,availableTools:c,hint:`${e} is now active. You now have these tools: ${c.join(", ")}. Use them immediately to help the user \u2014 don't just confirm installation.`})}if(t==="uninstall_skill"){let{skillId:e}=s;if(!e)return JSON.stringify({ok:!1,error:"skillId is required"});if(e==="skill-installer")return JSON.stringify({ok:!1,error:"Cannot uninstall the skill installer"});let i=o.indexOf(e);return i===-1?JSON.stringify({ok:!1,error:`${e} is not installed`}):(o.splice(i,1),JSON.stringify({ok:!0,uninstalled:e}))}return JSON.stringify({error:`Unknown tool: ${t}`})},resolve(){return null}};export{j as skillInstallerSkill};
13
+ 3. After OAuth completes, ask me to install Sentry again`},runner:{description:"Run zibby test workflows from chat (parallel supported)",envKeys:[],setupInstructions:"Ready to use. Runs zibby test workflows as background processes \u2014 each with its own browser and session."},browser:{description:"Playwright browser automation (navigate, click, fill, screenshot)",envKeys:[],setupInstructions:"Ready to use. Starts a Playwright browser for web automation."},memory:{description:"Test memory database (Dolt) \u2014 history, selectors, insights",envKeys:[],setupInstructions:"Ready to use. Requires Dolt (https://docs.dolthub.com/introduction/installation) and a memory DB via `zibby init --mem`."},"chat-memory":{description:"Persistent chat memory \u2014 remembers facts, decisions, and task history across sessions (Dolt-backed)",envKeys:[],setupInstructions:'Ready to use. Requires Dolt installed. Tables auto-create on first use. Install with: "add chat memory" or "install chat-memory".'},git:{description:"Clone and explore git repositories locally for codebase analysis",envKeys:[],setupInstructions:"Ready to use. Clone repos with git_checkout, explore with git_explore. Auto-authenticates with GitHub/GitLab tokens."}};function A(){let t=["## Available Skills"];for(let[e,n]of Object.entries(c)){let i=n.integrationProvider?`integration: ${n.integrationProvider}`:"ready";t.push(`- ${e}: ${n.description} [${i}]`)}return t.push(""),t.push("Use the install_skill / uninstall_skill / list_available_skills tools to manage skills."),t.push(`Zibby third party Integration settings page: ${h()}`),t.push(""),t.push("## Tool-First Policy (mandatory)"),t.push("CRITICAL RULES \u2014 follow these strictly:"),t.push("1. When user asks to do something and a matching skill is available but not installed, IMMEDIATELY call install_skill. Never ask for credentials or confirmation first."),t.push(`2. If install_skill succeeds, the skill's tools are now available. Use them RIGHT AWAY in the same turn \u2014 don't just say "it's connected", actually call the tools.`),t.push("3. If install_skill reports needsIntegration, tell the user to connect via the integration URL and try again after."),t.push("4. When the relevant skill is already installed, use its tools directly \u2014 don't ask for IDs or keys. Each skill's own instructions explain the workflow."),t.push("5. If a task needs multiple skills (e.g. data from one + execution from another), install all of them, then follow each skill's workflow instructions."),t.join(`
14
+ `)}function R(){if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let t=M(E(),".zibby","config.json");return O(t)&&JSON.parse(w(t,"utf-8")).sessionToken||null}catch{return null}}function U(){return(process.env.ZIBBY_API_URL||process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app").replace(/\/$/,"")}function h(){return`${(process.env.ZIBBY_FRONTEND_URL||process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.dev").replace(/\/$/,"")}/integrations`}function $(t){try{let e=process.platform;return L(e==="darwin"?"open":e==="win32"?"cmd":"xdg-open",e==="win32"?["/c","start","",t]:[t],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}async function P(){let t=R();if(!t)return{checked:!1,statuses:null,reason:"no-session-token"};try{let e=await g(`${U()}/integrations/status`,{method:"GET",headers:{Authorization:`Bearer ${t}`}},{kind:"api",what:"Zibby GET /integrations/status"});return e.ok?{checked:!0,statuses:await e.json()||{},reason:null}:{checked:!1,statuses:null,reason:`status-${e.status}`}}catch{return{checked:!1,statuses:null,reason:"network-error"}}}function m(t,e){if(!e||!t)return{connected:null};let n=t[e];return!n||typeof n.connected!="boolean"?{connected:null,details:n||null}:{connected:n.connected,details:n}}var C={id:"skill-installer",description:"Live skill installation for chat sessions",envKeys:[],catalog:c,promptFragment:A,tools:[{name:"install_skill",description:"Install a skill into the current chat session so its tools become available",input_schema:{type:"object",properties:{skillId:{type:"string",description:'Skill identifier to install (e.g. "jira", "github", "browser", "memory")'}},required:["skillId"]}},{name:"uninstall_skill",description:"Remove a skill from the current chat session",input_schema:{type:"object",properties:{skillId:{type:"string",description:"Skill identifier to remove"}},required:["skillId"]}},{name:"list_available_skills",description:"List all skills that can be installed, with their env-var readiness status",input_schema:{type:"object",properties:{}}}],async handleToolCall(t,e,n){let{activeSkills:i}=n,a=await P();if(t==="list_available_skills"){let s=Object.entries(c).map(([o,r])=>{let p=i.includes(o),l=m(a.statuses,r.integrationProvider);return{id:o,description:r.description,installed:p,integrationProvider:r.integrationProvider||void 0,integrationConnected:l.connected,setupInstructions:l.connected===!1?r.setupInstructions:void 0}});return JSON.stringify({skills:s})}if(t==="install_skill"){let{skillId:s}=e;if(!s)return JSON.stringify({ok:!1,error:"skillId is required"});if(i.includes(s)){let u=c[s],{getSkill:d}=await import("@zibby/agent-workflow"),b=(d(s)?.tools||[]).map(y=>y.name);return JSON.stringify({ok:!0,alreadyInstalled:!0,skillId:s,description:u?.description,availableTools:b,integrationUrl:u?.integrationProvider?h():void 0,hint:`${s} is already active. Tools available: ${b.join(", ")}. Use them directly.`})}if(!c[s])return JSON.stringify({ok:!1,error:`Unknown skill "${s}". Available: ${Object.keys(c).join(", ")}`});let o=c[s];if(o.integrationProvider){let u=m(a.statuses,o.integrationProvider),d=h();if(a.checked&&u.connected===!1){let k=$(d);return JSON.stringify({ok:!1,error:`${o.integrationProvider} is not connected for this Zibby account yet`,needsIntegration:!0,integrationUrl:d,openedBrowser:k,setupInstructions:`Please connect ${o.integrationProvider} first at ${d}. After you finish OAuth, ask me to install ${s} again.`})}}i.push(s);let{getSkill:r}=await import("@zibby/agent-workflow"),l=(r(s)?.tools||[]).map(u=>u.name);return JSON.stringify({ok:!0,installed:s,description:o.description,availableTools:l,hint:`${s} is now active. You now have these tools: ${l.join(", ")}. Use them immediately to help the user \u2014 don't just confirm installation.`})}if(t==="uninstall_skill"){let{skillId:s}=e;if(!s)return JSON.stringify({ok:!1,error:"skillId is required"});if(s==="skill-installer")return JSON.stringify({ok:!1,error:"Cannot uninstall the skill installer"});let o=i.indexOf(s);return o===-1?JSON.stringify({ok:!1,error:`${s} is not installed`}):(i.splice(o,1),JSON.stringify({ok:!0,uninstalled:s}))}return JSON.stringify({error:`Unknown tool: ${t}`})},resolve(){return null}};export{C as skillInstallerSkill};
package/dist/slack.js CHANGED
@@ -1,7 +1,7 @@
1
- import{existsSync as h}from"fs";import{fileURLToPath as g}from"url";import{dirname as k,resolve as f}from"path";import{resolveIntegrationToken as y}from"@zibby/core/backend-client.js";var _=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),S=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});function b(){if(process.env.MCP_SLACK_PATH)return process.env.MCP_SLACK_PATH;let a=k(g(import.meta.url)),t=f(a,"..","bin","mcp-slack.mjs");return h(t)?t:null}async function s(a,t={}){let{token:e}=await y("slack"),i=["conversations.list","users.list","users.profile.get","users.lookupByEmail","usergroups.list","usergroups.users.list","conversations.history","conversations.replies"].includes(a),c=`https://slack.com/api/${a}`,o={Authorization:`Bearer ${e}`},d;if(i){let n=new URLSearchParams(t).toString();n&&(c+=`?${n}`)}else o["Content-Type"]="application/json; charset=utf-8",d=JSON.stringify(t);let r=await(await fetch(c,{method:i?"GET":"POST",headers:o,body:d})).json();if(!r.ok)throw new Error(`Slack API error: ${r.error}`);return r}var w={id:"slack",callsBackend:!0,serverName:"slack",allowedTools:["mcp__slack__*"],requiresIntegration:_.SLACK,envKeys:["SLACK_BOT_TOKEN","SLACK_TEAM_ID"],description:"Slack MCP Server",promptFragment:`## Slack
1
+ import{existsSync as I}from"fs";import{fileURLToPath as O}from"url";import{dirname as T,resolve as N}from"path";import{resolveIntegrationToken as E}from"@zibby/core/backend-client.js";var h=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),v=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});var _={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function k(r,t,e=process.env){let n=Number(e?.[r]);return Number.isFinite(n)&&n>0?Math.min(6e5,Math.max(1e3,Math.floor(n))):t}function f(r="api",t=process.env){let e=_[r]||_.api;return k(e.knob,e.fallback,t)}function b(r){return r?.name==="TimeoutError"||r?.name==="AbortError"}function y(r){try{return new URL(String(r?.url??r)).host||"unknown host"}catch{return"unknown host"}}function S(r,t){if(!r)return t;let e=new AbortController,n=l=>{e.signal.aborted||e.abort(l)},c=()=>n(r.reason),a=()=>n(t.reason);return e.signal.addEventListener("abort",()=>{r.removeEventListener("abort",c),t.removeEventListener("abort",a)},{once:!0}),r.aborted?n(r.reason):t.aborted?n(t.reason):(r.addEventListener("abort",c,{once:!0}),t.addEventListener("abort",a,{once:!0})),e.signal}async function g(r,t={},e={}){let n=e.kind||"api",c=(_[n]||_.api).knob,a=e.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(e.timeoutMs))):f(n),l=t?.signal;if(l?.aborted)throw l.reason??new DOMException("This operation was aborted","AbortError");let p=AbortSignal.timeout(a),i=S(l,p);try{return await fetch(r,{...t,signal:i})}catch(s){throw p.aborted&&b(s)?new Error(`${e.what||"request"} TIMED OUT after ${a}ms against ${y(r)} (${c})`):s}}function L(){if(process.env.MCP_SLACK_PATH)return process.env.MCP_SLACK_PATH;let r=T(O(import.meta.url)),t=N(r,"..","bin","mcp-slack.mjs");return I(t)?t:null}async function o(r,t={}){let{token:e}=await E("slack"),n=["conversations.list","users.list","users.profile.get","users.lookupByEmail","usergroups.list","usergroups.users.list","conversations.history","conversations.replies"].includes(r),c=`https://slack.com/api/${r}`,a={Authorization:`Bearer ${e}`},l;if(n){let s=new URLSearchParams(t).toString();s&&(c+=`?${s}`)}else a["Content-Type"]="application/json; charset=utf-8",l=JSON.stringify(t);let i=await(await g(c,{method:n?"GET":"POST",headers:a,body:l},{kind:"api",what:`Slack ${r}`})).json();if(!i.ok)throw new Error(`Slack API error: ${i.error}`);return i}var U={id:"slack",callsBackend:!0,serverName:"slack",allowedTools:["mcp__slack__*"],requiresIntegration:h.SLACK,envKeys:["SLACK_BOT_TOKEN","SLACK_TEAM_ID"],description:"Slack MCP Server",promptFragment:`## Slack
2
2
  You have access to the user's Slack workspace. Use these tools:
3
3
  - slack_list_channels, slack_post_message, slack_reply_to_thread
4
4
  - slack_add_reaction, slack_get_channel_history, slack_get_thread_replies
5
5
  - slack_get_users, slack_get_user_profile
6
6
  - slack_lookup_user_by_email (precise email\u2192user_id, prefer this over scanning slack_get_users)
7
- - slack_list_usergroups, slack_get_usergroup_members (workspace-defined teams like @oncall, @platform)`,resolve(){let a=b();if(!a)return null;let t={};for(let e of["PROJECT_API_TOKEN","ZIBBY_USER_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","PROGRESS_API_URL","EXECUTION_ID","PROJECT_ID","STAGE"])process.env[e]&&(t[e]=process.env[e]);for(let e of this.envKeys)process.env[e]&&(t[e]=process.env[e]);return{type:"stdio",command:"node",args:[a],env:t,alwaysLoad:!0}},async handleToolCall(a,t){try{switch(a){case"slack_list_channels":{let e=await s("conversations.list",{types:"public_channel",limit:100});return JSON.stringify({channels:(e.channels||[]).map(i=>({id:i.id,name:i.name,topic:i.topic?.value}))})}case"slack_post_message":{if(!t.channel||!t.text)return JSON.stringify({error:"channel and text are required"});let e=await s("chat.postMessage",{channel:t.channel,text:t.text,...t.blocks?{blocks:t.blocks}:{}});return JSON.stringify({ok:!0,ts:e.ts,channel:e.channel})}case"slack_reply_to_thread":{if(!t.channel||!t.thread_ts||!t.text)return JSON.stringify({error:"channel, thread_ts, and text are required"});let e=await s("chat.postMessage",{channel:t.channel,thread_ts:t.thread_ts,text:t.text});return JSON.stringify({ok:!0,ts:e.ts})}case"slack_add_reaction":return!t.channel||!t.timestamp||!t.reaction?JSON.stringify({error:"channel, timestamp, and reaction are required"}):(await s("reactions.add",{channel:t.channel,timestamp:t.timestamp,name:t.reaction}),JSON.stringify({ok:!0}));case"slack_get_channel_history":{if(!t.channel)return JSON.stringify({error:"channel is required"});let e=await s("conversations.history",{channel:t.channel,limit:t.limit||20});return JSON.stringify({messages:(e.messages||[]).map(i=>({user:i.user,text:i.text,ts:i.ts}))})}case"slack_get_thread_replies":{if(!t.channel||!t.thread_ts)return JSON.stringify({error:"channel and thread_ts are required"});let e=await s("conversations.replies",{channel:t.channel,ts:t.thread_ts});return JSON.stringify({messages:(e.messages||[]).map(i=>({user:i.user,text:i.text,ts:i.ts}))})}case"slack_get_users":{let e=await s("users.list",{limit:100});return JSON.stringify({users:(e.members||[]).filter(i=>!i.is_bot&&!i.deleted).map(i=>({id:i.id,name:i.real_name||i.name}))})}case"slack_get_user_profile":{if(!t.user_id)return JSON.stringify({error:"user_id is required"});let e=await s("users.profile.get",{user:t.user_id});return JSON.stringify({profile:e.profile})}case"slack_lookup_user_by_email":{if(!t.email)return JSON.stringify({error:"email is required"});try{let e=await s("users.lookupByEmail",{email:t.email});return JSON.stringify({ok:!0,user:{id:e.user?.id,name:e.user?.real_name||e.user?.name,email:e.user?.profile?.email||t.email}})}catch(e){if(/users_not_found/.test(e.message))return JSON.stringify({ok:!1,reason:"users_not_found"});throw e}}case"slack_list_usergroups":{let e=await s("usergroups.list",{});return JSON.stringify({usergroups:(e.usergroups||[]).map(i=>({id:i.id,handle:i.handle,name:i.name,description:i.description||"",user_count:Number(i.user_count||0)}))})}case"slack_get_usergroup_members":{if(!t.usergroup)return JSON.stringify({error:"usergroup id is required"});let e=await s("usergroups.users.list",{usergroup:t.usergroup});return JSON.stringify({users:e.users||[]})}case"slack_search_users":{if(!t.query||typeof t.query!="string")return JSON.stringify({error:"query is required"});let e=t.query.trim().toLowerCase();if(!e)return JSON.stringify({ok:!0,matches:[]});let i=Math.max(1,Math.min(Number(t.limit)||5,25)),c=[],o,d=5;for(let r=0;r<d;r+=1){let n={limit:200};o&&(n.cursor=o);let l=await s("users.list",n);for(let p of l.members||[])p.deleted||p.is_bot||c.push(p);if(o=l.response_metadata?.next_cursor,!o)break}let m=[];for(let r of c){let n=(r.real_name||"").toLowerCase(),l=(r.profile?.display_name||"").toLowerCase(),p=(r.name||"").toLowerCase(),u=0;n.includes(e)&&(u+=100-Math.abs(n.length-e.length)),l.includes(e)&&(u+=60-Math.abs(l.length-e.length)),p.includes(e)&&(u+=30-Math.abs(p.length-e.length)),(n===e||l===e)&&(u+=200),u>0&&m.push({id:r.id,name:r.real_name||r.profile?.display_name||r.name,email:r.profile?.email||void 0,_score:u})}return m.sort((r,n)=>n._score-r._score),JSON.stringify({ok:!0,matches:m.slice(0,i).map(({_score:r,...n})=>n),scanned:c.length})}default:return JSON.stringify({error:`Unknown tool: ${a}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"slack_list_channels",description:"List public channels in the workspace",input_schema:{type:"object",properties:{}}},{name:"slack_post_message",description:"Post a message to a Slack channel or DM. Pass `blocks` (Block Kit) for a rich card; `text` is the required notification fallback.",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID or name"},text:{type:"string",description:"Notification/fallback text (required)"},blocks:{type:"array",description:"Block Kit blocks for rich formatting (optional). Each block is a Slack Block Kit object (header/section/divider/context). section blocks may carry a button accessory with a url."}},required:["channel","text"]}},{name:"slack_reply_to_thread",description:"Reply to a specific message thread",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},thread_ts:{type:"string",description:"Thread timestamp"},text:{type:"string",description:"Reply text"}},required:["channel","thread_ts","text"]}},{name:"slack_add_reaction",description:"Add an emoji reaction to a message",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},timestamp:{type:"string",description:"Message timestamp"},reaction:{type:"string",description:"Emoji name without colons"}},required:["channel","timestamp","reaction"]}},{name:"slack_get_channel_history",description:"Get recent messages from a channel",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},limit:{type:"number",description:"Number of messages"}},required:["channel"]}},{name:"slack_get_thread_replies",description:"Get all replies in a message thread",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},thread_ts:{type:"string",description:"Thread timestamp"}},required:["channel","thread_ts"]}},{name:"slack_get_users",description:"List workspace users with basic profiles",input_schema:{type:"object",properties:{}}},{name:"slack_get_user_profile",description:"Get detailed profile for a specific user",input_schema:{type:"object",properties:{user_id:{type:"string",description:"Slack user ID"}},required:["user_id"]}},{name:"slack_lookup_user_by_email",description:"Find a Slack user by email. Returns { ok:true, user:{id,name,email} } on hit, { ok:false } when no user has that email. Prefer this over slack_get_users for email-based routing \u2014 single API call, exact match.",input_schema:{type:"object",properties:{email:{type:"string",description:"Email address to look up"}},required:["email"]}},{name:"slack_list_usergroups",description:"List workspace-defined user groups (e.g. @oncall, @platform). Each item has { id, handle, name, description, user_count }. Use the id with slack_get_usergroup_members to expand the membership.",input_schema:{type:"object",properties:{}}},{name:"slack_get_usergroup_members",description:"List user IDs that belong to a Slack usergroup. Pair with slack_post_message to DM each member, or use the group id directly in a channel message as <!subteam^ID> to @-mention.",input_schema:{type:"object",properties:{usergroup:{type:"string",description:"Usergroup id, e.g. S012ABC"}},required:["usergroup"]}},{name:"slack_search_users",description:'Fuzzy-search workspace users by display name or real name. Use when the user said something like "send to Sam" without an email. Returns up to `limit` ranked matches { id, name, email }. Slack has no native name-search API \u2014 this scans paginated users.list + does substring scoring (real_name > display_name > name). For large workspaces consider higher limit + ask the user to confirm if multiple hit.',input_schema:{type:"object",properties:{query:{type:"string",description:"Substring to match against names (case-insensitive)"},limit:{type:"number",description:"Max matches to return (default 5, max 25)"}},required:["query"]}}]};export{w as slackSkill};
7
+ - slack_list_usergroups, slack_get_usergroup_members (workspace-defined teams like @oncall, @platform)`,resolve(){let r=L();if(!r)return null;let t={};for(let e of["PROJECT_API_TOKEN","ZIBBY_USER_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","PROGRESS_API_URL","EXECUTION_ID","PROJECT_ID","STAGE"])process.env[e]&&(t[e]=process.env[e]);for(let e of this.envKeys)process.env[e]&&(t[e]=process.env[e]);return{type:"stdio",command:"node",args:[r],env:t,alwaysLoad:!0}},async handleToolCall(r,t){try{switch(r){case"slack_list_channels":{let e=await o("conversations.list",{types:"public_channel",limit:100});return JSON.stringify({channels:(e.channels||[]).map(n=>({id:n.id,name:n.name,topic:n.topic?.value}))})}case"slack_post_message":{if(!t.channel||!t.text)return JSON.stringify({error:"channel and text are required"});let e=await o("chat.postMessage",{channel:t.channel,text:t.text,...t.blocks?{blocks:t.blocks}:{}});return JSON.stringify({ok:!0,ts:e.ts,channel:e.channel})}case"slack_reply_to_thread":{if(!t.channel||!t.thread_ts||!t.text)return JSON.stringify({error:"channel, thread_ts, and text are required"});let e=await o("chat.postMessage",{channel:t.channel,thread_ts:t.thread_ts,text:t.text});return JSON.stringify({ok:!0,ts:e.ts})}case"slack_add_reaction":return!t.channel||!t.timestamp||!t.reaction?JSON.stringify({error:"channel, timestamp, and reaction are required"}):(await o("reactions.add",{channel:t.channel,timestamp:t.timestamp,name:t.reaction}),JSON.stringify({ok:!0}));case"slack_get_channel_history":{if(!t.channel)return JSON.stringify({error:"channel is required"});let e=await o("conversations.history",{channel:t.channel,limit:t.limit||20});return JSON.stringify({messages:(e.messages||[]).map(n=>({user:n.user,text:n.text,ts:n.ts}))})}case"slack_get_thread_replies":{if(!t.channel||!t.thread_ts)return JSON.stringify({error:"channel and thread_ts are required"});let e=await o("conversations.replies",{channel:t.channel,ts:t.thread_ts});return JSON.stringify({messages:(e.messages||[]).map(n=>({user:n.user,text:n.text,ts:n.ts}))})}case"slack_get_users":{let e=await o("users.list",{limit:100});return JSON.stringify({users:(e.members||[]).filter(n=>!n.is_bot&&!n.deleted).map(n=>({id:n.id,name:n.real_name||n.name}))})}case"slack_get_user_profile":{if(!t.user_id)return JSON.stringify({error:"user_id is required"});let e=await o("users.profile.get",{user:t.user_id});return JSON.stringify({profile:e.profile})}case"slack_lookup_user_by_email":{if(!t.email)return JSON.stringify({error:"email is required"});try{let e=await o("users.lookupByEmail",{email:t.email});return JSON.stringify({ok:!0,user:{id:e.user?.id,name:e.user?.real_name||e.user?.name,email:e.user?.profile?.email||t.email}})}catch(e){if(/users_not_found/.test(e.message))return JSON.stringify({ok:!1,reason:"users_not_found"});throw e}}case"slack_list_usergroups":{let e=await o("usergroups.list",{});return JSON.stringify({usergroups:(e.usergroups||[]).map(n=>({id:n.id,handle:n.handle,name:n.name,description:n.description||"",user_count:Number(n.user_count||0)}))})}case"slack_get_usergroup_members":{if(!t.usergroup)return JSON.stringify({error:"usergroup id is required"});let e=await o("usergroups.users.list",{usergroup:t.usergroup});return JSON.stringify({users:e.users||[]})}case"slack_search_users":{if(!t.query||typeof t.query!="string")return JSON.stringify({error:"query is required"});let e=t.query.trim().toLowerCase();if(!e)return JSON.stringify({ok:!0,matches:[]});let n=Math.max(1,Math.min(Number(t.limit)||5,25)),c=[],a,l=5;for(let i=0;i<l;i+=1){let s={limit:200};a&&(s.cursor=a);let u=await o("users.list",s);for(let d of u.members||[])d.deleted||d.is_bot||c.push(d);if(a=u.response_metadata?.next_cursor,!a)break}let p=[];for(let i of c){let s=(i.real_name||"").toLowerCase(),u=(i.profile?.display_name||"").toLowerCase(),d=(i.name||"").toLowerCase(),m=0;s.includes(e)&&(m+=100-Math.abs(s.length-e.length)),u.includes(e)&&(m+=60-Math.abs(u.length-e.length)),d.includes(e)&&(m+=30-Math.abs(d.length-e.length)),(s===e||u===e)&&(m+=200),m>0&&p.push({id:i.id,name:i.real_name||i.profile?.display_name||i.name,email:i.profile?.email||void 0,_score:m})}return p.sort((i,s)=>s._score-i._score),JSON.stringify({ok:!0,matches:p.slice(0,n).map(({_score:i,...s})=>s),scanned:c.length})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"slack_list_channels",description:"List public channels in the workspace",input_schema:{type:"object",properties:{}}},{name:"slack_post_message",description:"Post a message to a Slack channel or DM. Pass `blocks` (Block Kit) for a rich card; `text` is the required notification fallback.",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID or name"},text:{type:"string",description:"Notification/fallback text (required)"},blocks:{type:"array",description:"Block Kit blocks for rich formatting (optional). Each block is a Slack Block Kit object (header/section/divider/context). section blocks may carry a button accessory with a url."}},required:["channel","text"]}},{name:"slack_reply_to_thread",description:"Reply to a specific message thread",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},thread_ts:{type:"string",description:"Thread timestamp"},text:{type:"string",description:"Reply text"}},required:["channel","thread_ts","text"]}},{name:"slack_add_reaction",description:"Add an emoji reaction to a message",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},timestamp:{type:"string",description:"Message timestamp"},reaction:{type:"string",description:"Emoji name without colons"}},required:["channel","timestamp","reaction"]}},{name:"slack_get_channel_history",description:"Get recent messages from a channel",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},limit:{type:"number",description:"Number of messages"}},required:["channel"]}},{name:"slack_get_thread_replies",description:"Get all replies in a message thread",input_schema:{type:"object",properties:{channel:{type:"string",description:"Channel ID"},thread_ts:{type:"string",description:"Thread timestamp"}},required:["channel","thread_ts"]}},{name:"slack_get_users",description:"List workspace users with basic profiles",input_schema:{type:"object",properties:{}}},{name:"slack_get_user_profile",description:"Get detailed profile for a specific user",input_schema:{type:"object",properties:{user_id:{type:"string",description:"Slack user ID"}},required:["user_id"]}},{name:"slack_lookup_user_by_email",description:"Find a Slack user by email. Returns { ok:true, user:{id,name,email} } on hit, { ok:false } when no user has that email. Prefer this over slack_get_users for email-based routing \u2014 single API call, exact match.",input_schema:{type:"object",properties:{email:{type:"string",description:"Email address to look up"}},required:["email"]}},{name:"slack_list_usergroups",description:"List workspace-defined user groups (e.g. @oncall, @platform). Each item has { id, handle, name, description, user_count }. Use the id with slack_get_usergroup_members to expand the membership.",input_schema:{type:"object",properties:{}}},{name:"slack_get_usergroup_members",description:"List user IDs that belong to a Slack usergroup. Pair with slack_post_message to DM each member, or use the group id directly in a channel message as <!subteam^ID> to @-mention.",input_schema:{type:"object",properties:{usergroup:{type:"string",description:"Usergroup id, e.g. S012ABC"}},required:["usergroup"]}},{name:"slack_search_users",description:'Fuzzy-search workspace users by display name or real name. Use when the user said something like "send to Sam" without an email. Returns up to `limit` ranked matches { id, name, email }. Slack has no native name-search API \u2014 this scans paginated users.list + does substring scoring (real_name > display_name > name). For large workspaces consider higher limit + ask the user to confirm if multiple hit.',input_schema:{type:"object",properties:{query:{type:"string",description:"Substring to match against names (case-insensitive)"},limit:{type:"number",description:"Max matches to return (default 5, max 25)"}},required:["query"]}}]};export{U as slackSkill};