@zibby/skills 0.1.84 → 0.1.86

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,114 @@
1
+ export namespace artifactSkill {
2
+ let id: string;
3
+ let serverName: string;
4
+ let allowedTools: string[];
5
+ let description: string;
6
+ let promptFragment: string;
7
+ function resolve(): {
8
+ command: any;
9
+ args: any[];
10
+ env: {};
11
+ description: string;
12
+ type?: undefined;
13
+ alwaysLoad?: undefined;
14
+ } | {
15
+ type: string;
16
+ command: string;
17
+ args: any[];
18
+ env: {};
19
+ description: string;
20
+ alwaysLoad: boolean;
21
+ };
22
+ function handleToolCall(name: any, args: any): Promise<string>;
23
+ let tools: ({
24
+ name: string;
25
+ description: string;
26
+ input_schema: {
27
+ type: string;
28
+ properties: {
29
+ title: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ html: {
34
+ type: string;
35
+ description: string;
36
+ };
37
+ markdown: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ kind: {
42
+ type: string;
43
+ description: string;
44
+ };
45
+ favicon: {
46
+ type: string;
47
+ description: string;
48
+ };
49
+ summary: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ id?: undefined;
54
+ };
55
+ required: string[];
56
+ };
57
+ } | {
58
+ name: string;
59
+ description: string;
60
+ input_schema: {
61
+ type: string;
62
+ properties: {
63
+ id: {
64
+ type: string;
65
+ description: string;
66
+ };
67
+ title: {
68
+ type: string;
69
+ description: string;
70
+ };
71
+ html: {
72
+ type: string;
73
+ description: string;
74
+ };
75
+ markdown: {
76
+ type: string;
77
+ description: string;
78
+ };
79
+ kind: {
80
+ type: string;
81
+ description: string;
82
+ };
83
+ favicon: {
84
+ type: string;
85
+ description: string;
86
+ };
87
+ summary: {
88
+ type: string;
89
+ description: string;
90
+ };
91
+ };
92
+ required: string[];
93
+ };
94
+ } | {
95
+ name: string;
96
+ description: string;
97
+ input_schema: {
98
+ type: string;
99
+ properties: {
100
+ id: {
101
+ type: string;
102
+ description: string;
103
+ };
104
+ title?: undefined;
105
+ html?: undefined;
106
+ markdown?: undefined;
107
+ kind?: undefined;
108
+ favicon?: undefined;
109
+ summary?: undefined;
110
+ };
111
+ required: string[];
112
+ };
113
+ })[];
114
+ }
@@ -0,0 +1,22 @@
1
+ import{existsSync as y,readFileSync as b}from"node:fs";import{homedir as k}from"node:os";import{join as O,dirname as g,resolve as _}from"node:path";import{fileURLToPath as v}from"node:url";function S(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let i=g(v(import.meta.url)),t=_(i,"..","bin","mcp-skill.mjs");return y(t)?t:null}function l(){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 i=O(k(),".zibby","config.json");return y(i)&&JSON.parse(b(i,"utf-8")).sessionToken||null}catch{return null}}function p(){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 h(i){return`${T()}:artifact:${i}`}async function u(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact write failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function N(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/artifacts/${encodeURIComponent(i)}`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact get failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function f(i,t){let e=l();if(!e)return;let r=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:h(i),content:JSON.stringify(t)})});if(!r.ok){let n=await r.text().catch(()=>"");throw new Error(`artifact index write failed (${r.status}): ${n.slice(0,200)}`)}}async function P(i){let t=l();if(!t)return null;let e=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:h(i)})});if(!e.ok)return null;let r=await e.json().catch(()=>null);if(!r?.found||!r?.memory?.content)return null;try{return JSON.parse(r.memory.content)}catch{return null}}function m(i){return typeof i?.html=="string"&&i.html.length>0?{format:"html",content:i.html}:typeof i?.markdown=="string"&&i.markdown.length>0?{format:"markdown",content:i.markdown}:null}var L={id:"artifact",serverName:"artifact",allowedTools:["mcp__artifact__*"],description:"Artifacts \u2014 publish a self-contained, shareable HTML/Markdown page to Zibby and get back a URL; the index of what you published is your memory.",promptFragment:`## Artifacts (publish a shareable page, remember what you made)
2
+ You can PUBLISH a standalone page \u2014 a status report, a plan, a comparison table,
3
+ a dashboard-y summary, a diagram, a "here's what I found" write-up \u2014 and get back
4
+ a shareable URL. The page is a self-contained HTML (or Markdown) document; it is
5
+ sandboxed when viewed (no external network, no ambient credentials), so keep all
6
+ CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 external URLs will
7
+ be blocked.
8
+
9
+ Tools:
10
+ - artifact_publish: Publish a NEW page. Pass a \`title\` and EITHER \`html\` OR
11
+ \`markdown\` (not both). Optional \`kind\` (e.g. "report", "plan", "dashboard"),
12
+ \`favicon\` (an emoji), and \`summary\` (one line for your own index). Returns
13
+ { id, url }. Share the url; keep the id if you'll update it later.
14
+ - artifact_update: Revise an EXISTING page by \`id\` (same url, new version). Pass
15
+ the fields to change (\`title\`, \`html\`|\`markdown\`).
16
+ - artifact_get: Fetch one artifact by \`id\` \u2192 { metadata, content } so you can
17
+ reuse / edit / re-publish it.
18
+
19
+ To recall WHAT YOU HAVE ALREADY PUBLISHED, use your kv-memory tool
20
+ kv_recall_prefix with keyPrefix "artifact:" \u2014 each entry is the index record
21
+ { id, title, url, kind, createdAt, summary } for a page you made. (Publishing
22
+ records this automatically; you don't store it yourself.)`,resolve(){let i=S();if(!i)return{command:null,args:[],env:{},description:this.description};let t={};for(let e of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[e]&&(t[e]=process.env[e]);return{type:"stdio",command:"node",args:[i,"../dist/artifact.js","artifactSkill"],env:t,description:this.description,alwaysLoad:!0}},async handleToolCall(i,t){try{switch(i){case"artifact_publish":{let e=typeof t?.title=="string"?t.title.trim():"";if(!e)return JSON.stringify({error:"title is required"});let r=m(t);if(!r)return JSON.stringify({error:"provide exactly one of html or markdown (non-empty string)"});let n={title:e,[r.format]:r.content};typeof t?.kind=="string"&&t.kind.trim()&&(n.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(n.favicon=t.favicon.trim());let o=await u(n),a=o?.id,s=o?.url;if(!a||!s)return JSON.stringify({error:"artifact write returned no id/url",response:o});let d={id:a,title:e,url:s,kind:n.kind||null,createdAt:o.createdAt||new Date().toISOString(),summary:typeof t?.summary=="string"&&t.summary.trim()?t.summary.trim():e};try{await f(a,d)}catch(c){return JSON.stringify({id:a,url:s,indexWarning:c.message})}return JSON.stringify({id:a,url:s})}case"artifact_update":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=m(t),n=typeof t?.title=="string"?t.title.trim():"";if(!r&&!n)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or html|markdown"});let o={id:e};n&&(o.title=n),r&&(o[r.format]=r.content),typeof t?.kind=="string"&&t.kind.trim()&&(o.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(o.favicon=t.favicon.trim());let a=await u(o),s=a?.url;if(!s)return JSON.stringify({error:"artifact update returned no url",response:a});let d=await P(e)||{},c={...d,id:e,url:s,title:n||d.title||"Untitled",kind:o.kind||d.kind||null,createdAt:d.createdAt||a.createdAt||new Date().toISOString(),updatedAt:a.updatedAt||new Date().toISOString()};typeof t?.summary=="string"&&t.summary.trim()?c.summary=t.summary.trim():c.summary||(c.summary=c.title);try{await f(e,c)}catch(w){return JSON.stringify({id:e,url:s,indexWarning:w.message})}return JSON.stringify({id:e,url:s})}case"artifact_get":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=await N(e);return JSON.stringify(r)}default:return JSON.stringify({error:`Unknown tool: ${i}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"artifact_publish",description:"Publish a NEW self-contained, shareable page (report/plan/table/dashboard/diagram/write-up) and get back a shareable URL. Pass a title and EITHER html OR markdown. Keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 the page is sandboxed on view and external URLs are blocked. Returns { id, url }.",input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},html:{type:"string",description:"The page content as a self-contained HTML document (or fragment). Provide this OR markdown, not both."},markdown:{type:"string",description:"The page content as Markdown (rendered to HTML on serve). Provide this OR html, not both."},kind:{type:"string",description:'Optional label for what this is, e.g. "report", "plan", "dashboard", "diagram". Stored in your index.'},favicon:{type:"string",description:'Optional emoji used as the browser-tab icon, e.g. "\u{1F4CA}".'},summary:{type:"string",description:"Optional one-line summary for your own index (defaults to the title). Helps you recall later what this page was."}},required:["title"]}},{name:"artifact_update",description:"Revise an EXISTING artifact by id \u2014 the shareable URL stays the same, the content is replaced (new version). Pass the fields to change (title and/or html|markdown). Returns { id, url }.",input_schema:{type:"object",properties:{id:{type:"string",description:'The id of the artifact to update (from a prior artifact_publish, or your kv-memory "artifact:" index).'},title:{type:"string",description:"New title (optional)."},html:{type:"string",description:"New HTML content (optional). Provide this OR markdown."},markdown:{type:"string",description:"New Markdown content (optional). Provide this OR html."},kind:{type:"string",description:"Optional updated kind label."},favicon:{type:"string",description:"Optional updated emoji favicon."},summary:{type:"string",description:"Optional updated one-line index summary."}},required:["id"]}},{name:"artifact_get",description:'Fetch ONE artifact you published, by id \u2192 { metadata, content }. Use to reuse / edit / re-publish a page. To LIST what you have published, use your kv-memory tool kv_recall_prefix with keyPrefix "artifact:".',input_schema:{type:"object",properties:{id:{type:"string",description:"The artifact id."}},required:["id"]}}]};export{L as artifactSkill};
@@ -0,0 +1,115 @@
1
+ export namespace gbrainSkill {
2
+ let id: string;
3
+ let serverName: string;
4
+ let allowedTools: string[];
5
+ let meta: any;
6
+ let description: string;
7
+ let promptFragment: string;
8
+ /**
9
+ * Spawn the GENERIC skill MCP server (bin/mcp-skill.mjs) pointing at this
10
+ * module's gbrainSkill export — same FIXED pattern as datasetStore. Forwards
11
+ * the backend-auth env + every bound-store mapping the spawned process needs.
12
+ */
13
+ function resolve(): {
14
+ command: any;
15
+ args: any[];
16
+ env: {};
17
+ description: string;
18
+ type?: undefined;
19
+ alwaysLoad?: undefined;
20
+ } | {
21
+ type: string;
22
+ command: string;
23
+ args: any[];
24
+ env: {};
25
+ description: string;
26
+ alwaysLoad: boolean;
27
+ };
28
+ function handleToolCall(name: any, args: any): Promise<string>;
29
+ let tools: ({
30
+ name: string;
31
+ description: string;
32
+ input_schema: {
33
+ type: string;
34
+ properties: {
35
+ docs: {
36
+ type: string;
37
+ description: string;
38
+ items: {
39
+ type: string;
40
+ properties: {
41
+ sourceId: {
42
+ type: string;
43
+ description: string;
44
+ };
45
+ markdown: {
46
+ type: string;
47
+ description: string;
48
+ };
49
+ deleted: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ };
54
+ required: string[];
55
+ };
56
+ };
57
+ store: {
58
+ type: string;
59
+ description: string;
60
+ };
61
+ query?: undefined;
62
+ topK?: undefined;
63
+ sourceIds?: undefined;
64
+ };
65
+ required: string[];
66
+ };
67
+ } | {
68
+ name: string;
69
+ description: string;
70
+ input_schema: {
71
+ type: string;
72
+ properties: {
73
+ query: {
74
+ type: string;
75
+ description: string;
76
+ };
77
+ topK: {
78
+ type: string;
79
+ description: string;
80
+ };
81
+ store: {
82
+ type: string;
83
+ description: string;
84
+ };
85
+ docs?: undefined;
86
+ sourceIds?: undefined;
87
+ };
88
+ required: string[];
89
+ };
90
+ } | {
91
+ name: string;
92
+ description: string;
93
+ input_schema: {
94
+ type: string;
95
+ properties: {
96
+ sourceIds: {
97
+ type: string;
98
+ description: string;
99
+ items: {
100
+ type: string;
101
+ };
102
+ };
103
+ store: {
104
+ type: string;
105
+ description: string;
106
+ };
107
+ docs?: undefined;
108
+ query?: undefined;
109
+ topK?: undefined;
110
+ };
111
+ required: string[];
112
+ };
113
+ })[];
114
+ }
115
+ export default gbrainSkill;
package/dist/gbrain.js ADDED
@@ -0,0 +1,12 @@
1
+ import{existsSync as f,readFileSync as b}from"node:fs";import{homedir as h}from"node:os";import{join as I,dirname as _,resolve as S}from"node:path";import{fileURLToPath as O}from"node:url";import{SKILL_META as E}from"@zibby/skill-ids";var T=3e4,p=1;function w(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let o=_(O(import.meta.url)),r=S(o,"..","bin","mcp-skill.mjs");return f(r)?r:null}function A(){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 o=I(h(),".zibby","config.json");return f(o)&&JSON.parse(b(o,"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 v(){let o={};for(let[r,e]of Object.entries(process.env)){let t=/^ZIBBY_STORE__(.+)$/.exec(r);if(!t)continue;let n=typeof e=="string"?e.trim():"";n&&(o[t[1]]=n)}return o}function l(o){let r=v(),e=Object.keys(r),t=typeof o=="string"?o.trim():"";return t?Object.prototype.hasOwnProperty.call(r,t)?{storeId:r[t],name:t}:{error:`unknown store '${t}'; available: ${e.join(", ")}`}:e.length===1?{storeId:r[e[0]],name:e[0]}:e.length===0?{error:"no knowledge-base store is bound to this agent"}:{error:`multiple stores are bound; pass \`store\` (one of: ${e.join(", ")})`}}async function m(o,r,e){let t=A();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). The knowledge base is only available inside a Zibby run.");let n=`${k()}/datasets/stores/${encodeURIComponent(o)}/${r}`,s={Authorization:`Bearer ${t}`,"Content-Type":"application/json"},u=JSON.stringify(e),i;for(let c=0;c<=p;c++){let y=new AbortController,g=setTimeout(()=>y.abort(),T);try{let a=await fetch(n,{method:"POST",headers:s,body:u,signal:y.signal}),d=await a.text().catch(()=>"");if(a.status>=500&&c<p){i=new Error(`store ${a.status}`);continue}if(!a.ok)throw new Error(`gbrain ${r} failed (${a.status}): ${d.slice(0,300)}`);try{return JSON.parse(d)}catch{throw new Error(`store returned non-JSON: ${d.slice(0,200)}`)}}catch(a){if(i=a,!((a?.name==="AbortError"||a?.code==="ECONNREFUSED"||/fetch failed|network/i.test(String(a?.message)))&&c<p))break}finally{clearTimeout(g)}}throw i||new Error(`gbrain ${r} request failed`)}var N={id:"gbrain",serverName:"gbrain",allowedTools:["mcp__gbrain__*"],meta:E.gbrain,description:"Knowledge base (GBrain) \u2014 ingest source documents into, semantically query, and prune a per-tenant Postgres/pgvector brain (a `postgres`-type store, brokered by the control-plane)",promptFragment:`## Knowledge Base (GBrain \u2014 per-tenant document brain)
2
+ You have a per-tenant KNOWLEDGE BASE (a Postgres + pgvector "brain"), bound as a
3
+ \`postgres\`-type store in the "AVAILABLE STORES" block below. Ingested documents
4
+ are addressed by a STABLE \`sourceId\` so re-ingesting the same source UPSERTS
5
+ (updates in place) rather than duplicating, and a source can be removed. If
6
+ exactly one store is bound you may omit \`store\`; otherwise pass its NAME.
7
+ Tools:
8
+ - gbrain_ingest({ docs, store? }): upsert an array of { sourceId, markdown, deleted? }.
9
+ Pass deleted:true to remove a source that no longer exists upstream.
10
+ - gbrain_query({ query, topK, store? }): semantic search; returns the topK most
11
+ relevant document chunks with their sourceId and relevance score.
12
+ - gbrain_delete({ sourceIds, store? }): remove documents by their stable sourceId(s).`,resolve(){let o=w();if(!o)return{command:null,args:[],env:{},description:this.description};let r={};for(let e of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[e]&&(r[e]=process.env[e]);for(let e of Object.keys(process.env))/^ZIBBY_STORE__.+$/.test(e)&&process.env[e]&&(r[e]=process.env[e]);return{type:"stdio",command:"node",args:[o,"../dist/gbrain.js","gbrainSkill"],env:r,description:this.description,alwaysLoad:!0}},async handleToolCall(o,r){try{switch(o){case"gbrain_ingest":{let e=Array.isArray(r?.docs)?r.docs:null;if(!e)return JSON.stringify({error:"docs is required (array of { sourceId, markdown, deleted? })"});if(e.length===0)return JSON.stringify({ok:!0,upserted:0,deleted:0,chunks:0,note:"empty docs \u2014 nothing to ingest"});if(e.find(i=>!i||typeof i.sourceId!="string"||!i.sourceId.trim())!==void 0)return JSON.stringify({error:"every doc requires a non-empty string sourceId"});let n=e.find(i=>i.deleted!==!0&&(typeof i.markdown!="string"||!i.markdown.length));if(n!==void 0)return JSON.stringify({error:`doc "${n.sourceId}" has no markdown (required unless deleted:true)`});let s=l(r?.store);if(s.error)return JSON.stringify({error:s.error});let u=await m(s.storeId,"ingest",{docs:e});return JSON.stringify({...u,store:s.name,storeId:s.storeId})}case"gbrain_query":{let e=typeof r?.query=="string"?r.query.trim():"";if(!e)return JSON.stringify({error:"query is required"});let t=l(r?.store);if(t.error)return JSON.stringify({error:t.error});let n={query:e};Number.isInteger(r?.topK)&&r.topK>0&&(n.topK=Math.min(r.topK,50));let s=await m(t.storeId,"query",n);return JSON.stringify({...s,store:t.name,storeId:t.storeId})}case"gbrain_delete":{let e=Array.isArray(r?.sourceIds)?r.sourceIds.filter(s=>typeof s=="string"&&s.trim()):null;if(!e||e.length===0)return JSON.stringify({error:"sourceIds is required (non-empty array of strings)"});let t=l(r?.store);if(t.error)return JSON.stringify({error:t.error});let n=await m(t.storeId,"delete",{sourceIds:e});return JSON.stringify({...n,store:t.name,storeId:t.storeId})}default:return JSON.stringify({error:`Unknown tool: ${o}`})}}catch(e){return JSON.stringify({error:String(e?.message||e)})}},tools:[{name:"gbrain_ingest",description:'Upsert documents into the knowledge base. Each doc is { sourceId, markdown, deleted? }; sourceId is a STABLE id (e.g. "owner/repo#docs/x.md" or a Lark doc token) so re-ingesting the same source UPDATES in place instead of duplicating. Set deleted:true to remove a source.',input_schema:{type:"object",properties:{docs:{type:"array",description:"Documents to upsert.",items:{type:"object",properties:{sourceId:{type:"string",description:"Stable source id \u2014 the upsert/delete key."},markdown:{type:"string",description:"The normalized markdown content of the document."},deleted:{type:"boolean",description:"When true, remove this sourceId from the brain (markdown ignored)."}},required:["sourceId"]}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["docs"]}},{name:"gbrain_query",description:"Semantic-search the knowledge base and return the most relevant document chunks (each with its sourceId and relevance score).",input_schema:{type:"object",properties:{query:{type:"string",description:"Natural-language query."},topK:{type:"integer",description:"How many chunks to return (default 8, max 50)."},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["query"]}},{name:"gbrain_delete",description:"Remove documents from the knowledge base by their stable sourceId(s).",input_schema:{type:"object",properties:{sourceIds:{type:"array",description:"Stable source ids to delete.",items:{type:"string"}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["sourceIds"]}}]},U=N;export{U as default,N as gbrainSkill};
@@ -1,7 +1,7 @@
1
- import{existsSync as E,statSync as P,readFileSync as R}from"fs";import{fileURLToPath as D}from"url";import{basename as L,dirname as G,resolve as B}from"path";import{resolveIntegrationToken as $,clearTokenCache as C}from"@zibby/core/backend-client.js";var x=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",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),X=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"},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 j(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let n=G(D(import.meta.url)),e=B(n,"..","bin","mcp-skill.mjs");return E(e)?e:null}var h="https://docs.googleapis.com/v1",T="https://www.googleapis.com/drive/v3",U="https://www.googleapis.com/upload/drive/v3",M=5*1024*1024;function q(){let n=String(process.env.ZIBBY_INJECTED_GOOGLE_TOKEN||"").trim();if(!n)return null;let e=String(process.env.ZIBBY_INJECTED_GOOGLE_EMAIL||"").trim();return{token:n,email:e}}function J(){return String(process.env.ZIBBY_SENDER_IS_NON_OWNER||"").trim()==="1"}function F(){return String(process.env.ZIBBY_CHAT_STRICT_PERSONAL||"").trim()==="1"}var Y="You haven't connected your own Google account \u2014 connect it at https://studio.zibby.dev/integrations (Google Docs). For privacy, I can't use anyone else's Google (including the project owner's) on your behalf.",S=2e4,H=25;function k(n){if(!n||typeof n!="string")return null;let e=n.trim(),t=e.match(/\/document\/(?:u\/\d+\/)?d\/([a-zA-Z0-9_-]+)/);return t?t[1]:/^[a-zA-Z0-9_-]{20,}$/.test(e)?e:null}async function z(){let n,e=q();if(e)n=e.token;else{if(F()||J())throw new Error(Y);({token:n}=await $("google"))}if(typeof n!="string"||!n)throw new Error(`Invalid google token type: ${typeof n}`);return n}async function p(n,e={}){let t=async()=>{let i=await z(),o=await fetch(n,{method:e.method||"GET",headers:{Authorization:`Bearer ${i}`,Accept:"application/json",...e.rawBody&&e.contentType?{"Content-Type":e.contentType}:e.body?{"Content-Type":"application/json"}:{},...e.headers},body:e.rawBody?e.rawBody:e.body?JSON.stringify(e.body):void 0});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`Google API ${o.status}: ${a.slice(0,300)}`)}let r=await o.text().catch(()=>"");if(!r||!r.trim())return{};try{return JSON.parse(r)}catch{return{raw:r}}};try{return await t()}catch(i){let o=String(i?.message||i||"").toLowerCase();if(!(o.includes("token")||o.includes("401")||o.includes("unauthorized")))throw i;return C("google"),t()}}function Z(n){let e=[],t="",i=0;for(;i<n.length;){let o=/^\[([^\]]+)\]\(([^)\s]+)\)/.exec(n.slice(i));if(o){e.push({start:t.length,end:t.length+o[1].length,link:o[2]}),t+=o[1],i+=o[0].length;continue}let r=/^\*\*([^*]+)\*\*/.exec(n.slice(i));if(r){e.push({start:t.length,end:t.length+r[1].length,bold:!0}),t+=r[1],i+=r[0].length;continue}let a=/^`([^`]+)`/.exec(n.slice(i));if(a){e.push({start:t.length,end:t.length+a[1].length,code:!0}),t+=a[1],i+=a[0].length;continue}t+=n[i],i+=1}return{text:t,styles:e}}function N(n,e){let t=String(n??"").replace(/\r\n/g,`
1
+ import{existsSync as E,statSync as P,readFileSync as R}from"fs";import{fileURLToPath as D}from"url";import{basename as L,dirname as G,resolve as B}from"path";import{resolveIntegrationToken as $,clearTokenCache as C}from"@zibby/core/backend-client.js";var T=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",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),X=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"},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 j(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let n=G(D(import.meta.url)),e=B(n,"..","bin","mcp-skill.mjs");return E(e)?e:null}var h="https://docs.googleapis.com/v1",x="https://www.googleapis.com/drive/v3",U="https://www.googleapis.com/upload/drive/v3",M=5*1024*1024;function q(){let n=String(process.env.ZIBBY_INJECTED_GOOGLE_TOKEN||"").trim();if(!n)return null;let e=String(process.env.ZIBBY_INJECTED_GOOGLE_EMAIL||"").trim();return{token:n,email:e}}function J(){return String(process.env.ZIBBY_SENDER_IS_NON_OWNER||"").trim()==="1"}function F(){return String(process.env.ZIBBY_CHAT_STRICT_PERSONAL||"").trim()==="1"}var Y="You haven't connected your own Google account \u2014 connect it at https://studio.zibby.dev/integrations (Google Docs). For privacy, I can't use anyone else's Google (including the project owner's) on your behalf.",S=2e4,H=25;function k(n){if(!n||typeof n!="string")return null;let e=n.trim(),t=e.match(/\/document\/(?:u\/\d+\/)?d\/([a-zA-Z0-9_-]+)/);return t?t[1]:/^[a-zA-Z0-9_-]{20,}$/.test(e)?e:null}async function K(){let n,e=q();if(e)n=e.token;else{if(F()||J())throw new Error(Y);({token:n}=await $("google"))}if(typeof n!="string"||!n)throw new Error(`Invalid google token type: ${typeof n}`);return n}async function p(n,e={}){let t=async()=>{let i=await K(),o=await fetch(n,{method:e.method||"GET",headers:{Authorization:`Bearer ${i}`,Accept:"application/json",...e.rawBody&&e.contentType?{"Content-Type":e.contentType}:e.body?{"Content-Type":"application/json"}:{},...e.headers},body:e.rawBody?e.rawBody:e.body?JSON.stringify(e.body):void 0});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`Google API ${o.status}: ${a.slice(0,300)}`)}let r=await o.text().catch(()=>"");if(!r||!r.trim())return{};try{return JSON.parse(r)}catch{return{raw:r}}};try{return await t()}catch(i){let o=String(i?.message||i||"").toLowerCase();if(!(o.includes("token")||o.includes("401")||o.includes("unauthorized")))throw i;return C("google"),t()}}function z(n){let e=[],t="",i=0;for(;i<n.length;){let o=/^\[([^\]]+)\]\(([^)\s]+)\)/.exec(n.slice(i));if(o){e.push({start:t.length,end:t.length+o[1].length,link:o[2]}),t+=o[1],i+=o[0].length;continue}let r=/^\*\*([^*]+)\*\*/.exec(n.slice(i));if(r){e.push({start:t.length,end:t.length+r[1].length,bold:!0}),t+=r[1],i+=r[0].length;continue}let a=/^`([^`]+)`/.exec(n.slice(i));if(a){e.push({start:t.length,end:t.length+a[1].length,code:!0}),t+=a[1],i+=a[0].length;continue}t+=n[i],i+=1}return{text:t,styles:e}}function N(n,e){let t=String(n??"").replace(/\r\n/g,`
2
2
  `);if(!t.trim())return{requests:[],endIndex:e};let i=t.split(`
3
- `),o="",r=[],a=[];for(let s of i){let c=s,I=null,b=null,f=/^(#{1,3})\s+(.*)$/.exec(c),g=/^\s*[-*]\s+(.*)$/.exec(c),m=/^\s*\d+[.)]\s+(.*)$/.exec(c);f?(I=`HEADING_${f[1].length}`,c=f[2]):g?(b="BULLET_DISC_CIRCLE_SQUARE",c=g[1]):m&&(b="NUMBERED_DECIMAL_ALPHA_ROMAN",c=m[1]);let{text:A,styles:v}=Z(c),_=e+o.length;for(let w of v)a.push({...w,start:_+w.start,end:_+w.end});o+=`${A}
4
- `,r.push({start:_,end:e+o.length,named:I,bullet:b})}if(!o)return{requests:[],endIndex:e};let u=[{insertText:{location:{index:e},text:o}}];for(let s of r)s.named&&u.push({updateParagraphStyle:{range:{startIndex:s.start,endIndex:s.end},paragraphStyle:{namedStyleType:s.named},fields:"namedStyleType"}});let d=null,l=()=>{d&&(u.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:(l(),d={start:s.start,end:s.end,preset:s.bullet}):l();l();for(let s of a)s.end<=s.start||(s.bold?u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{bold:!0},fields:"bold"}}):s.link?u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{link:{url:s.link}},fields:"link"}}):s.code&&u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{weightedFontFamily:{fontFamily:"Courier New"}},fields:"weightedFontFamily"}}));return{requests:u,endIndex:e+o.length}}function K(n){let e="",t=i=>{for(let o of Array.isArray(i)?i:[]){if(e.length>=S)return;if(o.paragraph)for(let r of o.paragraph.elements||[])e+=r?.textRun?.content||"";else if(o.table)for(let r of o.table.tableRows||[])for(let a of r.tableCells||[])t(a.content);else o.tableOfContents&&t(o.tableOfContents.content)}};return t(n?.content),e.slice(0,S)}var y=n=>`https://docs.google.com/document/d/${n}/edit`;function V(n){let e=typeof n=="string"?n.trim():"";if(!e)throw new Error("imagePath is required");if(!E(e)||!P(e).isFile())throw new Error(`imagePath not found (or not a file): ${e}`);if(!/\.(png|jpe?g)$/i.test(e))throw new Error("imagePath must be a .png or .jpg/.jpeg file");let t=R(e);if(t.length>M)throw new Error(`image is ${(t.length/(1024*1024)).toFixed(1)}MB \u2014 max 5MB (Drive multipart upload cap)`);return{bytes:t,fileName:L(e),mimeType:/\.png$/i.test(e)?"image/png":"image/jpeg"}}function W(n,e,t){let i=`zibby-gdocs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;return{rawBody:Buffer.concat([Buffer.from(`--${i}\r
3
+ `),o="",r=[],a=[];for(let s of i){let c=s,I=null,b=null,f=/^(#{1,3})\s+(.*)$/.exec(c),g=/^\s*[-*]\s+(.*)$/.exec(c),m=/^\s*\d+[.)]\s+(.*)$/.exec(c);f?(I=`HEADING_${f[1].length}`,c=f[2]):g?(b="BULLET_DISC_CIRCLE_SQUARE",c=g[1]):m&&(b="NUMBERED_DECIMAL_ALPHA_ROMAN",c=m[1]);let{text:v,styles:A}=z(c),_=e+o.length;for(let w of A)a.push({...w,start:_+w.start,end:_+w.end});o+=`${v}
4
+ `,r.push({start:_,end:e+o.length,named:I,bullet:b})}if(!o)return{requests:[],endIndex:e};let u=[{insertText:{location:{index:e},text:o}}];for(let s of r)s.named&&u.push({updateParagraphStyle:{range:{startIndex:s.start,endIndex:s.end},paragraphStyle:{namedStyleType:s.named},fields:"namedStyleType"}});let d=null,l=()=>{d&&(u.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:(l(),d={start:s.start,end:s.end,preset:s.bullet}):l();l();for(let s of a)s.end<=s.start||(s.bold?u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{bold:!0},fields:"bold"}}):s.link?u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{link:{url:s.link}},fields:"link"}}):s.code&&u.push({updateTextStyle:{range:{startIndex:s.start,endIndex:s.end},textStyle:{weightedFontFamily:{fontFamily:"Courier New"}},fields:"weightedFontFamily"}}));return{requests:u,endIndex:e+o.length}}function Z(n){let e="",t=i=>{for(let o of Array.isArray(i)?i:[]){if(e.length>=S)return;if(o.paragraph)for(let r of o.paragraph.elements||[])e+=r?.textRun?.content||"";else if(o.table)for(let r of o.table.tableRows||[])for(let a of r.tableCells||[])t(a.content);else o.tableOfContents&&t(o.tableOfContents.content)}};return t(n?.content),e.slice(0,S)}var y=n=>`https://docs.google.com/document/d/${n}/edit`;function V(n){let e=typeof n=="string"?n.trim():"";if(!e)throw new Error("imagePath is required");if(!E(e)||!P(e).isFile())throw new Error(`imagePath not found (or not a file): ${e}`);if(!/\.(png|jpe?g)$/i.test(e))throw new Error("imagePath must be a .png or .jpg/.jpeg file");let t=R(e);if(t.length>M)throw new Error(`image is ${(t.length/(1024*1024)).toFixed(1)}MB \u2014 max 5MB (Drive multipart upload cap)`);return{bytes:t,fileName:L(e),mimeType:/\.png$/i.test(e)?"image/png":"image/jpeg"}}function W(n,e,t){let i=`zibby-gdocs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;return{rawBody:Buffer.concat([Buffer.from(`--${i}\r
5
5
  Content-Type: application/json; charset=UTF-8\r
6
6
  \r
7
7
  ${JSON.stringify(n)}\r
@@ -10,13 +10,13 @@ Content-Type: ${t}\r
10
10
  \r
11
11
  `,"utf8"),e,Buffer.from(`\r
12
12
  --${i}--\r
13
- `,"utf8")]),contentType:`multipart/related; boundary=${i}`}}var O=n=>{let e=typeof n?.markdown=="string"?n.markdown:null,t=typeof n?.text=="string"?n.text:null;return e??t},re={id:"google-docs",serverName:"gdocs",allowedTools:["mcp__gdocs__*"],requiresIntegration:x.GOOGLE,description:"Google Docs \u2014 create, append to, insert images into, and read Google Docs (drive.file scoped)",promptFragment:`## Google Docs (connected)
13
+ `,"utf8")]),contentType:`multipart/related; boundary=${i}`}}var O=n=>{let e=typeof n?.markdown=="string"?n.markdown:null,t=typeof n?.text=="string"?n.text:null;return e??t},re={id:"google-docs",serverName:"gdocs",allowedTools:["mcp__gdocs__*"],requiresIntegration:T.GOOGLE,description:"Google Docs \u2014 create, append to, insert images into, and read Google Docs (drive.file scoped)",promptFragment:`## Google Docs (connected)
14
14
  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.
15
15
  Docs access is PER-USER: each teammate connects their OWN Google account (Integrations \u2192 Google Docs). In shared-chat contexts the runtime routes these tools to the SENDER's own Google; a teammate who hasn't connected their own Google gets { ok:false } with connect instructions \u2014 for privacy the project owner's Google is NEVER used on someone else's behalf. Relay those instructions rather than retrying.
16
16
  - 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.
17
17
  - gdocs_append: append markdown/text to the end of a doc you created earlier (pass the documentId or doc URL).
18
18
  - gdocs_insert_image: append a LOCAL image file (png/jpg, \u22645MB) to the end of a doc \u2014 pass { documentId, imagePath, width?, height? } (width/height in PT, optional). The image is uploaded to the user's Drive and made link-readable (anyone with the link) so Docs can render it. Returns { ok, documentId, fileId, url }.
19
- - gdocs_get: read a doc back as plain text (works for app-created/user-picked docs; arbitrary docs need the extended documents.readonly connection).
19
+ - gdocs_get: read a doc back as plain text (works for app-created/user-picked docs only; to read an arbitrary pre-existing doc the user must PICK it once first via the Google Picker \u2014 drive.file has no access to un-picked files).
20
20
  - gdocs_list_created: list the Google Docs visible to this app (drive.file \u2192 only docs it created or the user picked).
21
21
  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 n=j();if(!n)return null;let e={};for(let t of["ZIBBY_INJECTED_GOOGLE_TOKEN","ZIBBY_INJECTED_GOOGLE_EMAIL","ZIBBY_SENDER_IS_NON_OWNER","ZIBBY_CHAT_STRICT_PERSONAL"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[n,"../dist/googleDocs.js","googleDocsSkill"],env:e,description:this.description,alwaysLoad:!0}},async handleToolCall(n,e){try{switch(n){case"gdocs_create_doc":{let t=typeof e?.title=="string"&&e.title.trim()?e.title.trim():null;if(!t)return JSON.stringify({ok:!1,error:"title is required"});let o=(await p(`${h}/documents`,{method:"POST",body:{title:t}}))?.documentId;if(!o)return JSON.stringify({ok:!1,error:"Google Docs create returned no documentId"});let r=O(e);if(r&&r.trim()){let{requests:a}=N(r,1);a.length&&await p(`${h}/documents/${o}:batchUpdate`,{method:"POST",body:{requests:a}})}return JSON.stringify({ok:!0,documentId:o,title:t,url:y(o)})}case"gdocs_append":{let t=k(e?.documentId||e?.url||e?.id);if(!t)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});let i=O(e);if(!i||!i.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let r=(await p(`${h}/documents/${t}`))?.body,a=Array.isArray(r?.content)?r.content:[],u=a.length&&a[a.length-1].endIndex||2,d=Math.max(1,u-1),l=[],s=d;d>1&&(l.push({insertText:{location:{index:d},text:`
22
- `}}),s=d+1);let c=N(i,s);return l.push(...c.requests),await p(`${h}/documents/${t}:batchUpdate`,{method:"POST",body:{requests:l}}),JSON.stringify({ok:!0,documentId:t,url:y(t)})}case"gdocs_insert_image":{let t=k(e?.documentId||e?.url||e?.id);if(!t)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});if(!e?.imagePath||typeof e.imagePath!="string"||!e.imagePath.trim())return JSON.stringify({ok:!1,error:"imagePath is required"});let{bytes:i,fileName:o,mimeType:r}=V(e.imagePath),{rawBody:a,contentType:u}=W({name:o,mimeType:r},i,r),l=(await p(`${U}/files?uploadType=multipart&fields=id`,{method:"POST",rawBody:a,contentType:u}))?.id;if(!l)return JSON.stringify({ok:!1,error:"Drive upload returned no file id"});await p(`${T}/files/${l}/permissions`,{method:"POST",body:{role:"reader",type:"anyone"}});let s=await p(`${h}/documents/${t}`),c=Array.isArray(s?.body?.content)?s.body.content:[],I=c.length&&c[c.length-1].endIndex||2,f={location:{index:Math.max(1,I-1)},uri:`https://drive.google.com/uc?export=download&id=${l}`},g=Number(e?.width),m=Number(e?.height);return(Number.isFinite(g)&&g>0||Number.isFinite(m)&&m>0)&&(f.objectSize={...Number.isFinite(g)&&g>0?{width:{magnitude:g,unit:"PT"}}:{},...Number.isFinite(m)&&m>0?{height:{magnitude:m,unit:"PT"}}:{}}),await p(`${h}/documents/${t}:batchUpdate`,{method:"POST",body:{requests:[{insertInlineImage:f}]}}),JSON.stringify({ok:!0,documentId:t,fileId:l,url:y(t)})}case"gdocs_get":{let t=k(e?.documentId||e?.url||e?.id);if(!t)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});let i=await p(`${h}/documents/${t}`),o=K(i?.body);return JSON.stringify({ok:!0,documentId:t,title:i?.title||"",url:y(t),text:o,...o.length>=S?{truncated:!0}:{}})}case"gdocs_list_created":{let t=new URLSearchParams({q:"'me' in owners and mimeType='application/vnd.google-apps.document' and trashed=false",fields:"files(id,name,modifiedTime,webViewLink)",pageSize:String(H),orderBy:"modifiedTime desc"}),i=await p(`${T}/files?${t.toString()}`),o=(Array.isArray(i?.files)?i.files:[]).map(r=>({documentId:r.id,title:r.name,modifiedTime:r.modifiedTime,url:r.webViewLink||y(r.id)}));return JSON.stringify({ok:!0,count:o.length,files:o})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${n}`})}}catch(t){return JSON.stringify({ok:!1,error:t.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_insert_image",description:"Append a LOCAL image file (png/jpg, max 5MB) to the END of an existing Google Doc. The image is uploaded to the user's Drive, made link-readable (role reader / type anyone \u2014 required: the Docs API only renders publicly fetchable image URIs, <2KB URI length, image <50MB and <25 megapixels), then inserted inline. Optional width/height in points (PT). Returns { ok, documentId, fileId, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Google Docs documentId OR a full https://docs.google.com/document/d/... URL."},imagePath:{type:"string",description:"Local filesystem path to a .png or .jpg/.jpeg image (max 5MB)."},width:{type:"number",description:"Optional display width in points (PT)."},height:{type:"number",description:"Optional display height in points (PT)."}},required:["documentId","imagePath"]}},{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{Y as NON_OWNER_REFUSAL,F as chatStrictPersonal,K as extractPlainText,p as googleApi,re as googleDocsSkill,q as injectedGoogleToken,N as markdownToRequests,k as parseDocId,Z as parseInlineMarkdown,J as senderIsNonOwner};
22
+ `}}),s=d+1);let c=N(i,s);return l.push(...c.requests),await p(`${h}/documents/${t}:batchUpdate`,{method:"POST",body:{requests:l}}),JSON.stringify({ok:!0,documentId:t,url:y(t)})}case"gdocs_insert_image":{let t=k(e?.documentId||e?.url||e?.id);if(!t)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});if(!e?.imagePath||typeof e.imagePath!="string"||!e.imagePath.trim())return JSON.stringify({ok:!1,error:"imagePath is required"});let{bytes:i,fileName:o,mimeType:r}=V(e.imagePath),{rawBody:a,contentType:u}=W({name:o,mimeType:r},i,r),l=(await p(`${U}/files?uploadType=multipart&fields=id`,{method:"POST",rawBody:a,contentType:u}))?.id;if(!l)return JSON.stringify({ok:!1,error:"Drive upload returned no file id"});await p(`${x}/files/${l}/permissions`,{method:"POST",body:{role:"reader",type:"anyone"}});let s=await p(`${h}/documents/${t}`),c=Array.isArray(s?.body?.content)?s.body.content:[],I=c.length&&c[c.length-1].endIndex||2,f={location:{index:Math.max(1,I-1)},uri:`https://drive.google.com/uc?export=download&id=${l}`},g=Number(e?.width),m=Number(e?.height);return(Number.isFinite(g)&&g>0||Number.isFinite(m)&&m>0)&&(f.objectSize={...Number.isFinite(g)&&g>0?{width:{magnitude:g,unit:"PT"}}:{},...Number.isFinite(m)&&m>0?{height:{magnitude:m,unit:"PT"}}:{}}),await p(`${h}/documents/${t}:batchUpdate`,{method:"POST",body:{requests:[{insertInlineImage:f}]}}),JSON.stringify({ok:!0,documentId:t,fileId:l,url:y(t)})}case"gdocs_get":{let t=k(e?.documentId||e?.url||e?.id);if(!t)return JSON.stringify({ok:!1,error:"A valid Google Docs documentId or URL is required"});let i=await p(`${h}/documents/${t}`),o=Z(i?.body);return JSON.stringify({ok:!0,documentId:t,title:i?.title||"",url:y(t),text:o,...o.length>=S?{truncated:!0}:{}})}case"gdocs_list_created":{let t=new URLSearchParams({q:"'me' in owners and mimeType='application/vnd.google-apps.document' and trashed=false",fields:"files(id,name,modifiedTime,webViewLink)",pageSize:String(H),orderBy:"modifiedTime desc"}),i=await p(`${x}/files?${t.toString()}`),o=(Array.isArray(i?.files)?i.files:[]).map(r=>({documentId:r.id,title:r.name,modifiedTime:r.modifiedTime,url:r.webViewLink||y(r.id)}));return JSON.stringify({ok:!0,count:o.length,files:o})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${n}`})}}catch(t){return JSON.stringify({ok:!1,error:t.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_insert_image",description:"Append a LOCAL image file (png/jpg, max 5MB) to the END of an existing Google Doc. The image is uploaded to the user's Drive, made link-readable (role reader / type anyone \u2014 required: the Docs API only renders publicly fetchable image URIs, <2KB URI length, image <50MB and <25 megapixels), then inserted inline. Optional width/height in points (PT). Returns { ok, documentId, fileId, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Google Docs documentId OR a full https://docs.google.com/document/d/... URL."},imagePath:{type:"string",description:"Local filesystem path to a .png or .jpg/.jpeg image (max 5MB)."},width:{type:"number",description:"Optional display width in points (PT)."},height:{type:"number",description:"Optional display height in points (PT)."}},required:["documentId","imagePath"]}},{name:"gdocs_get",description:"Read a Google Doc back as plain text (truncated to ~20k chars). The drive.file scope grants access ONLY to docs this app created or the user explicitly picked; to read an arbitrary pre-existing doc the user must PICK it once first via the Google Picker (drive.file cannot see un-picked files). 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{Y as NON_OWNER_REFUSAL,F as chatStrictPersonal,Z as extractPlainText,p as googleApi,re as googleDocsSkill,q as injectedGoogleToken,N as markdownToRequests,k as parseDocId,z as parseInlineMarkdown,J as senderIsNonOwner};
package/dist/index.d.ts CHANGED
@@ -23,15 +23,17 @@ import { memorySkill } from './memory.js';
23
23
  import { chatMemorySkill } from './chat-memory.js';
24
24
  import { kvMemorySkill } from './kvMemory.js';
25
25
  import { datasetStoreSkill } from './datasetStore.js';
26
+ import { artifactSkill } from './artifact.js';
26
27
  import { chartRenderSkill } from './chartRender.js';
27
28
  import { socialCardSkill } from './socialCard.js';
28
29
  import { codeScanSkill } from './code-scan.js';
29
30
  import { codebaseMemorySkill } from './codebaseMemory.js';
31
+ import { gbrainSkill } from './gbrain.js';
30
32
  import { testRunnerSkill } from './test-runner.js';
31
33
  import { skillInstallerSkill } from './skill-installer.js';
32
34
  import { coreToolsSkill } from './core-tools.js';
33
35
  import { workflowBuilderSkill } from './workflow-builder.js';
34
- export { browserSkill, jiraSkill, githubSkill, gitlabSkill, figmaSkill, hubspotSkill, linearSkill, planeSkill, opendesignSkill, gitSkill, gitWriteSkill, slackSkill, larkSkill, discordSkill, notionSkill, linkedinSkill, googleDocsSkill, larkDocsSkill, chatNotifySkill, sentrySkill, memorySkill, chatMemorySkill, kvMemorySkill, datasetStoreSkill, chartRenderSkill, socialCardSkill, codeScanSkill, codebaseMemorySkill, testRunnerSkill, testRunnerSkill as runnerSkill, skillInstallerSkill, coreToolsSkill, workflowBuilderSkill };
36
+ export { browserSkill, jiraSkill, githubSkill, gitlabSkill, figmaSkill, hubspotSkill, linearSkill, planeSkill, opendesignSkill, gitSkill, gitWriteSkill, slackSkill, larkSkill, discordSkill, notionSkill, linkedinSkill, googleDocsSkill, larkDocsSkill, chatNotifySkill, sentrySkill, memorySkill, chatMemorySkill, kvMemorySkill, datasetStoreSkill, artifactSkill, chartRenderSkill, socialCardSkill, codeScanSkill, codebaseMemorySkill, gbrainSkill, testRunnerSkill, testRunnerSkill as runnerSkill, skillInstallerSkill, coreToolsSkill, workflowBuilderSkill };
35
37
  export { openaiBillingSkill, anthropicBillingSkill, cursorAdminSkill, fetchOpenAICosts, fetchOpenAIProjects, fetchAnthropicCosts, fetchAnthropicWorkspaces, fetchCursorSpend, fetchAllProviders, groupByKey, meanStddev } from "./llm-billing.js";
36
38
  export { reportObjectSchema, reportToBlockKit, reportToLarkCard, reportToNotionBlocks, reportToMarkdown, SEVERITIES as REPORT_SEVERITIES } from "./report.js";
37
39
  export { skill, functionSkill } from "./function-skill.js";