@zibby/skills 0.1.64 → 0.1.65

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.
@@ -0,0 +1,11 @@
1
+ import{existsSync as L}from"fs";import{fileURLToPath as O}from"url";import{dirname as R,resolve as P}from"path";import{resolveIntegrationToken as D,clearTokenCache as G}from"@zibby/core/backend-client.js";var w=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",FIGMA:"figma",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business"}),J=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",connectPath:"/integrations?provider=lark"},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"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},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"}});function E(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let i=R(O(import.meta.url)),t=P(i,"..","bin","mcp-skill.mjs");return L(t)?t:null}var g="https://docs.googleapis.com/v1",C="https://www.googleapis.com/drive/v3",b=2e4,$=25;function S(i){if(!i||typeof i!="string")return null;let t=i.trim(),e=t.match(/\/document\/(?:u\/\d+\/)?d\/([a-zA-Z0-9_-]+)/);return e?e[1]:/^[a-zA-Z0-9_-]{20,}$/.test(t)?t:null}async function p(i,t={}){let e=async()=>{let{token:o}=await D("google");if(typeof o!="string"||!o)throw new Error(`Invalid google token type: ${typeof o}`);let n=await fetch(i,{method:t.method||"GET",headers:{Authorization:`Bearer ${o}`,Accept:"application/json",...t.body?{"Content-Type":"application/json"}:{},...t.headers},body:t.body?JSON.stringify(t.body):void 0});if(!n.ok){let a=await n.text().catch(()=>"");throw new Error(`Google API ${n.status}: ${a.slice(0,300)}`)}let r=await n.text().catch(()=>"");if(!r||!r.trim())return{};try{return JSON.parse(r)}catch{return{raw:r}}};try{return await e()}catch(o){let n=String(o?.message||o||"").toLowerCase();if(!(n.includes("token")||n.includes("401")||n.includes("unauthorized")))throw o;return G("google"),e()}}function q(i){let t=[],e="",o=0;for(;o<i.length;){let n=/^\[([^\]]+)\]\(([^)\s]+)\)/.exec(i.slice(o));if(n){t.push({start:e.length,end:e.length+n[1].length,link:n[2]}),e+=n[1],o+=n[0].length;continue}let r=/^\*\*([^*]+)\*\*/.exec(i.slice(o));if(r){t.push({start:e.length,end:e.length+r[1].length,bold:!0}),e+=r[1],o+=r[0].length;continue}let a=/^`([^`]+)`/.exec(i.slice(o));if(a){t.push({start:e.length,end:e.length+a[1].length,code:!0}),e+=a[1],o+=a[0].length;continue}e+=i[o],o+=1}return{text:e,styles:t}}function A(i,t){let e=String(i??"").replace(/\r\n/g,`
2
+ `);if(!e.trim())return{requests:[],endIndex:t};let o=e.split(`
3
+ `),n="",r=[],a=[];for(let s of o){let c=s,I=null,h=null,f=/^(#{1,3})\s+(.*)$/.exec(c),_=/^\s*[-*]\s+(.*)$/.exec(c),x=/^\s*\d+[.)]\s+(.*)$/.exec(c);f?(I=`HEADING_${f[1].length}`,c=f[2]):_?(h="BULLET_DISC_CIRCLE_SQUARE",c=_[1]):x&&(h="NUMBERED_DECIMAL_ALPHA_ROMAN",c=x[1]);let{text:N,styles:v}=q(c),y=t+n.length;for(let k of v)a.push({...k,start:y+k.start,end:y+k.end});n+=`${N}
4
+ `,r.push({start:y,end:t+n.length,named:I,bullet:h})}if(!n)return{requests:[],endIndex:t};let l=[{insertText:{location:{index:t},text:n}}];for(let s of r)s.named&&l.push({updateParagraphStyle:{range:{startIndex:s.start,endIndex:s.end},paragraphStyle:{namedStyleType:s.named},fields:"namedStyleType"}});let d=null,u=()=>{d&&(l.push({createParagraphBullets:{range:{startIndex:d.start,endIndex:d.end},bulletPreset:d.preset}}),d=null)};for(let s of r)s.bullet?d&&d.preset===s.bullet?d.end=s.end:(u(),d={start:s.start,end:s.end,preset:s.bullet}):u();u();for(let s of a)s.end<=s.start||(s.bold?l.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{bold:!0},fields:"bold"}}):s.link?l.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{link:{url:s.link}},fields:"link"}}):s.code&&l.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{weightedFontFamily:{fontFamily:"Courier New"}},fields:"weightedFontFamily"}}));return{requests:l,endIndex:t+n.length}}function U(i){let t="",e=o=>{for(let n of Array.isArray(o)?o:[]){if(t.length>=b)return;if(n.paragraph)for(let r of n.paragraph.elements||[])t+=r?.textRun?.content||"";else if(n.table)for(let r of n.table.tableRows||[])for(let a of r.tableCells||[])e(a.content);else n.tableOfContents&&e(n.tableOfContents.content)}};return e(i?.content),t.slice(0,b)}var m=i=>`https://docs.google.com/document/d/${i}/edit`,T=i=>{let t=typeof i?.markdown=="string"?i.markdown:null,e=typeof i?.text=="string"?i.text:null;return t??e},K={id:"google-docs",serverName:"gdocs",allowedTools:["mcp__gdocs__*"],requiresIntegration:w.GOOGLE,description:"Google Docs \u2014 create, append to, and read Google Docs (drive.file scoped)",promptFragment:`## Google Docs (connected)
5
+ You can create and edit Google Docs for the user. IMPORTANT visibility caveat: the integration uses Google's per-file drive.file scope, so you can only see docs this app CREATED (or the user explicitly picked) \u2014 not the user's whole Drive.
6
+ - gdocs_create_doc: create a new Google Doc from a title + markdown (headings/bold/bullets/links supported) or plain text; returns { documentId, url }. Share the url with the user.
7
+ - gdocs_append: append markdown/text to the end of a doc you created earlier (pass the documentId or doc URL).
8
+ - gdocs_get: read a doc back as plain text (works for app-created/user-picked docs; arbitrary docs need the extended documents.readonly connection).
9
+ - gdocs_list_created: list the Google Docs visible to this app (drive.file \u2192 only docs it created or the user picked).
10
+ These tools return { ok:false, error } on failure \u2014 treat an unavailable Google connection as "cannot deliver to Docs" and report it rather than blocking the task.`,resolve(){let i=E();return i?{type:"stdio",command:"node",args:[i,"../dist/googleDocs.js","googleDocsSkill"],env:{},description:this.description,alwaysLoad:!0}:null},async handleToolCall(i,t){try{switch(i){case"gdocs_create_doc":{let e=typeof t?.title=="string"&&t.title.trim()?t.title.trim():null;if(!e)return JSON.stringify({ok:!1,error:"title is required"});let n=(await p(`${g}/documents`,{method:"POST",body:{title:e}}))?.documentId;if(!n)return JSON.stringify({ok:!1,error:"Google Docs create returned no documentId"});let r=T(t);if(r&&r.trim()){let{requests:a}=A(r,1);a.length&&await p(`${g}/documents/${n}:batchUpdate`,{method:"POST",body:{requests:a}})}return JSON.stringify({ok:!0,documentId:n,title:e,url:m(n)})}case"gdocs_append":{let e=S(t?.documentId||t?.url||t?.id);if(!e)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});let o=T(t);if(!o||!o.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let r=(await p(`${g}/documents/${e}`))?.body,a=Array.isArray(r?.content)?r.content:[],l=a.length&&a[a.length-1].endIndex||2,d=Math.max(1,l-1),u=[],s=d;d>1&&(u.push({insertText:{location:{index:d},text:`
11
+ `}}),s=d+1);let c=A(o,s);return u.push(...c.requests),await p(`${g}/documents/${e}:batchUpdate`,{method:"POST",body:{requests:u}}),JSON.stringify({ok:!0,documentId:e,url:m(e)})}case"gdocs_get":{let e=S(t?.documentId||t?.url||t?.id);if(!e)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});let o=await p(`${g}/documents/${e}`),n=U(o?.body);return JSON.stringify({ok:!0,documentId:e,title:o?.title||"",url:m(e),text:n,...n.length>=b?{truncated:!0}:{}})}case"gdocs_list_created":{let e=new URLSearchParams({q:"'me' in owners and mimeType='application/vnd.google-apps.document' and trashed=false",fields:"files(id,name,modifiedTime,webViewLink)",pageSize:String($),orderBy:"modifiedTime desc"}),o=await p(`${C}/files?${e.toString()}`),n=(Array.isArray(o?.files)?o.files:[]).map(r=>({documentId:r.id,title:r.name,modifiedTime:r.modifiedTime,url:r.webViewLink||m(r.id)}));return JSON.stringify({ok:!0,count:n.length,files:n})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${i}`})}}catch(e){return JSON.stringify({ok:!1,error:e.message})}},tools:[{name:"gdocs_create_doc",description:"Create a new Google Doc with a title and optional content (markdown: #/##/### headings, - bullets, 1. numbered lists, **bold**, [links](url), `code`; or plain text). Returns { ok, documentId, url } \u2014 share the url with the user.",input_schema:{type:"object",properties:{title:{type:"string",description:"Document title."},markdown:{type:"string",description:"Document body as markdown (preferred)."},text:{type:"string",description:"Document body as plain text (used when markdown is absent)."}},required:["title"]}},{name:"gdocs_append",description:"Append markdown/text content to the END of an existing Google Doc. Only works on docs this app created or the user explicitly picked (drive.file scope). Accepts a documentId or a full docs.google.com URL. Returns { ok, documentId, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Google Docs documentId OR a full https://docs.google.com/document/d/... URL."},markdown:{type:"string",description:"Content to append, as markdown (preferred)."},text:{type:"string",description:"Content to append, as plain text (used when markdown is absent)."}},required:["documentId"]}},{name:"gdocs_get",description:"Read a Google Doc back as plain text (truncated to ~20k chars). Under the default drive.file scope this works ONLY for docs this app created or the user explicitly picked; reading arbitrary docs requires the extended documents.readonly connection. Returns { ok, documentId, title, url, text }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Google Docs documentId OR a full https://docs.google.com/document/d/... URL."}},required:["documentId"]}},{name:"gdocs_list_created",description:"List the Google Docs visible to this integration, newest first (max 25). NOTE: under the drive.file scope this lists ONLY docs the app created or the user explicitly picked \u2014 it is NOT a full Drive search. Returns { ok, count, files:[{ documentId, title, modifiedTime, url }] }.",input_schema:{type:"object",properties:{}}}]};export{U as extractPlainText,p as googleApi,K as googleDocsSkill,A as markdownToRequests,S as parseDocId,q as parseInlineMarkdown};
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export namespace SKILLS {
12
12
  let SLACK: string;
13
13
  let LARK: string;
14
14
  let NOTION: string;
15
+ let GOOGLE_DOCS: string;
15
16
  let LINKEDIN: string;
16
17
  let CHAT_NOTIFY: string;
17
18
  let SENTRY: string;
@@ -44,6 +45,7 @@ import { slackSkill } from './slack.js';
44
45
  import { larkSkill } from './lark.js';
45
46
  import { notionSkill } from './notion.js';
46
47
  import { linkedinSkill } from './linkedin.js';
48
+ import { googleDocsSkill } from './googleDocs.js';
47
49
  import { chatNotifySkill } from './chat-notify.js';
48
50
  import { sentrySkill } from './sentry.js';
49
51
  import { memorySkill } from './memory.js';
@@ -55,7 +57,7 @@ import { testRunnerSkill } from './test-runner.js';
55
57
  import { skillInstallerSkill } from './skill-installer.js';
56
58
  import { coreToolsSkill } from './core-tools.js';
57
59
  import { workflowBuilderSkill } from './workflow-builder.js';
58
- export { browserSkill, jiraSkill, githubSkill, gitlabSkill, figmaSkill, linearSkill, planeSkill, opendesignSkill, gitSkill, gitWriteSkill, slackSkill, larkSkill, notionSkill, linkedinSkill, chatNotifySkill, sentrySkill, memorySkill, chatMemorySkill, kvMemorySkill, datasetStoreSkill, codebaseMemorySkill, testRunnerSkill, testRunnerSkill as runnerSkill, skillInstallerSkill, coreToolsSkill, workflowBuilderSkill };
60
+ export { browserSkill, jiraSkill, githubSkill, gitlabSkill, figmaSkill, linearSkill, planeSkill, opendesignSkill, gitSkill, gitWriteSkill, slackSkill, larkSkill, notionSkill, linkedinSkill, googleDocsSkill, chatNotifySkill, sentrySkill, memorySkill, chatMemorySkill, kvMemorySkill, datasetStoreSkill, codebaseMemorySkill, testRunnerSkill, testRunnerSkill as runnerSkill, skillInstallerSkill, coreToolsSkill, workflowBuilderSkill };
59
61
  export { openaiBillingSkill, anthropicBillingSkill, cursorAdminSkill, fetchOpenAICosts, fetchOpenAIProjects, fetchAnthropicCosts, fetchAnthropicWorkspaces, fetchCursorSpend, fetchAllProviders, groupByKey, meanStddev } from "./llm-billing.js";
60
62
  export { reportObjectSchema, reportToBlockKit, reportToLarkCard, SEVERITIES as REPORT_SEVERITIES } from "./report.js";
61
63
  export { skill, functionSkill } from "./function-skill.js";