@cotal-ai/connector-claude-code 0.116.3 → 0.116.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -77,6 +77,6 @@ ${result.output}`)}if(!Array.isArray(entries))throw new Error(`could not parse p
77
77
  ${result.output}`);return entries}function verify(name,scope,expectedVersion){const match=listPlugins(cotalHome()).find(entry=>entry.id===`${name}@${MARKETPLACE}`&&entry.scope===scope);const where=`${scope} scope`;if(!match)throw new Error(`plugin ${name}@${MARKETPLACE} (${where}) is not installed`);if(match.enabled!==true||match.errors?.length)throw new Error(`plugin ${name}@${MARKETPLACE} (${where}) did not load: ${JSON.stringify({enabled:match.enabled,errors:match.errors})}`);if(expectedVersion&&!resolvedPluginCurrent(match,expectedVersion,name))throw new Error(`plugin ${name}@${MARKETPLACE} resolved payload does not match version ${expectedVersion}`)}function install(name,scope,expectedVersion){writeMarketplace();const result=command("plugin","install",`${name}@${MARKETPLACE}`,"--scope",scope);if(result.status!==0&&!/already installed/i.test(result.output))throw new Error(`plugin install failed (${name}):
78
78
  ${result.output}`);if(/already installed/i.test(result.output)){const update=command("plugin","update",`${name}@${MARKETPLACE}`,"--scope",scope);if(update.status!==0)throw new Error(`plugin update failed (${name}):
79
79
  ${update.output}`)}verify(name,scope,expectedVersion)}function refreshInstalls(name,entries,version2){const warnings=[];for(const entry of entries){const scope=entry.scope;if(scope!=="user"&&scope!=="local"&&scope!=="project")throw new Error(`cannot refresh ${name}@${MARKETPLACE} at ${String(scope)} scope; its administrator must update it`);const cwd=scope==="user"?cotalHome():entry.projectPath;if(typeof cwd!=="string")throw new Error(`${name}@${MARKETPLACE} (${scope} scope) has no project path`);if(!statSync2(cwd,{throwIfNoEntry:false})?.isDirectory())continue;if(resolvedPluginCurrent(entry,version2,name))continue;const where=scope==="user"?"user scope":`${scope} scope, ${cwd}`;const update=commandIn(cwd,"plugin","update",`${name}@${MARKETPLACE}`,"--scope",scope);if(update.status!==0)throw new Error(`plugin update failed (${name}, ${where}):
80
- ${update.output}`);const current=listPlugins(cwd).find(candidate=>candidate.id===entry.id&&candidate.scope===scope&&(scope==="user"||candidate.projectPath===entry.projectPath));if(current&&resolvedPluginCurrent(current,version2,name))continue;const stale=`${name}@${MARKETPLACE} (${where}) did not load the updated plugin ${version2}`;if(scope==="user")throw new Error(stale);warnings.push(`warning: ${stale}; to remove it, run \`claude plugin uninstall ${name}@${MARKETPLACE} --scope ${scope}\` in that directory`)}return warnings}function resolvedPluginCurrent(entry,version2,name="cotal"){const sourceRoot=name==="cotal"?PACKAGE_ROOT:join4(marketplaceDir(),name);const assets=name==="cotal"?COTAL_ASSETS:[".claude-plugin","skills"];const root=entry.readFromFolder??entry.installPath;if(typeof root!=="string")return false;try{const manifest=JSON.parse(readFileSync3(join4(root,".claude-plugin","plugin.json"),"utf8"));if(manifest.version!==version2)return false;const record2=name==="cotal"?JSON.parse(readFileSync3(join4(marketplaceDir(),"cotal","dist",CLI_RECORD),"utf8")):void 0;const generated=record2?pluginFiles(record2.argv):{};const same=asset=>{const source=join4(sourceRoot,asset);const target=join4(root,asset);const generatedText=generated[asset.replaceAll("\\","/")];if(generatedText!==void 0)return readFileSync3(target).equals(Buffer.from(generatedText));if(statSync2(source).isDirectory())return readdirSync(source).every(name2=>same(join4(asset,name2)));if(asset===join4(".claude-plugin","plugin.json")){const expected=JSON.parse(readFileSync3(source,"utf8"));return JSON.stringify({...expected,version:version2})===JSON.stringify(manifest)}return readFileSync3(source).equals(readFileSync3(target))};return assets.every(same)&&(name!=="cotal"||readFileSync3(join4(root,"dist",CLI_RECORD)).equals(readFileSync3(join4(marketplaceDir(),"cotal","dist",CLI_RECORD))))}catch{return false}}function retireProjectInstalls(){let removed=0;const failed=[];for(const entry of listPlugins()){if(entry.id!==`cotal@${MARKETPLACE}`||entry.scope!=="local"||typeof entry.projectPath!=="string")continue;if(!statSync2(entry.projectPath,{throwIfNoEntry:false})?.isDirectory())continue;const result=commandIn(entry.projectPath,"plugin","uninstall",`cotal@${MARKETPLACE}`,"--scope","local");if(result.status===0)removed+=1;else failed.push(`${entry.projectPath}: ${result.output}`)}if(failed.length)throw new Error(`cotal@${MARKETPLACE} is installed at user scope, but ${failed.length} earlier per-project install${failed.length===1?"":"s"} could not be removed. In each directory, run \`claude plugin uninstall cotal@${MARKETPLACE} --scope local\`:
81
- ${failed.join("\n")}`);return removed}function pluginList(){try{const result=command("plugin","list","--json");if(result.status!==0)return void 0;const parsed=JSON.parse(result.output||"[]");return Array.isArray(parsed)?parsed:void 0}catch{return void 0}}function skillsRow(entries,{version:version2,skillsRemedy}){const row=(state,text)=>({label:"Claude skills",state,text});if(entries===void 0)return row("off","unknown");const match=entries.find(entry=>entry.id===`cotal-skills@${MARKETPLACE}`&&entry.scope==="user");if(!match||match.enabled===false)return row("off",`not installed \xB7 ${skillsRemedy}`);const errors=match.errors??match.error;if(Array.isArray(errors)?errors.length>0:Boolean(errors))return row("error",`load error \xB7 ${skillsRemedy}`);if(resolvedPluginCurrent(match,version2,"cotal-skills"))return row("ok","current");return row("warn",`${typeof match.version==="string"?`v${match.version} \u2260 v${version2} \xB7 `:""}stale \xB7 ${skillsRemedy}`)}function pluginRow(entries,{version:version2}){const row=(state,text)=>({label:"Claude plugin",state,text});if(entries===void 0)return row("off","unknown");const installs=entries.filter(entry=>entry.id===`cotal@${MARKETPLACE}`);if(!installs.length)return row("off","not installed");if(installs.some(entry=>Array.isArray(entry.errors)?entry.errors.length>0:Boolean(entry.errors??entry.error)))return row("error","load error \xB7 run `cotal setup`");let materialized;try{materialized=JSON.parse(readFileSync3(join4(marketplaceDir(),"cotal",".claude-plugin","plugin.json"),"utf8")).version}catch{materialized=void 0}if(typeof materialized!=="string"||!materialized.startsWith(`${version2}+`))return row("warn","stale \xB7 run `cotal setup`");const active=installs.filter(entry=>entry.scope==="user"||typeof entry.projectPath!=="string"||statSync2(entry.projectPath,{throwIfNoEntry:false})?.isDirectory());if(active.some(entry=>!resolvedPluginCurrent(entry,materialized)))return row("warn","stale \xB7 run `cotal setup`");if(installs.some(entry=>entry.scope==="user"))return row("ok","current (user scope)");return row("ok",`current (${installs.length} install${installs.length===1?"":"s"})`)}function desktopRow(signedIn){const path=desktopConfigPath();if(!path||!existsSync3(dirname2(path)))return void 0;const row=(state,text)=>({label:"Claude Desktop",state,text});let entry;try{entry=readDesktopEntry(path)}catch(e){return row("error",e.message)}if(signedIn)return entry===void 0?row("off",hostedConnector(signedIn)):row("warn",`added, but Desktop ${hostedConnector(signedIn)}`);if(entry===void 0)return row("off","not added \xB7 run `cotal setup`");const server=entry.args;const expected=join4(marketplaceDir(),"cotal","dist","seat.cjs");if(!Array.isArray(server)||server[0]!==expected||!existsSync3(expected))return row("warn","stale \xB7 run `cotal setup`");return row("ok","added")}function copyable(name,spec){if(mcpServerProblem2(name,spec)!==void 0)return false;const{env,headers}=spec;return[env,headers].every(values=>values===void 0||Object.values(values).every(v=>v.replace(ENV_REFERENCE,"")===""))}var assistSession;function materializeSkills({skillsDir,version:version2,stateDir}){const manifestRoot=join4(PACKAGE_ROOT,"skills-plugin");if(!existsSync3(join4(manifestRoot,".claude-plugin","plugin.json")))throw new Error("Claude skills plugin manifest is missing");const payload=join4(stateDir,`.claude-skills-payload.${process.pid}`);rmSync(payload,{recursive:true,force:true});try{mkdirSync(stateDir,{recursive:true});mkdirSync(payload);cpSync(join4(manifestRoot,".claude-plugin"),join4(payload,".claude-plugin"),{recursive:true,dereference:true});cpSync(skillsDir,join4(payload,"skills"),{recursive:true,dereference:true});materialize("cotal-skills",payload,[".claude-plugin","skills"],version2)}finally{rmSync(payload,{recursive:true,force:true})}}var claudeSetupProvider={kind:"connector-setup",name:"claude",requires:["claude"],available:claudeAvailable,connector:{name:"claude-plugin",title:"Install the Claude Code plugin",explain:"Installs Cotal messaging for Claude Code, including supported local sessions in Desktop's Code tab. Reopen Claude after setup.",context:[DOCS_URL],run({cotal,signedIn}){return withPluginLock(()=>{const version2=materializeCotal(cotal);install("cotal","user",version2);const app=desktop(cotal,true,signedIn);const removed=retireProjectInstalls();const replaced=removed?`, replacing ${removed} project install${removed===1?"":"s"}`:"";return`cotal@${MARKETPLACE} (user scope${replaced})${app?`; ${app}`:""}`})}},refresh:{name:"claude-plugin-refresh",title:"Refresh the Claude Code plugin",explain:"Rewrites the copy of the Cotal plugin that Claude Code sessions load, and Cotal's entry in Claude Desktop, for this release.",context:[DOCS_URL],run({cotal,skills}){return withPluginLock(()=>{const entries=installedForRefresh();const installs=entries.filter(entry=>entry.id===`cotal@${MARKETPLACE}`);const skillInstalls=entries.filter(entry=>entry.id===`cotal-skills@${MARKETPLACE}`);const path=desktopConfigPath();const app=path!==void 0&&readDesktopEntry(path)!==void 0;if(!installs.length&&!skillInstalls.length&&!app)return"not installed";if(skillInstalls.length&&!skills)throw new Error("current skills are required to refresh the installed Claude skills plugin");const version2=installs.length||app?materializeCotal(cotal):void 0;if(skillInstalls.length)materializeSkills(skills);if(installs.length||skillInstalls.length)writeMarketplace();const parts=[];if(installs.length){const warnings=refreshInstalls("cotal",installs,version2);parts.push(`cotal@${MARKETPLACE} ${version2}; reload plugins or reopen Claude to load it in running sessions`,...warnings)}if(skillInstalls.length){const warnings=refreshInstalls("cotal-skills",skillInstalls,skills.version);parts.push(`cotal-skills@${MARKETPLACE} ${skills.version}`,...warnings)}const desktopText=app?desktop(cotal,false):void 0;if(desktopText)parts.push(desktopText);return parts.join("; ")})}},skills:{name:"claude-skills-plugin",title:"Add Cotal's skills to Claude Code",explain:"Installs Cotal's authored skills as a Claude Code plugin.",context:[DOCS_URL],run(input){return withPluginLock(()=>{materializeSkills(input);install("cotal-skills","user",input.version);return`cotal-skills@${MARKETPLACE} (user scope)`})}},mcpServers:{name:"claude-mcp-servers",title:"Share your MCP servers with spawned sessions",explain:"A spawned Claude Code session gets the cotal tools plus the MCP servers your own sessions load. Each spawn starts its own copy of every shared server, so on a small machine remove the heavy ones from the cotal config or spawn with --share-tools none.",context:[DOCS_URL],run({seed}){const installed=Object.entries(claudeUserMcpServers());const shared=Object.fromEntries(installed.filter(entry=>copyable(...entry)));const left=installed.filter(entry=>!copyable(...entry)).map(([name])=>name);const path=globalConfigPath();if(!seed(shared))return`kept the list ${path} already declares`;const parts=[`${Object.keys(shared).join(", ")||"none"} via ${path}`];if(left.length)parts.push(`not shared: ${left.join(", ")} (setup copies a server only when it is well formed and its env and headers values are all \${VAR} references; add it to ${path} by hand to share it)`);return parts.join("; ")}},assist:{title:"Claude",run(prompt){const sessionArgs=assistSession?["--resume",assistSession]:["--session-id",assistSession=randomUUID()];const child=spawn(claudeCommand(),[prompt,"--permission-mode","auto",...sessionArgs],{stdio:"inherit"});return new Promise((resolve2,reject)=>{child.on("exit",()=>resolve2());child.on("error",reject)})}},status(input){const plugins=pluginList();const app=desktopRow(input.signedIn);return[pluginRow(plugins,input),...app?[app]:[],skillsRow(plugins,input)]}};registry3.register(claudeSetupProvider);import{resolve}from"node:path";import{fileURLToPath as fileURLToPath2}from"node:url";import{loadAgentFile as loadAgentFile2,registry as registry4,writeLaunchArtifact}from"@cotal-ai/core";import{createHash as createHash3}from"node:crypto";import{execFileSync}from"node:child_process";import{constants,copyFileSync,lstatSync,mkdirSync as mkdirSync2,readdirSync as readdirSync2,readFileSync as readFileSync4,writeFileSync as writeFileSync3}from"node:fs";import{homedir as homedir4,hostname as hostname3}from"node:os";import{join as join5}from"node:path";import{writePrivateRecord as writePrivateRecord2}from"@cotal-ai/core";var SEAT_PROJECT="seat";var FORK_RECORD="cotal-fork.json";var MIN_VERSION=[2,1,234];var MAX_TITLE=1024;function configDir(env){return env.CLAUDE_CONFIG_DIR||join5(env.HOME||homedir4(),".claude")}function transcripts(env){const projects=join5(configDir(env),"projects");let dirs;try{dirs=readdirSync2(projects,{withFileTypes:true}).filter(d=>d.isDirectory()).map(d=>join5(projects,d.name))}catch(e){if(e.code==="ENOENT")return[];throw e}return dirs.flatMap(dir=>readdirSync2(dir,{withFileTypes:true}).filter(f=>f.isFile()&&f.name.endsWith(".jsonl")).map(f=>join5(dir,f.name)))}function titleOf(path){let title;for(const line of readFileSync4(path,"utf8").split("\n")){try{const entry=JSON.parse(line);if(entry.type==="custom-title"&&typeof entry.customTitle==="string")title=entry.customTitle}catch{}}return title}function sha256(path){return createHash3("sha256").update(readFileSync4(path)).digest("hex")}var claudeResumeTranscript={find(id,env){const all=transcripts(env);const held=all.filter(p=>p.endsWith(`/${id}.jsonl`));if(held.length>0){const digests=new Set(held.map(sha256));if(digests.size>1)throw new Error(`session ${id} is held by ${digests.size} different transcripts on this host (${held.join(", ")}); remove the stale copy`);const title=titleOf(held[0]);if(title!==void 0&&title.length>MAX_TITLE)throw new Error(`session ${id} has a title of ${title.length} characters; a carried session's title is at most ${MAX_TITLE}`);return{path:held[0],...title?{title}:{}}}const named=all.filter(p=>titleOf(p)===id);if(named.length===0)return void 0;const rows=named.map(p=>{const sid=p.slice(p.lastIndexOf("/")+1,-".jsonl".length);return` ${hostname3()} ${sid} sha256:${sha256(p)} ${lstatSync(p).mtime.toISOString()}`});throw new Error(`"${id}" is a session name, not a session id; resume by id:
80
+ ${update.output}`);const current=listPlugins(cwd).find(candidate=>candidate.id===entry.id&&candidate.scope===scope&&(scope==="user"||candidate.projectPath===entry.projectPath));if(current&&resolvedPluginCurrent(current,version2,name))continue;const stale=`${name}@${MARKETPLACE} (${where}) did not load the updated plugin ${version2}`;if(scope==="user")throw new Error(stale);warnings.push(`warning: ${stale}; to remove it, run \`claude plugin uninstall ${name}@${MARKETPLACE} --scope ${scope}\` in that directory`)}return warnings}function resolvedPluginCurrent(entry,version2,name="cotal"){const sourceRoot=name==="cotal"?PACKAGE_ROOT:join4(marketplaceDir(),name);const assets=name==="cotal"?COTAL_ASSETS:[".claude-plugin","skills"];const root=entry.readFromFolder??entry.installPath;if(typeof root!=="string")return false;try{const manifest=JSON.parse(readFileSync3(join4(root,".claude-plugin","plugin.json"),"utf8"));if(manifest.version!==version2)return false;const record2=name==="cotal"?JSON.parse(readFileSync3(join4(marketplaceDir(),"cotal","dist",CLI_RECORD),"utf8")):void 0;const generated=record2?pluginFiles(record2.argv):{};const same=asset=>{const source=join4(sourceRoot,asset);const target=join4(root,asset);const generatedText=generated[asset.replaceAll("\\","/")];if(generatedText!==void 0)return readFileSync3(target).equals(Buffer.from(generatedText));if(statSync2(source).isDirectory())return readdirSync(source).every(name2=>same(join4(asset,name2)));if(asset===join4(".claude-plugin","plugin.json")){const expected=JSON.parse(readFileSync3(source,"utf8"));return JSON.stringify({...expected,version:version2})===JSON.stringify(manifest)}return readFileSync3(source).equals(readFileSync3(target))};return assets.every(same)&&(name!=="cotal"||readFileSync3(join4(root,"dist",CLI_RECORD)).equals(readFileSync3(join4(marketplaceDir(),"cotal","dist",CLI_RECORD))))}catch{return false}}function retireProjectInstalls(entries=listPlugins()){let removed=0;const failed=[];for(const entry of entries){if(entry.id!==`cotal@${MARKETPLACE}`||entry.scope!=="local"||typeof entry.projectPath!=="string")continue;if(!statSync2(entry.projectPath,{throwIfNoEntry:false})?.isDirectory())continue;const result=commandIn(entry.projectPath,"plugin","uninstall",`cotal@${MARKETPLACE}`,"--scope","local");if(result.status===0)removed+=1;else failed.push(`${entry.projectPath}: ${result.output}`)}if(failed.length)throw new Error(`cotal@${MARKETPLACE} is installed at user scope, but ${failed.length} earlier per-project install${failed.length===1?"":"s"} could not be removed. In each directory, run \`claude plugin uninstall cotal@${MARKETPLACE} --scope local\`:
81
+ ${failed.join("\n")}`);return removed}function offForAccount(){const path=join4(process.env.CLAUDE_CONFIG_DIR?.trim()||join4(homedir3(),".claude"),"settings.json");if(!existsSync3(path))return false;const settings=JSON.parse(readFileSync3(path,"utf8"));return settings.enabledPlugins?.[`cotal@${MARKETPLACE}`]===false}function promoteLocalOnly(installs,version2){if(installs.some(entry=>entry.scope!=="local"&&entry.scope!=="project")||offForAccount())return void 0;const on=installs.filter(entry=>{const project=entry.projectPath;if(entry.scope!=="local"||typeof project!=="string"||!statSync2(project,{throwIfNoEntry:false})?.isDirectory())return false;return listPlugins(project).some(own=>own.id===entry.id&&own.scope==="local"&&own.projectPath===project&&own.enabled===true)});if(!on.length)return void 0;install("cotal","user",version2);return retireProjectInstalls(on)}function pluginList(){try{const result=command("plugin","list","--json");if(result.status!==0)return void 0;const parsed=JSON.parse(result.output||"[]");return Array.isArray(parsed)?parsed:void 0}catch{return void 0}}function skillsRow(entries,{version:version2,skillsRemedy}){const row=(state,text)=>({label:"Claude skills",state,text});if(entries===void 0)return row("off","unknown");const match=entries.find(entry=>entry.id===`cotal-skills@${MARKETPLACE}`&&entry.scope==="user");if(!match||match.enabled===false)return row("off",`not installed \xB7 ${skillsRemedy}`);const errors=match.errors??match.error;if(Array.isArray(errors)?errors.length>0:Boolean(errors))return row("error",`load error \xB7 ${skillsRemedy}`);if(resolvedPluginCurrent(match,version2,"cotal-skills"))return row("ok","current");return row("warn",`${typeof match.version==="string"?`v${match.version} \u2260 v${version2} \xB7 `:""}stale \xB7 ${skillsRemedy}`)}function pluginRow(entries,{version:version2}){const row=(state,text)=>({label:"Claude plugin",state,text});if(entries===void 0)return row("off","unknown");const installs=entries.filter(entry=>entry.id===`cotal@${MARKETPLACE}`);if(!installs.length)return row("off","not installed");if(installs.some(entry=>Array.isArray(entry.errors)?entry.errors.length>0:Boolean(entry.errors??entry.error)))return row("error","load error \xB7 run `cotal setup`");let materialized;try{materialized=JSON.parse(readFileSync3(join4(marketplaceDir(),"cotal",".claude-plugin","plugin.json"),"utf8")).version}catch{materialized=void 0}if(typeof materialized!=="string"||!materialized.startsWith(`${version2}+`))return row("warn","stale \xB7 run `cotal setup`");const active=installs.filter(entry=>entry.scope==="user"||typeof entry.projectPath!=="string"||statSync2(entry.projectPath,{throwIfNoEntry:false})?.isDirectory());if(active.some(entry=>!resolvedPluginCurrent(entry,materialized)))return row("warn","stale \xB7 run `cotal setup`");if(installs.some(entry=>entry.scope==="user"))return row("ok","current (user scope)");return row("ok",`current (${installs.length} install${installs.length===1?"":"s"})`)}function desktopRow(signedIn){const path=desktopConfigPath();if(!path||!existsSync3(dirname2(path)))return void 0;const row=(state,text)=>({label:"Claude Desktop",state,text});let entry;try{entry=readDesktopEntry(path)}catch(e){return row("error",e.message)}if(signedIn)return entry===void 0?row("off",hostedConnector(signedIn)):row("warn",`added, but Desktop ${hostedConnector(signedIn)}`);if(entry===void 0)return row("off","not added \xB7 run `cotal setup`");const server=entry.args;const expected=join4(marketplaceDir(),"cotal","dist","seat.cjs");if(!Array.isArray(server)||server[0]!==expected||!existsSync3(expected))return row("warn","stale \xB7 run `cotal setup`");return row("ok","added")}function copyable(name,spec){if(mcpServerProblem2(name,spec)!==void 0)return false;const{env,headers}=spec;return[env,headers].every(values=>values===void 0||Object.values(values).every(v=>v.replace(ENV_REFERENCE,"")===""))}var assistSession;function materializeSkills({skillsDir,version:version2,stateDir}){const manifestRoot=join4(PACKAGE_ROOT,"skills-plugin");if(!existsSync3(join4(manifestRoot,".claude-plugin","plugin.json")))throw new Error("Claude skills plugin manifest is missing");const payload=join4(stateDir,`.claude-skills-payload.${process.pid}`);rmSync(payload,{recursive:true,force:true});try{mkdirSync(stateDir,{recursive:true});mkdirSync(payload);cpSync(join4(manifestRoot,".claude-plugin"),join4(payload,".claude-plugin"),{recursive:true,dereference:true});cpSync(skillsDir,join4(payload,"skills"),{recursive:true,dereference:true});materialize("cotal-skills",payload,[".claude-plugin","skills"],version2)}finally{rmSync(payload,{recursive:true,force:true})}}var claudeSetupProvider={kind:"connector-setup",name:"claude",requires:["claude"],available:claudeAvailable,connector:{name:"claude-plugin",title:"Install the Claude Code plugin",explain:"Installs Cotal messaging for Claude Code, including supported local sessions in Desktop's Code tab. Reopen Claude after setup.",context:[DOCS_URL],run({cotal,signedIn}){return withPluginLock(()=>{const version2=materializeCotal(cotal);install("cotal","user",version2);const app=desktop(cotal,true,signedIn);const removed=retireProjectInstalls();const replaced=removed?`, replacing ${removed} project install${removed===1?"":"s"}`:"";return`cotal@${MARKETPLACE} (user scope${replaced})${app?`; ${app}`:""}`})}},refresh:{name:"claude-plugin-refresh",title:"Refresh the Claude Code plugin",explain:"Rewrites the copy of the Cotal plugin that Claude Code sessions load, and Cotal's entry in Claude Desktop, for this release.",context:[DOCS_URL],run({cotal,skills}){return withPluginLock(()=>{const entries=installedForRefresh();const installs=entries.filter(entry=>entry.id===`cotal@${MARKETPLACE}`);const skillInstalls=entries.filter(entry=>entry.id===`cotal-skills@${MARKETPLACE}`);const path=desktopConfigPath();const app=path!==void 0&&readDesktopEntry(path)!==void 0;if(!installs.length&&!skillInstalls.length&&!app)return"not installed";if(skillInstalls.length&&!skills)throw new Error("current skills are required to refresh the installed Claude skills plugin");const version2=installs.length||app?materializeCotal(cotal):void 0;if(skillInstalls.length)materializeSkills(skills);if(installs.length||skillInstalls.length)writeMarketplace();const parts=[];if(installs.length){const promoted=promoteLocalOnly(installs,version2);const current=promoted===void 0?installs:listPlugins().filter(entry=>entry.id===`cotal@${MARKETPLACE}`);const warnings=refreshInstalls("cotal",current,version2);if(promoted!==void 0)parts.push(`cotal@${MARKETPLACE} (user scope${promoted?`, replacing ${promoted} project install${promoted===1?"":"s"}`:""})`);parts.push(`cotal@${MARKETPLACE} ${version2}; reload plugins or reopen Claude to load it in running sessions`,...warnings)}if(skillInstalls.length){const warnings=refreshInstalls("cotal-skills",skillInstalls,skills.version);parts.push(`cotal-skills@${MARKETPLACE} ${skills.version}`,...warnings)}const desktopText=app?desktop(cotal,false):void 0;if(desktopText)parts.push(desktopText);return parts.join("; ")})}},skills:{name:"claude-skills-plugin",title:"Add Cotal's skills to Claude Code",explain:"Installs Cotal's authored skills as a Claude Code plugin.",context:[DOCS_URL],run(input){return withPluginLock(()=>{materializeSkills(input);install("cotal-skills","user",input.version);return`cotal-skills@${MARKETPLACE} (user scope)`})}},mcpServers:{name:"claude-mcp-servers",title:"Share your MCP servers with spawned sessions",explain:"A spawned Claude Code session gets the cotal tools plus the MCP servers your own sessions load. Each spawn starts its own copy of every shared server, so on a small machine remove the heavy ones from the cotal config or spawn with --share-tools none.",context:[DOCS_URL],run({seed}){const installed=Object.entries(claudeUserMcpServers());const shared=Object.fromEntries(installed.filter(entry=>copyable(...entry)));const left=installed.filter(entry=>!copyable(...entry)).map(([name])=>name);const path=globalConfigPath();if(!seed(shared))return`kept the list ${path} already declares`;const parts=[`${Object.keys(shared).join(", ")||"none"} via ${path}`];if(left.length)parts.push(`not shared: ${left.join(", ")} (setup copies a server only when it is well formed and its env and headers values are all \${VAR} references; add it to ${path} by hand to share it)`);return parts.join("; ")}},assist:{title:"Claude",run(prompt){const sessionArgs=assistSession?["--resume",assistSession]:["--session-id",assistSession=randomUUID()];const child=spawn(claudeCommand(),[prompt,"--permission-mode","auto",...sessionArgs],{stdio:"inherit"});return new Promise((resolve2,reject)=>{child.on("exit",()=>resolve2());child.on("error",reject)})}},status(input){const plugins=pluginList();const app=desktopRow(input.signedIn);return[pluginRow(plugins,input),...app?[app]:[],skillsRow(plugins,input)]}};registry3.register(claudeSetupProvider);import{resolve}from"node:path";import{fileURLToPath as fileURLToPath2}from"node:url";import{loadAgentFile as loadAgentFile2,registry as registry4,writeLaunchArtifact}from"@cotal-ai/core";import{createHash as createHash3}from"node:crypto";import{execFileSync}from"node:child_process";import{constants,copyFileSync,lstatSync,mkdirSync as mkdirSync2,readdirSync as readdirSync2,readFileSync as readFileSync4,writeFileSync as writeFileSync3}from"node:fs";import{homedir as homedir4,hostname as hostname3}from"node:os";import{join as join5}from"node:path";import{writePrivateRecord as writePrivateRecord2}from"@cotal-ai/core";var SEAT_PROJECT="seat";var FORK_RECORD="cotal-fork.json";var MIN_VERSION=[2,1,234];var MAX_TITLE=1024;function configDir(env){return env.CLAUDE_CONFIG_DIR||join5(env.HOME||homedir4(),".claude")}function transcripts(env){const projects=join5(configDir(env),"projects");let dirs;try{dirs=readdirSync2(projects,{withFileTypes:true}).filter(d=>d.isDirectory()).map(d=>join5(projects,d.name))}catch(e){if(e.code==="ENOENT")return[];throw e}return dirs.flatMap(dir=>readdirSync2(dir,{withFileTypes:true}).filter(f=>f.isFile()&&f.name.endsWith(".jsonl")).map(f=>join5(dir,f.name)))}function titleOf(path){let title;for(const line of readFileSync4(path,"utf8").split("\n")){try{const entry=JSON.parse(line);if(entry.type==="custom-title"&&typeof entry.customTitle==="string")title=entry.customTitle}catch{}}return title}function sha256(path){return createHash3("sha256").update(readFileSync4(path)).digest("hex")}var claudeResumeTranscript={find(id,env){const all=transcripts(env);const held=all.filter(p=>p.endsWith(`/${id}.jsonl`));if(held.length>0){const digests=new Set(held.map(sha256));if(digests.size>1)throw new Error(`session ${id} is held by ${digests.size} different transcripts on this host (${held.join(", ")}); remove the stale copy`);const title=titleOf(held[0]);if(title!==void 0&&title.length>MAX_TITLE)throw new Error(`session ${id} has a title of ${title.length} characters; a carried session's title is at most ${MAX_TITLE}`);return{path:held[0],...title?{title}:{}}}const named=all.filter(p=>titleOf(p)===id);if(named.length===0)return void 0;const rows=named.map(p=>{const sid=p.slice(p.lastIndexOf("/")+1,-".jsonl".length);return` ${hostname3()} ${sid} sha256:${sha256(p)} ${lstatSync(p).mtime.toISOString()}`});throw new Error(`"${id}" is a session name, not a session id; resume by id:
82
82
  ${rows.join("\n")}`)}};function versionAtLeast(text){const m=/(\d+)\.(\d+)\.(\d+)/.exec(text);if(!m)return false;const v=[Number(m[1]),Number(m[2]),Number(m[3])];for(let i=0;i<3;i++)if(v[i]!==MIN_VERSION[i])return v[i]>MIN_VERSION[i];return true}function refuseCarriedLaunch(binary,seatEnv){const version2=execFileSync(binary,["--version"],{encoding:"utf8",timeout:1e4});if(!versionAtLeast(version2))throw new Error(`claude connector: a carried resume needs Claude ${MIN_VERSION.join(".")} or later (CLAUDE_CODE_PROJECT_DIR_NAME); this host runs ${version2.trim()}`);const provider=Object.keys(seatEnv).some(k=>k.startsWith("CLAUDE_CODE_USE_")&&seatEnv[k]!==""&&seatEnv[k]!=="0");if(!seatEnv.CLAUDE_CODE_OAUTH_TOKEN&&!seatEnv.ANTHROPIC_AUTH_TOKEN&&!provider)throw new Error("claude connector: a carried resume runs in a seat-private Claude home that holds no login"+(seatEnv.ANTHROPIC_API_KEY?", and ANTHROPIC_API_KEY alone needs an approval that home cannot remember":"")+"; run `claude setup-token` and set CLAUDE_CODE_OAUTH_TOKEN in the manager's environment")}function carriedForkRecord(home){return join5(home,FORK_RECORD)}function placeCarried(home,transcript,resume,cwd){writePrivateRecord2(join5(home,".claude.json"),JSON.stringify({hasCompletedOnboarding:true,projects:{[cwd]:{hasTrustDialogAccepted:true}}}));if(resume!==void 0){const dir=join5(home,"projects",SEAT_PROJECT);mkdirSync2(dir,{recursive:true,mode:448});copyFileSync(transcript,join5(dir,`${resume}.jsonl`),constants.COPYFILE_EXCL)}return{CLAUDE_CONFIG_DIR:home,CLAUDE_CODE_PROJECT_DIR_NAME:SEAT_PROJECT,...resume!==void 0?{COTAL_CLAUDE_CARRIED:resume}:{}}}var CLAUDE_WORKSPACE_TRUST_REPLIES=Object.freeze([Object.freeze({prompt:"\u276F 1. Yes, I trust this folder\n2. No, exit",key:"Enter",optional:true}),Object.freeze({prompt:"\u276F No, exit\nYes, I trust this folder",key:"Down"}),Object.freeze({prompt:"No, exit\n\u276F Yes, I trust this folder",key:"Enter"})]);var CLAUDE_READY_TEXTS=Object.freeze(["(shift+tab to cycle)","? for shortcuts"]);var CLAUDE_STARTUP_BLOCKERS=Object.freeze([Object.freeze({prompt:"Choose the text style that looks best with your terminal",reason:"Claude Code is showing its first-run setup, which Cotal does not answer. Run `claude` once in a terminal as this user, finish setup and log in, then launch again."}),Object.freeze({prompt:"Select login method:",reason:"Claude Code is not logged in for this user and asks for a login method, which Cotal does not answer. Run `claude` once in a terminal and log in, or pass a credential the connector forwards, such as CLAUDE_CODE_OAUTH_TOKEN, then launch again."}),Object.freeze({prompt:"WARNING: Claude Code running in Bypass Permissions mode",reason:"Claude Code asks this user to accept Bypass Permissions mode, which a managed seat uses by default, and Cotal never accepts it for you. Accept it once yourself with `claude --permission-mode bypassPermissions` in a terminal, or launch with permission-mode=default (`--opt permission-mode=default`, or `launchOptions` in a manifest)."})]);var MCP_SERVER_NAME="cotal";var CLAUDE_PROVIDER_KEYS=["CLAUDE_CODE_OAUTH_TOKEN","CLAUDE_CODE_OAUTH_REFRESH_TOKEN","CLAUDE_CODE_OAUTH_SCOPES","ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","ANTHROPIC_BASE_URL","CLAUDE_CONFIG_DIR","CLAUDE_CODE_USE_BEDROCK","CLAUDE_CODE_USE_VERTEX","CLAUDE_CODE_USE_FOUNDRY","CLAUDE_CODE_USE_ANTHROPIC_AWS","CLAUDE_CODE_USE_MANTLE","CLAUDE_CODE_SKIP_BEDROCK_AUTH","CLAUDE_CODE_SKIP_VERTEX_AUTH","CLAUDE_CODE_SKIP_FOUNDRY_AUTH","CLAUDE_CODE_SKIP_ANTHROPIC_AWS_AUTH","CLAUDE_CODE_SKIP_MANTLE_AUTH","CLAUDE_CODE_SKIP_AWS_CRED_CACHE","CLAUDE_CODE_AWS_CHAIN_RESOLVE_TIMEOUT_MS","ANTHROPIC_AWS_API_KEY","ANTHROPIC_AWS_BASE_URL","ANTHROPIC_AWS_WORKSPACE_ID","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_SESSION_TOKEN","AWS_PROFILE","AWS_REGION","AWS_DEFAULT_REGION","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","AWS_BEARER_TOKEN_BEDROCK","ANTHROPIC_BEDROCK_BASE_URL","ANTHROPIC_BEDROCK_MANTLE_BASE_URL","ANTHROPIC_BEDROCK_REGION_PREFIX","ANTHROPIC_BEDROCK_SERVICE_TIER","ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION","ANTHROPIC_VERTEX_BASE_URL","ANTHROPIC_VERTEX_PROJECT_ID","GOOGLE_APPLICATION_CREDENTIALS","CLOUD_ML_REGION","GCLOUD_PROJECT","GOOGLE_CLOUD_PROJECT","VERTEX_REGION_CLAUDE_3_5_HAIKU","VERTEX_REGION_CLAUDE_3_5_SONNET","VERTEX_REGION_CLAUDE_3_7_SONNET","VERTEX_REGION_CLAUDE_4_0_OPUS","VERTEX_REGION_CLAUDE_4_0_SONNET","VERTEX_REGION_CLAUDE_4_1_OPUS","VERTEX_REGION_CLAUDE_4_5_OPUS","VERTEX_REGION_CLAUDE_4_5_SONNET","VERTEX_REGION_CLAUDE_4_6_OPUS","VERTEX_REGION_CLAUDE_4_6_SONNET","VERTEX_REGION_CLAUDE_4_7_OPUS","VERTEX_REGION_CLAUDE_4_8_OPUS","VERTEX_REGION_CLAUDE_5_OPUS","VERTEX_REGION_CLAUDE_5_SONNET","VERTEX_REGION_CLAUDE_FABLE_5","VERTEX_REGION_CLAUDE_FABLE_5_1","VERTEX_REGION_CLAUDE_HAIKU_4_5","ANTHROPIC_FOUNDRY_API_KEY","ANTHROPIC_FOUNDRY_AUTH_TOKEN","ANTHROPIC_FOUNDRY_BASE_URL","ANTHROPIC_FOUNDRY_RESOURCE","ANTHROPIC_PROFILE","ANTHROPIC_FEDERATION_RULE_ID","ANTHROPIC_ORGANIZATION_ID","ANTHROPIC_WORKSPACE_ID","ANTHROPIC_IDENTITY_TOKEN","ANTHROPIC_IDENTITY_TOKEN_FILE"];var CHANNEL_REF=`server:${MCP_SERVER_NAME}`;var PLUGIN_ROOT=fileURLToPath2(new URL("..",import.meta.url));var SEAT_ENTRY=resolve(PLUGIN_ROOT,"dist","seat.cjs");var CLAUDE_MCP_KEYS=["ENABLE_CLAUDEAI_MCP_SERVERS","ENABLE_TOOL_SEARCH","MCP_TIMEOUT","MCP_CONNECTION_NONBLOCKING"];function assertServableModel(model){if(!model.includes("/")||model.startsWith("arn:"))return;throw new Error(`claude connector: cannot serve model ${JSON.stringify(model)} - a "provider/model" specifier belongs to another runtime (use the opencode connector for it). Refusing at launch: passing it through starts a session on a DIFFERENT model than the one named, and everything that session produces would carry the wrong attribution.`)}var claudeConnector={kind:"connector",name:"claude",setup:{kind:"connector-setup",name:"claude"},eventChannel:eventChannel3,pluginRoot:PLUGIN_ROOT,requires:["claude"],supportsResume:true,resumeTranscript:claudeResumeTranscript,supportsToolListAnnounce:true,supportsPrompt:true,launchHint:"press Enter at the dev-channels prompt",waitsForOperator:launchOptions=>launchOptions?.resume===""?"Claude's session picker":void 0,buildLaunch(opts){if(opts.continueSession)throw new Error("claude connector does not support exact-session continuation");if(opts.variant)throw new Error("claude connector: model variants are not supported");const shared=opts.mcpServers??{};const exclusive=opts.mcpServersExclusive===true||opts.carried!==void 0;const nativeEnvKeys=exclusive?[]:claudeUserMcpEnvKeys();const control=controlEndpoint(opts.space,opts.name);const env={...launchEnv({providerKeys:CLAUDE_PROVIDER_KEYS,mcpKeys:[...mcpServerEnvKeys(shared),...nativeEnvKeys,...CLAUDE_MCP_KEYS],envAllow:opts.envAllow}),...aclEnv(opts),COTAL_SPACE:opts.space,COTAL_NAME:opts.name,COTAL_CHANNEL:"1",COTAL_CONTROL_SOCKET:control.path};if(launchArmsEvents(opts)){env.COTAL_EVENTS="1";if(!opts.workspaceRoot)throw new Error("claude connector: events were requested but the launch carries no workspaceRoot, so the event write-ahead log has nowhere to live that a later start would look. Refusing rather than defaulting to the working directory.");env.COTAL_WORKSPACE_ROOT=opts.workspaceRoot}const binary=opts.resolvedBinaries?.claude??"claude";if(opts.carried)refuseCarriedLaunch(binary,env);if(opts.role)env.COTAL_ROLE=opts.role;if(opts.id)env.COTAL_ID=opts.id;if(opts.lifecycleUid)env.COTAL_LIFECYCLE_UID=opts.lifecycleUid;if(opts.backfillFloor!==void 0)env.COTAL_BACKFILL_FLOOR=String(opts.backfillFloor);if(opts.acceptedToken)env.COTAL_ACCEPTED_TOKEN=opts.acceptedToken;const prompt=opts.prompt===void 0?void 0:opts.prompt.trim();if(prompt==="")throw new Error("claude connector: an initial prompt was given but it is empty, there is no first turn to submit");const args=prompt?[prompt,"--dangerously-load-development-channels",CHANNEL_REF,"--plugin-dir",PLUGIN_ROOT]:["--dangerously-load-development-channels",CHANNEL_REF,"--plugin-dir",PLUGIN_ROOT];args.push("--allowedTools","WebFetch(domain:github.com),WebFetch(domain:raw.githubusercontent.com)");const mcpServers={...shared,[MCP_SERVER_NAME]:{command:"node",args:["--optimize-for-size","--max-semi-space-size=1",SEAT_ENTRY]}};const agentFile=opts.configPath?resolve(opts.configPath):void 0;const def=agentFile?loadAgentFile2(agentFile):void 0;if(opts.model)assertServableModel(opts.model);const passthrough=connectorLaunchOptions("claude",opts.launchOptions).map(([k,v])=>[k,String(v)]);const artifacts=[];Object.assign(env,materialEnv({creds:opts.creds,servers:opts.servers,controlToken:control.token,eventsRequired:opts.eventsRequired,userAuth:opts.userAuth},artifacts));let mcpConfig;if(Object.keys(shared).length===0){mcpConfig=JSON.stringify({mcpServers})}else{mcpConfig=writeLaunchArtifact(artifacts,"cotal-mcp-","mcp.json",JSON.stringify({mcpServers},null,2))}if(exclusive)args.push("--strict-mcp-config");args.push("--mcp-config",mcpConfig);if(agentFile)env.COTAL_AGENT_FILE=agentFile;if(def?.persona){args.push("--append-system-prompt-file",writeLaunchArtifact(artifacts,"cotal-claude-persona-","persona.md",def.persona))}if(opts.model){args.push("--model",opts.model);env.COTAL_MODEL=opts.model}if(opts.resume)args.push("--resume",opts.resume,"--fork-session");if(opts.carried)Object.assign(env,placeCarried(opts.carried.home,opts.carried.transcript,opts.resume,opts.carried.cwd));if(opts.cwd&&!passthrough.some(([k])=>k==="permission-mode"||k==="dangerously-skip-permissions"))args.push("--permission-mode","bypassPermissions");for(const[k,val]of passthrough){if(val==="")args.push(`--${k}`);else args.push(`--${k}`,val)}return{command:binary,args,env,confirm:"WARNING: Loading development channels",confirmReady:CLAUDE_READY_TEXTS,confirmBlockers:CLAUDE_STARTUP_BLOCKERS,...opts.cwd?{confirmBefore:CLAUDE_WORKSPACE_TRUST_REPLIES}:{},control,...opts.carried?{resumeRecordPath:carriedForkRecord(opts.carried.home)}:{},...artifacts.length>0?{artifacts}:{}}}};registry4.register(claudeConnector);export{CLAUDE_PROVIDER_KEYS,claudeConnector,claudeSetupProvider,marketplaceManifest,materialize,writeMarketplaceManifest};