@iaforged/context-code 2.4.2 → 2.4.4

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.
@@ -1 +1 @@
1
- import{join as e,normalize as t,sep as r}from"path";import{getProjectRoot as o}from"../../bootstrap/state.js";import{buildMemoryPrompt as n,ensureMemoryDirExists as s}from"../../memdir/memdir.js";import{getMemoryBaseDir as c}from"../../memdir/paths.js";import{getCwd as i}from"../../utils/cwd.js";import{findCanonicalGitRoot as m}from"../../utils/git.js";import{sanitizePath as a}from"../../utils/path.js";function resolveProjectAgentMemoryRoot(t){const r=i();return e(r,".contextcli",t)}function getLocalAgentMemoryDir(t){const n=process.env.CONTEXT_CODE_REMOTE_MEMORY_DIR??process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR;return n?e(n,"projects",a(m(o())??o()),"agent-memory-local",t)+r:e(resolveProjectAgentMemoryRoot("agent-memory-local"),t)+r}export function getAgentMemoryDir(t,o){const n=function(e){return e.replace(/:/g,"-")}(t);switch(o){case"project":return e(resolveProjectAgentMemoryRoot("agent-memory"),n)+r;case"local":return getLocalAgentMemoryDir(n);case"user":return e(c(),"agent-memory",n)+r}}export function isAgentMemoryPath(o){const n=t(o),s=c();if(n.startsWith(e(s,"agent-memory")+r))return!0;if(n.startsWith(e(i(),".contextcli","agent-memory")+r))return!0;const m=process.env.CONTEXT_CODE_REMOTE_MEMORY_DIR??process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR;if(m){if(n.includes(r+"agent-memory-local"+r)&&n.startsWith(e(m,"projects")+r))return!0}else if(n.startsWith(e(i(),".contextcli","agent-memory-local")+r))return!0;return!1}export function getAgentMemoryEntrypoint(t,r){return e(getAgentMemoryDir(t,r),"MEMORY.md")}export function getMemoryScopeDisplay(t){switch(t){case"user":return`User (${e(c(),"agent-memory")}/)`;case"project":return"Project (.contextcli/agent-memory/)";case"local":return`Local (${getLocalAgentMemoryDir("...")})`;default:return"None"}}export function loadAgentMemoryPrompt(e,t){let r;switch(t){case"user":r="- Since this memory is user-scope, keep learnings general since they apply across all projects";break;case"project":r="- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project";break;case"local":r="- Since this memory is local-scope (not checked into version control), tailor your memories to this project and machine"}const o=getAgentMemoryDir(e,t);s(o);const c=process.env.CONTEXT_COWORK_MEMORY_EXTRA_GUIDELINES??process.env.CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES;return n({displayName:"Persistent Agent Memory",memoryDir:o,extraGuidelines:c&&c.trim().length>0?[r,c]:[r]})}
1
+ import{join as e,normalize as t,sep as r}from"path";import{getProjectRoot as o}from"../../bootstrap/state.js";import{buildMemoryPrompt as n,ensureMemoryDirExists as s}from"../../memdir/memdir.js";import{getMemoryBaseDir as c}from"../../memdir/paths.js";import{getCwd as i}from"../../utils/cwd.js";import{findCanonicalGitRoot as m}from"../../utils/git.js";import{sanitizePath as a}from"../../utils/path.js";import{CONTEXT_DIR_NAME as l}from"../../utils/configConstants.js";function resolveProjectAgentMemoryRoot(t){const r=i();return e(r,l,t)}function getLocalAgentMemoryDir(t){const n=process.env.CONTEXT_CODE_REMOTE_MEMORY_DIR??process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR;return n?e(n,"projects",a(m(o())??o()),"agent-memory-local",t)+r:e(resolveProjectAgentMemoryRoot("agent-memory-local"),t)+r}export function getAgentMemoryDir(t,o){const n=function(e){return e.replace(/:/g,"-")}(t);switch(o){case"project":return e(resolveProjectAgentMemoryRoot("agent-memory"),n)+r;case"local":return getLocalAgentMemoryDir(n);case"user":return e(c(),"agent-memory",n)+r}}export function isAgentMemoryPath(o){const n=t(o),s=c();if(n.startsWith(e(s,"agent-memory")+r))return!0;if(n.startsWith(e(i(),l,"agent-memory")+r))return!0;const m=process.env.CONTEXT_CODE_REMOTE_MEMORY_DIR??process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR;if(m){if(n.includes(r+"agent-memory-local"+r)&&n.startsWith(e(m,"projects")+r))return!0}else if(n.startsWith(e(i(),l,"agent-memory-local")+r))return!0;return!1}export function getAgentMemoryEntrypoint(t,r){return e(getAgentMemoryDir(t,r),"MEMORY.md")}export function getMemoryScopeDisplay(t){switch(t){case"user":return`User (${e(c(),"agent-memory")}/)`;case"project":return"Project (.contextcli/agent-memory/)";case"local":return`Local (${getLocalAgentMemoryDir("...")})`;default:return"None"}}export function loadAgentMemoryPrompt(e,t){let r;switch(t){case"user":r="- Since this memory is user-scope, keep learnings general since they apply across all projects";break;case"project":r="- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project";break;case"local":r="- Since this memory is local-scope (not checked into version control), tailor your memories to this project and machine"}const o=getAgentMemoryDir(e,t);s(o);const c=process.env.CONTEXT_COWORK_MEMORY_EXTRA_GUIDELINES??process.env.CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES;return n({displayName:"Persistent Agent Memory",memoryDir:o,extraGuidelines:c&&c.trim().length>0?[r,c]:[r]})}
@@ -1 +1 @@
1
- import{feature as e}from"../recovery/bunBundleShim.js";import{createRequire as t}from"module";const s=t(import.meta.url);import o from"ignore";import n from"lodash-es/memoize.js";import{Lexer as r}from"marked";import{basename as i,dirname as c,extname as a,isAbsolute as l,join as u,parse as m,relative as p,sep as d}from"path";import f from"picomatch";import{logEvent as h}from"../services/analytics/index.js";import{getAdditionalDirectoriesForClaudeMd as y,getOriginalCwd as M}from"../bootstrap/state.js";import{truncateEntrypointContent as g}from"../memdir/memdir.js";import{getAutoMemEntrypoint as x,isAutoMemoryEnabled as E}from"../memdir/paths.js";import{getFeatureValue_CACHED_MAY_BE_STALE as C}from"../services/analytics/growthbook.js";import{getCurrentProjectConfig as w,getManagedClaudeRulesDir as j,getMemoryPath as _,getUserClaudeRulesDir as A}from"./config.js";import{logForDebugging as F}from"./debug.js";import{logForDiagnosticsNoPII as D}from"./diagLogs.js";import{getClaudeConfigHomeDir as P,isEnvTruthy as T}from"./envUtils.js";import{getErrnoCode as R}from"./errors.js";import{normalizePathForComparison as S}from"./file.js";import{cacheKeys as v}from"./fileStateCache.js";import{parseFrontmatter as I,splitPathInFrontmatter as L}from"./frontmatterParser.js";import{getFsImplementation as b,safeResolvePath as U}from"./fsOperations.js";import{findCanonicalGitRoot as O,findGitRoot as k}from"./git.js";import{executeInstructionsLoadedHooks as N,hasInstructionsLoadedHook as W}from"./hooks.js";import{expandPath as $}from"./path.js";import{pathInWorkingPath as X}from"./permissions/filesystem.js";import{isSettingSourceEnabled as H}from"./settings/constants.js";import{getInitialSettings as z}from"./settings/settings.js";const q=e("TEAMMEM")?s("../memdir/teamMemPaths.js"):null;let B=!1;export const MAX_MEMORY_CHARACTER_COUNT=4e4;const G=new Set([".md",".txt",".text",".json",".yaml",".yml",".toml",".xml",".csv",".html",".htm",".css",".scss",".sass",".less",".js",".ts",".tsx",".jsx",".mjs",".cjs",".mts",".cts",".py",".pyi",".pyw",".rb",".erb",".rake",".go",".rs",".java",".kt",".kts",".scala",".c",".cpp",".cc",".cxx",".h",".hpp",".hxx",".cs",".swift",".sh",".bash",".zsh",".fish",".ps1",".bat",".cmd",".env",".ini",".cfg",".conf",".config",".properties",".sql",".graphql",".gql",".proto",".vue",".svelte",".astro",".ejs",".hbs",".pug",".jade",".php",".pl",".pm",".lua",".r",".R",".dart",".ex",".exs",".erl",".hrl",".clj",".cljs",".cljc",".edn",".hs",".lhs",".elm",".ml",".mli",".f",".f90",".f95",".for",".cmake",".make",".makefile",".gradle",".sbt",".rst",".adoc",".asciidoc",".org",".tex",".latex",".lock",".log",".diff",".patch"]);function pathInOriginalCwd(e){return X(e,M())}export function stripHtmlComments(e){return e.includes("\x3c!--")?stripHtmlCommentsFromTokens(new r({gfm:!1}).lex(e)):{content:e,stripped:!1}}function stripHtmlCommentsFromTokens(e){let t="",s=!1;const o=/<!--[\s\S]*?-->/g;for(const n of e){if("html"===n.type){const e=n.raw.trimStart();if(e.startsWith("\x3c!--")&&e.includes("--\x3e")){const e=n.raw.replace(o,"");s=!0,e.trim().length>0&&(t+=e);continue}}t+=n.raw}return{content:t,stripped:s}}function parseMemoryFileContent(e,t,s,o){const n=a(t).toLowerCase();if(n&&!G.has(n))return F(`Skipping non-text file in @include: ${t}`),{info:null,includePaths:[]};const{content:i,paths:l}=function(e){const{frontmatter:t,content:s}=I(e);if(!t.paths)return{content:s};const o=L(t.paths).map(e=>e.endsWith("/**")?e.slice(0,-3):e).filter(e=>e.length>0);return 0===o.length||o.every(e=>"**"===e)?{content:s}:{content:s,paths:o}}(e),u=i.includes("\x3c!--"),m=u||void 0!==o?new r({gfm:!1}).lex(i):void 0,p=u&&m?stripHtmlCommentsFromTokens(m).content:i,d=m&&void 0!==o?function(e,t){const s=new Set;function extractPathsFromText(e){const o=/(?:^|\s)@((?:[^\s\\]|\\ )+)/g;let n;for(;null!==(n=o.exec(e));){let e=n[1];if(!e)continue;const o=e.indexOf("#");if(-1!==o&&(e=e.substring(0,o)),e&&(e=e.replace(/\\ /g," "),e)){if(e.startsWith("./")||e.startsWith("~/")||e.startsWith("/")&&"/"!==e||!e.startsWith("@")&&!e.match(/^[#%^&*()]+/)&&e.match(/^[a-zA-Z0-9._-]/)){const o=$(e,c(t));s.add(o)}}}}function processElements(e){for(const t of e)if("code"!==t.type&&"codespan"!==t.type){if("html"===t.type){const e=t.raw||"",s=e.trimStart();if(s.startsWith("\x3c!--")&&s.includes("--\x3e")){const t=/<!--[\s\S]*?-->/g,s=e.replace(t,"");s.trim().length>0&&extractPathsFromText(s)}continue}"text"===t.type&&extractPathsFromText(t.text||""),t.tokens&&processElements(t.tokens),t.items&&processElements(t.items)}}return processElements(e),[...s]}(m,o):[];let f=p;"AutoMem"!==s&&"TeamMem"!==s||(f=g(p).content);const h=f!==e;return{info:{path:t,type:s,content:f,globs:l,contentDiffersFromDisk:h,rawContent:h?e:void 0},includePaths:d}}async function safelyReadMemoryFileAsync(e,t,s){try{const o=b();return parseMemoryFileContent(await o.readFile(e,{encoding:"utf-8"}),e,t,s)}catch(t){return function(e,t){const s=R(e);"ENOENT"!==s&&"EISDIR"!==s&&"EACCES"===s&&h("tengu_claude_md_permission_error",{is_access_error:1,has_home_dir:t.includes(P())?1:0})}(t,e),{info:null,includePaths:[]}}}function isClaudeMdExcluded(e,t){if("User"!==t&&"Project"!==t&&"Local"!==t)return!1;const s=z().claudeMdExcludes;if(!s||0===s.length)return!1;const o=e.replaceAll("\\","/"),n=function(e){const t=b(),s=e.map(e=>e.replaceAll("\\","/"));for(const e of s){if(!e.startsWith("/"))continue;const o=e.search(/[*?{[]/),n=-1===o?e:e.slice(0,o),r=c(n);try{const o=t.realpathSync(r).replaceAll("\\","/");if(o!==r){const t=o+e.slice(r.length);s.push(t)}}catch{}}return s}(s).filter(e=>e.length>0);return 0!==n.length&&f.isMatch(o,n,{dot:!0})}export async function processMemoryFile(e,t,s,o,n=0,r){const i=S(e);if(s.has(i)||n>=5)return[];if(isClaudeMdExcluded(e,t))return[];const{resolvedPath:c,isSymlink:a}=U(b(),e);s.add(i),a&&s.add(S(c));const{info:l,includePaths:u}=await safelyReadMemoryFileAsync(e,t,c);if(!l||!l.content.trim())return[];r&&(l.parent=r);const m=[];m.push(l);for(const r of u){if(!pathInOriginalCwd(r)&&!o)continue;const i=await processMemoryFile(r,t,s,o,n+1,e);m.push(...i)}return m}export async function processMdRules({rulesDir:e,type:t,processedPaths:s,includeExternal:o,conditionalRule:n,visitedDirs:r=new Set}){if(r.has(e))return[];try{const i=b(),{resolvedPath:c,isSymlink:a}=U(i,dotContextRulesDir);r.add(e),a&&r.add(c);const l=[];let m;try{m=await i.readdir(c)}catch(e){const t=R(e);if("ENOENT"===t||"EACCES"===t||"ENOTDIR"===t)return[];throw e}for(const c of m){const a=u(e,c.name),{resolvedPath:m,isSymlink:p}=U(i,a),d=p?await i.stat(m):null,f=d?d.isDirectory():c.isDirectory(),h=d?d.isFile():c.isFile();if(f)l.push(...await processMdRules({rulesDir:m,type:t,processedPaths:s,includeExternal:o,conditionalRule:n,visitedDirs:r}));else if(h&&c.name.endsWith(".md")){const e=await processMemoryFile(m,t,s,o);l.push(...e.filter(e=>n?e.globs:!e.globs))}}return l}catch(t){return t instanceof Error&&t.message.includes("EACCES")&&h("tengu_claude_rules_md_permission_error",{is_access_error:1,has_home_dir:e.includes(P())?1:0}),[]}}export const getMemoryFiles=n(async(t=!1)=>{const s=Date.now();D("info","memory_files_started");const o=[],n=new Set,r=w(),i=t||r.hasClaudeMdExternalIncludesApproved||!1,a=_("Managed");o.push(...await processMemoryFile(a,"Managed",n,i));const l=j();if(o.push(...await processMdRules({rulesDir:l,type:"Managed",processedPaths:n,includeExternal:i,conditionalRule:!1})),H("userSettings")){const e=_("User");o.push(...await processMemoryFile(e,"User",n,!0));const t=A();o.push(...await processMdRules({rulesDir:t,type:"User",processedPaths:n,includeExternal:!0,conditionalRule:!1}))}const p=[],d=M();let f=d;for(;f!==m(f).root;)p.push(f),f=c(f);const g=k(d),C=O(d),F=null!==g&&null!==C&&S(g)!==S(C)&&X(g,C);for(const e of p.reverse()){const t=F&&X(e,C)&&!X(e,g);if(H("projectSettings")&&!t){const t=u(e,"CLAUDE.md");o.push(...await processMemoryFile(t,"Project",n,i));const s=u(e,".contextcli","CONTEXT.md");o.push(...await processMemoryFile(s,"Project",n,i));const r=u(e,".contextcli","CLAUDE.md");o.push(...await processMemoryFile(r,"Project",n,i));const c=u(e,".contextcli","rules");o.push(...await processMdRules({rulesDir:c,type:"Project",processedPaths:n,includeExternal:i,conditionalRule:!1}))}if(H("localSettings")){const t=u(e,"CLAUDE.local.md");o.push(...await processMemoryFile(t,"Local",n,i))}}if(T(process.env.CONTEXT_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD)||T(process.env.CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD)){const e=y();for(const t of e){const e=u(t,"CLAUDE.md");o.push(...await processMemoryFile(e,"Project",n,i));const s=u(t,".contextcli","CONTEXT.md");o.push(...await processMemoryFile(s,"Project",n,i));const r=u(t,".contextcli","CLAUDE.md");o.push(...await processMemoryFile(r,"Project",n,i));const c=u(t,".contextcli","rules");o.push(...await processMdRules({rulesDir:c,type:"Project",processedPaths:n,includeExternal:i,conditionalRule:!1}))}}if(E()){const{info:e}=await safelyReadMemoryFileAsync(x(),"AutoMem");if(e){const t=S(e.path);n.has(t)||(n.add(t),o.push(e))}}if(e("TEAMMEM")&&q.isTeamMemoryEnabled()){const{info:e}=await safelyReadMemoryFileAsync(q.getTeamMemEntrypoint(),"TeamMem");if(e){const t=S(e.path);n.has(t)||(n.add(t),o.push(e))}}const P=o.reduce((e,t)=>e+t.content.length,0);D("info","memory_files_completed",{duration_ms:Date.now()-s,file_count:o.length,total_content_length:P});const R={};for(const e of o)R[e.type]=(R[e.type]??0)+1;if(B||(B=!0,h("tengu_claudemd__initial_load",{file_count:o.length,total_content_length:P,user_count:R.User??0,project_count:R.Project??0,local_count:R.Local??0,managed_count:R.Managed??0,automem_count:R.AutoMem??0,...e("TEAMMEM")?{teammem_count:R.TeamMem??0}:{},duration_ms:Date.now()-s})),!t){const e=function(){if(!Y)return;Y=!1;const e=V;return V="session_start",e}();if(void 0!==e&&W())for(const t of o){if(!isInstructionsMemoryType(t.type))continue;const s=t.parent?"include":e;N(t.path,t.type,s,{globs:t.globs,parentFilePath:t.parent})}}return o});function isInstructionsMemoryType(e){return"User"===e||"Project"===e||"Local"===e||"Managed"===e}let V="session_start",Y=!0;export function clearMemoryFileCaches(){getMemoryFiles.cache?.clear?.()}export function resetGetMemoryFilesCache(e="session_start"){V=e,Y=!0,clearMemoryFileCaches()}export function getLargeMemoryFiles(e){return e.filter(e=>e.content.length>4e4)}export function filterInjectedMemoryFiles(e){return C("tengu_moth_copse",!1)?e.filter(e=>"AutoMem"!==e.type&&"TeamMem"!==e.type):e}export const getClaudeMds=(t,s)=>{const o=[],n=C("tengu_paper_halyard",!1);for(const r of t)if((!s||s(r.type))&&(!n||"Project"!==r.type&&"Local"!==r.type)&&r.content){const t="Project"===r.type?" (project instructions, checked into the codebase)":"Local"===r.type?" (user's private project instructions, not checked in)":e("TEAMMEM")&&"TeamMem"===r.type?" (shared team memory, synced across the organization)":"AutoMem"===r.type?" (user's auto-memory, persists across conversations)":" (user's private global instructions for all projects)",s=r.content.trim();e("TEAMMEM")&&"TeamMem"===r.type?o.push(`Contents of ${r.path}${t}:\n\n<team-memory-content source="shared">\n${s}\n</team-memory-content>`):o.push(`Contents of ${r.path}${t}:\n\n${s}`)}return 0===o.length?"":`Codebase and user instructions are shown below. Be sure to adhere to these instructions. IMPORTANT: These instructions OVERRIDE any default behavior and you MUST follow them exactly as written.\n\n${o.join("\n\n")}`};export async function getManagedAndUserConditionalRules(e,t){const s=[],o=j();if(s.push(...await processConditionedMdRules(e,o,"Managed",t,!1)),H("userSettings")){const o=A();s.push(...await processConditionedMdRules(e,o,"User",t,!0))}return s}export async function getMemoryFilesForNestedDirectory(e,t,s){const o=[];if(H("projectSettings")){const t=u(e,"CLAUDE.md");o.push(...await processMemoryFile(t,"Project",s,!1));const n=u(e,".contextcli","CONTEXT.md");o.push(...await processMemoryFile(n,"Project",s,!1));const r=u(e,".contextcli","CLAUDE.md");o.push(...await processMemoryFile(r,"Project",s,!1))}if(H("localSettings")){const t=u(e,"CLAUDE.local.md");o.push(...await processMemoryFile(t,"Local",s,!1))}const n=u(e,".contextcli","rules"),r=new Set(s);o.push(...await processMdRules({dotContextRulesDir:n,type:"Project",processedPaths:r,includeExternal:!1,conditionalRule:!1})),o.push(...await processConditionedMdRules(t,n,"Project",s,!1));for(const e of r)s.add(e);return o}export async function getConditionalRulesForCwdLevelDirectory(e,t,s){return processConditionedMdRules(t,u(e,".contextcli","rules"),"Project",s,!1)}export async function processConditionedMdRules(e,t,s,n,r){return(await processMdRules({rulesDir:t,type:s,processedPaths:n,includeExternal:r,conditionalRule:!0})).filter(n=>{if(!n.globs||0===n.globs.length)return!1;const r="Project"===s?c(c(t)):M(),i=l(e)?p(r,e):e;return!(!i||i.startsWith("..")||l(i))&&o().add(n.globs).ignores(i)})}export function getExternalClaudeMdIncludes(e){const t=[];for(const s of e)"User"!==s.type&&s.parent&&!pathInOriginalCwd(s.path)&&t.push({path:s.path,parent:s.parent});return t}export function hasExternalClaudeMdIncludes(e){return getExternalClaudeMdIncludes(e).length>0}export async function shouldShowClaudeMdExternalIncludesWarning(){const e=w();return!e.hasClaudeMdExternalIncludesApproved&&!e.hasClaudeMdExternalIncludesWarningShown&&hasExternalClaudeMdIncludes(await getMemoryFiles(!0))}export function isMemoryFilePath(e){const t=i(e);return"CLAUDE.md"===t||"CLAUDE.local.md"===t||(!("CONTEXT.md"!==t||!e.includes(`${d}.contextcli${d}`))||!(!t.endsWith(".md")||!e.includes(`${d}.contextcli${d}rules${d}`)))}export function getAllMemoryFilePaths(e,t){const s=new Set;for(const t of e)t.content.trim().length>0&&s.add(t.path);for(const e of v(t))isMemoryFilePath(e)&&s.add(e);return Array.from(s)}
1
+ import{feature as e}from"../recovery/bunBundleShim.js";import{createRequire as t}from"module";const s=t(import.meta.url);import o from"ignore";import n from"lodash-es/memoize.js";import{Lexer as r}from"marked";import{basename as i,dirname as c,extname as a,isAbsolute as l,join as u,parse as m,relative as p,sep as d}from"path";import f from"picomatch";import{logEvent as h}from"../services/analytics/index.js";import{getAdditionalDirectoriesForClaudeMd as y,getOriginalCwd as M}from"../bootstrap/state.js";import{truncateEntrypointContent as g}from"../memdir/memdir.js";import{getAutoMemEntrypoint as x,isAutoMemoryEnabled as E}from"../memdir/paths.js";import{getFeatureValue_CACHED_MAY_BE_STALE as C}from"../services/analytics/growthbook.js";import{getCurrentProjectConfig as j,getManagedClaudeRulesDir as w,getMemoryPath as _,getUserClaudeRulesDir as A}from"./config.js";import{logForDebugging as F}from"./debug.js";import{logForDiagnosticsNoPII as D}from"./diagLogs.js";import{getClaudeConfigHomeDir as P,isEnvTruthy as T}from"./envUtils.js";import{CONTEXT_DIR_NAME as R}from"./configConstants.js";import{getErrnoCode as S}from"./errors.js";import{normalizePathForComparison as v}from"./file.js";import{cacheKeys as I}from"./fileStateCache.js";import{parseFrontmatter as L,splitPathInFrontmatter as b}from"./frontmatterParser.js";import{getFsImplementation as U,safeResolvePath as O}from"./fsOperations.js";import{findCanonicalGitRoot as k,findGitRoot as N}from"./git.js";import{executeInstructionsLoadedHooks as W,hasInstructionsLoadedHook as $}from"./hooks.js";import{expandPath as X}from"./path.js";import{pathInWorkingPath as H}from"./permissions/filesystem.js";import{isSettingSourceEnabled as z}from"./settings/constants.js";import{getInitialSettings as q}from"./settings/settings.js";const B=e("TEAMMEM")?s("../memdir/teamMemPaths.js"):null;let G=!1;export const MAX_MEMORY_CHARACTER_COUNT=4e4;const V=new Set([".md",".txt",".text",".json",".yaml",".yml",".toml",".xml",".csv",".html",".htm",".css",".scss",".sass",".less",".js",".ts",".tsx",".jsx",".mjs",".cjs",".mts",".cts",".py",".pyi",".pyw",".rb",".erb",".rake",".go",".rs",".java",".kt",".kts",".scala",".c",".cpp",".cc",".cxx",".h",".hpp",".hxx",".cs",".swift",".sh",".bash",".zsh",".fish",".ps1",".bat",".cmd",".env",".ini",".cfg",".conf",".config",".properties",".sql",".graphql",".gql",".proto",".vue",".svelte",".astro",".ejs",".hbs",".pug",".jade",".php",".pl",".pm",".lua",".r",".R",".dart",".ex",".exs",".erl",".hrl",".clj",".cljs",".cljc",".edn",".hs",".lhs",".elm",".ml",".mli",".f",".f90",".f95",".for",".cmake",".make",".makefile",".gradle",".sbt",".rst",".adoc",".asciidoc",".org",".tex",".latex",".lock",".log",".diff",".patch"]);function pathInOriginalCwd(e){return H(e,M())}export function stripHtmlComments(e){return e.includes("\x3c!--")?stripHtmlCommentsFromTokens(new r({gfm:!1}).lex(e)):{content:e,stripped:!1}}function stripHtmlCommentsFromTokens(e){let t="",s=!1;const o=/<!--[\s\S]*?-->/g;for(const n of e){if("html"===n.type){const e=n.raw.trimStart();if(e.startsWith("\x3c!--")&&e.includes("--\x3e")){const e=n.raw.replace(o,"");s=!0,e.trim().length>0&&(t+=e);continue}}t+=n.raw}return{content:t,stripped:s}}function parseMemoryFileContent(e,t,s,o){const n=a(t).toLowerCase();if(n&&!V.has(n))return F(`Skipping non-text file in @include: ${t}`),{info:null,includePaths:[]};const{content:i,paths:l}=function(e){const{frontmatter:t,content:s}=L(e);if(!t.paths)return{content:s};const o=b(t.paths).map(e=>e.endsWith("/**")?e.slice(0,-3):e).filter(e=>e.length>0);return 0===o.length||o.every(e=>"**"===e)?{content:s}:{content:s,paths:o}}(e),u=i.includes("\x3c!--"),m=u||void 0!==o?new r({gfm:!1}).lex(i):void 0,p=u&&m?stripHtmlCommentsFromTokens(m).content:i,d=m&&void 0!==o?function(e,t){const s=new Set;function extractPathsFromText(e){const o=/(?:^|\s)@((?:[^\s\\]|\\ )+)/g;let n;for(;null!==(n=o.exec(e));){let e=n[1];if(!e)continue;const o=e.indexOf("#");if(-1!==o&&(e=e.substring(0,o)),e&&(e=e.replace(/\\ /g," "),e)){if(e.startsWith("./")||e.startsWith("~/")||e.startsWith("/")&&"/"!==e||!e.startsWith("@")&&!e.match(/^[#%^&*()]+/)&&e.match(/^[a-zA-Z0-9._-]/)){const o=X(e,c(t));s.add(o)}}}}function processElements(e){for(const t of e)if("code"!==t.type&&"codespan"!==t.type){if("html"===t.type){const e=t.raw||"",s=e.trimStart();if(s.startsWith("\x3c!--")&&s.includes("--\x3e")){const t=/<!--[\s\S]*?-->/g,s=e.replace(t,"");s.trim().length>0&&extractPathsFromText(s)}continue}"text"===t.type&&extractPathsFromText(t.text||""),t.tokens&&processElements(t.tokens),t.items&&processElements(t.items)}}return processElements(e),[...s]}(m,o):[];let f=p;"AutoMem"!==s&&"TeamMem"!==s||(f=g(p).content);const h=f!==e;return{info:{path:t,type:s,content:f,globs:l,contentDiffersFromDisk:h,rawContent:h?e:void 0},includePaths:d}}async function safelyReadMemoryFileAsync(e,t,s){try{const o=U();return parseMemoryFileContent(await o.readFile(e,{encoding:"utf-8"}),e,t,s)}catch(t){return function(e,t){const s=S(e);"ENOENT"!==s&&"EISDIR"!==s&&"EACCES"===s&&h("tengu_claude_md_permission_error",{is_access_error:1,has_home_dir:t.includes(P())?1:0})}(t,e),{info:null,includePaths:[]}}}function isClaudeMdExcluded(e,t){if("User"!==t&&"Project"!==t&&"Local"!==t)return!1;const s=q().claudeMdExcludes;if(!s||0===s.length)return!1;const o=e.replaceAll("\\","/"),n=function(e){const t=U(),s=e.map(e=>e.replaceAll("\\","/"));for(const e of s){if(!e.startsWith("/"))continue;const o=e.search(/[*?{[]/),n=-1===o?e:e.slice(0,o),r=c(n);try{const o=t.realpathSync(r).replaceAll("\\","/");if(o!==r){const t=o+e.slice(r.length);s.push(t)}}catch{}}return s}(s).filter(e=>e.length>0);return 0!==n.length&&f.isMatch(o,n,{dot:!0})}export async function processMemoryFile(e,t,s,o,n=0,r){const i=v(e);if(s.has(i)||n>=5)return[];if(isClaudeMdExcluded(e,t))return[];const{resolvedPath:c,isSymlink:a}=O(U(),e);s.add(i),a&&s.add(v(c));const{info:l,includePaths:u}=await safelyReadMemoryFileAsync(e,t,c);if(!l||!l.content.trim())return[];r&&(l.parent=r);const m=[];m.push(l);for(const r of u){if(!pathInOriginalCwd(r)&&!o)continue;const i=await processMemoryFile(r,t,s,o,n+1,e);m.push(...i)}return m}export async function processMdRules({rulesDir:e,type:t,processedPaths:s,includeExternal:o,conditionalRule:n,visitedDirs:r=new Set}){if(r.has(e))return[];try{const i=U(),{resolvedPath:c,isSymlink:a}=O(i,dotContextRulesDir);r.add(e),a&&r.add(c);const l=[];let m;try{m=await i.readdir(c)}catch(e){const t=S(e);if("ENOENT"===t||"EACCES"===t||"ENOTDIR"===t)return[];throw e}for(const c of m){const a=u(e,c.name),{resolvedPath:m,isSymlink:p}=O(i,a),d=p?await i.stat(m):null,f=d?d.isDirectory():c.isDirectory(),h=d?d.isFile():c.isFile();if(f)l.push(...await processMdRules({rulesDir:m,type:t,processedPaths:s,includeExternal:o,conditionalRule:n,visitedDirs:r}));else if(h&&c.name.endsWith(".md")){const e=await processMemoryFile(m,t,s,o);l.push(...e.filter(e=>n?e.globs:!e.globs))}}return l}catch(t){return t instanceof Error&&t.message.includes("EACCES")&&h("tengu_claude_rules_md_permission_error",{is_access_error:1,has_home_dir:e.includes(P())?1:0}),[]}}export const getMemoryFiles=n(async(t=!1)=>{const s=Date.now();D("info","memory_files_started");const o=[],n=new Set,r=j(),i=t||r.hasClaudeMdExternalIncludesApproved||!1,a=_("Managed");o.push(...await processMemoryFile(a,"Managed",n,i));const l=w();if(o.push(...await processMdRules({rulesDir:l,type:"Managed",processedPaths:n,includeExternal:i,conditionalRule:!1})),z("userSettings")){const e=_("User");o.push(...await processMemoryFile(e,"User",n,!0));const t=A();o.push(...await processMdRules({rulesDir:t,type:"User",processedPaths:n,includeExternal:!0,conditionalRule:!1}))}const p=[],d=M();let f=d;for(;f!==m(f).root;)p.push(f),f=c(f);const g=N(d),C=k(d),F=null!==g&&null!==C&&v(g)!==v(C)&&H(g,C);for(const e of p.reverse()){const t=F&&H(e,C)&&!H(e,g);if(z("projectSettings")&&!t){const t=u(e,"CLAUDE.md");o.push(...await processMemoryFile(t,"Project",n,i));const s=u(e,R,"CONTEXT.md");o.push(...await processMemoryFile(s,"Project",n,i));const r=u(e,R,"CLAUDE.md");o.push(...await processMemoryFile(r,"Project",n,i));const c=u(e,R,"rules");o.push(...await processMdRules({rulesDir:c,type:"Project",processedPaths:n,includeExternal:i,conditionalRule:!1}))}if(z("localSettings")){const t=u(e,"CLAUDE.local.md");o.push(...await processMemoryFile(t,"Local",n,i))}}if(T(process.env.CONTEXT_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD)||T(process.env.CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD)){const e=y();for(const t of e){const e=u(t,"CLAUDE.md");o.push(...await processMemoryFile(e,"Project",n,i));const s=u(t,R,"CONTEXT.md");o.push(...await processMemoryFile(s,"Project",n,i));const r=u(t,R,"CLAUDE.md");o.push(...await processMemoryFile(r,"Project",n,i));const c=u(t,R,"rules");o.push(...await processMdRules({rulesDir:c,type:"Project",processedPaths:n,includeExternal:i,conditionalRule:!1}))}}if(E()){const{info:e}=await safelyReadMemoryFileAsync(x(),"AutoMem");if(e){const t=v(e.path);n.has(t)||(n.add(t),o.push(e))}}if(e("TEAMMEM")&&B.isTeamMemoryEnabled()){const{info:e}=await safelyReadMemoryFileAsync(B.getTeamMemEntrypoint(),"TeamMem");if(e){const t=v(e.path);n.has(t)||(n.add(t),o.push(e))}}const P=o.reduce((e,t)=>e+t.content.length,0);D("info","memory_files_completed",{duration_ms:Date.now()-s,file_count:o.length,total_content_length:P});const S={};for(const e of o)S[e.type]=(S[e.type]??0)+1;if(G||(G=!0,h("tengu_claudemd__initial_load",{file_count:o.length,total_content_length:P,user_count:S.User??0,project_count:S.Project??0,local_count:S.Local??0,managed_count:S.Managed??0,automem_count:S.AutoMem??0,...e("TEAMMEM")?{teammem_count:S.TeamMem??0}:{},duration_ms:Date.now()-s})),!t){const e=function(){if(!Z)return;Z=!1;const e=Y;return Y="session_start",e}();if(void 0!==e&&$())for(const t of o){if(!isInstructionsMemoryType(t.type))continue;const s=t.parent?"include":e;W(t.path,t.type,s,{globs:t.globs,parentFilePath:t.parent})}}return o});function isInstructionsMemoryType(e){return"User"===e||"Project"===e||"Local"===e||"Managed"===e}let Y="session_start",Z=!0;export function clearMemoryFileCaches(){getMemoryFiles.cache?.clear?.()}export function resetGetMemoryFilesCache(e="session_start"){Y=e,Z=!0,clearMemoryFileCaches()}export function getLargeMemoryFiles(e){return e.filter(e=>e.content.length>4e4)}export function filterInjectedMemoryFiles(e){return C("tengu_moth_copse",!1)?e.filter(e=>"AutoMem"!==e.type&&"TeamMem"!==e.type):e}export const getClaudeMds=(t,s)=>{const o=[],n=C("tengu_paper_halyard",!1);for(const r of t)if((!s||s(r.type))&&(!n||"Project"!==r.type&&"Local"!==r.type)&&r.content){const t="Project"===r.type?" (project instructions, checked into the codebase)":"Local"===r.type?" (user's private project instructions, not checked in)":e("TEAMMEM")&&"TeamMem"===r.type?" (shared team memory, synced across the organization)":"AutoMem"===r.type?" (user's auto-memory, persists across conversations)":" (user's private global instructions for all projects)",s=r.content.trim();e("TEAMMEM")&&"TeamMem"===r.type?o.push(`Contents of ${r.path}${t}:\n\n<team-memory-content source="shared">\n${s}\n</team-memory-content>`):o.push(`Contents of ${r.path}${t}:\n\n${s}`)}return 0===o.length?"":`Codebase and user instructions are shown below. Be sure to adhere to these instructions. IMPORTANT: These instructions OVERRIDE any default behavior and you MUST follow them exactly as written.\n\n${o.join("\n\n")}`};export async function getManagedAndUserConditionalRules(e,t){const s=[],o=w();if(s.push(...await processConditionedMdRules(e,o,"Managed",t,!1)),z("userSettings")){const o=A();s.push(...await processConditionedMdRules(e,o,"User",t,!0))}return s}export async function getMemoryFilesForNestedDirectory(e,t,s){const o=[];if(z("projectSettings")){const t=u(e,"CLAUDE.md");o.push(...await processMemoryFile(t,"Project",s,!1));const n=u(e,R,"CONTEXT.md");o.push(...await processMemoryFile(n,"Project",s,!1));const r=u(e,R,"CLAUDE.md");o.push(...await processMemoryFile(r,"Project",s,!1))}if(z("localSettings")){const t=u(e,"CLAUDE.local.md");o.push(...await processMemoryFile(t,"Local",s,!1))}const n=u(e,R,"rules"),r=new Set(s);o.push(...await processMdRules({dotContextRulesDir:n,type:"Project",processedPaths:r,includeExternal:!1,conditionalRule:!1})),o.push(...await processConditionedMdRules(t,n,"Project",s,!1));for(const e of r)s.add(e);return o}export async function getConditionalRulesForCwdLevelDirectory(e,t,s){return processConditionedMdRules(t,u(e,R,"rules"),"Project",s,!1)}export async function processConditionedMdRules(e,t,s,n,r){return(await processMdRules({rulesDir:t,type:s,processedPaths:n,includeExternal:r,conditionalRule:!0})).filter(n=>{if(!n.globs||0===n.globs.length)return!1;const r="Project"===s?c(c(t)):M(),i=l(e)?p(r,e):e;return!(!i||i.startsWith("..")||l(i))&&o().add(n.globs).ignores(i)})}export function getExternalClaudeMdIncludes(e){const t=[];for(const s of e)"User"!==s.type&&s.parent&&!pathInOriginalCwd(s.path)&&t.push({path:s.path,parent:s.parent});return t}export function hasExternalClaudeMdIncludes(e){return getExternalClaudeMdIncludes(e).length>0}export async function shouldShowClaudeMdExternalIncludesWarning(){const e=j();return!e.hasClaudeMdExternalIncludesApproved&&!e.hasClaudeMdExternalIncludesWarningShown&&hasExternalClaudeMdIncludes(await getMemoryFiles(!0))}export function isMemoryFilePath(e){const t=i(e);return"CLAUDE.md"===t||"CLAUDE.local.md"===t||(!("CONTEXT.md"!==t||!e.includes(`${d}.contextcli${d}`))||!(!t.endsWith(".md")||!e.includes(`${d}.contextcli${d}rules${d}`)))}export function getAllMemoryFilePaths(e,t){const s=new Set;for(const t of e)t.content.trim().length>0&&s.add(t.path);for(const e of I(t))isMemoryFilePath(e)&&s.add(e);return Array.from(s)}
@@ -1 +1 @@
1
- export const NOTIFICATION_CHANNELS=["auto","iterm2","iterm2_with_bell","terminal_bell","kitty","ghostty","notifications_disabled"];export const EDITOR_MODES=["normal","vim"];export const TEAMMATE_MODES=["auto","tmux","in-process"];
1
+ export const NOTIFICATION_CHANNELS=["auto","iterm2","iterm2_with_bell","terminal_bell","kitty","ghostty","notifications_disabled"];export const EDITOR_MODES=["normal","vim"];export const TEAMMATE_MODES=["auto","tmux","in-process"];export const CONTEXT_DIR_NAME=".contextcli";export const DOCS_URL="https://docs.iaforged.com";export const DOCS_DOMAIN="docs.iaforged.com";
@@ -1 +1 @@
1
- import{randomUUID as t}from"crypto";import{readFileSync as r}from"fs";import{mkdir as n,writeFile as e}from"fs/promises";import{dirname as o,join as s}from"path";import{addSessionCronTask as i,getProjectRoot as a,getSessionCronTasks as c,removeSessionCronTasks as u}from"../bootstrap/state.js";import{computeNextCronRun as f,parseCronExpression as p}from"./cron.js";import{logForDebugging as l}from"./debug.js";import{isFsInaccessible as d}from"./errors.js";import{getFsImplementation as m}from"./fsOperations.js";import{safeParseJSON as C}from"./json.js";import{logError as h}from"./log.js";import{jsonStringify as g}from"./slowOperations.js";const k=s(".contextcli","scheduled_tasks.json");export function getCronFilePath(t){const r=t??a();return s(r,k)}export async function readCronTasks(t){const r=m();let n;try{n=await r.readFile(getCronFilePath(t),{encoding:"utf-8"})}catch(t){return d(t)||h(t),[]}const e=C(n,!1);if(!e||"object"!=typeof e)return[];const o=e;if(!Array.isArray(o.tasks))return[];const s=[];for(const t of o.tasks)t&&"string"==typeof t.id&&"string"==typeof t.cron&&"string"==typeof t.prompt&&"number"==typeof t.createdAt?p(t.cron)?s.push({id:t.id,cron:t.cron,prompt:t.prompt,createdAt:t.createdAt,..."number"==typeof t.lastFiredAt?{lastFiredAt:t.lastFiredAt}:{},...t.recurring?{recurring:!0}:{},...t.permanent?{permanent:!0}:{}}):l(`[ScheduledTasks] skipping task ${t.id} with invalid cron '${t.cron}'`):l(`[ScheduledTasks] skipping malformed task: ${g(t)}`);return s}export function hasCronTasksSync(t){let n;try{n=r(getCronFilePath(t),"utf-8")}catch{return!1}const e=C(n,!1);if(!e||"object"!=typeof e)return!1;const o=e.tasks;return Array.isArray(o)&&o.length>0}export async function writeCronTasks(t,r){const s=getCronFilePath(r??a());await n(o(s),{recursive:!0});const i={tasks:t.map(({durable:t,...r})=>r)};await e(s,g(i,null,2)+"\n","utf-8")}export async function addCronTask(r,n,e,o,s){const a=t().slice(0,8),c={id:a,cron:r,prompt:n,createdAt:Date.now(),...e?{recurring:!0}:{}};if(!o)return i({...c,...s?{agentId:s}:{}}),a;const u=await readCronTasks();return u.push(c),await writeCronTasks(u),a}export async function removeCronTasks(t,r){if(0===t.length)return;if(void 0===r&&u(t)===t.length)return;const n=new Set(t),e=await readCronTasks(r),o=e.filter(t=>!n.has(t.id));o.length!==e.length&&await writeCronTasks(o,r)}export async function markCronTasksFired(t,r,n){if(0===t.length)return;const e=new Set(t),o=await readCronTasks(n);let s=!1;for(const t of o)e.has(t.id)&&(t.lastFiredAt=r,s=!0);s&&await writeCronTasks(o,n)}export async function listAllCronTasks(t){const r=await readCronTasks(t);if(void 0!==t)return r;const n=c().map(t=>({...t,durable:!1}));return[...r,...n]}export function nextCronRunMs(t,r){const n=p(t);if(!n)return null;const e=f(n,new Date(r));return e?e.getTime():null}export const DEFAULT_CRON_JITTER_CONFIG={recurringFrac:.1,recurringCapMs:9e5,oneShotMaxMs:9e4,oneShotFloorMs:0,oneShotMinuteMod:30,recurringMaxAgeMs:6048e5};function jitterFrac(t){const r=parseInt(t.slice(0,8),16)/4294967296;return Number.isFinite(r)?r:0}export function jitteredNextCronRunMs(t,r,n,e=DEFAULT_CRON_JITTER_CONFIG){const o=nextCronRunMs(t,r);if(null===o)return null;const s=nextCronRunMs(t,o);if(null===s)return o;return o+Math.min(jitterFrac(n)*e.recurringFrac*(s-o),e.recurringCapMs)}export function oneShotJitteredNextCronRunMs(t,r,n,e=DEFAULT_CRON_JITTER_CONFIG){const o=nextCronRunMs(t,r);if(null===o)return null;if(new Date(o).getMinutes()%e.oneShotMinuteMod!==0)return o;const s=e.oneShotFloorMs+jitterFrac(n)*(e.oneShotMaxMs-e.oneShotFloorMs);return Math.max(o-s,r)}export function findMissedTasks(t,r){return t.filter(t=>{const n=nextCronRunMs(t.cron,t.createdAt);return null!==n&&n<r})}
1
+ import{randomUUID as t}from"crypto";import{readFileSync as r}from"fs";import{mkdir as n,writeFile as e}from"fs/promises";import{dirname as o,join as s}from"path";import{addSessionCronTask as i,getProjectRoot as a,getSessionCronTasks as c,removeSessionCronTasks as u}from"../bootstrap/state.js";import{computeNextCronRun as f,parseCronExpression as p}from"./cron.js";import{logForDebugging as l}from"./debug.js";import{CONTEXT_DIR_NAME as d}from"./configConstants.js";import{isFsInaccessible as m}from"./errors.js";import{getFsImplementation as C}from"./fsOperations.js";import{safeParseJSON as h}from"./json.js";import{logError as g}from"./log.js";import{jsonStringify as k}from"./slowOperations.js";const T=s(d,"scheduled_tasks.json");export function getCronFilePath(t){const r=t??a();return s(r,T)}export async function readCronTasks(t){const r=C();let n;try{n=await r.readFile(getCronFilePath(t),{encoding:"utf-8"})}catch(t){return m(t)||g(t),[]}const e=h(n,!1);if(!e||"object"!=typeof e)return[];const o=e;if(!Array.isArray(o.tasks))return[];const s=[];for(const t of o.tasks)t&&"string"==typeof t.id&&"string"==typeof t.cron&&"string"==typeof t.prompt&&"number"==typeof t.createdAt?p(t.cron)?s.push({id:t.id,cron:t.cron,prompt:t.prompt,createdAt:t.createdAt,..."number"==typeof t.lastFiredAt?{lastFiredAt:t.lastFiredAt}:{},...t.recurring?{recurring:!0}:{},...t.permanent?{permanent:!0}:{}}):l(`[ScheduledTasks] skipping task ${t.id} with invalid cron '${t.cron}'`):l(`[ScheduledTasks] skipping malformed task: ${k(t)}`);return s}export function hasCronTasksSync(t){let n;try{n=r(getCronFilePath(t),"utf-8")}catch{return!1}const e=h(n,!1);if(!e||"object"!=typeof e)return!1;const o=e.tasks;return Array.isArray(o)&&o.length>0}export async function writeCronTasks(t,r){const s=getCronFilePath(r??a());await n(o(s),{recursive:!0});const i={tasks:t.map(({durable:t,...r})=>r)};await e(s,k(i,null,2)+"\n","utf-8")}export async function addCronTask(r,n,e,o,s){const a=t().slice(0,8),c={id:a,cron:r,prompt:n,createdAt:Date.now(),...e?{recurring:!0}:{}};if(!o)return i({...c,...s?{agentId:s}:{}}),a;const u=await readCronTasks();return u.push(c),await writeCronTasks(u),a}export async function removeCronTasks(t,r){if(0===t.length)return;if(void 0===r&&u(t)===t.length)return;const n=new Set(t),e=await readCronTasks(r),o=e.filter(t=>!n.has(t.id));o.length!==e.length&&await writeCronTasks(o,r)}export async function markCronTasksFired(t,r,n){if(0===t.length)return;const e=new Set(t),o=await readCronTasks(n);let s=!1;for(const t of o)e.has(t.id)&&(t.lastFiredAt=r,s=!0);s&&await writeCronTasks(o,n)}export async function listAllCronTasks(t){const r=await readCronTasks(t);if(void 0!==t)return r;const n=c().map(t=>({...t,durable:!1}));return[...r,...n]}export function nextCronRunMs(t,r){const n=p(t);if(!n)return null;const e=f(n,new Date(r));return e?e.getTime():null}export const DEFAULT_CRON_JITTER_CONFIG={recurringFrac:.1,recurringCapMs:9e5,oneShotMaxMs:9e4,oneShotFloorMs:0,oneShotMinuteMod:30,recurringMaxAgeMs:6048e5};function jitterFrac(t){const r=parseInt(t.slice(0,8),16)/4294967296;return Number.isFinite(r)?r:0}export function jitteredNextCronRunMs(t,r,n,e=DEFAULT_CRON_JITTER_CONFIG){const o=nextCronRunMs(t,r);if(null===o)return null;const s=nextCronRunMs(t,o);if(null===s)return o;return o+Math.min(jitterFrac(n)*e.recurringFrac*(s-o),e.recurringCapMs)}export function oneShotJitteredNextCronRunMs(t,r,n,e=DEFAULT_CRON_JITTER_CONFIG){const o=nextCronRunMs(t,r);if(null===o)return null;if(new Date(o).getMinutes()%e.oneShotMinuteMod!==0)return o;const s=e.oneShotFloorMs+jitterFrac(n)*(e.oneShotMaxMs-e.oneShotFloorMs);return Math.max(o-s,r)}export function findMissedTasks(t,r){return t.filter(t=>{const n=nextCronRunMs(t.cron,t.createdAt);return null!==n&&n<r})}
@@ -1 +1 @@
1
- import{mkdir as e,readFile as t,unlink as r,writeFile as s}from"fs/promises";import{dirname as o,join as c}from"path";import{z as i}from"zod/v4";import{getProjectRoot as a,getSessionId as n}from"../bootstrap/state.js";import{registerCleanup as d}from"./cleanupRegistry.js";import{logForDebugging as u}from"./debug.js";import{getErrnoCode as l}from"./errors.js";import{isProcessRunning as p}from"./genericProcessUtils.js";import{safeParseJSON as f}from"./json.js";import{lazySchema as h}from"./lazySchema.js";import{jsonStringify as k}from"./slowOperations.js";const m=c(".contextcli","scheduled_tasks.lock"),w=h(()=>i.object({sessionId:i.string(),pid:i.number(),acquiredAt:i.number()}));let y,g;function getLockPath(e){const t=e??a();return c(t,m)}async function readLock(e){let r;try{r=await t(getLockPath(e),"utf8")}catch{return}const s=w().safeParse(f(r,!1));return s.success?s.data:void 0}async function tryCreateExclusive(t,r){const c=getLockPath(r),i=k(t);try{return await s(c,i,{flag:"wx"}),!0}catch(t){const r=l(t);if("EEXIST"===r)return!1;if("ENOENT"===r){await e(o(c),{recursive:!0});try{return await s(c,i,{flag:"wx"}),!0}catch(e){if("EEXIST"===l(e))return!1;throw e}}throw t}}function registerLockCleanup(e){y?.(),y=d(async()=>{await releaseSchedulerLock(e)})}export async function tryAcquireSchedulerLock(e){const t=e?.dir,o=e?.lockIdentity??n(),c={sessionId:o,pid:process.pid,acquiredAt:Date.now()};if(await tryCreateExclusive(c,t))return g=void 0,registerLockCleanup(e),u(`[ScheduledTasks] acquired scheduler lock (PID ${process.pid})`),!0;const i=await readLock(t);return i?.sessionId===o?(i.pid!==process.pid&&(await s(getLockPath(t),k(c)),registerLockCleanup(e)),!0):i&&p(i.pid)?(g!==i.sessionId&&(g=i.sessionId,u(`[ScheduledTasks] scheduler lock held by session ${i.sessionId} (PID ${i.pid})`)),!1):(i&&u(`[ScheduledTasks] recovering stale scheduler lock from PID ${i.pid}`),await r(getLockPath(t)).catch(()=>{}),!!await tryCreateExclusive(c,t)&&(g=void 0,registerLockCleanup(e),!0))}export async function releaseSchedulerLock(e){y?.(),y=void 0,g=void 0;const t=e?.dir,s=e?.lockIdentity??n(),o=await readLock(t);if(o&&o.sessionId===s)try{await r(getLockPath(t)),u("[ScheduledTasks] released scheduler lock")}catch{}}
1
+ import{mkdir as e,readFile as t,unlink as r,writeFile as s}from"fs/promises";import{dirname as o,join as c}from"path";import{z as i}from"zod/v4";import{getProjectRoot as a,getSessionId as n}from"../bootstrap/state.js";import{registerCleanup as d}from"./cleanupRegistry.js";import{logForDebugging as u}from"./debug.js";import{CONTEXT_DIR_NAME as l}from"./configConstants.js";import{getErrnoCode as p}from"./errors.js";import{isProcessRunning as f}from"./genericProcessUtils.js";import{safeParseJSON as m}from"./json.js";import{lazySchema as h}from"./lazySchema.js";import{jsonStringify as k}from"./slowOperations.js";const g=c(l,"scheduled_tasks.lock"),w=h(()=>i.object({sessionId:i.string(),pid:i.number(),acquiredAt:i.number()}));let y,L;function getLockPath(e){const t=e??a();return c(t,g)}async function readLock(e){let r;try{r=await t(getLockPath(e),"utf8")}catch{return}const s=w().safeParse(m(r,!1));return s.success?s.data:void 0}async function tryCreateExclusive(t,r){const c=getLockPath(r),i=k(t);try{return await s(c,i,{flag:"wx"}),!0}catch(t){const r=p(t);if("EEXIST"===r)return!1;if("ENOENT"===r){await e(o(c),{recursive:!0});try{return await s(c,i,{flag:"wx"}),!0}catch(e){if("EEXIST"===p(e))return!1;throw e}}throw t}}function registerLockCleanup(e){y?.(),y=d(async()=>{await releaseSchedulerLock(e)})}export async function tryAcquireSchedulerLock(e){const t=e?.dir,o=e?.lockIdentity??n(),c={sessionId:o,pid:process.pid,acquiredAt:Date.now()};if(await tryCreateExclusive(c,t))return L=void 0,registerLockCleanup(e),u(`[ScheduledTasks] acquired scheduler lock (PID ${process.pid})`),!0;const i=await readLock(t);return i?.sessionId===o?(i.pid!==process.pid&&(await s(getLockPath(t),k(c)),registerLockCleanup(e)),!0):i&&f(i.pid)?(L!==i.sessionId&&(L=i.sessionId,u(`[ScheduledTasks] scheduler lock held by session ${i.sessionId} (PID ${i.pid})`)),!1):(i&&u(`[ScheduledTasks] recovering stale scheduler lock from PID ${i.pid}`),await r(getLockPath(t)).catch(()=>{}),!!await tryCreateExclusive(c,t)&&(L=void 0,registerLockCleanup(e),!0))}export async function releaseSchedulerLock(e){y?.(),y=void 0,L=void 0;const t=e?.dir,s=e?.lockIdentity??n(),o=await readLock(t);if(o&&o.sessionId===s)try{await r(getLockPath(t)),u("[ScheduledTasks] released scheduler lock")}catch{}}
@@ -1 +1 @@
1
- import{MACRO as t}from"../recovery/bunBundleShim.js";import{execa as e}from"execa";import{readFile as n,realpath as a}from"fs/promises";import{homedir as o}from"os";import{delimiter as s,join as i,posix as r,win32 as l}from"path";import{checkGlobalInstallPermissions as c}from"./autoUpdater.js";import{isInBundledMode as p}from"./bundledMode.js";import{formatAutoUpdaterDisabledReason as u,getAutoUpdaterDisabledReason as m,getGlobalConfig as d}from"./config.js";import{getCwd as f}from"./cwd.js";import{isEnvTruthy as g}from"./envUtils.js";import{execFileNoThrow as h}from"./execFileNoThrow.js";import{getFsImplementation as x}from"./fsOperations.js";import{getShellType as b,isRunningFromLocalInstallation as w,localInstallationExists as v}from"./localInstaller.js";import{detectApk as y,detectAsdf as j,detectDeb as E,detectHomebrew as $,detectMise as P,detectPacman as A,detectRpm as C,detectWinget as k,getPackageManager as L}from"./nativeInstaller/packageManagers.js";import{getPlatform as I}from"./platform.js";import{getRipgrepStatus as _}from"./ripgrep.js";import{SandboxManager as M}from"./sandbox/sandbox-adapter.js";import{getManagedFilePath as O}from"./settings/managedPath.js";import{CUSTOMIZATION_SURFACES as T}from"./settings/types.js";import{findClaudeAlias as U,findValidClaudeAlias as H,getShellConfigPaths as N}from"./shellConfig.js";import{jsonParse as S}from"./slowOperations.js";import{which as G}from"./which.js";export async function getCurrentInstallationType(){if("development"===process.env.NODE_ENV)return"development";const[t]=function(){let t=process.argv[1]||"",e=process.execPath||process.argv[0]||"";return"windows"===I()&&(t=t.split(l.sep).join(r.sep),e=e.split(l.sep).join(r.sep)),[t,e]}();if(p())return $()||k()||P()||j()||await A()||await E()||await C()||await y()?"package-manager":"native";if(w())return"npm-local";if(["/usr/local/lib/node_modules","/usr/lib/node_modules","/opt/homebrew/lib/node_modules","/opt/homebrew/bin","/usr/local/bin","/.nvm/versions/node/"].some(e=>t.includes(e)))return"npm-global";if(t.includes("/npm/")||t.includes("/nvm/"))return"npm-global";const n=await e("npm config get prefix",{shell:!0,reject:!1}),a=0===n.exitCode?n.stdout.trim():null;return a&&t.startsWith(a)?"npm-global":"unknown"}export function getInvokedBinary(){try{return p()?process.execPath||"unknown":process.argv[1]||"unknown"}catch{return"unknown"}}export function detectLinuxGlobPatternWarnings(){if("linux"!==I())return[];const t=[],e=M.getLinuxGlobPatternWarnings();if(e.length>0){const n=e.slice(0,3).join(", "),a=e.length-3,o=a>0?`${n} (${a} more)`:n;t.push({issue:"Los patrones glob en las reglas de permisos del sandbox no están totalmente soportados en Linux",fix:`Se han encontrado ${e.length} patrón(es): ${o}. En Linux, los patrones glob en las reglas de Editar/Leer serán ignorados.`})}return t}export async function getDoctorDiagnostic(){const e=await getCurrentInstallationType(),w=void 0!==t&&t.VERSION?t.VERSION:"unknown",y=await async function(){if("development"===process.env.NODE_ENV)return f();if(p()){try{return await a(process.execPath)}catch{}try{const t=await G("context");if(t)return t}catch{}try{return await x().stat(i(o(),".local/bin/context")),i(o(),".local/bin/context")}catch{}return"native"}try{return process.argv[0]||"unknown"}catch{return"unknown"}}(),j=getInvokedBinary(),E=await async function(){const e=x(),n=[],s=i(o(),".contextcli","local");await v()&&n.push({type:"npm-local",path:s});const r=["@iaforged/context-code"];t.PACKAGE_URL&&"@iaforged/context-code"!==t.PACKAGE_URL&&r.push(t.PACKAGE_URL);const l=await h("npm",["-g","config","get","prefix"]);if(0===l.code&&l.stdout){const t=l.stdout.trim(),o="windows"===I(),s=o?i(t,"context"):i(t,"bin","context");let c=!1;try{await e.stat(s),c=!0}catch{}if(c){let t=!1;try{(await a(s)).includes("/Caskroom/")&&(t=$())}catch{}t||n.push({type:"npm-global",path:s})}else for(const a of r){const s=o?i(t,"node_modules",a):i(t,"lib","node_modules",a);try{await e.stat(s),n.push({type:"npm-global-orphan",path:s})}catch{}}}const c=i(o(),".local","bin","context");try{await e.stat(c),n.push({type:"native",path:c})}catch{}if("native"===d().installMethod){const t=i(o(),".local","share","context");try{await e.stat(t),n.some(t=>"native"===t.type)||n.push({type:"native",path:t})}catch{}}return n}(),P=await async function(t){const e=[];try{const t=await n(i(O(),"managed-settings.json"),"utf-8"),a=S(t),o=a&&"object"==typeof a?a.strictPluginOnlyCustomization:void 0;if(void 0!==o&&"boolean"!=typeof o)if(Array.isArray(o)){const t=o.filter(t=>"string"==typeof t&&!T.includes(t));t.length>0&&e.push({issue:`managed-settings.json: strictPluginOnlyCustomization tiene ${t.length} valor(es) que este cliente no reconoce: ${t.map(String).join(", ")}`,fix:`Estos se ignoran silenciosamente. Superficies conocidas para esta versión: ${T.join(", ")}. Elimínalos o actualiza el cliente.`})}else e.push({issue:`managed-settings.json: strictPluginOnlyCustomization tiene un valor no válido (se esperaba boolean o array, se obtuvo ${typeof o})`,fix:`El campo se ignora silenciosamente. Establécelo en true o en un array de: ${T.join(", ")}.`})}catch{}const a=d();if("development"===t)return e;if("native"===t){const t=(process.env.PATH||"").split(s),n=o(),a=i(n,".local","bin");let c=a;if("windows"===I()&&(c=a.split(l.sep).join(r.sep)),!t.some(t=>{let e=t;"windows"===I()&&(e=t.split(l.sep).join(r.sep));const n=e.replace(/\/+$/,""),a=t.replace(/[/\\]+$/,"");return n===c||"~/.local/bin"===a||"$HOME/.local/bin"===a}))if("windows"===I()){const t=a.split(r.sep).join(l.sep);e.push({issue:`Existe una instalación nativa pero ${t} no está en tu PATH`,fix:"Añádelo abriendo: Propiedades del sistema → Variables de entorno → Editar PATH de usuario → Nuevo → Añade la ruta de arriba. Luego reinicia tu terminal."})}else{const t=b(),n=N()[t],a=n?n.replace(o(),"~"):"your shell config file";e.push({issue:"Existe una instalación nativa pero ~/.local/bin no está en tu PATH",fix:`Ejecuta: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ${a} luego abre una nueva terminal o ejecuta: source ${a}`})}}g(process.env.DISABLE_INSTALLATION_CHECKS)||("npm-local"===t&&"local"!==a.installMethod&&e.push({issue:`Ejecutando desde una instalación local pero el método de instalación configurado es '${a.installMethod}'`,fix:"Considera usar la instalación nativa: context install"}),"native"===t&&"native"!==a.installMethod&&e.push({issue:`Ejecutando instalación nativa pero el método de instalación configurado es '${a.installMethod}'`,fix:"Ejecuta context install para actualizar la configuración"})),"npm-global"===t&&await v()&&e.push({issue:"Existe una instalación local pero no se está usando",fix:"Considera usar la instalación nativa: context install"});const c=await U(),p=await H();"npm-local"===t&&(await G("claude")||p||(c?e.push({issue:"Instalación local no accesible",fix:`El alias existe pero apunta a un destino no válido: ${c}. Actualiza el alias: alias context="~/.contextcli/local/context"`}):e.push({issue:"Instalación local no accesible",fix:'Crea un alias: alias context="~/.contextcli/local/context"'})));return e}(e);if(P.push(...detectLinuxGlobPatternWarnings()),"native"===e){const e=E.filter(t=>"npm-global"===t.type||"npm-global-orphan"===t.type||"npm-local"===t.type),n="windows"===I();for(const a of e)if("npm-global"===a.type){let e="npm -g uninstall @iaforged/context-code";t.PACKAGE_URL&&"@iaforged/context-code"!==t.PACKAGE_URL&&(e+=` && npm -g uninstall ${t.PACKAGE_URL}`),P.push({issue:`Quedan restos de una instalación global de npm en ${a.path}`,fix:`Ejecuta: ${e}`})}else"npm-global-orphan"===a.type?P.push({issue:`Paquete global de npm huérfano en ${a.path}`,fix:n?`Ejecuta: rmdir /s /q "${a.path}"`:`Ejecuta: rm -rf ${a.path}`}):"npm-local"===a.type&&P.push({issue:`Quedan restos de una instalación local de npm en ${a.path}`,fix:n?`Ejecuta: rmdir /s /q "${a.path}"`:`Ejecuta: rm -rf ${a.path}`})}const A=d().installMethod||"not set";let C=null;if("npm-global"===e){C=(await c()).hasPermissions,C||m()||P.push({issue:"Permisos insuficientes para actualizaciones automáticas",fix:"Haz una de las dos: (1) Reinstala node sin sudo, o (2) Usa `context install` para una instalación nativa"})}const k=_(),M={working:k.working??!0,mode:k.mode,systemPath:"system"===k.mode?k.path:null},R="package-manager"===e?await L():void 0;return{installationType:e,version:w,installationPath:y,invokedBinary:j,configInstallMethod:A,autoUpdates:(()=>{const t=m();return t?`disabled (${u(t)})`:"enabled"})(),hasUpdatePermissions:C,multipleInstallations:E,warnings:P,packageManager:R,ripgrepStatus:M}}
1
+ import{MACRO as t}from"../recovery/bunBundleShim.js";import{execa as e}from"execa";import{readFile as n,realpath as a}from"fs/promises";import{homedir as o}from"os";import{delimiter as s,join as i,posix as r,win32 as l}from"path";import{checkGlobalInstallPermissions as c}from"./autoUpdater.js";import{isInBundledMode as p}from"./bundledMode.js";import{formatAutoUpdaterDisabledReason as u,getAutoUpdaterDisabledReason as m,getGlobalConfig as d}from"./config.js";import{getCwd as f}from"./cwd.js";import{isEnvTruthy as g}from"./envUtils.js";import{CONTEXT_DIR_NAME as h}from"./configConstants.js";import{execFileNoThrow as x}from"./execFileNoThrow.js";import{getFsImplementation as b}from"./fsOperations.js";import{getShellType as w,isRunningFromLocalInstallation as v,localInstallationExists as y}from"./localInstaller.js";import{detectApk as j,detectAsdf as E,detectDeb as $,detectHomebrew as P,detectMise as A,detectPacman as C,detectRpm as k,detectWinget as L,getPackageManager as I}from"./nativeInstaller/packageManagers.js";import{getPlatform as _}from"./platform.js";import{getRipgrepStatus as M}from"./ripgrep.js";import{SandboxManager as O}from"./sandbox/sandbox-adapter.js";import{getManagedFilePath as T}from"./settings/managedPath.js";import{CUSTOMIZATION_SURFACES as U}from"./settings/types.js";import{findClaudeAlias as H,findValidClaudeAlias as N,getShellConfigPaths as S}from"./shellConfig.js";import{jsonParse as G}from"./slowOperations.js";import{which as R}from"./which.js";export async function getCurrentInstallationType(){if("development"===process.env.NODE_ENV)return"development";const[t]=function(){let t=process.argv[1]||"",e=process.execPath||process.argv[0]||"";return"windows"===_()&&(t=t.split(l.sep).join(r.sep),e=e.split(l.sep).join(r.sep)),[t,e]}();if(p())return P()||L()||A()||E()||await C()||await $()||await k()||await j()?"package-manager":"native";if(v())return"npm-local";if(["/usr/local/lib/node_modules","/usr/lib/node_modules","/opt/homebrew/lib/node_modules","/opt/homebrew/bin","/usr/local/bin","/.nvm/versions/node/"].some(e=>t.includes(e)))return"npm-global";if(t.includes("/npm/")||t.includes("/nvm/"))return"npm-global";const n=await e("npm config get prefix",{shell:!0,reject:!1}),a=0===n.exitCode?n.stdout.trim():null;return a&&t.startsWith(a)?"npm-global":"unknown"}export function getInvokedBinary(){try{return p()?process.execPath||"unknown":process.argv[1]||"unknown"}catch{return"unknown"}}export function detectLinuxGlobPatternWarnings(){if("linux"!==_())return[];const t=[],e=O.getLinuxGlobPatternWarnings();if(e.length>0){const n=e.slice(0,3).join(", "),a=e.length-3,o=a>0?`${n} (${a} more)`:n;t.push({issue:"Los patrones glob en las reglas de permisos del sandbox no están totalmente soportados en Linux",fix:`Se han encontrado ${e.length} patrón(es): ${o}. En Linux, los patrones glob en las reglas de Editar/Leer serán ignorados.`})}return t}export async function getDoctorDiagnostic(){const e=await getCurrentInstallationType(),v=void 0!==t&&t.VERSION?t.VERSION:"unknown",j=await async function(){if("development"===process.env.NODE_ENV)return f();if(p()){try{return await a(process.execPath)}catch{}try{const t=await R("context");if(t)return t}catch{}try{return await b().stat(i(o(),".local/bin/context")),i(o(),".local/bin/context")}catch{}return"native"}try{return process.argv[0]||"unknown"}catch{return"unknown"}}(),E=getInvokedBinary(),$=await async function(){const e=b(),n=[],s=i(o(),h,"local");await y()&&n.push({type:"npm-local",path:s});const r=["@iaforged/context-code"];t.PACKAGE_URL&&"@iaforged/context-code"!==t.PACKAGE_URL&&r.push(t.PACKAGE_URL);const l=await x("npm",["-g","config","get","prefix"]);if(0===l.code&&l.stdout){const t=l.stdout.trim(),o="windows"===_(),s=o?i(t,"context"):i(t,"bin","context");let c=!1;try{await e.stat(s),c=!0}catch{}if(c){let t=!1;try{(await a(s)).includes("/Caskroom/")&&(t=P())}catch{}t||n.push({type:"npm-global",path:s})}else for(const a of r){const s=o?i(t,"node_modules",a):i(t,"lib","node_modules",a);try{await e.stat(s),n.push({type:"npm-global-orphan",path:s})}catch{}}}const c=i(o(),".local","bin","context");try{await e.stat(c),n.push({type:"native",path:c})}catch{}if("native"===d().installMethod){const t=i(o(),".local","share","context");try{await e.stat(t),n.some(t=>"native"===t.type)||n.push({type:"native",path:t})}catch{}}return n}(),A=await async function(t){const e=[];try{const t=await n(i(T(),"managed-settings.json"),"utf-8"),a=G(t),o=a&&"object"==typeof a?a.strictPluginOnlyCustomization:void 0;if(void 0!==o&&"boolean"!=typeof o)if(Array.isArray(o)){const t=o.filter(t=>"string"==typeof t&&!U.includes(t));t.length>0&&e.push({issue:`managed-settings.json: strictPluginOnlyCustomization tiene ${t.length} valor(es) que este cliente no reconoce: ${t.map(String).join(", ")}`,fix:`Estos se ignoran silenciosamente. Superficies conocidas para esta versión: ${U.join(", ")}. Elimínalos o actualiza el cliente.`})}else e.push({issue:`managed-settings.json: strictPluginOnlyCustomization tiene un valor no válido (se esperaba boolean o array, se obtuvo ${typeof o})`,fix:`El campo se ignora silenciosamente. Establécelo en true o en un array de: ${U.join(", ")}.`})}catch{}const a=d();if("development"===t)return e;if("native"===t){const t=(process.env.PATH||"").split(s),n=o(),a=i(n,".local","bin");let c=a;if("windows"===_()&&(c=a.split(l.sep).join(r.sep)),!t.some(t=>{let e=t;"windows"===_()&&(e=t.split(l.sep).join(r.sep));const n=e.replace(/\/+$/,""),a=t.replace(/[/\\]+$/,"");return n===c||"~/.local/bin"===a||"$HOME/.local/bin"===a}))if("windows"===_()){const t=a.split(r.sep).join(l.sep);e.push({issue:`Existe una instalación nativa pero ${t} no está en tu PATH`,fix:"Añádelo abriendo: Propiedades del sistema → Variables de entorno → Editar PATH de usuario → Nuevo → Añade la ruta de arriba. Luego reinicia tu terminal."})}else{const t=w(),n=S()[t],a=n?n.replace(o(),"~"):"your shell config file";e.push({issue:"Existe una instalación nativa pero ~/.local/bin no está en tu PATH",fix:`Ejecuta: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ${a} luego abre una nueva terminal o ejecuta: source ${a}`})}}g(process.env.DISABLE_INSTALLATION_CHECKS)||("npm-local"===t&&"local"!==a.installMethod&&e.push({issue:`Ejecutando desde una instalación local pero el método de instalación configurado es '${a.installMethod}'`,fix:"Considera usar la instalación nativa: context install"}),"native"===t&&"native"!==a.installMethod&&e.push({issue:`Ejecutando instalación nativa pero el método de instalación configurado es '${a.installMethod}'`,fix:"Ejecuta context install para actualizar la configuración"})),"npm-global"===t&&await y()&&e.push({issue:"Existe una instalación local pero no se está usando",fix:"Considera usar la instalación nativa: context install"});const c=await H(),p=await N();"npm-local"===t&&(await R("claude")||p||(c?e.push({issue:"Instalación local no accesible",fix:`El alias existe pero apunta a un destino no válido: ${c}. Actualiza el alias: alias context="~/${h}/local/context"`}):e.push({issue:"Instalación local no accesible",fix:`Crea un alias: alias context="~/${h}/local/context"`})));return e}(e);if(A.push(...detectLinuxGlobPatternWarnings()),"native"===e){const e=$.filter(t=>"npm-global"===t.type||"npm-global-orphan"===t.type||"npm-local"===t.type),n="windows"===_();for(const a of e)if("npm-global"===a.type){let e="npm -g uninstall @iaforged/context-code";t.PACKAGE_URL&&"@iaforged/context-code"!==t.PACKAGE_URL&&(e+=` && npm -g uninstall ${t.PACKAGE_URL}`),A.push({issue:`Quedan restos de una instalación global de npm en ${a.path}`,fix:`Ejecuta: ${e}`})}else"npm-global-orphan"===a.type?A.push({issue:`Paquete global de npm huérfano en ${a.path}`,fix:n?`Ejecuta: rmdir /s /q "${a.path}"`:`Ejecuta: rm -rf ${a.path}`}):"npm-local"===a.type&&A.push({issue:`Quedan restos de una instalación local de npm en ${a.path}`,fix:n?`Ejecuta: rmdir /s /q "${a.path}"`:`Ejecuta: rm -rf ${a.path}`})}const C=d().installMethod||"not set";let k=null;if("npm-global"===e){k=(await c()).hasPermissions,k||m()||A.push({issue:"Permisos insuficientes para actualizaciones automáticas",fix:"Haz una de las dos: (1) Reinstala node sin sudo, o (2) Usa `context install` para una instalación nativa"})}const L=M(),O={working:L.working??!0,mode:L.mode,systemPath:"system"===L.mode?L.path:null},z="package-manager"===e?await I():void 0;return{installationType:e,version:v,installationPath:j,invokedBinary:E,configInstallMethod:C,autoUpdates:(()=>{const t=m();return t?`disabled (${u(t)})`:"enabled"})(),hasUpdatePermissions:k,multipleInstallations:$,warnings:A,packageManager:z,ripgrepStatus:O}}
@@ -1 +1 @@
1
- import{createRequire as e}from"module";const n=e(import.meta.url);import r from"lodash-es/memoize.js";import{homedir as o}from"os";import{join as t}from"path";export const getContextConfigHomeDir=r(()=>(process.env.CONTEXT_CONFIG_DIR??t(o(),".contextcli")).normalize("NFC"),()=>`${process.env.CONTEXT_CONFIG_DIR??""}`);export const getClaudeConfigHomeDir=getContextConfigHomeDir;export function getTeamsDir(){return t(getContextConfigHomeDir(),"teams")}export function hasNodeOption(e){const n=process.env.NODE_OPTIONS;return!!n&&n.split(/\s+/).includes(e)}export function isEnvTruthy(e){if(!e)return!1;if("boolean"==typeof e)return e;const n=e.toLowerCase().trim();return["1","true","yes","on"].includes(n)}export function isEnvDefinedFalsy(e){if(void 0===e)return!1;if("boolean"==typeof e)return!e;if(!e)return!1;const n=e.toLowerCase().trim();return["0","false","no","off"].includes(n)}export function isBareMode(){return isEnvTruthy(process.env.CONTEXT_CODE_SIMPLE)||isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)||process.argv.includes("--bare")}export function parseEnvVars(e){const n={};if(e)for(const r of e){const[e,...o]=r.split("=");if(!e||0===o.length)throw new Error(`Invalid environment variable format: ${r}, environment variables should be added as: -e KEY1=value1 -e KEY2=value2`);n[e]=o.join("=")}return n}export function getAWSRegion(){return process.env.AWS_REGION||process.env.AWS_DEFAULT_REGION||"us-east-1"}export function getDefaultVertexRegion(){return process.env.CLOUD_ML_REGION||"us-east5"}export function shouldMaintainProjectWorkingDir(){return isEnvTruthy(process.env.CONTEXT_BASH_MAINTAIN_PROJECT_WORKING_DIR)||isEnvTruthy(process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR)}export function readForkEnvVar(e,n){return process.env[e]??process.env[n]}export function isForkEnvVarTruthy(e,n){return isEnvTruthy(process.env[e])||isEnvTruthy(process.env[n])}export function isBackgroundTasksDisabled(){return isForkEnvVarTruthy("CONTEXT_CODE_DISABLE_BACKGROUND_TASKS","CLAUDE_CODE_DISABLE_BACKGROUND_TASKS")}export function isCoordinatorMode(){return isForkEnvVarTruthy("CONTEXT_CODE_COORDINATOR_MODE","CLAUDE_CODE_COORDINATOR_MODE")}export function isRunningOnHomespace(){return"ant"===process.env.USER_TYPE&&isEnvTruthy(process.env.COO_RUNNING_ON_HOMESPACE)}export function isInProtectedNamespace(){return"ant"===process.env.USER_TYPE&&n("./protectedNamespace.js").checkProtectedNamespace()}const s=[["claude-haiku-4-5","VERTEX_REGION_CLAUDE_HAIKU_4_5"],["claude-3-5-haiku","VERTEX_REGION_CLAUDE_3_5_HAIKU"],["claude-3-5-sonnet","VERTEX_REGION_CLAUDE_3_5_SONNET"],["claude-3-7-sonnet","VERTEX_REGION_CLAUDE_3_7_SONNET"],["claude-opus-4-8","VERTEX_REGION_CLAUDE_4_8_OPUS"],["claude-opus-4-1","VERTEX_REGION_CLAUDE_4_1_OPUS"],["claude-opus-4","VERTEX_REGION_CLAUDE_4_0_OPUS"],["claude-sonnet-4-6","VERTEX_REGION_CLAUDE_4_6_SONNET"],["claude-sonnet-4-5","VERTEX_REGION_CLAUDE_4_5_SONNET"],["claude-sonnet-4","VERTEX_REGION_CLAUDE_4_0_SONNET"]];export function getVertexRegionForModel(e){if(e){const n=s.find(([n])=>e.startsWith(n));if(n)return process.env[n[1]]||getDefaultVertexRegion()}return getDefaultVertexRegion()}
1
+ import{createRequire as e}from"module";const n=e(import.meta.url);import r from"lodash-es/memoize.js";import{homedir as o}from"os";import{join as t}from"path";import{CONTEXT_DIR_NAME as s}from"./configConstants.js";export const getContextConfigHomeDir=r(()=>(process.env.CONTEXT_CONFIG_DIR??t(o(),s)).normalize("NFC"),()=>`${process.env.CONTEXT_CONFIG_DIR??""}`);export const getClaudeConfigHomeDir=getContextConfigHomeDir;export function getTeamsDir(){return t(getContextConfigHomeDir(),"teams")}export function hasNodeOption(e){const n=process.env.NODE_OPTIONS;return!!n&&n.split(/\s+/).includes(e)}export function isEnvTruthy(e){if(!e)return!1;if("boolean"==typeof e)return e;const n=e.toLowerCase().trim();return["1","true","yes","on"].includes(n)}export function isEnvDefinedFalsy(e){if(void 0===e)return!1;if("boolean"==typeof e)return!e;if(!e)return!1;const n=e.toLowerCase().trim();return["0","false","no","off"].includes(n)}export function isBareMode(){return isEnvTruthy(process.env.CONTEXT_CODE_SIMPLE)||isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)||process.argv.includes("--bare")}export function parseEnvVars(e){const n={};if(e)for(const r of e){const[e,...o]=r.split("=");if(!e||0===o.length)throw new Error(`Invalid environment variable format: ${r}, environment variables should be added as: -e KEY1=value1 -e KEY2=value2`);n[e]=o.join("=")}return n}export function getAWSRegion(){return process.env.AWS_REGION||process.env.AWS_DEFAULT_REGION||"us-east-1"}export function getDefaultVertexRegion(){return process.env.CLOUD_ML_REGION||"us-east5"}export function shouldMaintainProjectWorkingDir(){return isEnvTruthy(process.env.CONTEXT_BASH_MAINTAIN_PROJECT_WORKING_DIR)||isEnvTruthy(process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR)}export function readForkEnvVar(e,n){return process.env[e]??process.env[n]}export function isForkEnvVarTruthy(e,n){return isEnvTruthy(process.env[e])||isEnvTruthy(process.env[n])}export function isBackgroundTasksDisabled(){return isForkEnvVarTruthy("CONTEXT_CODE_DISABLE_BACKGROUND_TASKS","CLAUDE_CODE_DISABLE_BACKGROUND_TASKS")}export function isCoordinatorMode(){return isForkEnvVarTruthy("CONTEXT_CODE_COORDINATOR_MODE","CLAUDE_CODE_COORDINATOR_MODE")}export function isRunningOnHomespace(){return"ant"===process.env.USER_TYPE&&isEnvTruthy(process.env.COO_RUNNING_ON_HOMESPACE)}export function isInProtectedNamespace(){return"ant"===process.env.USER_TYPE&&n("./protectedNamespace.js").checkProtectedNamespace()}const E=[["claude-haiku-4-5","VERTEX_REGION_CLAUDE_HAIKU_4_5"],["claude-3-5-haiku","VERTEX_REGION_CLAUDE_3_5_HAIKU"],["claude-3-5-sonnet","VERTEX_REGION_CLAUDE_3_5_SONNET"],["claude-3-7-sonnet","VERTEX_REGION_CLAUDE_3_7_SONNET"],["claude-opus-4-8","VERTEX_REGION_CLAUDE_4_8_OPUS"],["claude-opus-4-1","VERTEX_REGION_CLAUDE_4_1_OPUS"],["claude-opus-4","VERTEX_REGION_CLAUDE_4_0_OPUS"],["claude-sonnet-4-6","VERTEX_REGION_CLAUDE_4_6_SONNET"],["claude-sonnet-4-5","VERTEX_REGION_CLAUDE_4_5_SONNET"],["claude-sonnet-4","VERTEX_REGION_CLAUDE_4_0_SONNET"]];export function getVertexRegionForModel(e){if(e){const n=E.find(([n])=>e.startsWith(n));if(n)return process.env[n[1]]||getDefaultVertexRegion()}return getDefaultVertexRegion()}
@@ -1 +1 @@
1
- import{feature as e}from"../../recovery/bunBundleShim.js";import{getInvokedSkillsForAgent as t}from"../../bootstrap/state.js";import{getFeatureValue_CACHED_MAY_BE_STALE as s}from"../../services/analytics/growthbook.js";import{logEvent as n}from"../../services/analytics/index.js";import{queryModelWithoutStreaming as o}from"../../services/api/claude.js";import{getEmptyToolPermissionContext as r}from"../../Tool.js";import{createAbortController as i}from"../abortController.js";import{count as a}from"../array.js";import{getCwd as l}from"../cwd.js";import{toError as m}from"../errors.js";import{logError as p}from"../log.js";import{createUserMessage as u,extractTag as c,extractTextContent as d}from"../messages.js";import{getSmallFastModel as f}from"../model/model.js";import{jsonParse as g}from"../slowOperations.js";import{asSystemPrompt as h}from"../systemPromptType.js";import{createApiQueryHook as y}from"./apiQueryHookHelper.js";import{registerPostSamplingHook as k}from"./postSamplingHooks.js";function findProjectSkill(){const e=t(null);for(const[,t]of e)if(t.skillPath.startsWith("projectSettings:"))return t}export function initSkillImprovement(){e("SKILL_IMPROVEMENT")&&s("tengu_copper_panda",!1)&&k(function(){let e=0,t=0;return y({name:"skill_improvement",async shouldRun(t){if("repl_main_thread"!==t.querySource)return!1;if(!findProjectSkill())return!1;const s=a(t.messages,e=>"user"===e.type);return!(s-e<5||(e=s,0))},buildMessages(e){const s=findProjectSkill(),n=e.messages.slice(t);return t=e.messages.length,[u({content:`You are analyzing a conversation where a user is executing a skill (a repeatable process).\nYour job: identify if the user's recent messages contain preferences, requests, or corrections that should be permanently added to the skill definition for future runs.\n\n<skill_definition>\n${s.content}\n</skill_definition>\n\n<recent_messages>\n${o=n,o.filter(e=>"user"===e.type||"assistant"===e.type).map(e=>{const t="user"===e.type?"User":"Assistant",s=e.message.content;return"string"==typeof s?`${t}: ${s.slice(0,500)}`:`${t}: ${s.filter(e=>"text"===e.type).map(e=>e.text).join("\n").slice(0,500)}`}).join("\n\n")}\n</recent_messages>\n\nLook for:\n- Requests to add, change, or remove steps: "can you also ask me X", "please do Y too", "don't do Z"\n- Preferences about how steps should work: "ask me about energy levels", "note the time", "use a casual tone"\n- Corrections: "no, do X instead", "always use Y", "make sure to..."\n\nIgnore:\n- Routine conversation that doesn't generalize (one-time answers, chitchat)\n- Things the skill already does\n\nOutput a JSON array inside <updates> tags. Each item: {"section": "which step/section to modify or 'new step'", "change": "what to add/modify", "reason": "which user message prompted this"}.\nOutput <updates>[]</updates> if no updates are needed.`})];var o},systemPrompt:"You detect user preferences and process improvements during skill execution. Flag anything the user asks for that should be remembered for next time.",useTools:!1,parseResponse(e){const t=c(e,"updates");if(!t)return[];try{return g(t)}catch{return[]}},logResult(e,t){if("success"===e.type&&e.result.length>0){const s=findProjectSkill(),o=s?.skillName??"unknown";n("tengu_skill_improvement_detected",{updateCount:e.result.length,uuid:e.uuid,_PROTO_skill_name:o}),t.toolUseContext.setAppState(t=>({...t,skillImprovement:{suggestion:{skillName:o,updates:e.result}}}))}},getModel:f})}())}export async function applySkillImprovement(e,t){if(!e)return;const{join:s}=await import("path"),n=await import("fs/promises"),a=s(l(),".contextcli","skills",e,"SKILL.md");let g;try{g=await n.readFile(a,"utf-8")}catch{return void p(new Error(`Failed to read skill file for improvement: ${a}`))}const y=t.map(e=>`- ${e.section}: ${e.change}`).join("\n"),k=await o({messages:[u({content:`You are editing a skill definition file. Apply the following improvements to the skill.\n\n<current_skill_file>\n${g}\n</current_skill_file>\n\n<improvements>\n${y}\n</improvements>\n\nRules:\n- Integrate the improvements naturally into the existing structure\n- Preserve frontmatter (--- block) exactly as-is\n- Preserve the overall format and style\n- Do not remove existing content unless an improvement explicitly replaces it\n- Output the complete updated file inside <updated_file> tags`})],systemPrompt:h(["You edit skill definition files to incorporate user preferences. Output only the updated file content."]),thinkingConfig:{type:"disabled"},tools:[],signal:i().signal,options:{getToolPermissionContext:async()=>r(),model:f(),toolChoice:void 0,isNonInteractiveSession:!1,hasAppendSystemPrompt:!1,temperatureOverride:0,agents:[],querySource:"skill_improvement_apply",mcpTools:[]}}),v=d(k.message.content).trim(),j=c(v,"updated_file");if(j)try{await n.writeFile(a,j,"utf-8")}catch(e){p(m(e))}else p(new Error("Skill improvement apply: no updated_file tag in response"))}
1
+ import{feature as e}from"../../recovery/bunBundleShim.js";import{getInvokedSkillsForAgent as t}from"../../bootstrap/state.js";import{getFeatureValue_CACHED_MAY_BE_STALE as s}from"../../services/analytics/growthbook.js";import{logEvent as n}from"../../services/analytics/index.js";import{queryModelWithoutStreaming as o}from"../../services/api/claude.js";import{getEmptyToolPermissionContext as r}from"../../Tool.js";import{createAbortController as i}from"../abortController.js";import{count as a}from"../array.js";import{getCwd as l}from"../cwd.js";import{toError as m}from"../errors.js";import{logError as p}from"../log.js";import{CONTEXT_DIR_NAME as u}from"../configConstants.js";import{createUserMessage as c,extractTag as d,extractTextContent as f}from"../messages.js";import{getSmallFastModel as g}from"../model/model.js";import{jsonParse as h}from"../slowOperations.js";import{asSystemPrompt as y}from"../systemPromptType.js";import{createApiQueryHook as k}from"./apiQueryHookHelper.js";import{registerPostSamplingHook as v}from"./postSamplingHooks.js";function findProjectSkill(){const e=t(null);for(const[,t]of e)if(t.skillPath.startsWith("projectSettings:"))return t}export function initSkillImprovement(){e("SKILL_IMPROVEMENT")&&s("tengu_copper_panda",!1)&&v(function(){let e=0,t=0;return k({name:"skill_improvement",async shouldRun(t){if("repl_main_thread"!==t.querySource)return!1;if(!findProjectSkill())return!1;const s=a(t.messages,e=>"user"===e.type);return!(s-e<5||(e=s,0))},buildMessages(e){const s=findProjectSkill(),n=e.messages.slice(t);return t=e.messages.length,[c({content:`You are analyzing a conversation where a user is executing a skill (a repeatable process).\nYour job: identify if the user's recent messages contain preferences, requests, or corrections that should be permanently added to the skill definition for future runs.\n\n<skill_definition>\n${s.content}\n</skill_definition>\n\n<recent_messages>\n${o=n,o.filter(e=>"user"===e.type||"assistant"===e.type).map(e=>{const t="user"===e.type?"User":"Assistant",s=e.message.content;return"string"==typeof s?`${t}: ${s.slice(0,500)}`:`${t}: ${s.filter(e=>"text"===e.type).map(e=>e.text).join("\n").slice(0,500)}`}).join("\n\n")}\n</recent_messages>\n\nLook for:\n- Requests to add, change, or remove steps: "can you also ask me X", "please do Y too", "don't do Z"\n- Preferences about how steps should work: "ask me about energy levels", "note the time", "use a casual tone"\n- Corrections: "no, do X instead", "always use Y", "make sure to..."\n\nIgnore:\n- Routine conversation that doesn't generalize (one-time answers, chitchat)\n- Things the skill already does\n\nOutput a JSON array inside <updates> tags. Each item: {"section": "which step/section to modify or 'new step'", "change": "what to add/modify", "reason": "which user message prompted this"}.\nOutput <updates>[]</updates> if no updates are needed.`})];var o},systemPrompt:"You detect user preferences and process improvements during skill execution. Flag anything the user asks for that should be remembered for next time.",useTools:!1,parseResponse(e){const t=d(e,"updates");if(!t)return[];try{return h(t)}catch{return[]}},logResult(e,t){if("success"===e.type&&e.result.length>0){const s=findProjectSkill(),o=s?.skillName??"unknown";n("tengu_skill_improvement_detected",{updateCount:e.result.length,uuid:e.uuid,_PROTO_skill_name:o}),t.toolUseContext.setAppState(t=>({...t,skillImprovement:{suggestion:{skillName:o,updates:e.result}}}))}},getModel:g})}())}export async function applySkillImprovement(e,t){if(!e)return;const{join:s}=await import("path"),n=await import("fs/promises"),a=s(l(),u,"skills",e,"SKILL.md");let h;try{h=await n.readFile(a,"utf-8")}catch{return void p(new Error(`Failed to read skill file for improvement: ${a}`))}const k=t.map(e=>`- ${e.section}: ${e.change}`).join("\n"),v=await o({messages:[c({content:`You are editing a skill definition file. Apply the following improvements to the skill.\n\n<current_skill_file>\n${h}\n</current_skill_file>\n\n<improvements>\n${k}\n</improvements>\n\nRules:\n- Integrate the improvements naturally into the existing structure\n- Preserve frontmatter (--- block) exactly as-is\n- Preserve the overall format and style\n- Do not remove existing content unless an improvement explicitly replaces it\n- Output the complete updated file inside <updated_file> tags`})],systemPrompt:y(["You edit skill definition files to incorporate user preferences. Output only the updated file content."]),thinkingConfig:{type:"disabled"},tools:[],signal:i().signal,options:{getToolPermissionContext:async()=>r(),model:g(),toolChoice:void 0,isNonInteractiveSession:!1,hasAppendSystemPrompt:!1,temperatureOverride:0,agents:[],querySource:"skill_improvement_apply",mcpTools:[]}}),j=f(v.message.content).trim(),_=d(j,"updated_file");if(_)try{await n.writeFile(a,_,"utf-8")}catch(e){p(m(e))}else p(new Error("Skill improvement apply: no updated_file tag in response"))}
@@ -1 +1 @@
1
- import{feature as t}from"../recovery/bunBundleShim.js";import{statSync as o}from"fs";import{lstat as r,readdir as e,readFile as n,realpath as i,stat as s}from"fs/promises";import a from"lodash-es/memoize.js";import{homedir as c}from"os";import{dirname as l,join as m,resolve as u,sep as f}from"path";import{logEvent as d}from"../services/analytics/index.js";import{getProjectRoot as p}from"../bootstrap/state.js";import{logForDebugging as g}from"./debug.js";import{getClaudeConfigHomeDir as h,isEnvTruthy as y}from"./envUtils.js";import{isFsInaccessible as w}from"./errors.js";import{normalizePathForComparison as F}from"./file.js";import{parseFrontmatter as S}from"./frontmatterParser.js";import{findCanonicalGitRoot as j,findGitRoot as $}from"./git.js";import{parseToolListFromCLI as k}from"./permissions/permissionSetup.js";import{ripGrep as D}from"./ripgrep.js";import{isSettingSourceEnabled as v}from"./settings/constants.js";import{getManagedFilePath as b}from"./settings/managedPath.js";import{isRestrictedToPluginOnly as P}from"./settings/pluginOnlyPolicy.js";export const CLAUDE_CONFIG_DIRECTORIES=["commands","agents","output-styles","skills","workflows",...t("TEMPLATES")?["templates"]:[]];export function extractDescriptionFromMarkdown(t,o="Custom item"){const r=t.split("\n");for(const t of r){const o=t.trim();if(o){const t=o.match(/^#+\s+(.+)$/),r=t?.[1]??o;return r.length>100?r.substring(0,97)+"...":r}}return o}function parseToolListString(t){if(null==t)return null;if(!t)return[];let o=[];if("string"==typeof t?o=[t]:Array.isArray(t)&&(o=t.filter(t=>"string"==typeof t)),0===o.length)return[];const r=k(o);return r.includes("*")?["*"]:r}export function parseAgentToolsFromFrontmatter(t){const o=parseToolListString(t);return null===o?void 0===t?void 0:[]:o.includes("*")?void 0:o}export function parseSlashCommandToolsFromFrontmatter(t){const o=parseToolListString(t);return null===o?[]:o}export function getProjectDirsUpToHome(t,r,e=".claude"){const n=u(c()).normalize("NFC"),i=function(t){const o=$(t),r=$(p());if(!o||!r)return o;const e=j(t);if(e&&F(e)===F(r))return o;const n=F(o),i=F(r);return n!==i&&n.startsWith(i+f)?r:o}(r);let s=u(r);const a=[];for(;F(s)!==F(n);){const r=m(s,e,t);try{o(r),a.push(r)}catch(t){if(!w(t))throw t}if(i&&F(s)===F(i))break;const n=l(s);if(n===s)break;s=n}return a}export function getDualProjectDirsUpToHome(t,o){return getProjectDirsUpToHome(t,o,".contextcli")}export const loadMarkdownFilesForSubdir=a(async function(t,o){const e=Date.now(),n=m(h(),t),i=m(b(),".claude",t),s=getDualProjectDirsUpToHome(t,o),a=$(o),c=j(o);if(a&&c&&c!==a)for(const o of[".contextcli",".claude"]){const r=F(m(a,o,t));if(!s.some(t=>F(t)===r)){const r=m(c,o,t);s.includes(r)||s.push(r)}}const[l,u,f]=await Promise.all([loadMarkdownFiles(i).then(t=>t.map(t=>({...t,baseDir:i,source:"policySettings"}))),!v("userSettings")||"agents"===t&&P("agents")?Promise.resolve([]):loadMarkdownFiles(n).then(t=>t.map(t=>({...t,baseDir:n,source:"userSettings"}))),!v("projectSettings")||"agents"===t&&P("agents")?Promise.resolve([]):Promise.all(s.map(t=>loadMarkdownFiles(t).then(o=>o.map(o=>({...o,baseDir:t,source:"projectSettings"})))))]),p=f.flat(),y=[...l,...u,...p],w=await Promise.all(y.map(t=>async function(t){try{const o=await r(t,{bigint:!0});return 0n===o.dev&&0n===o.ino?null:`${o.dev}:${o.ino}`}catch{return null}}(t.filePath))),S=new Map,k=[];for(const[t,o]of y.entries()){const r=w[t]??null;if(null===r){k.push(o);continue}const e=S.get(r);void 0===e?(S.set(r,o.source),k.push(o)):g(`Skipping duplicate file '${o.filePath}' from ${o.source} (same inode already loaded from ${e})`)}const D=y.length-k.length;return D>0&&g(`Deduplicated ${D} files in ${t} (same inode via symlinks or hard links)`),d("tengu_dir_search",{durationMs:Date.now()-e,managedFilesFound:l.length,userFilesFound:u.length,projectFilesFound:p.length,projectDirsSearched:s.length,subdir:t}),k},(t,o)=>`${t}:${o}`);async function loadMarkdownFiles(t){const o=y(process.env.CLAUDE_CODE_USE_NATIVE_FILE_SEARCH),r=AbortSignal.timeout(3e3);let a;try{a=o?await async function(t,o){const r=[],n=new Set;return await async function walk(t){if(!o.aborted){try{const o=await s(t,{bigint:!0});if(o.isDirectory()){const r=void 0!==o.dev&&void 0!==o.ino?`${o.dev}:${o.ino}`:await i(t);if(n.has(r))return void g(`Skipping already visited directory (circular symlink): ${t}`);n.add(r)}}catch(o){const r=o instanceof Error?o.message:String(o);return void g(`Failed to stat directory ${t}: ${r}`)}try{const n=await e(t,{withFileTypes:!0});for(const e of n){if(o.aborted)break;const n=m(t,e.name);try{if(e.isSymbolicLink())try{const t=await s(n);t.isDirectory()?await walk(n):t.isFile()&&e.name.endsWith(".md")&&r.push(n)}catch(t){const o=t instanceof Error?t.message:String(t);g(`Failed to follow symlink ${n}: ${o}`)}else e.isDirectory()?await walk(n):e.isFile()&&e.name.endsWith(".md")&&r.push(n)}catch(t){const o=t instanceof Error?t.message:String(t);g(`Failed to access ${n}: ${o}`)}}}catch(o){const r=o instanceof Error?o.message:String(o);g(`Failed to read directory ${t}: ${r}`)}}}(t),r}(t,r):await D(["--files","--hidden","--follow","--no-ignore","--glob","*.md"],t,r)}catch(t){if(w(t))return[];throw t}return(await Promise.all(a.map(async t=>{try{const o=await n(t,{encoding:"utf-8"}),{frontmatter:r,content:e}=S(o,t);return{filePath:t,frontmatter:r,content:e}}catch(o){const r=o instanceof Error?o.message:String(o);return g(`Failed to read/parse markdown file: ${t}: ${r}`),null}}))).filter(t=>null!==t)}
1
+ import{feature as t}from"../recovery/bunBundleShim.js";import{statSync as o}from"fs";import{lstat as r,readdir as e,readFile as n,realpath as i,stat as s}from"fs/promises";import a from"lodash-es/memoize.js";import{homedir as c}from"os";import{dirname as l,join as m,resolve as f,sep as u}from"path";import{logEvent as d}from"../services/analytics/index.js";import{getProjectRoot as p}from"../bootstrap/state.js";import{logForDebugging as g}from"./debug.js";import{getClaudeConfigHomeDir as h,isEnvTruthy as y}from"./envUtils.js";import{CONTEXT_DIR_NAME as w}from"./configConstants.js";import{isFsInaccessible as F}from"./errors.js";import{normalizePathForComparison as S}from"./file.js";import{parseFrontmatter as j}from"./frontmatterParser.js";import{findCanonicalGitRoot as $,findGitRoot as k}from"./git.js";import{parseToolListFromCLI as D}from"./permissions/permissionSetup.js";import{ripGrep as v}from"./ripgrep.js";import{isSettingSourceEnabled as b}from"./settings/constants.js";import{getManagedFilePath as P}from"./settings/managedPath.js";import{isRestrictedToPluginOnly as E}from"./settings/pluginOnlyPolicy.js";export const CLAUDE_CONFIG_DIRECTORIES=["commands","agents","output-styles","skills","workflows",...t("TEMPLATES")?["templates"]:[]];export function extractDescriptionFromMarkdown(t,o="Custom item"){const r=t.split("\n");for(const t of r){const o=t.trim();if(o){const t=o.match(/^#+\s+(.+)$/),r=t?.[1]??o;return r.length>100?r.substring(0,97)+"...":r}}return o}function parseToolListString(t){if(null==t)return null;if(!t)return[];let o=[];if("string"==typeof t?o=[t]:Array.isArray(t)&&(o=t.filter(t=>"string"==typeof t)),0===o.length)return[];const r=D(o);return r.includes("*")?["*"]:r}export function parseAgentToolsFromFrontmatter(t){const o=parseToolListString(t);return null===o?void 0===t?void 0:[]:o.includes("*")?void 0:o}export function parseSlashCommandToolsFromFrontmatter(t){const o=parseToolListString(t);return null===o?[]:o}export function getProjectDirsUpToHome(t,r,e=".claude"){const n=f(c()).normalize("NFC"),i=function(t){const o=k(t),r=k(p());if(!o||!r)return o;const e=$(t);if(e&&S(e)===S(r))return o;const n=S(o),i=S(r);return n!==i&&n.startsWith(i+u)?r:o}(r);let s=f(r);const a=[];for(;S(s)!==S(n);){const r=m(s,e,t);try{o(r),a.push(r)}catch(t){if(!F(t))throw t}if(i&&S(s)===S(i))break;const n=l(s);if(n===s)break;s=n}return a}export function getDualProjectDirsUpToHome(t,o){return getProjectDirsUpToHome(t,o,w)}export const loadMarkdownFilesForSubdir=a(async function(t,o){const e=Date.now(),n=m(h(),t),i=m(P(),".claude",t),s=getDualProjectDirsUpToHome(t,o),a=k(o),c=$(o);if(a&&c&&c!==a)for(const o of[w,".claude"]){const r=S(m(a,o,t));if(!s.some(t=>S(t)===r)){const r=m(c,o,t);s.includes(r)||s.push(r)}}const[l,f,u]=await Promise.all([loadMarkdownFiles(i).then(t=>t.map(t=>({...t,baseDir:i,source:"policySettings"}))),!b("userSettings")||"agents"===t&&E("agents")?Promise.resolve([]):loadMarkdownFiles(n).then(t=>t.map(t=>({...t,baseDir:n,source:"userSettings"}))),!b("projectSettings")||"agents"===t&&E("agents")?Promise.resolve([]):Promise.all(s.map(t=>loadMarkdownFiles(t).then(o=>o.map(o=>({...o,baseDir:t,source:"projectSettings"})))))]),p=u.flat(),y=[...l,...f,...p],F=await Promise.all(y.map(t=>async function(t){try{const o=await r(t,{bigint:!0});return 0n===o.dev&&0n===o.ino?null:`${o.dev}:${o.ino}`}catch{return null}}(t.filePath))),j=new Map,D=[];for(const[t,o]of y.entries()){const r=F[t]??null;if(null===r){D.push(o);continue}const e=j.get(r);void 0===e?(j.set(r,o.source),D.push(o)):g(`Skipping duplicate file '${o.filePath}' from ${o.source} (same inode already loaded from ${e})`)}const v=y.length-D.length;return v>0&&g(`Deduplicated ${v} files in ${t} (same inode via symlinks or hard links)`),d("tengu_dir_search",{durationMs:Date.now()-e,managedFilesFound:l.length,userFilesFound:f.length,projectFilesFound:p.length,projectDirsSearched:s.length,subdir:t}),D},(t,o)=>`${t}:${o}`);async function loadMarkdownFiles(t){const o=y(process.env.CLAUDE_CODE_USE_NATIVE_FILE_SEARCH),r=AbortSignal.timeout(3e3);let a;try{a=o?await async function(t,o){const r=[],n=new Set;return await async function walk(t){if(!o.aborted){try{const o=await s(t,{bigint:!0});if(o.isDirectory()){const r=void 0!==o.dev&&void 0!==o.ino?`${o.dev}:${o.ino}`:await i(t);if(n.has(r))return void g(`Skipping already visited directory (circular symlink): ${t}`);n.add(r)}}catch(o){const r=o instanceof Error?o.message:String(o);return void g(`Failed to stat directory ${t}: ${r}`)}try{const n=await e(t,{withFileTypes:!0});for(const e of n){if(o.aborted)break;const n=m(t,e.name);try{if(e.isSymbolicLink())try{const t=await s(n);t.isDirectory()?await walk(n):t.isFile()&&e.name.endsWith(".md")&&r.push(n)}catch(t){const o=t instanceof Error?t.message:String(t);g(`Failed to follow symlink ${n}: ${o}`)}else e.isDirectory()?await walk(n):e.isFile()&&e.name.endsWith(".md")&&r.push(n)}catch(t){const o=t instanceof Error?t.message:String(t);g(`Failed to access ${n}: ${o}`)}}}catch(o){const r=o instanceof Error?o.message:String(o);g(`Failed to read directory ${t}: ${r}`)}}}(t),r}(t,r):await v(["--files","--hidden","--follow","--no-ignore","--glob","*.md"],t,r)}catch(t){if(F(t))return[];throw t}return(await Promise.all(a.map(async t=>{try{const o=await n(t,{encoding:"utf-8"}),{frontmatter:r,content:e}=j(o,t);return{filePath:t,frontmatter:r,content:e}}catch(o){const r=o instanceof Error?o.message:String(o);return g(`Failed to read/parse markdown file: ${t}: ${r}`),null}}))).filter(t=>null!==t)}
@@ -1 +1 @@
1
- import{MACRO as e}from"../../recovery/bunBundleShim.js";import{constants as t}from"fs";import{access as a,chmod as i,copyFile as r,lstat as o,mkdir as n,readdir as s,readlink as c,realpath as l,rename as u,rm as d,rmdir as m,stat as p,symlink as f,unlink as _,writeFile as w}from"fs/promises";import{homedir as h}from"os";import{basename as y,delimiter as g,dirname as v,join as $,resolve as k}from"path";import{logEvent as b}from"../../services/analytics/index.js";import{getMaxVersion as P,shouldSkipVersion as x}from"../autoUpdater.js";import{registerCleanup as E}from"../cleanupRegistry.js";import{getGlobalConfig as A,saveGlobalConfig as F}from"../config.js";import{logForDebugging as L}from"../debug.js";import{getCurrentInstallationType as N}from"../doctorDiagnostic.js";import{env as C}from"../env.js";import{envDynamic as V}from"../envDynamic.js";import{isEnvTruthy as T}from"../envUtils.js";import{errorMessage as R,getErrnoCode as D,isENOENT as S,toError as B}from"../errors.js";import{execFileNoThrowWithCwd as I}from"../execFileNoThrow.js";import{getShellType as j}from"../localInstaller.js";import*as O from"../lockfile.js";import{logError as q}from"../log.js";import{gt as M,gte as U}from"../semver.js";import{filterClaudeAliases as W,getShellConfigPaths as H,readFileLines as K,writeFileLines as z}from"../shellConfig.js";import{sleep as Y}from"../sleep.js";import{getUserBinDir as G,getXDGCacheHome as X,getXDGDataHome as J,getXDGStateHome as Q}from"../xdg.js";import{downloadVersion as Z,getLatestVersion as ee}from"./download.js";import{acquireProcessLifetimeLock as te,cleanupStaleLocks as ae,isLockActive as ie,isPidBasedLockingEnabled as re,readLockContent as oe,withLock as ne}from"./pidLock.js";export const VERSION_RETENTION_COUNT=2;const se=6048e5;export function getPlatform(){const e=C.platform,t="x64"===process.arch?"x64":"arm64"===process.arch?"arm64":null;if(!t){const e=new Error(`Unsupported architecture: ${process.arch}`);throw L(`Native installer does not support architecture: ${process.arch}`,{level:"error"}),e}return"linux"===e&&V.isMuslEnvironment()?`linux-${t}-musl`:`${e}-${t}`}export function getBinaryName(e){return e.startsWith("win32")?"claude.exe":"claude"}function getBaseDirectories(){const e=getBinaryName(getPlatform());return{versions:$(J(),"claude","versions"),staging:$(X(),"claude","staging"),locks:$(Q(),"claude","locks"),executable:$(G(),e)}}async function isPossibleClaudeBinary(e){try{const i=await p(e);return!(!i.isFile()||0===i.size)&&(await a(e,t.X_OK),!0)}catch{return!1}}async function getVersionPaths(e){const t=getBaseDirectories(),a=[t.versions,t.staging,t.locks];await Promise.all(a.map(e=>n(e,{recursive:!0})));const i=v(t.executable);await n(i,{recursive:!0});const r=$(t.versions,e);try{await p(r)}catch{await w(r,"",{encoding:"utf8"})}return{stagingPath:$(t.staging,e),installPath:r}}async function tryWithVersionLock(e,t,a=0){const i=getBaseDirectories(),r=getLockFilePathFromVersionPath(i,e);if(await n(i.locks,{recursive:!0}),re()){let i=0;const o=a+1,n=a>0?1e3:100,s=a>0?5e3:500;for(;i<o;){if(await ne(e,r,async()=>{try{await t()}catch(e){throw q(e),e}}))return b("tengu_version_lock_acquired",{is_pid_based:!0,is_lifetime_lock:!1,attempts:i+1}),!0;if(i++,i<o){const e=Math.min(n*Math.pow(2,i-1),s);await Y(e)}}return b("tengu_version_lock_failed",{is_pid_based:!0,is_lifetime_lock:!1,attempts:o}),logLockAcquisitionError(e,new Error("Lock held by another process")),!1}let o=null;try{try{o=await O.lock(e,{stale:se,retries:{retries:a,minTimeout:a>0?1e3:100,maxTimeout:a>0?5e3:500},lockfilePath:r,onCompromised:e=>{L(`NON-FATAL: Version lock was compromised during operation: ${e.message}`,{level:"info"})}})}catch(t){return b("tengu_version_lock_failed",{is_pid_based:!1,is_lifetime_lock:!1}),logLockAcquisitionError(e,t),!1}try{return await t(),b("tengu_version_lock_acquired",{is_pid_based:!1,is_lifetime_lock:!1}),!0}catch(e){throw q(e),e}}finally{o&&await o()}}async function atomicMoveToInstallPath(e,t){await n(v(t),{recursive:!0});const a=`${t}.tmp.${process.pid}.${Date.now()}`;try{await r(e,a),await i(a,493),await u(a,t),L(`Atomically installed binary to ${t}`)}catch(e){try{await _(a)}catch{}throw e}}async function installVersion(e,t,a){"npm"===a?await async function(e,t){try{const a=$(e,"node_modules","@anthropic-ai"),i=(await s(a)).find(e=>e.startsWith("claude-cli-native-"));if(!i)throw b("tengu_native_install_package_failure",{stage_find_package:!0,error_package_not_found:!0}),new Error("Could not find platform-specific native package");const r=$(a,i,"cli");try{await p(r)}catch{throw b("tengu_native_install_package_failure",{stage_binary_exists:!0,error_binary_not_found:!0}),new Error("Native binary not found in staged package")}await atomicMoveToInstallPath(r,t),await d(e,{recursive:!0,force:!0}),b("tengu_native_install_package_success",{})}catch(e){const t=R(e);throw t.includes("Could not find platform-specific")||t.includes("Native binary not found")||b("tengu_native_install_package_failure",{stage_atomic_move:!0,error_move_failed:!0}),q(B(e)),e}}(e,t):await async function(e,t){try{const a=getBinaryName(getPlatform()),i=$(e,a);try{await p(i)}catch{throw b("tengu_native_install_binary_failure",{stage_binary_exists:!0,error_binary_not_found:!0}),new Error("Staged binary not found")}await atomicMoveToInstallPath(i,t),await d(e,{recursive:!0,force:!0}),b("tengu_native_install_binary_success",{})}catch(e){throw R(e).includes("Staged binary not found")||b("tengu_native_install_binary_failure",{stage_atomic_move:!0,error_move_failed:!0}),q(B(e)),e}}(e,t)}async function performVersionUpdate(e,t){const{stagingPath:a,installPath:i}=await getVersionPaths(e),{executable:o}=getBaseDirectories(),s=T(process.env.ENABLE_LOCKLESS_UPDATES)?`${a}.${process.pid}.${Date.now()}`:a,l=!await versionIsAvailable(e)||t;if(l){L(t?`Force reinstalling native installer version ${e}`:`Downloading native installer version ${e}`);const a=await Z(e,s);await installVersion(s,i,a)}else L(`Version ${e} already installed, updating symlink`);if(await removeDirectoryIfEmpty(o),await async function(e,t){const a=getPlatform();if(a.startsWith("win32"))try{const a=v(e);let i;await n(a,{recursive:!0});try{i=await p(e)}catch{}if(i){try{const e=await p(t);if(i.size===e.size)return!1}catch{}const a=`${e}.old.${Date.now()}`;await u(e,a);try{await r(t,e);try{await _(a)}catch{}}catch(t){try{await u(a,e)}catch(e){const a=new Error(`Failed to restore old executable: ${e}`,{cause:t});throw q(a),a}throw t}}else try{await r(t,e)}catch(e){if(S(e))throw new Error(`Source file does not exist: ${t}`);throw e}return!0}catch(a){return q(new Error(`Failed to copy executable from ${t} to ${e}: ${a}`)),!1}const i=v(e);try{await n(i,{recursive:!0}),L(`Created directory ${i} for symlink`)}catch(e){return q(new Error(`Failed to create directory ${i}: ${e}`)),!1}try{let a=!1;try{await p(e),a=!0}catch{}if(a){try{const a=await c(e),i=k(v(e),a);if(i===k(t))return!1}catch{}await _(e)}}catch(e){q(new Error(`Failed to check/remove existing symlink: ${e}`))}const o=`${e}.tmp.${process.pid}.${Date.now()}`;try{return await f(t,o),await u(o,e),L(`Atomically updated symlink ${e} -> ${t}`),!0}catch(a){try{await _(o)}catch{}return q(new Error(`Failed to create symlink from ${e} to ${t}: ${a}`)),!1}}(o,i),!await isPossibleClaudeBinary(o)){let e=!1;try{await p(i),e=!0}catch{}throw new Error(`Failed to create executable at ${o}. Source file exists: ${e}. Check write permissions to ${o}.`)}return l}async function versionIsAvailable(e){const{installPath:t}=await getVersionPaths(e);return isPossibleClaudeBinary(t)}async function updateLatest(t,a=!1){const i=Date.now();let r=await ee(t);const{executable:o}=getBaseDirectories();if(L(`Checking for native installer update to version ${r}`),!a){const t=await P();if(t&&M(r,t)){if(L(`Native installer: maxVersion ${t} is set, capping update from ${r} to ${t}`),U(e.VERSION,t))return L(`Native installer: current version ${e.VERSION} is already at or above maxVersion ${t}, skipping update`),b("tengu_native_update_skipped_max_version",{latency_ms:Date.now()-i,max_version:t,available_version:r}),{success:!0,latestVersion:r};r=t}}if(!a&&r===e.VERSION&&await versionIsAvailable(r)&&await isPossibleClaudeBinary(o))return L(`Found ${r} at ${o}, skipping install`),b("tengu_native_update_complete",{latency_ms:Date.now()-i,was_new_install:!1,was_force_reinstall:!1,was_already_running:!0}),{success:!0,latestVersion:r};if(!a&&x(r))return b("tengu_native_update_skipped_minimum_version",{latency_ms:Date.now()-i,target_version:r}),{success:!0,latestVersion:r};let n,s=!1;if(T(process.env.ENABLE_LOCKLESS_UPDATES))s=await performVersionUpdate(r,a),n=Date.now()-i;else{const{installPath:e}=await getVersionPaths(r);a&&await async function(e){const t=getLockFilePathFromVersionPath(getBaseDirectories(),e);try{await _(t),L(`Force-removed lock file at ${t}`)}catch(e){L(`Failed to force-remove lock file: ${R(e)}`)}}(e);const t=await tryWithVersionLock(e,async()=>{s=await performVersionUpdate(r,a)},3);if(n=Date.now()-i,!t){const t=getBaseDirectories();let a;if(re()){const i=getLockFilePathFromVersionPath(t,e);ie(i)&&(a=oe(i)?.pid)}return b("tengu_native_update_lock_failed",{latency_ms:n,lock_holder_pid:a}),{success:!1,latestVersion:r,lockFailed:!0,lockHolderPid:a}}}return b("tengu_native_update_complete",{latency_ms:n,was_new_install:s,was_force_reinstall:a}),L(`Successfully updated to version ${r}`),{success:!0,latestVersion:r}}export async function removeDirectoryIfEmpty(e){try{await m(e),L(`Removed empty directory at ${e}`)}catch(t){const a=D(t);"ENOTDIR"!==a&&"ENOENT"!==a&&"ENOTEMPTY"!==a&&L(`Could not remove directory at ${e}: ${t}`)}}export async function checkInstall(e=!1){if(T(process.env.DISABLE_INSTALLATION_CHECKS))return[];const t=await N();if("development"===t)return[];const i=A();if(!(e||"native"===t||"native"===i.installMethod))return[];const r=getBaseDirectories(),o=[],n=v(r.executable),s=k(n),l=getPlatform().startsWith("win32");try{await a(n)}catch{o.push({message:`installMethod is native, but directory ${n} does not exist`,userActionRequired:!0,type:"error"})}if(l)await isPossibleClaudeBinary(r.executable)||o.push({message:`installMethod is native, but claude command is missing or invalid at ${r.executable}`,userActionRequired:!0,type:"error"});else try{const e=await c(r.executable),t=k(v(r.executable),e);await isPossibleClaudeBinary(t)||o.push({message:`Claude symlink points to missing or invalid binary: ${e}`,userActionRequired:!0,type:"error"})}catch(e){S(e)?o.push({message:`installMethod is native, but claude command not found at ${r.executable}`,userActionRequired:!0,type:"error"}):await isPossibleClaudeBinary(r.executable)||o.push({message:`${r.executable} exists but is not a valid Claude binary`,userActionRequired:!0,type:"error"})}if(!(process.env.PATH||"").split(g).some(e=>{try{const t=k(e);return l?t.toLowerCase()===s.toLowerCase():t===s}catch{return!1}}))if(l){const e=n.replace(/\//g,"\\");o.push({message:`Native installation exists but ${e} is not in your PATH. Add it by opening: System Properties → Environment Variables → Edit User PATH → New → Add the path above. Then restart your terminal.`,userActionRequired:!0,type:"path"})}else{const e=j(),t=H()[e],a=t?t.replace(h(),"~"):"your shell config file";o.push({message:`Native installation exists but ~/.local/bin is not in your PATH. Run:\n\necho 'export PATH="$HOME/.local/bin:$PATH"' >> ${a} && source ${a}`,userActionRequired:!0,type:"path"})}return o}let ce=null;export function installLatest(e,t=!1){if(t)return installLatestImpl(e,t);if(ce)return L("installLatest: joining in-flight call"),ce;const a=installLatestImpl(e,t);ce=a;const clear=()=>{ce=null};return a.then(clear,clear),a}async function installLatestImpl(e,t=!1){const a=await updateLatest(e,t);if(!a.success)return{latestVersion:null,wasUpdated:!1,lockFailed:a.lockFailed,lockHolderPid:a.lockHolderPid};return"native"!==A().installMethod&&(F(e=>({...e,installMethod:"native",autoUpdates:!1,autoUpdatesProtectedForNative:!0})),L('Native installer: Set installMethod to "native" and disabled legacy auto-updater for protection')),cleanupOldVersions(),{latestVersion:a.latestVersion,wasUpdated:a.success,lockFailed:!1}}function getLockFilePathFromVersionPath(e,t){const a=y(t);return $(e.locks,`${a}.lock`)}export async function lockCurrentVersion(){const e=getBaseDirectories();if(!process.execPath.includes(e.versions))return;const t=k(process.execPath);try{const a=getLockFilePathFromVersionPath(e,t);if(await n(e.locks,{recursive:!0}),re()){if(!await te(t,a))return b("tengu_version_lock_failed",{is_pid_based:!0,is_lifetime_lock:!0}),void logLockAcquisitionError(t,new Error("Lock already held by another process"));b("tengu_version_lock_acquired",{is_pid_based:!0,is_lifetime_lock:!0}),L(`Acquired PID lock on running version: ${t}`)}else{let e;try{e=await O.lock(t,{stale:se,retries:0,lockfilePath:a,onCompromised:e=>{L(`NON-FATAL: Lock on running version was compromised: ${e.message}`,{level:"info"})}}),b("tengu_version_lock_acquired",{is_pid_based:!1,is_lifetime_lock:!0}),L(`Acquired mtime-based lock on running version: ${t}`),E(async()=>{try{await(e?.())}catch{}})}catch(e){return S(e)?void L(`Cannot lock current version - file does not exist: ${t}`,{level:"info"}):(b("tengu_version_lock_failed",{is_pid_based:!1,is_lifetime_lock:!0}),void logLockAcquisitionError(t,e))}}}catch(e){if(S(e))return void L(`Cannot lock current version - file does not exist: ${t}`,{level:"info"});L(`NON-FATAL: Failed to lock current version during execution ${R(e)}`,{level:"info"})}}function logLockAcquisitionError(e,t){q(new Error(`NON-FATAL: Lock acquisition failed for ${e} (expected in multi-process scenarios)`,{cause:t}))}export async function cleanupOldVersions(){await Promise.resolve();const e=getBaseDirectories(),t=Date.now()-36e5;if(getPlatform().startsWith("win32")){const t=v(e.executable);try{const e=await s(t);let a=0;for(const i of e)if(/^claude\.exe\.old\.\d+$/.test(i))try{await _($(t,i)),a++}catch{}a>0&&L(`Cleaned up ${a} old Windows executables on startup`)}catch(e){S(e)||L(`Failed to clean up old Windows executables: ${e}`)}}try{const a=await s(e.staging);let i=0;for(const r of a){const a=$(e.staging,r);try{(await p(a)).mtime.getTime()<t&&(await d(a,{recursive:!0,force:!0}),i++,L(`Cleaned up old staging directory: ${r}`))}catch{}}i>0&&(L(`Cleaned up ${i} orphaned staging directories`),b("tengu_native_staging_cleanup",{cleaned_count:i}))}catch(e){S(e)||L(`Failed to clean up staging directories: ${e}`)}if(re()){const t=ae(e.locks);t>0&&(L(`Cleaned up ${t} stale version locks`),b("tengu_native_stale_locks_cleanup",{cleaned_count:t}))}let a;try{a=await s(e.versions)}catch(e){return void(S(e)||L(`Failed to readdir versions directory: ${e}`))}const i=[];let r=0;for(const o of a){const a=$(e.versions,o);if(/\.tmp\.\d+\.\d+$/.test(o))try{(await p(a)).mtime.getTime()<t&&(await _(a),r++,L(`Cleaned up orphaned temp install file: ${o}`))}catch{}else try{const e=await p(a);if(!e.isFile())continue;if("win32"!==process.platform&&e.size>0&&!(73&e.mode))continue;i.push({name:o,path:a,resolvedPath:k(a),mtime:e.mtime})}catch{}}if(r>0&&(L(`Cleaned up ${r} orphaned temp install files`),b("tengu_native_temp_files_cleanup",{cleaned_count:r})),0!==i.length)try{const t=process.execPath,a=new Set;t&&t.includes(e.versions)&&a.add(k(t));const r=await async function(e){try{const t=await c(e),a=k(v(e),t);if(await isPossibleClaudeBinary(a))return a}catch{}return null}(e.executable);r&&a.add(r);for(const t of i){if(a.has(t.resolvedPath))continue;const i=getLockFilePathFromVersionPath(e,t.resolvedPath);let r=!1;if(re())r=ie(i);else try{r=await O.check(t.resolvedPath,{stale:se,lockfilePath:i})}catch{r=!1}r&&(a.add(t.resolvedPath),L(`Protecting locked version from cleanup: ${t.name}`))}const o=i.filter(e=>!a.has(e.resolvedPath)).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()).slice(2);if(0===o.length)return void b("tengu_native_version_cleanup",{total_count:i.length,deleted_count:0,protected_count:a.size,retained_count:2,lock_failed_count:0,error_count:0});let n=0,s=0,l=0;await Promise.all(o.map(async e=>{try{await tryWithVersionLock(e.path,async()=>{await _(e.path)})?n++:(s++,L(`Skipping deletion of ${e.name} - locked by another process`))}catch(t){l++,q(new Error(`Failed to delete version ${e.name}: ${t}`))}})),b("tengu_native_version_cleanup",{total_count:i.length,deleted_count:n,protected_count:a.size,retained_count:2,lock_failed_count:s,error_count:l})}catch(e){S(e)||q(new Error(`Version cleanup failed: ${e}`))}}export async function removeInstalledSymlink(){const e=getBaseDirectories();try{if(await async function(e){let t=e;return(await o(e)).isSymbolicLink()&&(t=await l(e)),t.endsWith(".js")||t.includes("node_modules")}(e.executable))return void L(`Skipping removal of ${e.executable} - appears to be npm-managed`);await _(e.executable),L(`Removed claude symlink at ${e.executable}`)}catch(e){if(S(e))return;q(new Error(`Failed to remove claude symlink: ${e}`))}}export async function cleanupShellAliases(){const e=[],t=H();for(const[a,i]of Object.entries(t))try{const t=await K(i);if(!t)continue;const{filtered:r,hadAlias:o}=W(t);o&&(await z(i,r),e.push({message:`Removed claude alias from ${i}. Run: unalias claude`,userActionRequired:!0,type:"alias"}),L(`Cleaned up claude alias from ${a} config`))}catch(t){q(t),e.push({message:`Failed to clean up ${i}: ${t}`,userActionRequired:!1,type:"error"})}return e}async function attemptNpmUninstall(e){const{code:t,stderr:a}=await I("npm",["uninstall","-g",e],{cwd:process.cwd()});if(0===t)return L(`Removed global npm installation of ${e}`),{success:!0};if(a&&!a.includes("npm ERR! code E404")){if(a.includes("npm error code ENOTEMPTY")){L(`Failed to uninstall global npm package ${e}: ${a}`,{level:"error"}),L("Attempting manual removal due to ENOTEMPTY error");const t=await async function(e){try{const t=await I("npm",["config","get","prefix"]);if(0!==t.code||!t.stdout)return{success:!1,error:"Failed to get npm global prefix"};const a=t.stdout.trim();let i=!1;async function tryRemove(e,t){try{return await _(e),L(`Manually removed ${t}: ${e}`),!0}catch{return!1}}if(getPlatform().startsWith("win32")){const r=$(a,"claude.cmd"),o=$(a,"claude.ps1"),n=$(a,"claude");await tryRemove(r,"bin script")&&(i=!0),await tryRemove(o,"PowerShell script")&&(i=!0),await tryRemove(n,"bin executable")&&(i=!0)}else{const s=$(a,"bin","claude");await tryRemove(s,"bin symlink")&&(i=!0)}return i?(L(`Successfully removed ${e} manually`),{success:!0,warning:`${e} executables removed, but node_modules directory was left intact for safety. You may manually delete it later at: ${getPlatform().startsWith("win32")?$(a,"node_modules",e):$(a,"lib","node_modules",e)}`}):{success:!1}}catch(c){return L(`Manual removal failed: ${c}`,{level:"error"}),{success:!1,error:`Manual removal failed: ${c}`}}}(e);if(t.success)return{success:!0,warning:t.warning};if(t.error)return{success:!1,error:`Failed to remove global npm installation of ${e}: ${a}. Manual removal also failed: ${t.error}`}}return L(`Failed to uninstall global npm package ${e}: ${a}`,{level:"error"}),{success:!1,error:`Failed to remove global npm installation of ${e}: ${a}`}}return{success:!1}}export async function cleanupNpmInstallations(){const t=[],a=[];let i=0;const r=await attemptNpmUninstall("@anthropic-ai/claude-code");if(r.success?(i++,r.warning&&a.push(r.warning)):r.error&&t.push(r.error),e.PACKAGE_URL&&"@anthropic-ai/claude-code"!==e.PACKAGE_URL){const r=await attemptNpmUninstall(e.PACKAGE_URL);r.success?(i++,r.warning&&a.push(r.warning)):r.error&&t.push(r.error)}const o=$(h(),".contextcli","local");try{await d(o,{recursive:!0}),i++,L(`Removed local installation at ${o}`)}catch(e){S(e)||(t.push(`Failed to remove ${o}: ${e}`),L(`Failed to remove local installation: ${e}`,{level:"error"}))}return{removed:i,errors:t,warnings:a}}
1
+ import{MACRO as e}from"../../recovery/bunBundleShim.js";import{constants as t}from"fs";import{access as a,chmod as i,copyFile as r,lstat as o,mkdir as n,readdir as s,readlink as c,realpath as l,rename as u,rm as d,rmdir as m,stat as p,symlink as f,unlink as _,writeFile as w}from"fs/promises";import{homedir as h}from"os";import{basename as y,delimiter as g,dirname as v,join as $,resolve as k}from"path";import{logEvent as b}from"../../services/analytics/index.js";import{getMaxVersion as P,shouldSkipVersion as x}from"../autoUpdater.js";import{registerCleanup as E}from"../cleanupRegistry.js";import{getGlobalConfig as A,saveGlobalConfig as F}from"../config.js";import{logForDebugging as L}from"../debug.js";import{getCurrentInstallationType as N}from"../doctorDiagnostic.js";import{env as C}from"../env.js";import{envDynamic as V}from"../envDynamic.js";import{isEnvTruthy as T}from"../envUtils.js";import{CONTEXT_DIR_NAME as R}from"../configConstants.js";import{errorMessage as D,getErrnoCode as S,isENOENT as B,toError as j}from"../errors.js";import{execFileNoThrowWithCwd as I}from"../execFileNoThrow.js";import{getShellType as O}from"../localInstaller.js";import*as q from"../lockfile.js";import{logError as M}from"../log.js";import{gt as U,gte as W}from"../semver.js";import{filterClaudeAliases as H,getShellConfigPaths as K,readFileLines as z,writeFileLines as Y}from"../shellConfig.js";import{sleep as G}from"../sleep.js";import{getUserBinDir as X,getXDGCacheHome as J,getXDGDataHome as Q,getXDGStateHome as Z}from"../xdg.js";import{downloadVersion as ee,getLatestVersion as te}from"./download.js";import{acquireProcessLifetimeLock as ae,cleanupStaleLocks as ie,isLockActive as re,isPidBasedLockingEnabled as oe,readLockContent as ne,withLock as se}from"./pidLock.js";export const VERSION_RETENTION_COUNT=2;const ce=6048e5;export function getPlatform(){const e=C.platform,t="x64"===process.arch?"x64":"arm64"===process.arch?"arm64":null;if(!t){const e=new Error(`Unsupported architecture: ${process.arch}`);throw L(`Native installer does not support architecture: ${process.arch}`,{level:"error"}),e}return"linux"===e&&V.isMuslEnvironment()?`linux-${t}-musl`:`${e}-${t}`}export function getBinaryName(e){return e.startsWith("win32")?"claude.exe":"claude"}function getBaseDirectories(){const e=getBinaryName(getPlatform());return{versions:$(Q(),"claude","versions"),staging:$(J(),"claude","staging"),locks:$(Z(),"claude","locks"),executable:$(X(),e)}}async function isPossibleClaudeBinary(e){try{const i=await p(e);return!(!i.isFile()||0===i.size)&&(await a(e,t.X_OK),!0)}catch{return!1}}async function getVersionPaths(e){const t=getBaseDirectories(),a=[t.versions,t.staging,t.locks];await Promise.all(a.map(e=>n(e,{recursive:!0})));const i=v(t.executable);await n(i,{recursive:!0});const r=$(t.versions,e);try{await p(r)}catch{await w(r,"",{encoding:"utf8"})}return{stagingPath:$(t.staging,e),installPath:r}}async function tryWithVersionLock(e,t,a=0){const i=getBaseDirectories(),r=getLockFilePathFromVersionPath(i,e);if(await n(i.locks,{recursive:!0}),oe()){let i=0;const o=a+1,n=a>0?1e3:100,s=a>0?5e3:500;for(;i<o;){if(await se(e,r,async()=>{try{await t()}catch(e){throw M(e),e}}))return b("tengu_version_lock_acquired",{is_pid_based:!0,is_lifetime_lock:!1,attempts:i+1}),!0;if(i++,i<o){const e=Math.min(n*Math.pow(2,i-1),s);await G(e)}}return b("tengu_version_lock_failed",{is_pid_based:!0,is_lifetime_lock:!1,attempts:o}),logLockAcquisitionError(e,new Error("Lock held by another process")),!1}let o=null;try{try{o=await q.lock(e,{stale:ce,retries:{retries:a,minTimeout:a>0?1e3:100,maxTimeout:a>0?5e3:500},lockfilePath:r,onCompromised:e=>{L(`NON-FATAL: Version lock was compromised during operation: ${e.message}`,{level:"info"})}})}catch(t){return b("tengu_version_lock_failed",{is_pid_based:!1,is_lifetime_lock:!1}),logLockAcquisitionError(e,t),!1}try{return await t(),b("tengu_version_lock_acquired",{is_pid_based:!1,is_lifetime_lock:!1}),!0}catch(e){throw M(e),e}}finally{o&&await o()}}async function atomicMoveToInstallPath(e,t){await n(v(t),{recursive:!0});const a=`${t}.tmp.${process.pid}.${Date.now()}`;try{await r(e,a),await i(a,493),await u(a,t),L(`Atomically installed binary to ${t}`)}catch(e){try{await _(a)}catch{}throw e}}async function installVersion(e,t,a){"npm"===a?await async function(e,t){try{const a=$(e,"node_modules","@anthropic-ai"),i=(await s(a)).find(e=>e.startsWith("claude-cli-native-"));if(!i)throw b("tengu_native_install_package_failure",{stage_find_package:!0,error_package_not_found:!0}),new Error("Could not find platform-specific native package");const r=$(a,i,"cli");try{await p(r)}catch{throw b("tengu_native_install_package_failure",{stage_binary_exists:!0,error_binary_not_found:!0}),new Error("Native binary not found in staged package")}await atomicMoveToInstallPath(r,t),await d(e,{recursive:!0,force:!0}),b("tengu_native_install_package_success",{})}catch(e){const t=D(e);throw t.includes("Could not find platform-specific")||t.includes("Native binary not found")||b("tengu_native_install_package_failure",{stage_atomic_move:!0,error_move_failed:!0}),M(j(e)),e}}(e,t):await async function(e,t){try{const a=getBinaryName(getPlatform()),i=$(e,a);try{await p(i)}catch{throw b("tengu_native_install_binary_failure",{stage_binary_exists:!0,error_binary_not_found:!0}),new Error("Staged binary not found")}await atomicMoveToInstallPath(i,t),await d(e,{recursive:!0,force:!0}),b("tengu_native_install_binary_success",{})}catch(e){throw D(e).includes("Staged binary not found")||b("tengu_native_install_binary_failure",{stage_atomic_move:!0,error_move_failed:!0}),M(j(e)),e}}(e,t)}async function performVersionUpdate(e,t){const{stagingPath:a,installPath:i}=await getVersionPaths(e),{executable:o}=getBaseDirectories(),s=T(process.env.ENABLE_LOCKLESS_UPDATES)?`${a}.${process.pid}.${Date.now()}`:a,l=!await versionIsAvailable(e)||t;if(l){L(t?`Force reinstalling native installer version ${e}`:`Downloading native installer version ${e}`);const a=await ee(e,s);await installVersion(s,i,a)}else L(`Version ${e} already installed, updating symlink`);if(await removeDirectoryIfEmpty(o),await async function(e,t){const a=getPlatform();if(a.startsWith("win32"))try{const a=v(e);let i;await n(a,{recursive:!0});try{i=await p(e)}catch{}if(i){try{const e=await p(t);if(i.size===e.size)return!1}catch{}const a=`${e}.old.${Date.now()}`;await u(e,a);try{await r(t,e);try{await _(a)}catch{}}catch(t){try{await u(a,e)}catch(e){const a=new Error(`Failed to restore old executable: ${e}`,{cause:t});throw M(a),a}throw t}}else try{await r(t,e)}catch(e){if(B(e))throw new Error(`Source file does not exist: ${t}`);throw e}return!0}catch(a){return M(new Error(`Failed to copy executable from ${t} to ${e}: ${a}`)),!1}const i=v(e);try{await n(i,{recursive:!0}),L(`Created directory ${i} for symlink`)}catch(e){return M(new Error(`Failed to create directory ${i}: ${e}`)),!1}try{let a=!1;try{await p(e),a=!0}catch{}if(a){try{const a=await c(e),i=k(v(e),a);if(i===k(t))return!1}catch{}await _(e)}}catch(e){M(new Error(`Failed to check/remove existing symlink: ${e}`))}const o=`${e}.tmp.${process.pid}.${Date.now()}`;try{return await f(t,o),await u(o,e),L(`Atomically updated symlink ${e} -> ${t}`),!0}catch(a){try{await _(o)}catch{}return M(new Error(`Failed to create symlink from ${e} to ${t}: ${a}`)),!1}}(o,i),!await isPossibleClaudeBinary(o)){let e=!1;try{await p(i),e=!0}catch{}throw new Error(`Failed to create executable at ${o}. Source file exists: ${e}. Check write permissions to ${o}.`)}return l}async function versionIsAvailable(e){const{installPath:t}=await getVersionPaths(e);return isPossibleClaudeBinary(t)}async function updateLatest(t,a=!1){const i=Date.now();let r=await te(t);const{executable:o}=getBaseDirectories();if(L(`Checking for native installer update to version ${r}`),!a){const t=await P();if(t&&U(r,t)){if(L(`Native installer: maxVersion ${t} is set, capping update from ${r} to ${t}`),W(e.VERSION,t))return L(`Native installer: current version ${e.VERSION} is already at or above maxVersion ${t}, skipping update`),b("tengu_native_update_skipped_max_version",{latency_ms:Date.now()-i,max_version:t,available_version:r}),{success:!0,latestVersion:r};r=t}}if(!a&&r===e.VERSION&&await versionIsAvailable(r)&&await isPossibleClaudeBinary(o))return L(`Found ${r} at ${o}, skipping install`),b("tengu_native_update_complete",{latency_ms:Date.now()-i,was_new_install:!1,was_force_reinstall:!1,was_already_running:!0}),{success:!0,latestVersion:r};if(!a&&x(r))return b("tengu_native_update_skipped_minimum_version",{latency_ms:Date.now()-i,target_version:r}),{success:!0,latestVersion:r};let n,s=!1;if(T(process.env.ENABLE_LOCKLESS_UPDATES))s=await performVersionUpdate(r,a),n=Date.now()-i;else{const{installPath:e}=await getVersionPaths(r);a&&await async function(e){const t=getLockFilePathFromVersionPath(getBaseDirectories(),e);try{await _(t),L(`Force-removed lock file at ${t}`)}catch(e){L(`Failed to force-remove lock file: ${D(e)}`)}}(e);const t=await tryWithVersionLock(e,async()=>{s=await performVersionUpdate(r,a)},3);if(n=Date.now()-i,!t){const t=getBaseDirectories();let a;if(oe()){const i=getLockFilePathFromVersionPath(t,e);re(i)&&(a=ne(i)?.pid)}return b("tengu_native_update_lock_failed",{latency_ms:n,lock_holder_pid:a}),{success:!1,latestVersion:r,lockFailed:!0,lockHolderPid:a}}}return b("tengu_native_update_complete",{latency_ms:n,was_new_install:s,was_force_reinstall:a}),L(`Successfully updated to version ${r}`),{success:!0,latestVersion:r}}export async function removeDirectoryIfEmpty(e){try{await m(e),L(`Removed empty directory at ${e}`)}catch(t){const a=S(t);"ENOTDIR"!==a&&"ENOENT"!==a&&"ENOTEMPTY"!==a&&L(`Could not remove directory at ${e}: ${t}`)}}export async function checkInstall(e=!1){if(T(process.env.DISABLE_INSTALLATION_CHECKS))return[];const t=await N();if("development"===t)return[];const i=A();if(!(e||"native"===t||"native"===i.installMethod))return[];const r=getBaseDirectories(),o=[],n=v(r.executable),s=k(n),l=getPlatform().startsWith("win32");try{await a(n)}catch{o.push({message:`installMethod is native, but directory ${n} does not exist`,userActionRequired:!0,type:"error"})}if(l)await isPossibleClaudeBinary(r.executable)||o.push({message:`installMethod is native, but claude command is missing or invalid at ${r.executable}`,userActionRequired:!0,type:"error"});else try{const e=await c(r.executable),t=k(v(r.executable),e);await isPossibleClaudeBinary(t)||o.push({message:`Claude symlink points to missing or invalid binary: ${e}`,userActionRequired:!0,type:"error"})}catch(e){B(e)?o.push({message:`installMethod is native, but claude command not found at ${r.executable}`,userActionRequired:!0,type:"error"}):await isPossibleClaudeBinary(r.executable)||o.push({message:`${r.executable} exists but is not a valid Claude binary`,userActionRequired:!0,type:"error"})}if(!(process.env.PATH||"").split(g).some(e=>{try{const t=k(e);return l?t.toLowerCase()===s.toLowerCase():t===s}catch{return!1}}))if(l){const e=n.replace(/\//g,"\\");o.push({message:`Native installation exists but ${e} is not in your PATH. Add it by opening: System Properties → Environment Variables → Edit User PATH → New → Add the path above. Then restart your terminal.`,userActionRequired:!0,type:"path"})}else{const e=O(),t=K()[e],a=t?t.replace(h(),"~"):"your shell config file";o.push({message:`Native installation exists but ~/.local/bin is not in your PATH. Run:\n\necho 'export PATH="$HOME/.local/bin:$PATH"' >> ${a} && source ${a}`,userActionRequired:!0,type:"path"})}return o}let le=null;export function installLatest(e,t=!1){if(t)return installLatestImpl(e,t);if(le)return L("installLatest: joining in-flight call"),le;const a=installLatestImpl(e,t);le=a;const clear=()=>{le=null};return a.then(clear,clear),a}async function installLatestImpl(e,t=!1){const a=await updateLatest(e,t);if(!a.success)return{latestVersion:null,wasUpdated:!1,lockFailed:a.lockFailed,lockHolderPid:a.lockHolderPid};return"native"!==A().installMethod&&(F(e=>({...e,installMethod:"native",autoUpdates:!1,autoUpdatesProtectedForNative:!0})),L('Native installer: Set installMethod to "native" and disabled legacy auto-updater for protection')),cleanupOldVersions(),{latestVersion:a.latestVersion,wasUpdated:a.success,lockFailed:!1}}function getLockFilePathFromVersionPath(e,t){const a=y(t);return $(e.locks,`${a}.lock`)}export async function lockCurrentVersion(){const e=getBaseDirectories();if(!process.execPath.includes(e.versions))return;const t=k(process.execPath);try{const a=getLockFilePathFromVersionPath(e,t);if(await n(e.locks,{recursive:!0}),oe()){if(!await ae(t,a))return b("tengu_version_lock_failed",{is_pid_based:!0,is_lifetime_lock:!0}),void logLockAcquisitionError(t,new Error("Lock already held by another process"));b("tengu_version_lock_acquired",{is_pid_based:!0,is_lifetime_lock:!0}),L(`Acquired PID lock on running version: ${t}`)}else{let e;try{e=await q.lock(t,{stale:ce,retries:0,lockfilePath:a,onCompromised:e=>{L(`NON-FATAL: Lock on running version was compromised: ${e.message}`,{level:"info"})}}),b("tengu_version_lock_acquired",{is_pid_based:!1,is_lifetime_lock:!0}),L(`Acquired mtime-based lock on running version: ${t}`),E(async()=>{try{await(e?.())}catch{}})}catch(e){return B(e)?void L(`Cannot lock current version - file does not exist: ${t}`,{level:"info"}):(b("tengu_version_lock_failed",{is_pid_based:!1,is_lifetime_lock:!0}),void logLockAcquisitionError(t,e))}}}catch(e){if(B(e))return void L(`Cannot lock current version - file does not exist: ${t}`,{level:"info"});L(`NON-FATAL: Failed to lock current version during execution ${D(e)}`,{level:"info"})}}function logLockAcquisitionError(e,t){M(new Error(`NON-FATAL: Lock acquisition failed for ${e} (expected in multi-process scenarios)`,{cause:t}))}export async function cleanupOldVersions(){await Promise.resolve();const e=getBaseDirectories(),t=Date.now()-36e5;if(getPlatform().startsWith("win32")){const t=v(e.executable);try{const e=await s(t);let a=0;for(const i of e)if(/^claude\.exe\.old\.\d+$/.test(i))try{await _($(t,i)),a++}catch{}a>0&&L(`Cleaned up ${a} old Windows executables on startup`)}catch(e){B(e)||L(`Failed to clean up old Windows executables: ${e}`)}}try{const a=await s(e.staging);let i=0;for(const r of a){const a=$(e.staging,r);try{(await p(a)).mtime.getTime()<t&&(await d(a,{recursive:!0,force:!0}),i++,L(`Cleaned up old staging directory: ${r}`))}catch{}}i>0&&(L(`Cleaned up ${i} orphaned staging directories`),b("tengu_native_staging_cleanup",{cleaned_count:i}))}catch(e){B(e)||L(`Failed to clean up staging directories: ${e}`)}if(oe()){const t=ie(e.locks);t>0&&(L(`Cleaned up ${t} stale version locks`),b("tengu_native_stale_locks_cleanup",{cleaned_count:t}))}let a;try{a=await s(e.versions)}catch(e){return void(B(e)||L(`Failed to readdir versions directory: ${e}`))}const i=[];let r=0;for(const o of a){const a=$(e.versions,o);if(/\.tmp\.\d+\.\d+$/.test(o))try{(await p(a)).mtime.getTime()<t&&(await _(a),r++,L(`Cleaned up orphaned temp install file: ${o}`))}catch{}else try{const e=await p(a);if(!e.isFile())continue;if("win32"!==process.platform&&e.size>0&&!(73&e.mode))continue;i.push({name:o,path:a,resolvedPath:k(a),mtime:e.mtime})}catch{}}if(r>0&&(L(`Cleaned up ${r} orphaned temp install files`),b("tengu_native_temp_files_cleanup",{cleaned_count:r})),0!==i.length)try{const t=process.execPath,a=new Set;t&&t.includes(e.versions)&&a.add(k(t));const r=await async function(e){try{const t=await c(e),a=k(v(e),t);if(await isPossibleClaudeBinary(a))return a}catch{}return null}(e.executable);r&&a.add(r);for(const t of i){if(a.has(t.resolvedPath))continue;const i=getLockFilePathFromVersionPath(e,t.resolvedPath);let r=!1;if(oe())r=re(i);else try{r=await q.check(t.resolvedPath,{stale:ce,lockfilePath:i})}catch{r=!1}r&&(a.add(t.resolvedPath),L(`Protecting locked version from cleanup: ${t.name}`))}const o=i.filter(e=>!a.has(e.resolvedPath)).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()).slice(2);if(0===o.length)return void b("tengu_native_version_cleanup",{total_count:i.length,deleted_count:0,protected_count:a.size,retained_count:2,lock_failed_count:0,error_count:0});let n=0,s=0,l=0;await Promise.all(o.map(async e=>{try{await tryWithVersionLock(e.path,async()=>{await _(e.path)})?n++:(s++,L(`Skipping deletion of ${e.name} - locked by another process`))}catch(t){l++,M(new Error(`Failed to delete version ${e.name}: ${t}`))}})),b("tengu_native_version_cleanup",{total_count:i.length,deleted_count:n,protected_count:a.size,retained_count:2,lock_failed_count:s,error_count:l})}catch(e){B(e)||M(new Error(`Version cleanup failed: ${e}`))}}export async function removeInstalledSymlink(){const e=getBaseDirectories();try{if(await async function(e){let t=e;return(await o(e)).isSymbolicLink()&&(t=await l(e)),t.endsWith(".js")||t.includes("node_modules")}(e.executable))return void L(`Skipping removal of ${e.executable} - appears to be npm-managed`);await _(e.executable),L(`Removed claude symlink at ${e.executable}`)}catch(e){if(B(e))return;M(new Error(`Failed to remove claude symlink: ${e}`))}}export async function cleanupShellAliases(){const e=[],t=K();for(const[a,i]of Object.entries(t))try{const t=await z(i);if(!t)continue;const{filtered:r,hadAlias:o}=H(t);o&&(await Y(i,r),e.push({message:`Removed claude alias from ${i}. Run: unalias claude`,userActionRequired:!0,type:"alias"}),L(`Cleaned up claude alias from ${a} config`))}catch(t){M(t),e.push({message:`Failed to clean up ${i}: ${t}`,userActionRequired:!1,type:"error"})}return e}async function attemptNpmUninstall(e){const{code:t,stderr:a}=await I("npm",["uninstall","-g",e],{cwd:process.cwd()});if(0===t)return L(`Removed global npm installation of ${e}`),{success:!0};if(a&&!a.includes("npm ERR! code E404")){if(a.includes("npm error code ENOTEMPTY")){L(`Failed to uninstall global npm package ${e}: ${a}`,{level:"error"}),L("Attempting manual removal due to ENOTEMPTY error");const t=await async function(e){try{const t=await I("npm",["config","get","prefix"]);if(0!==t.code||!t.stdout)return{success:!1,error:"Failed to get npm global prefix"};const a=t.stdout.trim();let i=!1;async function tryRemove(e,t){try{return await _(e),L(`Manually removed ${t}: ${e}`),!0}catch{return!1}}if(getPlatform().startsWith("win32")){const r=$(a,"claude.cmd"),o=$(a,"claude.ps1"),n=$(a,"claude");await tryRemove(r,"bin script")&&(i=!0),await tryRemove(o,"PowerShell script")&&(i=!0),await tryRemove(n,"bin executable")&&(i=!0)}else{const s=$(a,"bin","claude");await tryRemove(s,"bin symlink")&&(i=!0)}return i?(L(`Successfully removed ${e} manually`),{success:!0,warning:`${e} executables removed, but node_modules directory was left intact for safety. You may manually delete it later at: ${getPlatform().startsWith("win32")?$(a,"node_modules",e):$(a,"lib","node_modules",e)}`}):{success:!1}}catch(c){return L(`Manual removal failed: ${c}`,{level:"error"}),{success:!1,error:`Manual removal failed: ${c}`}}}(e);if(t.success)return{success:!0,warning:t.warning};if(t.error)return{success:!1,error:`Failed to remove global npm installation of ${e}: ${a}. Manual removal also failed: ${t.error}`}}return L(`Failed to uninstall global npm package ${e}: ${a}`,{level:"error"}),{success:!1,error:`Failed to remove global npm installation of ${e}: ${a}`}}return{success:!1}}export async function cleanupNpmInstallations(){const t=[],a=[];let i=0;const r=await attemptNpmUninstall("@anthropic-ai/claude-code");if(r.success?(i++,r.warning&&a.push(r.warning)):r.error&&t.push(r.error),e.PACKAGE_URL&&"@anthropic-ai/claude-code"!==e.PACKAGE_URL){const r=await attemptNpmUninstall(e.PACKAGE_URL);r.success?(i++,r.warning&&a.push(r.warning)):r.error&&t.push(r.error)}const o=$(h(),R,"local");try{await d(o,{recursive:!0}),i++,L(`Removed local installation at ${o}`)}catch(e){B(e)||(t.push(`Failed to remove ${o}: ${e}`),L(`Failed to remove local installation: ${e}`,{level:"error"}))}return{removed:i,errors:t,warnings:a}}
@@ -1 +1 @@
1
- import{MACRO as e,feature as t}from"../../recovery/bunBundleShim.js";import{randomBytes as o}from"crypto";import r from"ignore";import s from"lodash-es/memoize.js";import{homedir as n,tmpdir as i}from"os";import{join as a,normalize as l,posix as c,sep as u}from"path";import{hasAutoMemPathOverride as p,isAutoMemPath as d}from"../../memdir/paths.js";import{isAgentMemoryPath as h}from"../../tools/AgentTool/agentMemory.js";import{CLAUDE_FOLDER_PERMISSION_PATTERN as f,FILE_EDIT_TOOL_NAME as m,GLOBAL_CLAUDE_FOLDER_PERMISSION_PATTERN as g}from"../../tools/FileEditTool/constants.js";import{getOriginalCwd as w,getSessionId as P}from"../../bootstrap/state.js";import{checkStatsigFeatureGate_CACHED_MAY_BE_STALE as v}from"../../services/analytics/growthbook.js";import{FILE_READ_TOOL_NAME as y}from"../../tools/FileReadTool/prompt.js";import{getCwd as C}from"../cwd.js";import{getClaudeConfigHomeDir as R}from"../envUtils.js";import{getFsImplementation as b,getPathsForPermissionCheck as k}from"../fsOperations.js";import{containsPathTraversal as S,expandPath as W,getDirectoryForPath as x,sanitizePath as I}from"../path.js";import{getPlanSlug as D,getPlansDirectory as F}from"../plans.js";import{getPlatform as j}from"../platform.js";import{getProjectDir as $}from"../sessionStorage.js";import{SETTING_SOURCES as E}from"../settings/constants.js";import{getSettingsFilePathForSource as T,getSettingsRootPathForSource as A}from"../settings/settings.js";import{containsVulnerableUncPath as z}from"../shell/readOnlyCommandValidation.js";import{getToolResultsDir as O}from"../toolResultStorage.js";import{windowsPathToPosixPath as N}from"../windowsPaths.js";import{createReadRuleSuggestion as M}from"./PermissionUpdate.js";import{getRuleByContentsForToolName as _}from"./permissions.js";export const DANGEROUS_FILES=[".gitconfig",".gitmodules",".bashrc",".bash_profile",".zshrc",".zprofile",".profile",".ripgreprc",".mcp.json",".claude.json",".context.json"];export const DANGEROUS_DIRECTORIES=[".git",".vscode",".idea",".claude"];export function normalizeCaseForComparison(e){return e.toLowerCase()}export function getClaudeSkillScope(e){const t=W(e),o=normalizeCaseForComparison(t),r=[{dir:W(a(w(),".contextcli","skills")),prefix:"/.contextcli/skills/"},{dir:W(a(R(),"skills")),prefix:"~/.contextcli/skills/"},{dir:W(a(w(),".contextcli","skills")),prefix:"/.contextcli/skills/"},{dir:W(a(n(),".contextcli","skills")),prefix:"~/.contextcli/skills/"}];for(const{dir:e,prefix:s}of r){const r=normalizeCaseForComparison(e);for(const n of[u,"/"])if(o.startsWith(r+n.toLowerCase())){const o=t.slice(e.length+n.length),r=o.indexOf("/"),i="\\"===u?o.indexOf("\\"):-1,a=-1===r?i:-1===i?r:Math.min(r,i);if(a<=0)return null;const l=o.slice(0,a);return!l||"."===l||l.includes("..")||/[*?[\]]/.test(l)?null:{skillName:l,pattern:s+l+"/**"}}}return null}const U=c.sep;export function relativePath(e,t){if("windows"===j()){const o=N(e),r=N(t);return c.relative(o,r)}return c.relative(e,t)}export function toPosixPath(e){return"windows"===j()?N(e):e}export function isClaudeSettingsPath(e){const t=normalizeCaseForComparison(W(e));return!(!t.endsWith(`${u}.contextcli${u}settings.json`)&&!t.endsWith(`${u}.contextcli${u}settings.local.json`))||E.map(e=>T(e)).filter(e=>void 0!==e).some(e=>normalizeCaseForComparison(e)===t)}function isClaudeConfigFilePath(e){if(isClaudeSettingsPath(e))return!0;const t=a(w(),".claude","commands"),o=a(w(),".claude","agents"),r=a(w(),".claude","skills"),s=a(w(),".contextcli","commands"),n=a(w(),".contextcli","agents"),i=a(w(),".contextcli","skills");return pathInWorkingPath(e,t)||pathInWorkingPath(e,o)||pathInWorkingPath(e,r)||pathInWorkingPath(e,s)||pathInWorkingPath(e,n)||pathInWorkingPath(e,i)}function isSessionPlanFile(e){const t=a(F(),D()),o=l(e);return o.startsWith(t)&&o.endsWith(".md")}export function getSessionMemoryDir(){return a($(C()),P(),"session-memory")+u}export function getSessionMemoryPath(){return a(getSessionMemoryDir(),"summary.md")}export function isScratchpadEnabled(){return v("tengu_scratch")}export function getClaudeTempDirName(){if("windows"===j())return"claude";return`claude-${process.getuid?.()??0}`}export const getClaudeTempDir=s(function(){const e=process.env.CONTEXT_CODE_TMPDIR||process.env.CLAUDE_CODE_TMPDIR||("windows"===j()?i():"/tmp"),t=b();let o=e;try{o=t.realpathSync(e)}catch{}return a(o,getClaudeTempDirName())+u});export const getBundledSkillsRoot=s(function(){const t=o(16).toString("hex");return a(getClaudeTempDir(),"bundled-skills",e.VERSION,t)});export function getProjectTempDir(){return a(getClaudeTempDir(),I(w()))+u}export function getScratchpadDir(){return a(getProjectTempDir(),P(),"scratchpad")}export async function ensureScratchpadDir(){if(!isScratchpadEnabled())throw new Error("Scratchpad directory feature is not enabled");const e=b(),t=getScratchpadDir();return await e.mkdir(t,{mode:448}),t}function isScratchpadPath(e){if(!isScratchpadEnabled())return!1;const t=getScratchpadDir(),o=l(e);return o===t||o.startsWith(t+u)}function isDangerousFilePathToAutoEdit(e){const t=W(e).split(u),o=t.at(-1);if(e.startsWith("\\\\")||e.startsWith("//"))return!0;for(let e=0;e<t.length;e++){const o=normalizeCaseForComparison(t[e]);for(const r of DANGEROUS_DIRECTORIES)if(o===normalizeCaseForComparison(r)){if(".claude"===r){const o=t[e+1];if(o&&"worktrees"===normalizeCaseForComparison(o))break}return!0}}if(o){const e=normalizeCaseForComparison(o);if(DANGEROUS_FILES.some(t=>normalizeCaseForComparison(t)===e))return!0}return!1}function hasSuspiciousWindowsPathPattern(e){if("windows"===j()||"wsl"===j()){if(-1!==e.indexOf(":",2))return!0}return!!/~\d/.test(e)||(!!(e.startsWith("\\\\?\\")||e.startsWith("\\\\.\\")||e.startsWith("//?/")||e.startsWith("//./"))||(!!/[.\s]+$/.test(e)||(!!/\.(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i.test(e)||(!!/(^|\/|\\)\.{3,}(\/|\\|$)/.test(e)||!!z(e)))))}export function checkPathSafetyForAutoEdit(e,t){const o=t??k(e);for(const t of o)if(hasSuspiciousWindowsPathPattern(t))return{safe:!1,message:`Claude requested permissions to write to ${e}, which contains a suspicious Windows path pattern that requires manual approval.`,classifierApprovable:!1};for(const t of o)if(isClaudeConfigFilePath(t))return{safe:!1,message:`Context solicitó permisos para escribir en ${e}, pero aún no se los has otorgado.`,classifierApprovable:!0};for(const t of o)if(isDangerousFilePathToAutoEdit(t))return{safe:!1,message:`Claude requested permissions to edit ${e} which is a sensitive file.`,classifierApprovable:!0};return{safe:!0}}export function allWorkingDirectories(e){return new Set([w(),...e.additionalWorkingDirectories.keys()])}export const getResolvedWorkingDirPaths=s(k);export function pathInAllowedWorkingPath(e,t,o){const r=o??k(e),s=Array.from(allWorkingDirectories(t)).flatMap(e=>getResolvedWorkingDirPaths(e));return r.every(e=>s.some(t=>pathInWorkingPath(e,t)))}export function pathInWorkingPath(e,t){const o=W(e),r=W(t),s=o.replace(/^\/private\/var\//,"/var/").replace(/^\/private\/tmp(\/|$)/,"/tmp$1"),n=r.replace(/^\/private\/var\//,"/var/").replace(/^\/private\/tmp(\/|$)/,"/tmp$1"),i=normalizeCaseForComparison(s),a=relativePath(normalizeCaseForComparison(n),i);return""===a||!S(a)&&!c.isAbsolute(a)}function rootPathForSource(e){switch(e){case"cliArg":case"command":case"session":return W(w());case"userSettings":case"policySettings":case"projectSettings":case"localSettings":case"flagSettings":return A(e)}}function prependDirSep(e){return c.join(U,e)}function normalizePatternToPath({patternRoot:e,pattern:t,rootPath:o}){const r=c.join(e,t);if(e===o)return prependDirSep(t);if(r.startsWith(`${o}${U}`)){return prependDirSep(r.slice(o.length))}{const r=c.relative(o,e);if(!r||r.startsWith(`..${U}`)||".."===r)return null;return prependDirSep(c.join(r,t))}}export function normalizePatternsToPath(e,t){const o=new Set(e.get(null)??[]);for(const[r,s]of e.entries())if(null!==r)for(const e of s){const s=normalizePatternToPath({patternRoot:r,pattern:e,rootPath:t});s&&o.add(s)}return Array.from(o)}export function getFileReadIgnorePatterns(e){const t=getPatternsByRoot(e,"read","deny"),o=new Map;for(const[e,r]of t.entries())o.set(e,Array.from(r.keys()));return o}function patternWithRoot(e,t){if(e.startsWith(`${U}${U}`)){const t=e.slice(1);if("windows"===j()&&t.match(/^\/[a-z]\//i)){const e=t[1]?.toUpperCase()??"C",o=t.slice(2),r=`${e}:\\`;return{relativePattern:o.startsWith("/")?o.slice(1):o,root:r}}return{relativePattern:t,root:U}}if(e.startsWith(`~${U}`))return{relativePattern:e.slice(1),root:n().normalize("NFC")};if(e.startsWith(U))return{relativePattern:e,root:rootPathForSource(t)};let o=e;return e.startsWith(`.${U}`)&&(o=e.slice(2)),{relativePattern:o,root:null}}function getPatternsByRoot(e,t,o){const r=(()=>{switch(t){case"edit":return m;case"read":return y}})(),s=_(e,r,o),n=new Map;for(const[e,t]of s.entries()){const{relativePattern:o,root:r}=patternWithRoot(e,t.source);let s=n.get(r);void 0===s&&(s=new Map,n.set(r,s)),s.set(o,t)}return n}export function matchingRuleForInput(e,t,o,s){let n=W(e);"windows"===j()&&n.includes("\\")&&(n=N(n));const i=getPatternsByRoot(t,o,s);for(const[e,t]of i.entries()){const o=Array.from(t.keys()).map(e=>{let t=e;return t.endsWith("/**")&&(t=t.slice(0,-3)),t}),s=r().add(o),i=relativePath(e??C(),n??C());if(i.startsWith(`..${U}`))continue;if(!i)continue;const a=s.test(i);if(a.ignored&&a.rule){const e=a.rule.pattern,o=e+"/**";return t.has(o)?t.get(o)??null:t.get(e)??null}}return null}export function checkReadPermissionForTool(e,t,o){if("function"!=typeof e.getPath)return{behavior:"ask",message:`Claude requested permissions to use ${e.name}, but you haven't granted it yet.`};const r=e.getPath(t),s=k(r);for(const e of s)if(e.startsWith("\\\\")||e.startsWith("//"))return{behavior:"ask",message:`Claude requested permissions to read from ${r}, which appears to be a UNC path that could access network resources.`,decisionReason:{type:"other",reason:"UNC path detected (defense-in-depth check)"}};for(const e of s)if(hasSuspiciousWindowsPathPattern(e))return{behavior:"ask",message:`Claude requested permissions to read from ${r}, which contains a suspicious Windows path pattern that requires manual approval.`,decisionReason:{type:"other",reason:"Path contains suspicious Windows-specific patterns (alternate data streams, short names, long path prefixes, or three or more consecutive dots) that require manual verification"}};for(const e of s){const t=matchingRuleForInput(e,o,"read","deny");if(t)return{behavior:"deny",message:`Permission to read ${r} has been denied.`,decisionReason:{type:"rule",rule:t}}}for(const e of s){const t=matchingRuleForInput(e,o,"read","ask");if(t)return{behavior:"ask",message:`Context solicitó permisos para leer desde ${r}, pero aún no se los has otorgado.`,decisionReason:{type:"rule",rule:t}}}const n=checkWritePermissionForTool(e,t,o,s);if("allow"===n.behavior)return n;if(pathInAllowedWorkingPath(r,o,s))return{behavior:"allow",updatedInput:t,decisionReason:{type:"mode",mode:"default"}};const i=checkReadableInternalPath(W(r),t);if("passthrough"!==i.behavior)return i;const a=matchingRuleForInput(r,o,"read","allow");return a?{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:a}}:{behavior:"ask",message:`Context solicitó permisos para leer desde ${r}, pero aún no se los has otorgado.`,suggestions:generateSuggestions(r,"read",o,s),decisionReason:{type:"workingDir",reason:"Path is outside allowed working directories"}}}export function checkWritePermissionForTool(e,t,o,r){if("function"!=typeof e.getPath)return{behavior:"ask",message:`Claude requested permissions to use ${e.name}, but you haven't granted it yet.`};const s=e.getPath(t),n=r??k(s);for(const e of n){const t=matchingRuleForInput(e,o,"edit","deny");if(t)return{behavior:"deny",message:`Permission to edit ${s} has been denied.`,decisionReason:{type:"rule",rule:t}}}const i=checkEditableInternalPath(W(s),t);if("passthrough"!==i.behavior)return i;const a=matchingRuleForInput(s,{...o,alwaysAllowRules:{session:o.alwaysAllowRules.session??[]}},"edit","allow");if(a){const e=a.ruleValue.ruleContent;if(e&&(e.startsWith(f.slice(0,-2))||e.startsWith(g.slice(0,-2)))&&!e.includes("..")&&e.endsWith("/**"))return{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:a}}}const l=checkPathSafetyForAutoEdit(s,n);if(!l.safe){const e=getClaudeSkillScope(s),t=e?[{type:"addRules",rules:[{toolName:m,ruleContent:e.pattern}],behavior:"allow",destination:"session"}]:generateSuggestions(s,"write",o,n);return{behavior:"ask",message:l.message,suggestions:t,decisionReason:{type:"safetyCheck",reason:l.message,classifierApprovable:l.classifierApprovable}}}for(const e of n){const t=matchingRuleForInput(e,o,"edit","ask");if(t)return{behavior:"ask",message:`Context solicitó permisos para escribir en ${s}, pero aún no se los has otorgado.`,decisionReason:{type:"rule",rule:t}}}const c=pathInAllowedWorkingPath(s,o,n);if("acceptEdits"===o.mode&&c)return{behavior:"allow",updatedInput:t,decisionReason:{type:"mode",mode:o.mode}};const u=matchingRuleForInput(s,o,"edit","allow");return u?{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:u}}:{behavior:"ask",message:`Context solicitó permisos para escribir en ${s}, pero aún no se los has otorgado.`,suggestions:generateSuggestions(s,"write",o,n),decisionReason:c?void 0:{type:"workingDir",reason:"Path is outside allowed working directories"}}}export function generateSuggestions(e,t,o,r){const s=!pathInAllowedWorkingPath(e,o,r);if("read"===t&&s){const t=x(e);return k(t).map(e=>M(e,"session")).filter(e=>void 0!==e)}const n="default"===o.mode||"plan"===o.mode;if("write"===t||"create"===t){const t=n?[{type:"setMode",mode:"acceptEdits",destination:"session"}]:[];if(s){const o=x(e),r=k(o);t.push({type:"addDirectories",directories:r,destination:"session"})}return t}return n?[{type:"setMode",mode:"acceptEdits",destination:"session"}]:[]}export function checkEditableInternalPath(e,o){const r=l(e);if(isSessionPlanFile(r))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Plan files for current session are allowed for writing"}};if(isScratchpadPath(r))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Scratchpad files for current session are allowed for writing"}};if(t("TEMPLATES")){const t=process.env.CONTEXT_JOB_DIR??process.env.CLAUDE_JOB_DIR;if(t){const r=a(R(),"jobs"),s=k(t).map(l),n=k(r).map(l);if(s.every(e=>n.some(t=>e.startsWith(t+u)))){if(k(e).every(e=>{const t=l(e);return s.some(e=>t===e||t.startsWith(e+u))}))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Job directory files for current job are allowed for writing"}}}}}return h(r)?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Agent memory files are allowed for writing"}}:!p()&&d(r)?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"auto memory files are allowed for writing"}}:normalizeCaseForComparison(r)===normalizeCaseForComparison(a(w(),".claude","launch.json"))?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Preview launch config is allowed for writing"}}:{behavior:"passthrough",message:""}}export function checkReadableInternalPath(e,t){const o=l(e);if(function(e){return l(e).startsWith(getSessionMemoryDir())}(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Session memory files are allowed for reading"}};if(function(e){const t=$(C()),o=l(e);return o===t||o.startsWith(t+u)}(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Project directory files are allowed for reading"}};if(isSessionPlanFile(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Plan files for current session are allowed for reading"}};const r=O(),s=r.endsWith(u)?r:r+u;if(o===r||o.startsWith(s))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Tool result files are allowed for reading"}};if(isScratchpadPath(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Scratchpad files for current session are allowed for reading"}};const n=getProjectTempDir();if(o.startsWith(n))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Project temp directory files are allowed for reading"}};if(h(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Agent memory files are allowed for reading"}};if(d(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"auto memory files are allowed for reading"}};const i=a(R(),"tasks")+u;if(o===i.slice(0,-1)||o.startsWith(i))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Task files are allowed for reading"}};const c=a(R(),"teams")+u;if(o===c.slice(0,-1)||o.startsWith(c))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Team files are allowed for reading"}};const p=getBundledSkillsRoot()+u;return o.startsWith(p)?{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Bundled skill reference files are allowed for reading"}}:{behavior:"passthrough",message:""}}
1
+ import{MACRO as e,feature as t}from"../../recovery/bunBundleShim.js";import{randomBytes as o}from"crypto";import r from"ignore";import s from"lodash-es/memoize.js";import{homedir as n,tmpdir as i}from"os";import{join as a,normalize as l,posix as c,sep as u}from"path";import{hasAutoMemPathOverride as p,isAutoMemPath as d}from"../../memdir/paths.js";import{isAgentMemoryPath as h}from"../../tools/AgentTool/agentMemory.js";import{CLAUDE_FOLDER_PERMISSION_PATTERN as f,FILE_EDIT_TOOL_NAME as m,GLOBAL_CLAUDE_FOLDER_PERMISSION_PATTERN as g}from"../../tools/FileEditTool/constants.js";import{getOriginalCwd as w,getSessionId as P}from"../../bootstrap/state.js";import{checkStatsigFeatureGate_CACHED_MAY_BE_STALE as v}from"../../services/analytics/growthbook.js";import{FILE_READ_TOOL_NAME as y}from"../../tools/FileReadTool/prompt.js";import{getCwd as C}from"../cwd.js";import{getClaudeConfigHomeDir as R}from"../envUtils.js";import{getFsImplementation as b,getPathsForPermissionCheck as k}from"../fsOperations.js";import{containsPathTraversal as S,expandPath as W,getDirectoryForPath as I,sanitizePath as x}from"../path.js";import{getPlanSlug as D,getPlansDirectory as F}from"../plans.js";import{getPlatform as j}from"../platform.js";import{getProjectDir as $}from"../sessionStorage.js";import{CONTEXT_DIR_NAME as E}from"../configConstants.js";import{SETTING_SOURCES as T}from"../settings/constants.js";import{getSettingsFilePathForSource as A,getSettingsRootPathForSource as z}from"../settings/settings.js";import{containsVulnerableUncPath as O}from"../shell/readOnlyCommandValidation.js";import{getToolResultsDir as N}from"../toolResultStorage.js";import{windowsPathToPosixPath as M}from"../windowsPaths.js";import{createReadRuleSuggestion as _}from"./PermissionUpdate.js";import{getRuleByContentsForToolName as U}from"./permissions.js";export const DANGEROUS_FILES=[".gitconfig",".gitmodules",".bashrc",".bash_profile",".zshrc",".zprofile",".profile",".ripgreprc",".mcp.json",".claude.json",".context.json"];export const DANGEROUS_DIRECTORIES=[".git",".vscode",".idea",".claude"];export function normalizeCaseForComparison(e){return e.toLowerCase()}export function getClaudeSkillScope(e){const t=W(e),o=normalizeCaseForComparison(t),r=[{dir:W(a(w(),E,"skills")),prefix:`/${E}/skills/`},{dir:W(a(R(),"skills")),prefix:`~/${E}/skills/`},{dir:W(a(w(),E,"skills")),prefix:`/${E}/skills/`},{dir:W(a(n(),E,"skills")),prefix:`~/${E}/skills/`}];for(const{dir:e,prefix:s}of r){const r=normalizeCaseForComparison(e);for(const n of[u,"/"])if(o.startsWith(r+n.toLowerCase())){const o=t.slice(e.length+n.length),r=o.indexOf("/"),i="\\"===u?o.indexOf("\\"):-1,a=-1===r?i:-1===i?r:Math.min(r,i);if(a<=0)return null;const l=o.slice(0,a);return!l||"."===l||l.includes("..")||/[*?[\]]/.test(l)?null:{skillName:l,pattern:s+l+"/**"}}}return null}const q=c.sep;export function relativePath(e,t){if("windows"===j()){const o=M(e),r=M(t);return c.relative(o,r)}return c.relative(e,t)}export function toPosixPath(e){return"windows"===j()?M(e):e}export function isClaudeSettingsPath(e){const t=normalizeCaseForComparison(W(e));return!(!t.endsWith(`${u}${E}${u}settings.json`)&&!t.endsWith(`${u}${E}${u}settings.local.json`))||T.map(e=>A(e)).filter(e=>void 0!==e).some(e=>normalizeCaseForComparison(e)===t)}function isClaudeConfigFilePath(e){if(isClaudeSettingsPath(e))return!0;const t=a(w(),".claude","commands"),o=a(w(),".claude","agents"),r=a(w(),".claude","skills"),s=a(w(),E,"commands"),n=a(w(),E,"agents"),i=a(w(),E,"skills");return pathInWorkingPath(e,t)||pathInWorkingPath(e,o)||pathInWorkingPath(e,r)||pathInWorkingPath(e,s)||pathInWorkingPath(e,n)||pathInWorkingPath(e,i)}function isSessionPlanFile(e){const t=a(F(),D()),o=l(e);return o.startsWith(t)&&o.endsWith(".md")}export function getSessionMemoryDir(){return a($(C()),P(),"session-memory")+u}export function getSessionMemoryPath(){return a(getSessionMemoryDir(),"summary.md")}export function isScratchpadEnabled(){return v("tengu_scratch")}export function getClaudeTempDirName(){if("windows"===j())return"claude";return`claude-${process.getuid?.()??0}`}export const getClaudeTempDir=s(function(){const e=process.env.CONTEXT_CODE_TMPDIR||process.env.CLAUDE_CODE_TMPDIR||("windows"===j()?i():"/tmp"),t=b();let o=e;try{o=t.realpathSync(e)}catch{}return a(o,getClaudeTempDirName())+u});export const getBundledSkillsRoot=s(function(){const t=o(16).toString("hex");return a(getClaudeTempDir(),"bundled-skills",e.VERSION,t)});export function getProjectTempDir(){return a(getClaudeTempDir(),x(w()))+u}export function getScratchpadDir(){return a(getProjectTempDir(),P(),"scratchpad")}export async function ensureScratchpadDir(){if(!isScratchpadEnabled())throw new Error("Scratchpad directory feature is not enabled");const e=b(),t=getScratchpadDir();return await e.mkdir(t,{mode:448}),t}function isScratchpadPath(e){if(!isScratchpadEnabled())return!1;const t=getScratchpadDir(),o=l(e);return o===t||o.startsWith(t+u)}function isDangerousFilePathToAutoEdit(e){const t=W(e).split(u),o=t.at(-1);if(e.startsWith("\\\\")||e.startsWith("//"))return!0;for(let e=0;e<t.length;e++){const o=normalizeCaseForComparison(t[e]);for(const r of DANGEROUS_DIRECTORIES)if(o===normalizeCaseForComparison(r)){if(".claude"===r){const o=t[e+1];if(o&&"worktrees"===normalizeCaseForComparison(o))break}return!0}}if(o){const e=normalizeCaseForComparison(o);if(DANGEROUS_FILES.some(t=>normalizeCaseForComparison(t)===e))return!0}return!1}function hasSuspiciousWindowsPathPattern(e){if("windows"===j()||"wsl"===j()){if(-1!==e.indexOf(":",2))return!0}return!!/~\d/.test(e)||(!!(e.startsWith("\\\\?\\")||e.startsWith("\\\\.\\")||e.startsWith("//?/")||e.startsWith("//./"))||(!!/[.\s]+$/.test(e)||(!!/\.(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i.test(e)||(!!/(^|\/|\\)\.{3,}(\/|\\|$)/.test(e)||!!O(e)))))}export function checkPathSafetyForAutoEdit(e,t){const o=t??k(e);for(const t of o)if(hasSuspiciousWindowsPathPattern(t))return{safe:!1,message:`Claude requested permissions to write to ${e}, which contains a suspicious Windows path pattern that requires manual approval.`,classifierApprovable:!1};for(const t of o)if(isClaudeConfigFilePath(t))return{safe:!1,message:`Context solicitó permisos para escribir en ${e}, pero aún no se los has otorgado.`,classifierApprovable:!0};for(const t of o)if(isDangerousFilePathToAutoEdit(t))return{safe:!1,message:`Claude requested permissions to edit ${e} which is a sensitive file.`,classifierApprovable:!0};return{safe:!0}}export function allWorkingDirectories(e){return new Set([w(),...e.additionalWorkingDirectories.keys()])}export const getResolvedWorkingDirPaths=s(k);export function pathInAllowedWorkingPath(e,t,o){const r=o??k(e),s=Array.from(allWorkingDirectories(t)).flatMap(e=>getResolvedWorkingDirPaths(e));return r.every(e=>s.some(t=>pathInWorkingPath(e,t)))}export function pathInWorkingPath(e,t){const o=W(e),r=W(t),s=o.replace(/^\/private\/var\//,"/var/").replace(/^\/private\/tmp(\/|$)/,"/tmp$1"),n=r.replace(/^\/private\/var\//,"/var/").replace(/^\/private\/tmp(\/|$)/,"/tmp$1"),i=normalizeCaseForComparison(s),a=relativePath(normalizeCaseForComparison(n),i);return""===a||!S(a)&&!c.isAbsolute(a)}function rootPathForSource(e){switch(e){case"cliArg":case"command":case"session":return W(w());case"userSettings":case"policySettings":case"projectSettings":case"localSettings":case"flagSettings":return z(e)}}function prependDirSep(e){return c.join(q,e)}function normalizePatternToPath({patternRoot:e,pattern:t,rootPath:o}){const r=c.join(e,t);if(e===o)return prependDirSep(t);if(r.startsWith(`${o}${q}`)){return prependDirSep(r.slice(o.length))}{const r=c.relative(o,e);if(!r||r.startsWith(`..${q}`)||".."===r)return null;return prependDirSep(c.join(r,t))}}export function normalizePatternsToPath(e,t){const o=new Set(e.get(null)??[]);for(const[r,s]of e.entries())if(null!==r)for(const e of s){const s=normalizePatternToPath({patternRoot:r,pattern:e,rootPath:t});s&&o.add(s)}return Array.from(o)}export function getFileReadIgnorePatterns(e){const t=getPatternsByRoot(e,"read","deny"),o=new Map;for(const[e,r]of t.entries())o.set(e,Array.from(r.keys()));return o}function patternWithRoot(e,t){if(e.startsWith(`${q}${q}`)){const t=e.slice(1);if("windows"===j()&&t.match(/^\/[a-z]\//i)){const e=t[1]?.toUpperCase()??"C",o=t.slice(2),r=`${e}:\\`;return{relativePattern:o.startsWith("/")?o.slice(1):o,root:r}}return{relativePattern:t,root:q}}if(e.startsWith(`~${q}`))return{relativePattern:e.slice(1),root:n().normalize("NFC")};if(e.startsWith(q))return{relativePattern:e,root:rootPathForSource(t)};let o=e;return e.startsWith(`.${q}`)&&(o=e.slice(2)),{relativePattern:o,root:null}}function getPatternsByRoot(e,t,o){const r=(()=>{switch(t){case"edit":return m;case"read":return y}})(),s=U(e,r,o),n=new Map;for(const[e,t]of s.entries()){const{relativePattern:o,root:r}=patternWithRoot(e,t.source);let s=n.get(r);void 0===s&&(s=new Map,n.set(r,s)),s.set(o,t)}return n}export function matchingRuleForInput(e,t,o,s){let n=W(e);"windows"===j()&&n.includes("\\")&&(n=M(n));const i=getPatternsByRoot(t,o,s);for(const[e,t]of i.entries()){const o=Array.from(t.keys()).map(e=>{let t=e;return t.endsWith("/**")&&(t=t.slice(0,-3)),t}),s=r().add(o),i=relativePath(e??C(),n??C());if(i.startsWith(`..${q}`))continue;if(!i)continue;const a=s.test(i);if(a.ignored&&a.rule){const e=a.rule.pattern,o=e+"/**";return t.has(o)?t.get(o)??null:t.get(e)??null}}return null}export function checkReadPermissionForTool(e,t,o){if("function"!=typeof e.getPath)return{behavior:"ask",message:`Claude requested permissions to use ${e.name}, but you haven't granted it yet.`};const r=e.getPath(t),s=k(r);for(const e of s)if(e.startsWith("\\\\")||e.startsWith("//"))return{behavior:"ask",message:`Claude requested permissions to read from ${r}, which appears to be a UNC path that could access network resources.`,decisionReason:{type:"other",reason:"UNC path detected (defense-in-depth check)"}};for(const e of s)if(hasSuspiciousWindowsPathPattern(e))return{behavior:"ask",message:`Claude requested permissions to read from ${r}, which contains a suspicious Windows path pattern that requires manual approval.`,decisionReason:{type:"other",reason:"Path contains suspicious Windows-specific patterns (alternate data streams, short names, long path prefixes, or three or more consecutive dots) that require manual verification"}};for(const e of s){const t=matchingRuleForInput(e,o,"read","deny");if(t)return{behavior:"deny",message:`Permission to read ${r} has been denied.`,decisionReason:{type:"rule",rule:t}}}for(const e of s){const t=matchingRuleForInput(e,o,"read","ask");if(t)return{behavior:"ask",message:`Context solicitó permisos para leer desde ${r}, pero aún no se los has otorgado.`,decisionReason:{type:"rule",rule:t}}}const n=checkWritePermissionForTool(e,t,o,s);if("allow"===n.behavior)return n;if(pathInAllowedWorkingPath(r,o,s))return{behavior:"allow",updatedInput:t,decisionReason:{type:"mode",mode:"default"}};const i=checkReadableInternalPath(W(r),t);if("passthrough"!==i.behavior)return i;const a=matchingRuleForInput(r,o,"read","allow");return a?{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:a}}:{behavior:"ask",message:`Context solicitó permisos para leer desde ${r}, pero aún no se los has otorgado.`,suggestions:generateSuggestions(r,"read",o,s),decisionReason:{type:"workingDir",reason:"Path is outside allowed working directories"}}}export function checkWritePermissionForTool(e,t,o,r){if("function"!=typeof e.getPath)return{behavior:"ask",message:`Claude requested permissions to use ${e.name}, but you haven't granted it yet.`};const s=e.getPath(t),n=r??k(s);for(const e of n){const t=matchingRuleForInput(e,o,"edit","deny");if(t)return{behavior:"deny",message:`Permission to edit ${s} has been denied.`,decisionReason:{type:"rule",rule:t}}}const i=checkEditableInternalPath(W(s),t);if("passthrough"!==i.behavior)return i;const a=matchingRuleForInput(s,{...o,alwaysAllowRules:{session:o.alwaysAllowRules.session??[]}},"edit","allow");if(a){const e=a.ruleValue.ruleContent;if(e&&(e.startsWith(f.slice(0,-2))||e.startsWith(g.slice(0,-2)))&&!e.includes("..")&&e.endsWith("/**"))return{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:a}}}const l=checkPathSafetyForAutoEdit(s,n);if(!l.safe){const e=getClaudeSkillScope(s),t=e?[{type:"addRules",rules:[{toolName:m,ruleContent:e.pattern}],behavior:"allow",destination:"session"}]:generateSuggestions(s,"write",o,n);return{behavior:"ask",message:l.message,suggestions:t,decisionReason:{type:"safetyCheck",reason:l.message,classifierApprovable:l.classifierApprovable}}}for(const e of n){const t=matchingRuleForInput(e,o,"edit","ask");if(t)return{behavior:"ask",message:`Context solicitó permisos para escribir en ${s}, pero aún no se los has otorgado.`,decisionReason:{type:"rule",rule:t}}}const c=pathInAllowedWorkingPath(s,o,n);if("acceptEdits"===o.mode&&c)return{behavior:"allow",updatedInput:t,decisionReason:{type:"mode",mode:o.mode}};const u=matchingRuleForInput(s,o,"edit","allow");return u?{behavior:"allow",updatedInput:t,decisionReason:{type:"rule",rule:u}}:{behavior:"ask",message:`Context solicitó permisos para escribir en ${s}, pero aún no se los has otorgado.`,suggestions:generateSuggestions(s,"write",o,n),decisionReason:c?void 0:{type:"workingDir",reason:"Path is outside allowed working directories"}}}export function generateSuggestions(e,t,o,r){const s=!pathInAllowedWorkingPath(e,o,r);if("read"===t&&s){const t=I(e);return k(t).map(e=>_(e,"session")).filter(e=>void 0!==e)}const n="default"===o.mode||"plan"===o.mode;if("write"===t||"create"===t){const t=n?[{type:"setMode",mode:"acceptEdits",destination:"session"}]:[];if(s){const o=I(e),r=k(o);t.push({type:"addDirectories",directories:r,destination:"session"})}return t}return n?[{type:"setMode",mode:"acceptEdits",destination:"session"}]:[]}export function checkEditableInternalPath(e,o){const r=l(e);if(isSessionPlanFile(r))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Plan files for current session are allowed for writing"}};if(isScratchpadPath(r))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Scratchpad files for current session are allowed for writing"}};if(t("TEMPLATES")){const t=process.env.CONTEXT_JOB_DIR??process.env.CLAUDE_JOB_DIR;if(t){const r=a(R(),"jobs"),s=k(t).map(l),n=k(r).map(l);if(s.every(e=>n.some(t=>e.startsWith(t+u)))){if(k(e).every(e=>{const t=l(e);return s.some(e=>t===e||t.startsWith(e+u))}))return{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Job directory files for current job are allowed for writing"}}}}}return h(r)?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Agent memory files are allowed for writing"}}:!p()&&d(r)?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"auto memory files are allowed for writing"}}:normalizeCaseForComparison(r)===normalizeCaseForComparison(a(w(),".claude","launch.json"))?{behavior:"allow",updatedInput:o,decisionReason:{type:"other",reason:"Preview launch config is allowed for writing"}}:{behavior:"passthrough",message:""}}export function checkReadableInternalPath(e,t){const o=l(e);if(function(e){return l(e).startsWith(getSessionMemoryDir())}(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Session memory files are allowed for reading"}};if(function(e){const t=$(C()),o=l(e);return o===t||o.startsWith(t+u)}(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Project directory files are allowed for reading"}};if(isSessionPlanFile(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Plan files for current session are allowed for reading"}};const r=N(),s=r.endsWith(u)?r:r+u;if(o===r||o.startsWith(s))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Tool result files are allowed for reading"}};if(isScratchpadPath(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Scratchpad files for current session are allowed for reading"}};const n=getProjectTempDir();if(o.startsWith(n))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Project temp directory files are allowed for reading"}};if(h(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Agent memory files are allowed for reading"}};if(d(o))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"auto memory files are allowed for reading"}};const i=a(R(),"tasks")+u;if(o===i.slice(0,-1)||o.startsWith(i))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Task files are allowed for reading"}};const c=a(R(),"teams")+u;if(o===c.slice(0,-1)||o.startsWith(c))return{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Team files are allowed for reading"}};const p=getBundledSkillsRoot()+u;return o.startsWith(p)?{behavior:"allow",updatedInput:t,decisionReason:{type:"other",reason:"Bundled skill reference files are allowed for reading"}}:{behavior:"passthrough",message:""}}
@@ -1 +1 @@
1
- import{join as t}from"path";import{getAdditionalDirectoriesForClaudeMd as n}from"../../bootstrap/state.js";import{parseSettingsFile as o}from"../settings/settings.js";const s=["settings.json","settings.local.json"],e=[".contextcli",".claude"];function readAddDirSettings(n,s){for(const r of e){const{settings:e}=o(t(n,r,s));if(e)return e}return null}export function getAddDirEnabledPlugins(){const t={};for(const o of n())for(const n of s){const s=readAddDirSettings(o,n);s?.enabledPlugins&&Object.assign(t,s.enabledPlugins)}return t}export function getAddDirExtraMarketplaces(){const t={};for(const o of n())for(const n of s){const s=readAddDirSettings(o,n);s?.extraKnownMarketplaces&&Object.assign(t,s.extraKnownMarketplaces)}return t}
1
+ import{join as t}from"path";import{getAdditionalDirectoriesForClaudeMd as n}from"../../bootstrap/state.js";import{parseSettingsFile as o}from"../settings/settings.js";import{CONTEXT_DIR_NAME as s}from"../configConstants.js";const r=["settings.json","settings.local.json"],e=[s,".claude"];function readAddDirSettings(n,s){for(const r of e){const{settings:e}=o(t(n,r,s));if(e)return e}return null}export function getAddDirEnabledPlugins(){const t={};for(const o of n())for(const n of r){const s=readAddDirSettings(o,n);s?.enabledPlugins&&Object.assign(t,s.enabledPlugins)}return t}export function getAddDirExtraMarketplaces(){const t={};for(const o of n())for(const n of r){const s=readAddDirSettings(o,n);s?.extraKnownMarketplaces&&Object.assign(t,s.extraKnownMarketplaces)}return t}
@@ -1 +1 @@
1
- import{SandboxManager as o,SandboxRuntimeConfigSchema as n,SandboxViolationStore as t}from"@anthropic-ai/sandbox-runtime";import{rmSync as e,statSync as s}from"fs";import{readFile as a}from"fs/promises";import{memoize as r}from"lodash-es";import{join as i,resolve as l,sep as d}from"path";import{getAdditionalDirectoriesForClaudeMd as c,getCwdState as u,getOriginalCwd as b}from"../../bootstrap/state.js";import{logForDebugging as f}from"../debug.js";import{expandPath as m}from"../path.js";import{getPlatform as g}from"../platform.js";import{settingsChangeDetector as x}from"../settings/changeDetector.js";import{SETTING_SOURCES as p}from"../settings/constants.js";import{getManagedSettingsDropInDir as S}from"../settings/managedPath.js";import{getInitialSettings as h,getSettings_DEPRECATED as w,getSettingsFilePathForSource as y,getSettingsForSource as P,getSettingsRootPathForSource as k,updateSettingsForSource as C}from"../settings/settings.js";import{BASH_TOOL_NAME as v}from"../../tools/BashTool/toolName.js";import{FILE_EDIT_TOOL_NAME as F}from"../../tools/FileEditTool/constants.js";import{FILE_READ_TOOL_NAME as W}from"../../tools/FileReadTool/prompt.js";import{WEB_FETCH_TOOL_NAME as R}from"../../tools/WebFetchTool/prompt.js";import{errorMessage as E}from"../errors.js";import{getClaudeTempDir as A}from"../permissions/filesystem.js";import{ripgrepCommand as N}from"../ripgrep.js";function permissionRuleValueFromString(o){const n=o.match(/^([^(]+)\(([^)]+)\)$/);if(!n)return{toolName:o};const t=n[1],e=n[2];return t&&e?{toolName:t,ruleContent:e}:{toolName:o}}export function resolvePathPatternForSandbox(o,n){if(o.startsWith("//"))return o.slice(1);if(o.startsWith("/")&&!o.startsWith("//")){const t=k(n);return l(t,o.slice(1))}return o}export function resolveSandboxFilesystemPath(o,n){return o.startsWith("//")?o.slice(1):m(o,k(n))}export function shouldAllowManagedSandboxDomainsOnly(){return!0===P("policySettings")?.sandbox?.network?.allowManagedDomainsOnly}function shouldAllowManagedReadPathsOnly(){return!0===P("policySettings")?.sandbox?.filesystem?.allowManagedReadPathsOnly}export function convertToSandboxRuntimeConfig(o){const n=o.permissions||{},t=[],e=[];if(shouldAllowManagedSandboxDomainsOnly()){const o=P("policySettings");for(const n of o?.sandbox?.network?.allowedDomains||[])t.push(n);for(const n of o?.permissions?.allow||[]){const o=permissionRuleValueFromString(n);o.toolName===R&&o.ruleContent?.startsWith("domain:")&&t.push(o.ruleContent.substring(7))}}else{for(const n of o.sandbox?.network?.allowedDomains||[])t.push(n);for(const o of n.allow||[]){const n=permissionRuleValueFromString(o);n.toolName===R&&n.ruleContent?.startsWith("domain:")&&t.push(n.ruleContent.substring(7))}}for(const o of n.deny||[]){const n=permissionRuleValueFromString(o);n.toolName===R&&n.ruleContent?.startsWith("domain:")&&e.push(n.ruleContent.substring(7))}const a=[".",A()],r=[],i=[],d=[],f=p.map(o=>y(o)).filter(o=>void 0!==o);r.push(...f),r.push(S());const m=u(),g=b();m!==g&&(r.push(l(m,".claude","settings.json")),r.push(l(m,".claude","settings.local.json"))),r.push(l(g,".claude","skills")),r.push(l(g,".contextcli","skills")),m!==g&&(r.push(l(m,".claude","skills")),r.push(l(m,".contextcli","skills"))),$.length=0;const x=["HEAD","objects","refs","hooks","config"];for(const o of m===g?[g]:[g,m])for(const n of x){const t=l(o,n);try{s(t),r.push(t)}catch{$.push(t)}}j&&j!==m&&a.push(j);const h=new Set([...o.permissions?.additionalDirectories||[],...c()]);a.push(...h);for(const o of p){const n=P(o);if(n?.permissions){for(const t of n.permissions.allow||[]){const n=permissionRuleValueFromString(t);n.toolName===F&&n.ruleContent&&a.push(resolvePathPatternForSandbox(n.ruleContent,o))}for(const t of n.permissions.deny||[]){const n=permissionRuleValueFromString(t);n.toolName===F&&n.ruleContent&&r.push(resolvePathPatternForSandbox(n.ruleContent,o)),n.toolName===W&&n.ruleContent&&i.push(resolvePathPatternForSandbox(n.ruleContent,o))}}const t=n?.sandbox?.filesystem;if(t){for(const n of t.allowWrite||[])a.push(resolveSandboxFilesystemPath(n,o));for(const n of t.denyWrite||[])r.push(resolveSandboxFilesystemPath(n,o));for(const n of t.denyRead||[])i.push(resolveSandboxFilesystemPath(n,o));if(!shouldAllowManagedReadPathsOnly()||"policySettings"===o)for(const n of t.allowRead||[])d.push(resolveSandboxFilesystemPath(n,o))}}const{rgPath:w,rgArgs:k,argv0:C}=N(),v=o.sandbox?.ripgrep??{command:w,args:k,argv0:C};return{network:{allowedDomains:t,deniedDomains:e,allowUnixSockets:o.sandbox?.network?.allowUnixSockets,allowAllUnixSockets:o.sandbox?.network?.allowAllUnixSockets,allowLocalBinding:o.sandbox?.network?.allowLocalBinding,httpProxyPort:o.sandbox?.network?.httpProxyPort,socksProxyPort:o.sandbox?.network?.socksProxyPort},filesystem:{denyRead:i,allowRead:d,allowWrite:a,denyWrite:r},ignoreViolations:o.sandbox?.ignoreViolations,enableWeakerNestedSandbox:o.sandbox?.enableWeakerNestedSandbox,enableWeakerNetworkIsolation:o.sandbox?.enableWeakerNetworkIsolation,ripgrep:v}}let I,L,j;const $=[];const D=r(()=>{const{rgPath:n,rgArgs:t}=N();return o.checkDependencies({command:n,args:t})});function getSandboxEnabledSetting(){try{const o=w();return o?.sandbox?.enabled??!1}catch(o){return f(`Failed to get settings for sandbox check: ${o}`),!1}}const U=r(()=>o.isSupportedPlatform());function isPlatformInEnabledList(){try{const o=h(),n=o?.sandbox?.enabledPlatforms;if(void 0===n)return!0;if(0===n.length)return!1;const t=g();return n.includes(t)}catch(o){return f(`Failed to check enabledPlatforms: ${o}`),!0}}function isSandboxingEnabled(){return!!U()&&(!(D().errors.length>0)&&(!!isPlatformInEnabledList()&&getSandboxEnabledSetting()))}export function addToExcludedCommands(o,n){const t=P("localSettings"),e=t?.sandbox?.excludedCommands||[];let s=o;if(n){const o=n.filter(o=>"addRules"===o.type&&o.rules.some(o=>o.toolName===v));if(o.length>0&&"addRules"===o[0].type){const n=o[0].rules.find(o=>o.toolName===v);if(n?.ruleContent){s=function(o){const n=o.match(/^(.+):\*$/);return n?.[1]??null}(n.ruleContent)||n.ruleContent}}}return e.includes(s)||C("localSettings",{sandbox:{...t?.sandbox,excludedCommands:[...e,s]}}),s}export const SandboxManager={initialize:async function(n){if(I)return I;if(!isSandboxingEnabled())return;const t=n?async o=>shouldAllowManagedSandboxDomainsOnly()?(f(`[sandbox] Blocked network request to ${o.host} (allowManagedDomainsOnly)`),!1):n(o):void 0;return I=(async()=>{try{void 0===j&&(j=await async function(o){const n=i(o,".git");try{const t=(await a(n,{encoding:"utf8"})).match(/^gitdir:\s*(.+)$/m);if(!t?.[1])return null;const e=l(o,t[1].trim()),s=`${d}.git${d}worktrees${d}`,r=e.lastIndexOf(s);return r>0?e.substring(0,r):null}catch{return null}}(u()));const n=convertToSandboxRuntimeConfig(w());await o.initialize(n,t),L=x.subscribe(()=>{const n=convertToSandboxRuntimeConfig(w());o.updateConfig(n),f("Sandbox configuration updated from settings change")})}catch(o){I=void 0,f(`Failed to initialize sandbox: ${E(o)}`)}})(),I},isSandboxingEnabled,isSandboxEnabledInSettings:getSandboxEnabledSetting,isPlatformInEnabledList,getSandboxUnavailableReason:function(){if(!getSandboxEnabledSetting())return;if(!U()){const o=g();return"wsl"===o?"sandbox.enabled is set but WSL1 is not supported (requires WSL2)":`sandbox.enabled is set but ${o} is not supported (requires macOS, Linux, or WSL2)`}if(!isPlatformInEnabledList())return`sandbox.enabled is set but ${g()} is not in sandbox.enabledPlatforms`;const o=D();if(o.errors.length>0){const n="macos"===g()?"run /sandbox or /doctor for details":"install missing tools (e.g. apt install bubblewrap socat) or run /sandbox for details";return`sandbox.enabled is set but dependencies are missing: ${o.errors.join(", ")} · ${n}`}},isAutoAllowBashIfSandboxedEnabled:function(){const o=w();return o?.sandbox?.autoAllowBashIfSandboxed??!0},areUnsandboxedCommandsAllowed:function(){const o=w();return o?.sandbox?.allowUnsandboxedCommands??!0},isSandboxRequired:function(){const o=w();return getSandboxEnabledSetting()&&(o?.sandbox?.failIfUnavailable??!1)},areSandboxSettingsLockedByPolicy:function(){const o=["flagSettings","policySettings"];for(const n of o){const o=P(n);if(void 0!==o?.sandbox?.enabled||void 0!==o?.sandbox?.autoAllowBashIfSandboxed||void 0!==o?.sandbox?.allowUnsandboxedCommands)return!0}return!1},setSandboxSettings:async function(o){const n=P("localSettings");C("localSettings",{sandbox:{...n?.sandbox,...void 0!==o.enabled&&{enabled:o.enabled},...void 0!==o.autoAllowBashIfSandboxed&&{autoAllowBashIfSandboxed:o.autoAllowBashIfSandboxed},...void 0!==o.allowUnsandboxedCommands&&{allowUnsandboxedCommands:o.allowUnsandboxedCommands}}})},getExcludedCommands:function(){const o=w();return o?.sandbox?.excludedCommands??[]},wrapWithSandbox:async function(n,t,e,s){if(isSandboxingEnabled()){if(!I)throw new Error("Sandbox failed to initialize. ");await I}return o.wrapWithSandbox(n,t,e,s)},refreshConfig:function(){if(!isSandboxingEnabled())return;const n=convertToSandboxRuntimeConfig(w());o.updateConfig(n)},reset:async function(){return L?.(),L=void 0,j=void 0,$.length=0,D.cache.clear?.(),U.cache.clear?.(),I=void 0,o.reset()},checkDependencies:D,getFsReadConfig:o.getFsReadConfig,getFsWriteConfig:o.getFsWriteConfig,getNetworkRestrictionConfig:o.getNetworkRestrictionConfig,getIgnoreViolations:o.getIgnoreViolations,getLinuxGlobPatternWarnings:function(){const o=g();if("linux"!==o&&"wsl"!==o)return[];try{const o=w();if(!o?.sandbox?.enabled)return[];const n=o?.permissions||{},t=[],hasGlobs=o=>{const n=o.replace(/\/\*\*$/,"");return/[*?[\]]/.test(n)};for(const o of[...n.allow||[],...n.deny||[]]){const n=permissionRuleValueFromString(o);(n.toolName===F||n.toolName===W)&&n.ruleContent&&hasGlobs(n.ruleContent)&&t.push(o)}return t}catch(o){return f(`Failed to get Linux glob pattern warnings: ${o}`),[]}},isSupportedPlatform:U,getAllowUnixSockets:o.getAllowUnixSockets,getAllowLocalBinding:o.getAllowLocalBinding,getEnableWeakerNestedSandbox:o.getEnableWeakerNestedSandbox,getProxyPort:o.getProxyPort,getSocksProxyPort:o.getSocksProxyPort,getLinuxHttpSocketPath:o.getLinuxHttpSocketPath,getLinuxSocksSocketPath:o.getLinuxSocksSocketPath,waitForNetworkInitialization:o.waitForNetworkInitialization,getSandboxViolationStore:o.getSandboxViolationStore,annotateStderrWithSandboxFailures:o.annotateStderrWithSandboxFailures,cleanupAfterCommand:()=>{o.cleanupAfterCommand(),function(){for(const o of $)try{e(o,{recursive:!0}),f(`[Sandbox] scrubbed planted bare-repo file: ${o}`)}catch{}}()}};export{t as SandboxViolationStore,n as SandboxRuntimeConfigSchema};
1
+ import{SandboxManager as o,SandboxRuntimeConfigSchema as n,SandboxViolationStore as t}from"@anthropic-ai/sandbox-runtime";import{rmSync as e,statSync as s}from"fs";import{readFile as a}from"fs/promises";import{memoize as r}from"lodash-es";import{join as i,resolve as l,sep as d}from"path";import{getAdditionalDirectoriesForClaudeMd as c,getCwdState as u,getOriginalCwd as f}from"../../bootstrap/state.js";import{logForDebugging as m}from"../debug.js";import{expandPath as b}from"../path.js";import{getPlatform as g}from"../platform.js";import{CONTEXT_DIR_NAME as x}from"../configConstants.js";import{settingsChangeDetector as p}from"../settings/changeDetector.js";import{SETTING_SOURCES as S}from"../settings/constants.js";import{getManagedSettingsDropInDir as h}from"../settings/managedPath.js";import{getInitialSettings as w,getSettings_DEPRECATED as y,getSettingsFilePathForSource as P,getSettingsForSource as k,getSettingsRootPathForSource as C,updateSettingsForSource as v}from"../settings/settings.js";import{BASH_TOOL_NAME as F}from"../../tools/BashTool/toolName.js";import{FILE_EDIT_TOOL_NAME as W}from"../../tools/FileEditTool/constants.js";import{FILE_READ_TOOL_NAME as R}from"../../tools/FileReadTool/prompt.js";import{WEB_FETCH_TOOL_NAME as E}from"../../tools/WebFetchTool/prompt.js";import{errorMessage as A}from"../errors.js";import{getClaudeTempDir as N}from"../permissions/filesystem.js";import{ripgrepCommand as j}from"../ripgrep.js";function permissionRuleValueFromString(o){const n=o.match(/^([^(]+)\(([^)]+)\)$/);if(!n)return{toolName:o};const t=n[1],e=n[2];return t&&e?{toolName:t,ruleContent:e}:{toolName:o}}export function resolvePathPatternForSandbox(o,n){if(o.startsWith("//"))return o.slice(1);if(o.startsWith("/")&&!o.startsWith("//")){const t=C(n);return l(t,o.slice(1))}return o}export function resolveSandboxFilesystemPath(o,n){return o.startsWith("//")?o.slice(1):b(o,C(n))}export function shouldAllowManagedSandboxDomainsOnly(){return!0===k("policySettings")?.sandbox?.network?.allowManagedDomainsOnly}function shouldAllowManagedReadPathsOnly(){return!0===k("policySettings")?.sandbox?.filesystem?.allowManagedReadPathsOnly}export function convertToSandboxRuntimeConfig(o){const n=o.permissions||{},t=[],e=[];if(shouldAllowManagedSandboxDomainsOnly()){const o=k("policySettings");for(const n of o?.sandbox?.network?.allowedDomains||[])t.push(n);for(const n of o?.permissions?.allow||[]){const o=permissionRuleValueFromString(n);o.toolName===E&&o.ruleContent?.startsWith("domain:")&&t.push(o.ruleContent.substring(7))}}else{for(const n of o.sandbox?.network?.allowedDomains||[])t.push(n);for(const o of n.allow||[]){const n=permissionRuleValueFromString(o);n.toolName===E&&n.ruleContent?.startsWith("domain:")&&t.push(n.ruleContent.substring(7))}}for(const o of n.deny||[]){const n=permissionRuleValueFromString(o);n.toolName===E&&n.ruleContent?.startsWith("domain:")&&e.push(n.ruleContent.substring(7))}const a=[".",N()],r=[],i=[],d=[],m=S.map(o=>P(o)).filter(o=>void 0!==o);r.push(...m),r.push(h());const b=u(),g=f();b!==g&&(r.push(l(b,".claude","settings.json")),r.push(l(b,".claude","settings.local.json"))),r.push(l(g,".claude","skills")),r.push(l(g,x,"skills")),b!==g&&(r.push(l(b,".claude","skills")),r.push(l(b,x,"skills"))),D.length=0;const p=["HEAD","objects","refs","hooks","config"];for(const o of b===g?[g]:[g,b])for(const n of p){const t=l(o,n);try{s(t),r.push(t)}catch{D.push(t)}}$&&$!==b&&a.push($);const w=new Set([...o.permissions?.additionalDirectories||[],...c()]);a.push(...w);for(const o of S){const n=k(o);if(n?.permissions){for(const t of n.permissions.allow||[]){const n=permissionRuleValueFromString(t);n.toolName===W&&n.ruleContent&&a.push(resolvePathPatternForSandbox(n.ruleContent,o))}for(const t of n.permissions.deny||[]){const n=permissionRuleValueFromString(t);n.toolName===W&&n.ruleContent&&r.push(resolvePathPatternForSandbox(n.ruleContent,o)),n.toolName===R&&n.ruleContent&&i.push(resolvePathPatternForSandbox(n.ruleContent,o))}}const t=n?.sandbox?.filesystem;if(t){for(const n of t.allowWrite||[])a.push(resolveSandboxFilesystemPath(n,o));for(const n of t.denyWrite||[])r.push(resolveSandboxFilesystemPath(n,o));for(const n of t.denyRead||[])i.push(resolveSandboxFilesystemPath(n,o));if(!shouldAllowManagedReadPathsOnly()||"policySettings"===o)for(const n of t.allowRead||[])d.push(resolveSandboxFilesystemPath(n,o))}}const{rgPath:y,rgArgs:C,argv0:v}=j(),F=o.sandbox?.ripgrep??{command:y,args:C,argv0:v};return{network:{allowedDomains:t,deniedDomains:e,allowUnixSockets:o.sandbox?.network?.allowUnixSockets,allowAllUnixSockets:o.sandbox?.network?.allowAllUnixSockets,allowLocalBinding:o.sandbox?.network?.allowLocalBinding,httpProxyPort:o.sandbox?.network?.httpProxyPort,socksProxyPort:o.sandbox?.network?.socksProxyPort},filesystem:{denyRead:i,allowRead:d,allowWrite:a,denyWrite:r},ignoreViolations:o.sandbox?.ignoreViolations,enableWeakerNestedSandbox:o.sandbox?.enableWeakerNestedSandbox,enableWeakerNetworkIsolation:o.sandbox?.enableWeakerNetworkIsolation,ripgrep:F}}let I,L,$;const D=[];const U=r(()=>{const{rgPath:n,rgArgs:t}=j();return o.checkDependencies({command:n,args:t})});function getSandboxEnabledSetting(){try{const o=y();return o?.sandbox?.enabled??!1}catch(o){return m(`Failed to get settings for sandbox check: ${o}`),!1}}const B=r(()=>o.isSupportedPlatform());function isPlatformInEnabledList(){try{const o=w(),n=o?.sandbox?.enabledPlatforms;if(void 0===n)return!0;if(0===n.length)return!1;const t=g();return n.includes(t)}catch(o){return m(`Failed to check enabledPlatforms: ${o}`),!0}}function isSandboxingEnabled(){return!!B()&&(!(U().errors.length>0)&&(!!isPlatformInEnabledList()&&getSandboxEnabledSetting()))}export function addToExcludedCommands(o,n){const t=k("localSettings"),e=t?.sandbox?.excludedCommands||[];let s=o;if(n){const o=n.filter(o=>"addRules"===o.type&&o.rules.some(o=>o.toolName===F));if(o.length>0&&"addRules"===o[0].type){const n=o[0].rules.find(o=>o.toolName===F);if(n?.ruleContent){s=function(o){const n=o.match(/^(.+):\*$/);return n?.[1]??null}(n.ruleContent)||n.ruleContent}}}return e.includes(s)||v("localSettings",{sandbox:{...t?.sandbox,excludedCommands:[...e,s]}}),s}export const SandboxManager={initialize:async function(n){if(I)return I;if(!isSandboxingEnabled())return;const t=n?async o=>shouldAllowManagedSandboxDomainsOnly()?(m(`[sandbox] Blocked network request to ${o.host} (allowManagedDomainsOnly)`),!1):n(o):void 0;return I=(async()=>{try{void 0===$&&($=await async function(o){const n=i(o,".git");try{const t=(await a(n,{encoding:"utf8"})).match(/^gitdir:\s*(.+)$/m);if(!t?.[1])return null;const e=l(o,t[1].trim()),s=`${d}.git${d}worktrees${d}`,r=e.lastIndexOf(s);return r>0?e.substring(0,r):null}catch{return null}}(u()));const n=convertToSandboxRuntimeConfig(y());await o.initialize(n,t),L=p.subscribe(()=>{const n=convertToSandboxRuntimeConfig(y());o.updateConfig(n),m("Sandbox configuration updated from settings change")})}catch(o){I=void 0,m(`Failed to initialize sandbox: ${A(o)}`)}})(),I},isSandboxingEnabled,isSandboxEnabledInSettings:getSandboxEnabledSetting,isPlatformInEnabledList,getSandboxUnavailableReason:function(){if(!getSandboxEnabledSetting())return;if(!B()){const o=g();return"wsl"===o?"sandbox.enabled is set but WSL1 is not supported (requires WSL2)":`sandbox.enabled is set but ${o} is not supported (requires macOS, Linux, or WSL2)`}if(!isPlatformInEnabledList())return`sandbox.enabled is set but ${g()} is not in sandbox.enabledPlatforms`;const o=U();if(o.errors.length>0){const n="macos"===g()?"run /sandbox or /doctor for details":"install missing tools (e.g. apt install bubblewrap socat) or run /sandbox for details";return`sandbox.enabled is set but dependencies are missing: ${o.errors.join(", ")} · ${n}`}},isAutoAllowBashIfSandboxedEnabled:function(){const o=y();return o?.sandbox?.autoAllowBashIfSandboxed??!0},areUnsandboxedCommandsAllowed:function(){const o=y();return o?.sandbox?.allowUnsandboxedCommands??!0},isSandboxRequired:function(){const o=y();return getSandboxEnabledSetting()&&(o?.sandbox?.failIfUnavailable??!1)},areSandboxSettingsLockedByPolicy:function(){const o=["flagSettings","policySettings"];for(const n of o){const o=k(n);if(void 0!==o?.sandbox?.enabled||void 0!==o?.sandbox?.autoAllowBashIfSandboxed||void 0!==o?.sandbox?.allowUnsandboxedCommands)return!0}return!1},setSandboxSettings:async function(o){const n=k("localSettings");v("localSettings",{sandbox:{...n?.sandbox,...void 0!==o.enabled&&{enabled:o.enabled},...void 0!==o.autoAllowBashIfSandboxed&&{autoAllowBashIfSandboxed:o.autoAllowBashIfSandboxed},...void 0!==o.allowUnsandboxedCommands&&{allowUnsandboxedCommands:o.allowUnsandboxedCommands}}})},getExcludedCommands:function(){const o=y();return o?.sandbox?.excludedCommands??[]},wrapWithSandbox:async function(n,t,e,s){if(isSandboxingEnabled()){if(!I)throw new Error("Sandbox failed to initialize. ");await I}return o.wrapWithSandbox(n,t,e,s)},refreshConfig:function(){if(!isSandboxingEnabled())return;const n=convertToSandboxRuntimeConfig(y());o.updateConfig(n)},reset:async function(){return L?.(),L=void 0,$=void 0,D.length=0,U.cache.clear?.(),B.cache.clear?.(),I=void 0,o.reset()},checkDependencies:U,getFsReadConfig:o.getFsReadConfig,getFsWriteConfig:o.getFsWriteConfig,getNetworkRestrictionConfig:o.getNetworkRestrictionConfig,getIgnoreViolations:o.getIgnoreViolations,getLinuxGlobPatternWarnings:function(){const o=g();if("linux"!==o&&"wsl"!==o)return[];try{const o=y();if(!o?.sandbox?.enabled)return[];const n=o?.permissions||{},t=[],hasGlobs=o=>{const n=o.replace(/\/\*\*$/,"");return/[*?[\]]/.test(n)};for(const o of[...n.allow||[],...n.deny||[]]){const n=permissionRuleValueFromString(o);(n.toolName===W||n.toolName===R)&&n.ruleContent&&hasGlobs(n.ruleContent)&&t.push(o)}return t}catch(o){return m(`Failed to get Linux glob pattern warnings: ${o}`),[]}},isSupportedPlatform:B,getAllowUnixSockets:o.getAllowUnixSockets,getAllowLocalBinding:o.getAllowLocalBinding,getEnableWeakerNestedSandbox:o.getEnableWeakerNestedSandbox,getProxyPort:o.getProxyPort,getSocksProxyPort:o.getSocksProxyPort,getLinuxHttpSocketPath:o.getLinuxHttpSocketPath,getLinuxSocksSocketPath:o.getLinuxSocksSocketPath,waitForNetworkInitialization:o.waitForNetworkInitialization,getSandboxViolationStore:o.getSandboxViolationStore,annotateStderrWithSandboxFailures:o.annotateStderrWithSandboxFailures,cleanupAfterCommand:()=>{o.cleanupAfterCommand(),function(){for(const o of D)try{e(o,{recursive:!0}),m(`[Sandbox] scrubbed planted bare-repo file: ${o}`)}catch{}}()}};export{t as SandboxViolationStore,n as SandboxRuntimeConfigSchema};
@@ -1 +1 @@
1
- import{feature as t}from"../../recovery/bunBundleShim.js";import e from"lodash-es/mergeWith.js";import{dirname as s,join as n,resolve as o}from"path";import{z as r}from"zod/v4";import{getFlagSettingsInline as i,getFlagSettingsPath as a,getOriginalCwd as g,getUseCoworkPlugins as c}from"../../bootstrap/state.js";import{getRemoteManagedSettingsSyncFromCache as l}from"../../services/remoteManagedSettings/syncCacheState.js";import{uniq as u}from"../array.js";import{logForDebugging as S}from"../debug.js";import{logForDiagnosticsNoPII as f}from"../diagLogs.js";import{getClaudeConfigHomeDir as p,isEnvTruthy as m}from"../envUtils.js";import{getErrnoCode as d,isENOENT as h}from"../errors.js";import{writeFileSyncAndFlush_DEPRECATED as y}from"../file.js";import{readFileSync as F}from"../fileRead.js";import{getFsImplementation as P,safeResolvePath as j}from"../fsOperations.js";import{addFileGlobRuleToGitignore as k}from"../git/gitignore.js";import{safeParseJSON as b}from"../json.js";import{logError as M}from"../log.js";import{getPlatform as x}from"../platform.js";import{clone as C,jsonStringify as v}from"../slowOperations.js";import{profileCheckpoint as A}from"../startupProfiler.js";import{getEnabledSettingSources as O}from"./constants.js";import{markInternalWrite as R}from"./internalWrites.js";import{getManagedFilePath as w,getManagedSettingsDropInDir as E}from"./managedPath.js";import{getHkcuSettings as _,getMdmSettings as I}from"./mdm/settings.js";import{getCachedParsedFile as D,getCachedSettingsForSource as T,getPluginSettingsBase as $,getSessionSettingsCache as U,resetSettingsCache as N,setCachedParsedFile as W,setCachedSettingsForSource as L,setSessionSettingsCache as z}from"./settingsCache.js";import{SettingsSchema as B}from"./types.js";import{filterInvalidPermissionRules as K,formatZodError as G}from"./validation.js";function getManagedSettingsFilePath(){return n(w(),"managed-settings.json")}export function loadManagedFileSettings(){const t=[];let s={},o=!1;const{settings:r,errors:i}=parseSettingsFile(getManagedSettingsFilePath());t.push(...i),r&&Object.keys(r).length>0&&(s=e(s,r,settingsMergeCustomizer),o=!0);const a=E();try{const r=P().readdirSync(a).filter(t=>(t.isFile()||t.isSymbolicLink())&&t.name.endsWith(".json")&&!t.name.startsWith(".")).map(t=>t.name).sort();for(const i of r){const{settings:r,errors:g}=parseSettingsFile(n(a,i));t.push(...g),r&&Object.keys(r).length>0&&(s=e(s,r,settingsMergeCustomizer),o=!0)}}catch(t){const e=d(t);"ENOENT"!==e&&"ENOTDIR"!==e&&M(t)}return{settings:o?s:null,errors:t}}export function getManagedFileSettingsPresence(){const{settings:t}=parseSettingsFile(getManagedSettingsFilePath()),e=!!t&&Object.keys(t).length>0;let s=!1;const n=E();try{s=P().readdirSync(n).some(t=>(t.isFile()||t.isSymbolicLink())&&t.name.endsWith(".json")&&!t.name.startsWith("."))}catch{}return{hasBase:e,hasDropIns:s}}function handleFileSystemError(t,e){"object"==typeof t&&t&&"code"in t&&"ENOENT"===t.code?S(`Broken symlink or missing file encountered for settings.json at path: ${e}`):M(t)}export function parseSettingsFile(t){const e=D(t);if(e)return{settings:e.settings?C(e.settings):null,errors:e.errors};const s=function(t){try{const{resolvedPath:e}=j(P(),t),s=F(e);if(""===s.trim())return{settings:{},errors:[]};const n=b(s,!1),o=K(n,t),r=B().safeParse(n);if(!r.success){const e=G(r.error,t);return{settings:null,errors:[...o,...e]}}return{settings:r.data,errors:o}}catch(e){return handleFileSystemError(e,t),{settings:null,errors:[]}}}(t);return W(t,s),{settings:s.settings?C(s.settings):null,errors:s.errors}}export function getSettingsRootPathForSource(t){switch(t){case"userSettings":return o(p());case"policySettings":case"projectSettings":case"localSettings":return o(g());case"flagSettings":{const t=a();return t?s(o(t)):o(g())}}}export function getSettingsFilePathForSource(t){switch(t){case"userSettings":return n(getSettingsRootPathForSource(t),c()||m(process.env.CONTEXT_CODE_USE_COWORK_PLUGINS)||m(process.env.CLAUDE_CODE_USE_COWORK_PLUGINS)?"cowork_settings.json":"settings.json");case"projectSettings":case"localSettings":{const e=getSettingsRootPathForSource(t),s=n(e,getPreferredRelativeSettingsFilePathForSource(t)),o=n(e,getRelativeSettingsFilePathForSource(t)),r=P();return r.existsSync(s)?s:r.existsSync(o)?o:s}case"policySettings":return getManagedSettingsFilePath();case"flagSettings":return a()}}export function getRelativeSettingsFilePathForSource(t){switch(t){case"projectSettings":return n(".claude","settings.json");case"localSettings":return n(".claude","settings.local.json")}}export function getPreferredRelativeSettingsFilePathForSource(t){switch(t){case"projectSettings":return n(".contextcli","settings.json");case"localSettings":return n(".contextcli","settings.local.json")}}export function getProjectRelativeSettingsPathForDisplay(t){const e=getPreferredRelativeSettingsFilePathForSource(t),s=getRelativeSettingsFilePathForSource(t),o=getSettingsRootPathForSource(t),r=P();return r.existsSync(n(o,e))?e:r.existsSync(n(o,s))?s:e}export function getSettingsForSource(t){const e=T(t);if(void 0!==e)return e;const s=getSettingsForSourceUncached(t);return L(t,s),s}function getSettingsForSourceUncached(t){if("policySettings"===t){const t=l();if(t&&Object.keys(t).length>0)return t;const e=I();if(Object.keys(e.settings).length>0)return e.settings;const{settings:s}=loadManagedFileSettings();if(s)return s;const n=_();return Object.keys(n.settings).length>0?n.settings:null}const s=getSettingsFilePathForSource(t),{settings:n}=s?parseSettingsFile(s):{settings:null};if("flagSettings"===t){const t=i();if(t){const s=B().safeParse(t);if(s.success)return e(n||{},s.data,settingsMergeCustomizer)}}return n}export function getPolicySettingsOrigin(){const t=l();if(t&&Object.keys(t).length>0)return"remote";const e=I();if(Object.keys(e.settings).length>0)return"macos"===x()?"plist":"hklm";const{settings:s}=loadManagedFileSettings();if(s)return"file";const n=_();return Object.keys(n.settings).length>0?"hkcu":null}export function updateSettingsForSource(t,n){if("policySettings"===t||"flagSettings"===t)return{error:null};const o=getSettingsFilePathForSource(t);if(!o)return{error:null};try{P().mkdirSync(s(o));let r=getSettingsForSourceUncached(t);if(!r){let t=null;try{t=F(o)}catch(t){if(!h(t))throw t}if(null!==t){const e=b(t);if(null===e)return{error:new Error(`Invalid JSON syntax in settings file at ${o}`)};e&&"object"==typeof e&&(r=e,S(`Using raw settings from ${o} due to validation failure`))}}const i=e(r||{},n,(t,e,s,n)=>{if(void 0!==e||!n||"string"!=typeof s)return Array.isArray(e)?e:void 0;delete n[s]});R(o),y(o,v(i,null,2)+"\n"),N(),"localSettings"===t&&(k(getRelativeSettingsFilePathForSource("localSettings"),g()),k(getPreferredRelativeSettingsFilePathForSource("localSettings"),g()))}catch(t){const e=new Error(`Failed to read raw settings from ${o}: ${t}`);return M(e),{error:e}}return{error:null}}export function settingsMergeCustomizer(t,e){if(Array.isArray(t)&&Array.isArray(e))return s=t,n=e,u([...s,...n]);var s,n}export function getManagedSettingsKeysForLogging(e){const s=B().strip().parse(e),n=["permissions","sandbox","hooks"],o=[],r={permissions:new Set(["allow","deny","ask","defaultMode","disableBypassPermissionsMode",...t("TRANSCRIPT_CLASSIFIER")?["disableAutoMode"]:[],"additionalDirectories"]),sandbox:new Set(["enabled","failIfUnavailable","allowUnsandboxedCommands","network","filesystem","ignoreViolations","excludedCommands","autoAllowBashIfSandboxed","enableWeakerNestedSandbox","enableWeakerNetworkIsolation","ripgrep"]),hooks:new Set(["PreToolUse","PostToolUse","Notification","UserPromptSubmit","SessionStart","SessionEnd","Stop","SubagentStop","PreCompact","PostCompact","TeammateIdle","TaskCreated","TaskCompleted"])};for(const t of Object.keys(s))if(n.includes(t)&&s[t]&&"object"==typeof s[t]){const e=s[t],n=r[t];if(n)for(const s of Object.keys(e))n.has(s)&&o.push(`${t}.${s}`)}else o.push(t);return o.sort()}let J=!1;export function getInitialSettings(){const{settings:t}=getSettingsWithErrors();return t||{}}export const getSettings_DEPRECATED=getInitialSettings;export function getSettingsWithSources(){N();const t=[];for(const e of O()){const s=getSettingsForSource(e);s&&Object.keys(s).length>0&&t.push({source:e,settings:s})}return{effective:getInitialSettings(),sources:t}}export function getSettingsWithErrors(){const t=U();if(null!==t)return t;const s=function(){if(J)return{settings:{},errors:[]};const t=Date.now();A("loadSettingsFromDisk_start"),f("info","settings_load_started"),J=!0;try{const s=$();let n={};s&&(n=e(n,s,settingsMergeCustomizer));const r=[],a=new Set,g=new Set;for(const t of O()){if("policySettings"===t){let t=null;const s=[],o=l();if(o&&Object.keys(o).length>0){const e=B().safeParse(o);e.success?t=e.data:s.push(...G(e.error,"remote managed settings"))}if(!t){const e=I();Object.keys(e.settings).length>0&&(t=e.settings),s.push(...e.errors)}if(!t){const{settings:e,errors:n}=loadManagedFileSettings();e&&(t=e),s.push(...n)}if(!t){const e=_();Object.keys(e.settings).length>0&&(t=e.settings),s.push(...e.errors)}t&&(n=e(n,t,settingsMergeCustomizer));for(const t of s){const e=`${t.file}:${t.path}:${t.message}`;a.has(e)||(a.add(e),r.push(t))}continue}const s=getSettingsFilePathForSource(t);if(s){const t=o(s);if(!g.has(t)){g.add(t);const{settings:o,errors:i}=parseSettingsFile(s);for(const t of i){const e=`${t.file}:${t.path}:${t.message}`;a.has(e)||(a.add(e),r.push(t))}o&&(n=e(n,o,settingsMergeCustomizer))}}if("flagSettings"===t){const t=i();if(t){const s=B().safeParse(t);s.success&&(n=e(n,s.data,settingsMergeCustomizer))}}}return f("info","settings_load_completed",{duration_ms:Date.now()-t,source_count:g.size,error_count:r.length}),{settings:n,errors:r}}finally{J=!1}}();return A("loadSettingsFromDisk_end"),z(s),s}export function hasSkipDangerousModePermissionPrompt(){return!!(getSettingsForSource("userSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("localSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("flagSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("policySettings")?.skipDangerousModePermissionPrompt)}export function hasAutoModeOptIn(){if(t("TRANSCRIPT_CLASSIFIER")){const t=getSettingsForSource("userSettings")?.skipAutoPermissionPrompt,e=getSettingsForSource("localSettings")?.skipAutoPermissionPrompt,s=getSettingsForSource("flagSettings")?.skipAutoPermissionPrompt,n=getSettingsForSource("policySettings")?.skipAutoPermissionPrompt,o=!!(t||e||s||n);return S(`[auto-mode] hasAutoModeOptIn=${o} skipAutoPermissionPrompt: user=${t} local=${e} flag=${s} policy=${n}`),o}return!1}export function getUseAutoModeDuringPlan(){return!t("TRANSCRIPT_CLASSIFIER")||!1!==getSettingsForSource("policySettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("flagSettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("userSettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("localSettings")?.useAutoModeDuringPlan}export function getAutoModeConfig(){if(t("TRANSCRIPT_CLASSIFIER")){const t=r.object({allow:r.array(r.string()).optional(),soft_deny:r.array(r.string()).optional(),deny:r.array(r.string()).optional(),environment:r.array(r.string()).optional()}),e=[],s=[],n=[];for(const o of["userSettings","localSettings","flagSettings","policySettings"]){const r=getSettingsForSource(o);if(!r)continue;const i=t.safeParse(r.autoMode);i.success&&(i.data.allow&&e.push(...i.data.allow),i.data.soft_deny&&s.push(...i.data.soft_deny),"ant"===process.env.USER_TYPE&&i.data.deny&&s.push(...i.data.deny),i.data.environment&&n.push(...i.data.environment))}if(e.length>0||s.length>0||n.length>0)return{...e.length>0&&{allow:e},...s.length>0&&{soft_deny:s},...n.length>0&&{environment:n}}}}export function rawSettingsContainsKey(t){for(const e of O()){if("policySettings"===e)continue;const s=getSettingsFilePathForSource(e);if(s)try{const{resolvedPath:e}=j(P(),s),n=F(e);if(!n.trim())continue;const o=b(n,!1);if(o&&"object"==typeof o&&t in o)return!0}catch(t){handleFileSystemError(t,s)}}return!1}
1
+ import{feature as t}from"../../recovery/bunBundleShim.js";import e from"lodash-es/mergeWith.js";import{dirname as s,join as n,resolve as o}from"path";import{z as r}from"zod/v4";import{getFlagSettingsInline as i,getFlagSettingsPath as a,getOriginalCwd as g,getUseCoworkPlugins as c}from"../../bootstrap/state.js";import{getRemoteManagedSettingsSyncFromCache as l}from"../../services/remoteManagedSettings/syncCacheState.js";import{uniq as u}from"../array.js";import{logForDebugging as S}from"../debug.js";import{logForDiagnosticsNoPII as f}from"../diagLogs.js";import{getClaudeConfigHomeDir as p,isEnvTruthy as m}from"../envUtils.js";import{getErrnoCode as d,isENOENT as h}from"../errors.js";import{writeFileSyncAndFlush_DEPRECATED as y}from"../file.js";import{readFileSync as F}from"../fileRead.js";import{getFsImplementation as P,safeResolvePath as j}from"../fsOperations.js";import{addFileGlobRuleToGitignore as k}from"../git/gitignore.js";import{safeParseJSON as b}from"../json.js";import{logError as M}from"../log.js";import{getPlatform as C}from"../platform.js";import{clone as x,jsonStringify as v}from"../slowOperations.js";import{profileCheckpoint as A}from"../startupProfiler.js";import{CONTEXT_DIR_NAME as O}from"../configConstants.js";import{getEnabledSettingSources as R}from"./constants.js";import{markInternalWrite as w}from"./internalWrites.js";import{getManagedFilePath as E,getManagedSettingsDropInDir as _}from"./managedPath.js";import{getHkcuSettings as I,getMdmSettings as D}from"./mdm/settings.js";import{getCachedParsedFile as T,getCachedSettingsForSource as $,getPluginSettingsBase as U,getSessionSettingsCache as N,resetSettingsCache as W,setCachedParsedFile as L,setCachedSettingsForSource as z,setSessionSettingsCache as B}from"./settingsCache.js";import{SettingsSchema as K}from"./types.js";import{filterInvalidPermissionRules as G,formatZodError as J}from"./validation.js";function getManagedSettingsFilePath(){return n(E(),"managed-settings.json")}export function loadManagedFileSettings(){const t=[];let s={},o=!1;const{settings:r,errors:i}=parseSettingsFile(getManagedSettingsFilePath());t.push(...i),r&&Object.keys(r).length>0&&(s=e(s,r,settingsMergeCustomizer),o=!0);const a=_();try{const r=P().readdirSync(a).filter(t=>(t.isFile()||t.isSymbolicLink())&&t.name.endsWith(".json")&&!t.name.startsWith(".")).map(t=>t.name).sort();for(const i of r){const{settings:r,errors:g}=parseSettingsFile(n(a,i));t.push(...g),r&&Object.keys(r).length>0&&(s=e(s,r,settingsMergeCustomizer),o=!0)}}catch(t){const e=d(t);"ENOENT"!==e&&"ENOTDIR"!==e&&M(t)}return{settings:o?s:null,errors:t}}export function getManagedFileSettingsPresence(){const{settings:t}=parseSettingsFile(getManagedSettingsFilePath()),e=!!t&&Object.keys(t).length>0;let s=!1;const n=_();try{s=P().readdirSync(n).some(t=>(t.isFile()||t.isSymbolicLink())&&t.name.endsWith(".json")&&!t.name.startsWith("."))}catch{}return{hasBase:e,hasDropIns:s}}function handleFileSystemError(t,e){"object"==typeof t&&t&&"code"in t&&"ENOENT"===t.code?S(`Broken symlink or missing file encountered for settings.json at path: ${e}`):M(t)}export function parseSettingsFile(t){const e=T(t);if(e)return{settings:e.settings?x(e.settings):null,errors:e.errors};const s=function(t){try{const{resolvedPath:e}=j(P(),t),s=F(e);if(""===s.trim())return{settings:{},errors:[]};const n=b(s,!1),o=G(n,t),r=K().safeParse(n);if(!r.success){const e=J(r.error,t);return{settings:null,errors:[...o,...e]}}return{settings:r.data,errors:o}}catch(e){return handleFileSystemError(e,t),{settings:null,errors:[]}}}(t);return L(t,s),{settings:s.settings?x(s.settings):null,errors:s.errors}}export function getSettingsRootPathForSource(t){switch(t){case"userSettings":return o(p());case"policySettings":case"projectSettings":case"localSettings":return o(g());case"flagSettings":{const t=a();return t?s(o(t)):o(g())}}}export function getSettingsFilePathForSource(t){switch(t){case"userSettings":return n(getSettingsRootPathForSource(t),c()||m(process.env.CONTEXT_CODE_USE_COWORK_PLUGINS)||m(process.env.CLAUDE_CODE_USE_COWORK_PLUGINS)?"cowork_settings.json":"settings.json");case"projectSettings":case"localSettings":{const e=getSettingsRootPathForSource(t),s=n(e,getPreferredRelativeSettingsFilePathForSource(t)),o=n(e,getRelativeSettingsFilePathForSource(t)),r=P();return r.existsSync(s)?s:r.existsSync(o)?o:s}case"policySettings":return getManagedSettingsFilePath();case"flagSettings":return a()}}export function getRelativeSettingsFilePathForSource(t){switch(t){case"projectSettings":return n(".claude","settings.json");case"localSettings":return n(".claude","settings.local.json")}}export function getPreferredRelativeSettingsFilePathForSource(t){switch(t){case"projectSettings":return n(O,"settings.json");case"localSettings":return n(O,"settings.local.json")}}export function getProjectRelativeSettingsPathForDisplay(t){const e=getPreferredRelativeSettingsFilePathForSource(t),s=getRelativeSettingsFilePathForSource(t),o=getSettingsRootPathForSource(t),r=P();return r.existsSync(n(o,e))?e:r.existsSync(n(o,s))?s:e}export function getSettingsForSource(t){const e=$(t);if(void 0!==e)return e;const s=getSettingsForSourceUncached(t);return z(t,s),s}function getSettingsForSourceUncached(t){if("policySettings"===t){const t=l();if(t&&Object.keys(t).length>0)return t;const e=D();if(Object.keys(e.settings).length>0)return e.settings;const{settings:s}=loadManagedFileSettings();if(s)return s;const n=I();return Object.keys(n.settings).length>0?n.settings:null}const s=getSettingsFilePathForSource(t),{settings:n}=s?parseSettingsFile(s):{settings:null};if("flagSettings"===t){const t=i();if(t){const s=K().safeParse(t);if(s.success)return e(n||{},s.data,settingsMergeCustomizer)}}return n}export function getPolicySettingsOrigin(){const t=l();if(t&&Object.keys(t).length>0)return"remote";const e=D();if(Object.keys(e.settings).length>0)return"macos"===C()?"plist":"hklm";const{settings:s}=loadManagedFileSettings();if(s)return"file";const n=I();return Object.keys(n.settings).length>0?"hkcu":null}export function updateSettingsForSource(t,n){if("policySettings"===t||"flagSettings"===t)return{error:null};const o=getSettingsFilePathForSource(t);if(!o)return{error:null};try{P().mkdirSync(s(o));let r=getSettingsForSourceUncached(t);if(!r){let t=null;try{t=F(o)}catch(t){if(!h(t))throw t}if(null!==t){const e=b(t);if(null===e)return{error:new Error(`Invalid JSON syntax in settings file at ${o}`)};e&&"object"==typeof e&&(r=e,S(`Using raw settings from ${o} due to validation failure`))}}const i=e(r||{},n,(t,e,s,n)=>{if(void 0!==e||!n||"string"!=typeof s)return Array.isArray(e)?e:void 0;delete n[s]});w(o),y(o,v(i,null,2)+"\n"),W(),"localSettings"===t&&(k(getRelativeSettingsFilePathForSource("localSettings"),g()),k(getPreferredRelativeSettingsFilePathForSource("localSettings"),g()))}catch(t){const e=new Error(`Failed to read raw settings from ${o}: ${t}`);return M(e),{error:e}}return{error:null}}export function settingsMergeCustomizer(t,e){if(Array.isArray(t)&&Array.isArray(e))return s=t,n=e,u([...s,...n]);var s,n}export function getManagedSettingsKeysForLogging(e){const s=K().strip().parse(e),n=["permissions","sandbox","hooks"],o=[],r={permissions:new Set(["allow","deny","ask","defaultMode","disableBypassPermissionsMode",...t("TRANSCRIPT_CLASSIFIER")?["disableAutoMode"]:[],"additionalDirectories"]),sandbox:new Set(["enabled","failIfUnavailable","allowUnsandboxedCommands","network","filesystem","ignoreViolations","excludedCommands","autoAllowBashIfSandboxed","enableWeakerNestedSandbox","enableWeakerNetworkIsolation","ripgrep"]),hooks:new Set(["PreToolUse","PostToolUse","Notification","UserPromptSubmit","SessionStart","SessionEnd","Stop","SubagentStop","PreCompact","PostCompact","TeammateIdle","TaskCreated","TaskCompleted"])};for(const t of Object.keys(s))if(n.includes(t)&&s[t]&&"object"==typeof s[t]){const e=s[t],n=r[t];if(n)for(const s of Object.keys(e))n.has(s)&&o.push(`${t}.${s}`)}else o.push(t);return o.sort()}let V=!1;export function getInitialSettings(){const{settings:t}=getSettingsWithErrors();return t||{}}export const getSettings_DEPRECATED=getInitialSettings;export function getSettingsWithSources(){W();const t=[];for(const e of R()){const s=getSettingsForSource(e);s&&Object.keys(s).length>0&&t.push({source:e,settings:s})}return{effective:getInitialSettings(),sources:t}}export function getSettingsWithErrors(){const t=N();if(null!==t)return t;const s=function(){if(V)return{settings:{},errors:[]};const t=Date.now();A("loadSettingsFromDisk_start"),f("info","settings_load_started"),V=!0;try{const s=U();let n={};s&&(n=e(n,s,settingsMergeCustomizer));const r=[],a=new Set,g=new Set;for(const t of R()){if("policySettings"===t){let t=null;const s=[],o=l();if(o&&Object.keys(o).length>0){const e=K().safeParse(o);e.success?t=e.data:s.push(...J(e.error,"remote managed settings"))}if(!t){const e=D();Object.keys(e.settings).length>0&&(t=e.settings),s.push(...e.errors)}if(!t){const{settings:e,errors:n}=loadManagedFileSettings();e&&(t=e),s.push(...n)}if(!t){const e=I();Object.keys(e.settings).length>0&&(t=e.settings),s.push(...e.errors)}t&&(n=e(n,t,settingsMergeCustomizer));for(const t of s){const e=`${t.file}:${t.path}:${t.message}`;a.has(e)||(a.add(e),r.push(t))}continue}const s=getSettingsFilePathForSource(t);if(s){const t=o(s);if(!g.has(t)){g.add(t);const{settings:o,errors:i}=parseSettingsFile(s);for(const t of i){const e=`${t.file}:${t.path}:${t.message}`;a.has(e)||(a.add(e),r.push(t))}o&&(n=e(n,o,settingsMergeCustomizer))}}if("flagSettings"===t){const t=i();if(t){const s=K().safeParse(t);s.success&&(n=e(n,s.data,settingsMergeCustomizer))}}}return f("info","settings_load_completed",{duration_ms:Date.now()-t,source_count:g.size,error_count:r.length}),{settings:n,errors:r}}finally{V=!1}}();return A("loadSettingsFromDisk_end"),B(s),s}export function hasSkipDangerousModePermissionPrompt(){return!!(getSettingsForSource("userSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("localSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("flagSettings")?.skipDangerousModePermissionPrompt||getSettingsForSource("policySettings")?.skipDangerousModePermissionPrompt)}export function hasAutoModeOptIn(){if(t("TRANSCRIPT_CLASSIFIER")){const t=getSettingsForSource("userSettings")?.skipAutoPermissionPrompt,e=getSettingsForSource("localSettings")?.skipAutoPermissionPrompt,s=getSettingsForSource("flagSettings")?.skipAutoPermissionPrompt,n=getSettingsForSource("policySettings")?.skipAutoPermissionPrompt,o=!!(t||e||s||n);return S(`[auto-mode] hasAutoModeOptIn=${o} skipAutoPermissionPrompt: user=${t} local=${e} flag=${s} policy=${n}`),o}return!1}export function getUseAutoModeDuringPlan(){return!t("TRANSCRIPT_CLASSIFIER")||!1!==getSettingsForSource("policySettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("flagSettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("userSettings")?.useAutoModeDuringPlan&&!1!==getSettingsForSource("localSettings")?.useAutoModeDuringPlan}export function getAutoModeConfig(){if(t("TRANSCRIPT_CLASSIFIER")){const t=r.object({allow:r.array(r.string()).optional(),soft_deny:r.array(r.string()).optional(),deny:r.array(r.string()).optional(),environment:r.array(r.string()).optional()}),e=[],s=[],n=[];for(const o of["userSettings","localSettings","flagSettings","policySettings"]){const r=getSettingsForSource(o);if(!r)continue;const i=t.safeParse(r.autoMode);i.success&&(i.data.allow&&e.push(...i.data.allow),i.data.soft_deny&&s.push(...i.data.soft_deny),"ant"===process.env.USER_TYPE&&i.data.deny&&s.push(...i.data.deny),i.data.environment&&n.push(...i.data.environment))}if(e.length>0||s.length>0||n.length>0)return{...e.length>0&&{allow:e},...s.length>0&&{soft_deny:s},...n.length>0&&{environment:n}}}}export function rawSettingsContainsKey(t){for(const e of R()){if("policySettings"===e)continue;const s=getSettingsFilePathForSource(e);if(s)try{const{resolvedPath:e}=j(P(),s),n=F(e);if(!n.trim())continue;const o=b(n,!1);if(o&&"object"==typeof o&&t in o)return!0}catch(t){handleFileSystemError(t,s)}}return!1}
@@ -1 +1 @@
1
- import t from"chokidar";import*as e from"path";import{getAdditionalDirectoriesForClaudeMd as s}from"../../bootstrap/state.js";import{clearCommandMemoizationCaches as i,clearCommandsCache as o}from"../../commands.js";import{logEvent as n}from"../../services/analytics/index.js";import{clearSkillCaches as r,getSkillsPath as l,onDynamicSkillsLoaded as a}from"../../skills/loadSkillsDir.js";import{resetSentSkillNames as c}from"../attachments.js";import{registerCleanup as u}from"../cleanupRegistry.js";import{logForDebugging as m}from"../debug.js";import{getFsImplementation as h}from"../fsOperations.js";import{executeConfigChangeHooks as p,hasBlockingResult as d}from"../hooks.js";import{createSignal as f}from"../signal.js";const g="undefined"!=typeof Bun;let k=null,y=null;const b=new Set;let j=!1,w=!1,T=!1,v=null;const x=f();let C=null;export async function initialize(){if(j||w)return;j=!0,T||(T=!0,a(()=>{i(),x.emit()}));const o=await async function(){const t=h(),i=[],o=l("userSettings","skills");if(o)try{await t.stat(o),i.push(o)}catch{}const n=l("userSettings","commands");if(n)try{await t.stat(n),i.push(n)}catch{}const r=l("projectSettings","skills");if(r)try{const s=e.resolve(r);await t.stat(s),i.push(s)}catch{}const a=l("projectSettings","commands");if(a)try{const s=e.resolve(a);await t.stat(s),i.push(s)}catch{}for(const o of s())for(const s of[".contextcli",".claude"]){const n=e.join(o,s,"skills");try{await t.stat(n),i.push(n)}catch{}}return i}();0!==o.length&&(m(`Watching for changes in skill/command directories: ${o.join(", ")}...`),k=t.watch(o,{persistent:!0,ignoreInitial:!0,depth:2,awaitWriteFinish:{stabilityThreshold:C?.stabilityThreshold??1e3,pollInterval:C?.pollInterval??500},ignored:(t,s)=>!(!s||s.isFile()||s.isDirectory())||t.split(e.sep).some(t=>".git"===t),ignorePermissionErrors:!0,usePolling:g,interval:C?.chokidarInterval??2e3,atomic:!0}),k.on("add",handleChange),k.on("change",handleChange),k.on("unlink",handleChange),v=u(async()=>{await dispose()}))}export function dispose(){w=!0,v&&(v(),v=null);let t=Promise.resolve();return k&&(t=k.close(),k=null),y&&(clearTimeout(y),y=null),b.clear(),x.clear(),t}export const subscribe=x.subscribe;function handleChange(t){m(`Detected skill change: ${t}`),n("tengu_skill_file_changed",{source:"chokidar"}),function(t){b.add(t),y&&clearTimeout(y);y=setTimeout(async()=>{y=null;const t=[...b];b.clear();const e=await p("skills",t[0]);d(e)?m(`ConfigChange hook blocked skill reload (${t.length} paths)`):(r(),o(),c(),x.emit())},C?.reloadDebounce??300)}(t)}export async function resetForTesting(t){k&&(await k.close(),k=null),y&&(clearTimeout(y),y=null),b.clear(),x.clear(),j=!1,w=!1,C=t??null}export const skillChangeDetector={initialize,dispose,subscribe,resetForTesting};
1
+ import t from"chokidar";import*as e from"path";import{getAdditionalDirectoriesForClaudeMd as s}from"../../bootstrap/state.js";import{clearCommandMemoizationCaches as i,clearCommandsCache as o}from"../../commands.js";import{logEvent as n}from"../../services/analytics/index.js";import{clearSkillCaches as r,getSkillsPath as l,onDynamicSkillsLoaded as a}from"../../skills/loadSkillsDir.js";import{resetSentSkillNames as c}from"../attachments.js";import{registerCleanup as m}from"../cleanupRegistry.js";import{logForDebugging as u}from"../debug.js";import{getFsImplementation as h}from"../fsOperations.js";import{executeConfigChangeHooks as p,hasBlockingResult as d}from"../hooks.js";import{createSignal as f}from"../signal.js";import{CONTEXT_DIR_NAME as g}from"../configConstants.js";const k="undefined"!=typeof Bun;let y=null,j=null;const b=new Set;let w=!1,T=!1,v=!1,C=null;const x=f();let S=null;export async function initialize(){if(w||T)return;w=!0,v||(v=!0,a(()=>{i(),x.emit()}));const o=await async function(){const t=h(),i=[],o=l("userSettings","skills");if(o)try{await t.stat(o),i.push(o)}catch{}const n=l("userSettings","commands");if(n)try{await t.stat(n),i.push(n)}catch{}const r=l("projectSettings","skills");if(r)try{const s=e.resolve(r);await t.stat(s),i.push(s)}catch{}const a=l("projectSettings","commands");if(a)try{const s=e.resolve(a);await t.stat(s),i.push(s)}catch{}for(const o of s())for(const s of[g,".claude"]){const n=e.join(o,s,"skills");try{await t.stat(n),i.push(n)}catch{}}return i}();0!==o.length&&(u(`Watching for changes in skill/command directories: ${o.join(", ")}...`),y=t.watch(o,{persistent:!0,ignoreInitial:!0,depth:2,awaitWriteFinish:{stabilityThreshold:S?.stabilityThreshold??1e3,pollInterval:S?.pollInterval??500},ignored:(t,s)=>!(!s||s.isFile()||s.isDirectory())||t.split(e.sep).some(t=>".git"===t),ignorePermissionErrors:!0,usePolling:k,interval:S?.chokidarInterval??2e3,atomic:!0}),y.on("add",handleChange),y.on("change",handleChange),y.on("unlink",handleChange),C=m(async()=>{await dispose()}))}export function dispose(){T=!0,C&&(C(),C=null);let t=Promise.resolve();return y&&(t=y.close(),y=null),j&&(clearTimeout(j),j=null),b.clear(),x.clear(),t}export const subscribe=x.subscribe;function handleChange(t){u(`Detected skill change: ${t}`),n("tengu_skill_file_changed",{source:"chokidar"}),function(t){b.add(t),j&&clearTimeout(j);j=setTimeout(async()=>{j=null;const t=[...b];b.clear();const e=await p("skills",t[0]);d(e)?u(`ConfigChange hook blocked skill reload (${t.length} paths)`):(r(),o(),c(),x.emit())},S?.reloadDebounce??300)}(t)}export async function resetForTesting(t){y&&(await y.close(),y=null),j&&(clearTimeout(j),j=null),b.clear(),x.clear(),w=!1,T=!1,S=t??null}export const skillChangeDetector={initialize,dispose,subscribe,resetForTesting};
@@ -1 +1 @@
1
- import{feature as e}from"../recovery/bunBundleShim.js";import t from"chalk";import{spawnSync as r}from"child_process";import{copyFile as o,mkdir as n,readdir as a,readFile as s,stat as i,symlink as c,utimes as l}from"fs/promises";import d from"ignore";import{basename as w,dirname as u,join as m}from"path";import{saveCurrentProjectConfig as h}from"./config.js";import{getCwd as f}from"./cwd.js";import{logForDebugging as k}from"./debug.js";import{errorMessage as p,getErrnoCode as g}from"./errors.js";import{execFileNoThrow as $,execFileNoThrowWithCwd as x}from"./execFileNoThrow.js";import{parseGitConfigValue as v}from"./git/gitConfigParser.js";import{getCommonDir as C,readWorktreeHeadSha as y,resolveGitDir as b,resolveRef as W}from"./git/gitFilesystem.js";import{findCanonicalGitRoot as P,findGitRoot as S,getBranch as E,getDefaultBranch as T,gitExe as I}from"./git.js";import{executeWorktreeCreateHook as F,executeWorktreeRemoveHook as A,hasWorktreeCreateHook as _}from"./hooks.js";import{containsPathTraversal as D}from"./path.js";import{getPlatform as B}from"./platform.js";import{getInitialSettings as N,getRelativeSettingsFilePathForSource as R}from"./settings/settings.js";import{sleep as j}from"./sleep.js";import{isInITerm2 as M}from"./swarm/backends/detection.js";const O=/^[a-zA-Z0-9._-]+$/;export function validateWorktreeSlug(e){if(e.length>64)throw new Error(`Invalid worktree name: must be 64 characters or fewer (got ${e.length})`);for(const t of e.split("/")){if("."===t||".."===t)throw new Error(`Invalid worktree name "${e}": must not contain "." or ".." path segments`);if(!O.test(t))throw new Error(`Invalid worktree name "${e}": each "/"-separated segment must be non-empty and contain only letters, digits, dots, underscores, and dashes`)}}let U=null;export function getCurrentWorktreeSession(){return U}export function restoreWorktreeSession(e){U=e}export function generateTmuxSessionName(e,t){return`${w(e)}_${t}`.replace(/[/.]/g,"_")}const H={GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:""};function worktreesDir(e){return m(e,".contextcli","worktrees")}function flattenSlug(e){return e.replaceAll("/","+")}export function worktreeBranchName(e){return`worktree-${flattenSlug(e)}`}function worktreePathFor(e,t){return m(worktreesDir(e),flattenSlug(t))}async function getOrCreateWorktree(e,t,r){const o=worktreePathFor(e,t),a=worktreeBranchName(t),s=await y(o);if(s)return{worktreePath:o,worktreeBranch:a,headCommit:s,existed:!0};await n(worktreesDir(e),{recursive:!0});const i={...process.env,...H};let c,l=null;if(r?.prNumber){const{code:t,stderr:o}=await x(I(),["fetch","origin",`pull/${r.prNumber}/head`],{cwd:e,stdin:"ignore",env:i});if(0!==t)throw new Error(`Failed to fetch PR #${r.prNumber}: ${o.trim()||'PR may not exist or the repository may not have a remote named "origin"'}`);c="FETCH_HEAD"}else{const[t,r]=await Promise.all([T(),b(e)]),o=`origin/${t}`,n=r?await W(r,`refs/remotes/origin/${t}`):null;if(n)c=o,l=n;else{const{code:r}=await x(I(),["fetch","origin",t],{cwd:e,stdin:"ignore",env:i});c=0===r?o:"HEAD"}}if(!l){const{stdout:t,code:r}=await x(I(),["rev-parse",c],{cwd:e});if(0!==r)throw new Error(`Failed to resolve base branch "${c}": git rev-parse failed`);l=t.trim()}const d=N().worktree?.sparsePaths,w=["worktree","add"];d?.length&&w.push("--no-checkout"),w.push("-B",a,o,c);const{code:u,stderr:m}=await x(I(),w,{cwd:e});if(0!==u)throw new Error(`Failed to create worktree: ${m}`);if(d?.length){const tearDown=async t=>{throw await x(I(),["worktree","remove","--force",o],{cwd:e}),new Error(t)},{code:t,stderr:r}=await x(I(),["sparse-checkout","set","--cone","--",...d],{cwd:o});0!==t&&await tearDown(`Failed to configure sparse-checkout: ${r}`);const{code:n,stderr:a}=await x(I(),["checkout","HEAD"],{cwd:o});0!==n&&await tearDown(`Failed to checkout sparse worktree: ${a}`)}return{worktreePath:o,worktreeBranch:a,headCommit:l,baseBranch:c,existed:!1}}export async function copyWorktreeIncludeFiles(e,t){let r;try{r=await s(m(e,".worktreeinclude"),"utf-8")}catch{return[]}const a=r.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("#"));if(0===a.length)return[];const i=await x(I(),["ls-files","--others","--ignored","--exclude-standard","--directory"],{cwd:e});if(0!==i.code||!i.stdout.trim())return[];const c=i.stdout.trim().split("\n").filter(Boolean),l=d().add(r),w=c.filter(e=>e.endsWith("/")),h=c.filter(e=>!e.endsWith("/")&&l.ignores(e)),f=w.filter(e=>!!a.some(t=>{const r=t.startsWith("/")?t.slice(1):t;if(r.startsWith(e))return!0;const o=r.search(/[*?[]/);if(o>0){const t=r.slice(0,o);if(e.startsWith(t))return!0}return!1})||!!l.ignores(e.slice(0,-1)));if(f.length>0){const t=await x(I(),["ls-files","--others","--ignored","--exclude-standard","--",...f],{cwd:e});if(0===t.code&&t.stdout.trim())for(const e of t.stdout.trim().split("\n").filter(Boolean))l.ignores(e)&&h.push(e)}const p=[];for(const r of h){const a=m(e,r),s=m(t,r);try{await n(u(s),{recursive:!0}),await o(a,s),p.push(r)}catch(e){k(`Failed to copy ${r} to worktree: ${e.message}`,{level:"warn"})}}return p.length>0&&k(`Copied ${p.length} files from .worktreeinclude: ${p.join(", ")}`),p}async function performPostCreationSetup(t,r){const a=R("localSettings"),s=m(t,a);try{const e=m(r,a);await async function(e){await n(e,{recursive:!0})}(u(e)),await o(s,e),k(`Copied settings.local.json to worktree: ${e}`)}catch(e){"ENOENT"!==g(e)&&k(`Failed to copy settings.local.json: ${e.message}`,{level:"warn"})}const l=m(t,".husky"),d=m(t,".git","hooks");let w=null;for(const e of[l,d])try{if((await i(e)).isDirectory()){w=e;break}}catch{}if(w){const e=await b(t),o=e?await C(e)??e:null;if((o?await v(o,"core",null,"hooksPath"):null)!==w){const{code:e,stderr:t}=await x(I(),["config","core.hooksPath",w],{cwd:r});0===e?k(`Configured worktree to use hooks from main repository: ${w}`):k(`Failed to configure hooks path: ${t}`,{level:"error"})}}const h=N(),f=h.worktree?.symlinkDirectories??[];if(f.length>0&&await async function(e,t,r){for(const o of r){if(D(o)){k(`Skipping symlink for "${o}": path traversal detected`,{level:"warn"});continue}const r=m(e,o),n=m(t,o);try{await c(r,n,"dir"),k(`Symlinked ${o} from main repository to worktree to avoid disk bloat`)}catch(e){const t=g(e);"ENOENT"!==t&&"EEXIST"!==t&&k(`Failed to symlink ${o} (${t??"unknown"}): ${p(e)}`,{level:"warn"})}}}(t,r,f),await copyWorktreeIncludeFiles(t,r),e("COMMIT_ATTRIBUTION")){const e=w===l?m(r,".husky"):void 0;import("./postCommitAttribution.js").then(t=>t.installPrepareCommitMsgHook(r,e).catch(e=>{k(`Failed to install attribution hook in worktree: ${e}`)})).catch(e=>{k(`Failed to load postCommitAttribution module: ${e}`)})}}export function parsePRReference(e){const t=e.match(/^https?:\/\/[^/]+\/[^/]+\/[^/]+\/pull\/(\d+)\/?(?:[?#].*)?$/i);if(t?.[1])return parseInt(t[1],10);const r=e.match(/^#(\d+)$/);return r?.[1]?parseInt(r[1],10):null}export async function isTmuxAvailable(){const{code:e}=await $("tmux",["-V"]);return 0===e}export function getTmuxInstallInstructions(){switch(B()){case"macos":return"Install tmux with: brew install tmux";case"linux":case"wsl":return"Install tmux with: sudo apt install tmux (Debian/Ubuntu) or sudo dnf install tmux (Fedora/RHEL)";case"windows":return"tmux is not natively available on Windows. Consider using WSL or Cygwin.";default:return"Install tmux using your system package manager."}}export async function createTmuxSessionForWorktree(e,t){const{code:r,stderr:o}=await $("tmux",["new-session","-d","-s",e,"-c",t]);return 0!==r?{created:!1,error:o}:{created:!0}}export async function killTmuxSession(e){const{code:t}=await $("tmux",["kill-session","-t",e]);return 0===t}export async function createWorktreeForSession(e,t,r,o){validateWorktreeSlug(t);const n=f();if(_()){const o=await F(t);k(`Created hook-based worktree at: ${o.worktreePath}`),U={originalCwd:n,worktreePath:o.worktreePath,worktreeName:t,sessionId:e,tmuxSessionName:r,hookBased:!0}}else{const a=S(f());if(!a)throw new Error("Cannot create a worktree: not in a git repository and no WorktreeCreate hooks are configured. Configure WorktreeCreate/WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.");const s=await E(),i=Date.now(),{worktreePath:c,worktreeBranch:l,headCommit:d,existed:w}=await getOrCreateWorktree(a,t,o);let u;w?k(`Resuming existing worktree at: ${c}`):(k(`Created worktree at: ${c} on branch: ${l}`),await performPostCreationSetup(a,c),u=Date.now()-i),U={originalCwd:n,worktreePath:c,worktreeName:t,worktreeBranch:l,originalBranch:s,originalHeadCommit:d,sessionId:e,tmuxSessionName:r,creationDurationMs:u,usedSparsePaths:(N().worktree?.sparsePaths?.length??0)>0}}return h(e=>({...e,activeWorktreeSession:U??void 0})),U}export async function keepWorktree(){if(U)try{const{worktreePath:e,originalCwd:t,worktreeBranch:r}=U;process.chdir(t),U=null,h(e=>({...e,activeWorktreeSession:void 0})),k(`Linked worktree preserved at: ${e}${r?` on branch: ${r}`:""}`),k(`You can continue working there by running: cd ${e}`)}catch(e){k(`Error keeping worktree: ${e}`,{level:"error"})}}export async function cleanupWorktree(){if(U)try{const{worktreePath:e,originalCwd:t,worktreeBranch:r,hookBased:o}=U;if(process.chdir(t),o){await A(e)?k(`Removed hook-based worktree at: ${e}`):k(`No WorktreeRemove hook configured, hook-based worktree left at: ${e}`,{level:"warn"})}else{const{code:r,stderr:o}=await x(I(),["worktree","remove","--force",e],{cwd:t});0!==r?k(`Failed to remove linked worktree: ${o}`,{level:"error"}):k(`Removed linked worktree at: ${e}`)}if(U=null,h(e=>({...e,activeWorktreeSession:void 0})),!o&&r){await j(100);const{code:e,stderr:o}=await x(I(),["branch","-D",r],{cwd:t});0!==e?k(`Could not delete worktree branch: ${o}`,{level:"error"}):k(`Deleted worktree branch: ${r}`)}k("Linked worktree cleaned up completely")}catch(e){k(`Error cleaning up worktree: ${e}`,{level:"error"})}}export async function createAgentWorktree(e){if(validateWorktreeSlug(e),_()){const t=await F(e);return k(`Created hook-based agent worktree at: ${t.worktreePath}`),{worktreePath:t.worktreePath,hookBased:!0}}const t=P(f());if(!t)throw new Error("Cannot create agent worktree: not in a git repository and no WorktreeCreate hooks are configured. Configure WorktreeCreate/WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.");const{worktreePath:r,worktreeBranch:o,headCommit:n,existed:a}=await getOrCreateWorktree(t,e);if(a){const e=new Date;await l(r,e,e),k(`Resuming existing agent worktree at: ${r}`)}else k(`Created agent worktree at: ${r} on branch: ${o}`),await performPostCreationSetup(t,r);return{worktreePath:r,worktreeBranch:o,headCommit:n,gitRoot:t}}export async function removeAgentWorktree(e,t,r,o){if(o){const t=await A(e);return t?k(`Removed hook-based agent worktree at: ${e}`):k(`No WorktreeRemove hook configured, hook-based agent worktree left at: ${e}`,{level:"warn"}),t}if(!r)return k("Cannot remove agent worktree: no git root provided",{level:"error"}),!1;const{code:n,stderr:a}=await x(I(),["worktree","remove","--force",e],{cwd:r});if(0!==n)return k(`Failed to remove agent worktree: ${a}`,{level:"error"}),!1;if(k(`Removed agent worktree at: ${e}`),!t)return!0;const{code:s,stderr:i}=await x(I(),["branch","-D",t],{cwd:r});return 0!==s&&k(`Could not delete agent worktree branch: ${i}`,{level:"error"}),!0}const L=[/^agent-a[0-9a-f]{7}$/,/^wf_[0-9a-f]{8}-[0-9a-f]{3}-\d+$/,/^wf-\d+$/,/^bridge-[A-Za-z0-9_]+(-[A-Za-z0-9_]+)*$/,/^job-[a-zA-Z0-9._-]{1,55}-[0-9a-f]{8}$/];export async function cleanupStaleAgentWorktrees(e){const t=P(f());if(!t)return 0;const r=worktreesDir(t);let o;try{o=await a(r)}catch{return 0}const n=e.getTime(),s=U?.worktreePath;let c=0;for(const e of o){if(!L.some(t=>t.test(e)))continue;const o=m(r,e);if(s===o)continue;let a;try{a=(await i(o)).mtimeMs}catch{continue}if(a>=n)continue;const[l,d]=await Promise.all([x(I(),["--no-optional-locks","status","--porcelain","-uno"],{cwd:o}),x(I(),["rev-list","--max-count=1","HEAD","--not","--remotes"],{cwd:o})]);0!==l.code||l.stdout.trim().length>0||(0!==d.code||d.stdout.trim().length>0||await removeAgentWorktree(o,worktreeBranchName(e),t)&&c++)}return c>0&&(await x(I(),["worktree","prune"],{cwd:t}),k(`cleanupStaleAgentWorktrees: removed ${c} stale worktree(s)`)),c}export async function hasWorktreeChanges(e,t){const{code:r,stdout:o}=await x(I(),["status","--porcelain"],{cwd:e});if(0!==r)return!0;if(o.trim().length>0)return!0;const{code:n,stdout:a}=await x(I(),["rev-list","--count",`${t}..HEAD`],{cwd:e});return 0!==n||parseInt(a.trim(),10)>0}export async function execIntoTmuxWorktree(e){if("win32"===process.platform)return{handled:!1,error:"Error: --tmux is not supported on Windows"};if(0!==r("tmux",["-V"],{encoding:"utf-8"}).status){return{handled:!1,error:`Error: tmux is not installed. ${"darwin"===process.platform?"Install tmux with: brew install tmux":"Install tmux with: sudo apt install tmux"}`}}let o,n=!1;for(let t=0;t<e.length;t++){const r=e[t];if(r)if("-w"===r||"--worktree"===r){const r=e[t+1];r&&!r.startsWith("-")&&(o=r)}else r.startsWith("--worktree=")?o=r.slice(11):"--tmux=classic"===r&&(n=!0)}let a,s,i=null;if(o&&(i=parsePRReference(o),null!==i&&(o=`pr-${i}`)),!o){const e=["swift","bright","calm","keen","bold"],t=["fox","owl","elm","oak","ray"];o=`${e[Math.floor(Math.random()*e.length)]}-${t[Math.floor(Math.random()*t.length)]}-${Math.random().toString(36).slice(2,6)}`}try{validateWorktreeSlug(o)}catch(e){return{handled:!1,error:`Error: ${e.message}`}}if(_()){try{a=(await F(o)).worktreePath}catch(e){return{handled:!1,error:`Error: ${p(e)}`}}s=w(P(f())??f()),console.log(`Using worktree via hook: ${a}`)}else{const e=P(f());if(!e)return{handled:!1,error:"Error: --worktree requires a git repository"};s=w(e),a=worktreePathFor(e,o);try{const t=await getOrCreateWorktree(e,o,null!==i?{prNumber:i}:void 0);t.existed||(console.log(`Created worktree: ${a} (based on ${t.baseBranch})`),await performPostCreationSetup(e,a))}catch(e){return{handled:!1,error:`Error: ${p(e)}`}}}const c=`${s}_${worktreeBranchName(o)}`.replace(/[/.]/g,"_"),l=[];for(let t=0;t<e.length;t++){const r=e[t];if(r&&("--tmux"!==r&&"--tmux=classic"!==r)){if("-w"===r||"--worktree"===r){const r=e[t+1];r&&!r.startsWith("-")&&t++;continue}r.startsWith("--worktree=")||l.push(r)}}let d="C-b";const u=r("tmux",["show-options","-g","prefix"],{encoding:"utf-8"});if(0===u.status&&u.stdout){const e=u.stdout.match(/prefix\s+(\S+)/);e?.[1]&&(d=e[1])}const m=["C-b","C-c","C-d","C-t","C-o","C-r","C-s","C-g","C-e"].includes(d),h={...process.env,CLAUDE_CODE_TMUX_SESSION:c,CLAUDE_CODE_TMUX_PREFIX:d,CLAUDE_CODE_TMUX_PREFIX_CONFLICTS:m?"1":""},k=0===r("tmux",["has-session","-t",c],{encoding:"utf-8"}).status,g=Boolean(process.env.TMUX),$=M()&&!n&&!g,x=$?["-CC"]:[];if($&&!k){const e=t.yellow;console.log(`\n${e("╭─ iTerm2 Tip ────────────────────────────────────────────────────────╮")}\n${e("│")} To open as a tab instead of a new window: ${e("│")}\n${e("│")} iTerm2 > Settings > General > tmux > "Tabs in attaching window" ${e("│")}\n${e("╰─────────────────────────────────────────────────────────────────────╯")}\n`)}if("ant"===process.env.USER_TYPE&&"claude-cli-internal"===s&&!k)r("tmux",["new-session","-d","-s",c,"-c",a,"--",process.execPath,...l],{cwd:a,env:h}),r("tmux",["split-window","-h","-t",c,"-c",a],{cwd:a}),r("tmux",["send-keys","-t",c,"bun run watch","Enter"],{cwd:a}),r("tmux",["split-window","-v","-t",c,"-c",a],{cwd:a}),r("tmux",["send-keys","-t",c,"bun run start"],{cwd:a}),r("tmux",["select-pane","-t",`${c}:0.0`],{cwd:a}),g?r("tmux",["switch-client","-t",c],{stdio:"inherit"}):r("tmux",[...x,"attach-session","-t",c],{stdio:"inherit",cwd:a});else if(g)k||r("tmux",["new-session","-d","-s",c,"-c",a,"--",process.execPath,...l],{cwd:a,env:h}),r("tmux",["switch-client","-t",c],{stdio:"inherit"});else{const e=[...x,"new-session","-A","-s",c,"-c",a,"--",process.execPath,...l];r("tmux",e,{stdio:"inherit",cwd:a,env:h})}return{handled:!0}}
1
+ import{feature as e}from"../recovery/bunBundleShim.js";import t from"chalk";import{spawnSync as r}from"child_process";import{copyFile as o,mkdir as n,readdir as a,readFile as s,stat as i,symlink as c,utimes as l}from"fs/promises";import d from"ignore";import{basename as w,dirname as u,join as m}from"path";import{saveCurrentProjectConfig as h}from"./config.js";import{CONTEXT_DIR_NAME as f}from"./configConstants.js";import{getCwd as k}from"./cwd.js";import{logForDebugging as p}from"./debug.js";import{errorMessage as g,getErrnoCode as $}from"./errors.js";import{execFileNoThrow as x,execFileNoThrowWithCwd as v}from"./execFileNoThrow.js";import{parseGitConfigValue as C}from"./git/gitConfigParser.js";import{getCommonDir as y,readWorktreeHeadSha as b,resolveGitDir as W,resolveRef as P}from"./git/gitFilesystem.js";import{findCanonicalGitRoot as S,findGitRoot as E,getBranch as T,getDefaultBranch as I,gitExe as F}from"./git.js";import{executeWorktreeCreateHook as A,executeWorktreeRemoveHook as _,hasWorktreeCreateHook as D}from"./hooks.js";import{containsPathTraversal as B}from"./path.js";import{getPlatform as N}from"./platform.js";import{getInitialSettings as R,getRelativeSettingsFilePathForSource as j}from"./settings/settings.js";import{sleep as M}from"./sleep.js";import{isInITerm2 as O}from"./swarm/backends/detection.js";const U=/^[a-zA-Z0-9._-]+$/;export function validateWorktreeSlug(e){if(e.length>64)throw new Error(`Invalid worktree name: must be 64 characters or fewer (got ${e.length})`);for(const t of e.split("/")){if("."===t||".."===t)throw new Error(`Invalid worktree name "${e}": must not contain "." or ".." path segments`);if(!U.test(t))throw new Error(`Invalid worktree name "${e}": each "/"-separated segment must be non-empty and contain only letters, digits, dots, underscores, and dashes`)}}let H=null;export function getCurrentWorktreeSession(){return H}export function restoreWorktreeSession(e){H=e}export function generateTmuxSessionName(e,t){return`${w(e)}_${t}`.replace(/[/.]/g,"_")}const L={GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:""};function worktreesDir(e){return m(e,f,"worktrees")}function flattenSlug(e){return e.replaceAll("/","+")}export function worktreeBranchName(e){return`worktree-${flattenSlug(e)}`}function worktreePathFor(e,t){return m(worktreesDir(e),flattenSlug(t))}async function getOrCreateWorktree(e,t,r){const o=worktreePathFor(e,t),a=worktreeBranchName(t),s=await b(o);if(s)return{worktreePath:o,worktreeBranch:a,headCommit:s,existed:!0};await n(worktreesDir(e),{recursive:!0});const i={...process.env,...L};let c,l=null;if(r?.prNumber){const{code:t,stderr:o}=await v(F(),["fetch","origin",`pull/${r.prNumber}/head`],{cwd:e,stdin:"ignore",env:i});if(0!==t)throw new Error(`Failed to fetch PR #${r.prNumber}: ${o.trim()||'PR may not exist or the repository may not have a remote named "origin"'}`);c="FETCH_HEAD"}else{const[t,r]=await Promise.all([I(),W(e)]),o=`origin/${t}`,n=r?await P(r,`refs/remotes/origin/${t}`):null;if(n)c=o,l=n;else{const{code:r}=await v(F(),["fetch","origin",t],{cwd:e,stdin:"ignore",env:i});c=0===r?o:"HEAD"}}if(!l){const{stdout:t,code:r}=await v(F(),["rev-parse",c],{cwd:e});if(0!==r)throw new Error(`Failed to resolve base branch "${c}": git rev-parse failed`);l=t.trim()}const d=R().worktree?.sparsePaths,w=["worktree","add"];d?.length&&w.push("--no-checkout"),w.push("-B",a,o,c);const{code:u,stderr:m}=await v(F(),w,{cwd:e});if(0!==u)throw new Error(`Failed to create worktree: ${m}`);if(d?.length){const tearDown=async t=>{throw await v(F(),["worktree","remove","--force",o],{cwd:e}),new Error(t)},{code:t,stderr:r}=await v(F(),["sparse-checkout","set","--cone","--",...d],{cwd:o});0!==t&&await tearDown(`Failed to configure sparse-checkout: ${r}`);const{code:n,stderr:a}=await v(F(),["checkout","HEAD"],{cwd:o});0!==n&&await tearDown(`Failed to checkout sparse worktree: ${a}`)}return{worktreePath:o,worktreeBranch:a,headCommit:l,baseBranch:c,existed:!1}}export async function copyWorktreeIncludeFiles(e,t){let r;try{r=await s(m(e,".worktreeinclude"),"utf-8")}catch{return[]}const a=r.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("#"));if(0===a.length)return[];const i=await v(F(),["ls-files","--others","--ignored","--exclude-standard","--directory"],{cwd:e});if(0!==i.code||!i.stdout.trim())return[];const c=i.stdout.trim().split("\n").filter(Boolean),l=d().add(r),w=c.filter(e=>e.endsWith("/")),h=c.filter(e=>!e.endsWith("/")&&l.ignores(e)),f=w.filter(e=>!!a.some(t=>{const r=t.startsWith("/")?t.slice(1):t;if(r.startsWith(e))return!0;const o=r.search(/[*?[]/);if(o>0){const t=r.slice(0,o);if(e.startsWith(t))return!0}return!1})||!!l.ignores(e.slice(0,-1)));if(f.length>0){const t=await v(F(),["ls-files","--others","--ignored","--exclude-standard","--",...f],{cwd:e});if(0===t.code&&t.stdout.trim())for(const e of t.stdout.trim().split("\n").filter(Boolean))l.ignores(e)&&h.push(e)}const k=[];for(const r of h){const a=m(e,r),s=m(t,r);try{await n(u(s),{recursive:!0}),await o(a,s),k.push(r)}catch(e){p(`Failed to copy ${r} to worktree: ${e.message}`,{level:"warn"})}}return k.length>0&&p(`Copied ${k.length} files from .worktreeinclude: ${k.join(", ")}`),k}async function performPostCreationSetup(t,r){const a=j("localSettings"),s=m(t,a);try{const e=m(r,a);await async function(e){await n(e,{recursive:!0})}(u(e)),await o(s,e),p(`Copied settings.local.json to worktree: ${e}`)}catch(e){"ENOENT"!==$(e)&&p(`Failed to copy settings.local.json: ${e.message}`,{level:"warn"})}const l=m(t,".husky"),d=m(t,".git","hooks");let w=null;for(const e of[l,d])try{if((await i(e)).isDirectory()){w=e;break}}catch{}if(w){const e=await W(t),o=e?await y(e)??e:null;if((o?await C(o,"core",null,"hooksPath"):null)!==w){const{code:e,stderr:t}=await v(F(),["config","core.hooksPath",w],{cwd:r});0===e?p(`Configured worktree to use hooks from main repository: ${w}`):p(`Failed to configure hooks path: ${t}`,{level:"error"})}}const h=R(),f=h.worktree?.symlinkDirectories??[];if(f.length>0&&await async function(e,t,r){for(const o of r){if(B(o)){p(`Skipping symlink for "${o}": path traversal detected`,{level:"warn"});continue}const r=m(e,o),n=m(t,o);try{await c(r,n,"dir"),p(`Symlinked ${o} from main repository to worktree to avoid disk bloat`)}catch(e){const t=$(e);"ENOENT"!==t&&"EEXIST"!==t&&p(`Failed to symlink ${o} (${t??"unknown"}): ${g(e)}`,{level:"warn"})}}}(t,r,f),await copyWorktreeIncludeFiles(t,r),e("COMMIT_ATTRIBUTION")){const e=w===l?m(r,".husky"):void 0;import("./postCommitAttribution.js").then(t=>t.installPrepareCommitMsgHook(r,e).catch(e=>{p(`Failed to install attribution hook in worktree: ${e}`)})).catch(e=>{p(`Failed to load postCommitAttribution module: ${e}`)})}}export function parsePRReference(e){const t=e.match(/^https?:\/\/[^/]+\/[^/]+\/[^/]+\/pull\/(\d+)\/?(?:[?#].*)?$/i);if(t?.[1])return parseInt(t[1],10);const r=e.match(/^#(\d+)$/);return r?.[1]?parseInt(r[1],10):null}export async function isTmuxAvailable(){const{code:e}=await x("tmux",["-V"]);return 0===e}export function getTmuxInstallInstructions(){switch(N()){case"macos":return"Install tmux with: brew install tmux";case"linux":case"wsl":return"Install tmux with: sudo apt install tmux (Debian/Ubuntu) or sudo dnf install tmux (Fedora/RHEL)";case"windows":return"tmux is not natively available on Windows. Consider using WSL or Cygwin.";default:return"Install tmux using your system package manager."}}export async function createTmuxSessionForWorktree(e,t){const{code:r,stderr:o}=await x("tmux",["new-session","-d","-s",e,"-c",t]);return 0!==r?{created:!1,error:o}:{created:!0}}export async function killTmuxSession(e){const{code:t}=await x("tmux",["kill-session","-t",e]);return 0===t}export async function createWorktreeForSession(e,t,r,o){validateWorktreeSlug(t);const n=k();if(D()){const o=await A(t);p(`Created hook-based worktree at: ${o.worktreePath}`),H={originalCwd:n,worktreePath:o.worktreePath,worktreeName:t,sessionId:e,tmuxSessionName:r,hookBased:!0}}else{const a=E(k());if(!a)throw new Error("Cannot create a worktree: not in a git repository and no WorktreeCreate hooks are configured. Configure WorktreeCreate/WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.");const s=await T(),i=Date.now(),{worktreePath:c,worktreeBranch:l,headCommit:d,existed:w}=await getOrCreateWorktree(a,t,o);let u;w?p(`Resuming existing worktree at: ${c}`):(p(`Created worktree at: ${c} on branch: ${l}`),await performPostCreationSetup(a,c),u=Date.now()-i),H={originalCwd:n,worktreePath:c,worktreeName:t,worktreeBranch:l,originalBranch:s,originalHeadCommit:d,sessionId:e,tmuxSessionName:r,creationDurationMs:u,usedSparsePaths:(R().worktree?.sparsePaths?.length??0)>0}}return h(e=>({...e,activeWorktreeSession:H??void 0})),H}export async function keepWorktree(){if(H)try{const{worktreePath:e,originalCwd:t,worktreeBranch:r}=H;process.chdir(t),H=null,h(e=>({...e,activeWorktreeSession:void 0})),p(`Linked worktree preserved at: ${e}${r?` on branch: ${r}`:""}`),p(`You can continue working there by running: cd ${e}`)}catch(e){p(`Error keeping worktree: ${e}`,{level:"error"})}}export async function cleanupWorktree(){if(H)try{const{worktreePath:e,originalCwd:t,worktreeBranch:r,hookBased:o}=H;if(process.chdir(t),o){await _(e)?p(`Removed hook-based worktree at: ${e}`):p(`No WorktreeRemove hook configured, hook-based worktree left at: ${e}`,{level:"warn"})}else{const{code:r,stderr:o}=await v(F(),["worktree","remove","--force",e],{cwd:t});0!==r?p(`Failed to remove linked worktree: ${o}`,{level:"error"}):p(`Removed linked worktree at: ${e}`)}if(H=null,h(e=>({...e,activeWorktreeSession:void 0})),!o&&r){await M(100);const{code:e,stderr:o}=await v(F(),["branch","-D",r],{cwd:t});0!==e?p(`Could not delete worktree branch: ${o}`,{level:"error"}):p(`Deleted worktree branch: ${r}`)}p("Linked worktree cleaned up completely")}catch(e){p(`Error cleaning up worktree: ${e}`,{level:"error"})}}export async function createAgentWorktree(e){if(validateWorktreeSlug(e),D()){const t=await A(e);return p(`Created hook-based agent worktree at: ${t.worktreePath}`),{worktreePath:t.worktreePath,hookBased:!0}}const t=S(k());if(!t)throw new Error("Cannot create agent worktree: not in a git repository and no WorktreeCreate hooks are configured. Configure WorktreeCreate/WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.");const{worktreePath:r,worktreeBranch:o,headCommit:n,existed:a}=await getOrCreateWorktree(t,e);if(a){const e=new Date;await l(r,e,e),p(`Resuming existing agent worktree at: ${r}`)}else p(`Created agent worktree at: ${r} on branch: ${o}`),await performPostCreationSetup(t,r);return{worktreePath:r,worktreeBranch:o,headCommit:n,gitRoot:t}}export async function removeAgentWorktree(e,t,r,o){if(o){const t=await _(e);return t?p(`Removed hook-based agent worktree at: ${e}`):p(`No WorktreeRemove hook configured, hook-based agent worktree left at: ${e}`,{level:"warn"}),t}if(!r)return p("Cannot remove agent worktree: no git root provided",{level:"error"}),!1;const{code:n,stderr:a}=await v(F(),["worktree","remove","--force",e],{cwd:r});if(0!==n)return p(`Failed to remove agent worktree: ${a}`,{level:"error"}),!1;if(p(`Removed agent worktree at: ${e}`),!t)return!0;const{code:s,stderr:i}=await v(F(),["branch","-D",t],{cwd:r});return 0!==s&&p(`Could not delete agent worktree branch: ${i}`,{level:"error"}),!0}const X=[/^agent-a[0-9a-f]{7}$/,/^wf_[0-9a-f]{8}-[0-9a-f]{3}-\d+$/,/^wf-\d+$/,/^bridge-[A-Za-z0-9_]+(-[A-Za-z0-9_]+)*$/,/^job-[a-zA-Z0-9._-]{1,55}-[0-9a-f]{8}$/];export async function cleanupStaleAgentWorktrees(e){const t=S(k());if(!t)return 0;const r=worktreesDir(t);let o;try{o=await a(r)}catch{return 0}const n=e.getTime(),s=H?.worktreePath;let c=0;for(const e of o){if(!X.some(t=>t.test(e)))continue;const o=m(r,e);if(s===o)continue;let a;try{a=(await i(o)).mtimeMs}catch{continue}if(a>=n)continue;const[l,d]=await Promise.all([v(F(),["--no-optional-locks","status","--porcelain","-uno"],{cwd:o}),v(F(),["rev-list","--max-count=1","HEAD","--not","--remotes"],{cwd:o})]);0!==l.code||l.stdout.trim().length>0||(0!==d.code||d.stdout.trim().length>0||await removeAgentWorktree(o,worktreeBranchName(e),t)&&c++)}return c>0&&(await v(F(),["worktree","prune"],{cwd:t}),p(`cleanupStaleAgentWorktrees: removed ${c} stale worktree(s)`)),c}export async function hasWorktreeChanges(e,t){const{code:r,stdout:o}=await v(F(),["status","--porcelain"],{cwd:e});if(0!==r)return!0;if(o.trim().length>0)return!0;const{code:n,stdout:a}=await v(F(),["rev-list","--count",`${t}..HEAD`],{cwd:e});return 0!==n||parseInt(a.trim(),10)>0}export async function execIntoTmuxWorktree(e){if("win32"===process.platform)return{handled:!1,error:"Error: --tmux is not supported on Windows"};if(0!==r("tmux",["-V"],{encoding:"utf-8"}).status){return{handled:!1,error:`Error: tmux is not installed. ${"darwin"===process.platform?"Install tmux with: brew install tmux":"Install tmux with: sudo apt install tmux"}`}}let o,n=!1;for(let t=0;t<e.length;t++){const r=e[t];if(r)if("-w"===r||"--worktree"===r){const r=e[t+1];r&&!r.startsWith("-")&&(o=r)}else r.startsWith("--worktree=")?o=r.slice(11):"--tmux=classic"===r&&(n=!0)}let a,s,i=null;if(o&&(i=parsePRReference(o),null!==i&&(o=`pr-${i}`)),!o){const e=["swift","bright","calm","keen","bold"],t=["fox","owl","elm","oak","ray"];o=`${e[Math.floor(Math.random()*e.length)]}-${t[Math.floor(Math.random()*t.length)]}-${Math.random().toString(36).slice(2,6)}`}try{validateWorktreeSlug(o)}catch(e){return{handled:!1,error:`Error: ${e.message}`}}if(D()){try{a=(await A(o)).worktreePath}catch(e){return{handled:!1,error:`Error: ${g(e)}`}}s=w(S(k())??k()),console.log(`Using worktree via hook: ${a}`)}else{const e=S(k());if(!e)return{handled:!1,error:"Error: --worktree requires a git repository"};s=w(e),a=worktreePathFor(e,o);try{const t=await getOrCreateWorktree(e,o,null!==i?{prNumber:i}:void 0);t.existed||(console.log(`Created worktree: ${a} (based on ${t.baseBranch})`),await performPostCreationSetup(e,a))}catch(e){return{handled:!1,error:`Error: ${g(e)}`}}}const c=`${s}_${worktreeBranchName(o)}`.replace(/[/.]/g,"_"),l=[];for(let t=0;t<e.length;t++){const r=e[t];if(r&&("--tmux"!==r&&"--tmux=classic"!==r)){if("-w"===r||"--worktree"===r){const r=e[t+1];r&&!r.startsWith("-")&&t++;continue}r.startsWith("--worktree=")||l.push(r)}}let d="C-b";const u=r("tmux",["show-options","-g","prefix"],{encoding:"utf-8"});if(0===u.status&&u.stdout){const e=u.stdout.match(/prefix\s+(\S+)/);e?.[1]&&(d=e[1])}const m=["C-b","C-c","C-d","C-t","C-o","C-r","C-s","C-g","C-e"].includes(d),h={...process.env,CLAUDE_CODE_TMUX_SESSION:c,CLAUDE_CODE_TMUX_PREFIX:d,CLAUDE_CODE_TMUX_PREFIX_CONFLICTS:m?"1":""},f=0===r("tmux",["has-session","-t",c],{encoding:"utf-8"}).status,p=Boolean(process.env.TMUX),$=O()&&!n&&!p,x=$?["-CC"]:[];if($&&!f){const e=t.yellow;console.log(`\n${e("╭─ iTerm2 Tip ────────────────────────────────────────────────────────╮")}\n${e("│")} To open as a tab instead of a new window: ${e("│")}\n${e("│")} iTerm2 > Settings > General > tmux > "Tabs in attaching window" ${e("│")}\n${e("╰─────────────────────────────────────────────────────────────────────╯")}\n`)}if("ant"===process.env.USER_TYPE&&"claude-cli-internal"===s&&!f)r("tmux",["new-session","-d","-s",c,"-c",a,"--",process.execPath,...l],{cwd:a,env:h}),r("tmux",["split-window","-h","-t",c,"-c",a],{cwd:a}),r("tmux",["send-keys","-t",c,"bun run watch","Enter"],{cwd:a}),r("tmux",["split-window","-v","-t",c,"-c",a],{cwd:a}),r("tmux",["send-keys","-t",c,"bun run start"],{cwd:a}),r("tmux",["select-pane","-t",`${c}:0.0`],{cwd:a}),p?r("tmux",["switch-client","-t",c],{stdio:"inherit"}):r("tmux",[...x,"attach-session","-t",c],{stdio:"inherit",cwd:a});else if(p)f||r("tmux",["new-session","-d","-s",c,"-c",a,"--",process.execPath,...l],{cwd:a,env:h}),r("tmux",["switch-client","-t",c],{stdio:"inherit"});else{const e=[...x,"new-session","-A","-s",c,"-c",a,"--",process.execPath,...l];r("tmux",e,{stdio:"inherit",cwd:a,env:h})}return{handled:!0}}
@@ -1 +1 @@
1
- import{homedir as e}from"node:os";import{basename as r,join as t,resolve as n}from"node:path";import{getGlobalConfig as a,saveGlobalConfig as o}from"../utils/config.js";import{getSecureStorage as i}from"../utils/secureStorage/index.js";function writeSecureConfig(e){const r=i(),t=r.read()??{};if(!e){const{whatsappBot:e,...n}=t;return void r.update(n)}r.update({...t,whatsappBot:e})}function clearLegacyConfig(){o(e=>{const{whatsappBot:r,...t}=e;return t})}export function getWhatsAppConfig(){const e=i().read()?.whatsappBot;if(e)return e;const r=a().whatsappBot;return r&&(writeSecureConfig(r),clearLegacyConfig()),r}export function getWhatsAppAuthDir(){const r=getWhatsAppConfig()?.authDir?.trim();if(r)return r;const n=process.env.CONTEXT_CONFIG_DIR?.trim()||process.env.CLAUDE_CONFIG_DIR?.trim()||t(e(),".contextcli");return t(n,"whatsapp-auth")}export function getAllowedNumbers(){return getWhatsAppConfig()?.allowedNumbers??[]}export function getPrimaryRecipient(){return getWhatsAppConfig()?.primaryRecipient}export function listWhatsAppWorkspaces(){const e=getWhatsAppConfig();return e?.workspaces??[]}export function getActiveWorkspaceAlias(){return getWhatsAppConfig()?.activeWorkspaceAlias}export function getActiveWorkspaceAliasForNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig();return t?.activeWorkspaceByNumber?.[r]??t?.activeWorkspaceAlias}export function getActiveWorkspace(){const e=getWhatsAppConfig(),r=e?.activeWorkspaceAlias;if(r)return(e?.workspaces??[]).find(e=>e.alias===r)}export function getActiveWorkspaceForNumber(e){const r=getActiveWorkspaceAliasForNumber(e);if(r)return listWhatsAppWorkspaces().find(e=>e.alias===r)}export function setActiveWorkspaceAlias(e){const r=normalizeAlias(e),t=getWhatsAppConfig(),n=t?.workspaces??[];return!!n.some(e=>e.alias===r)&&(writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],activeWorkspaceAlias:r,workspaces:n}),clearLegacyConfig(),!0)}export function setActiveWorkspaceAliasForNumber(e,r){const t=normalizeAlias(r),n=normalizeE164(e),a=getWhatsAppConfig(),o=a?.workspaces??[];if(!o.some(e=>e.alias===t)||!n)return!1;const i={...a?.activeWorkspaceByNumber??{}};return i[n]=t,writeSecureConfig({...a??{},running:a?.running??!1,allowedNumbers:a?.allowedNumbers??[],activeWorkspaceByNumber:i,activeWorkspaceAlias:a?.activeWorkspaceAlias??t,workspaces:o}),clearLegacyConfig(),!0}export function ensureCurrentWorkspaceRegistered(){const e=normalizeWorkspacePath(process.cwd()),r=(new Date).toISOString(),t=getWhatsAppConfig(),n=[...t?.workspaces??[]],a=n.findIndex(r=>normalizeWorkspacePath(r.path)===e);if(a>=0){const o={...n[a],lastSeenAt:r,path:e};return n[a]=o,writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],workspaces:n,activeWorkspaceAlias:t?.activeWorkspaceAlias??o.alias}),clearLegacyConfig(),o}const o=makeUniqueAlias(n,deriveAliasFromPath(e)),i={alias:o,path:e,createdAt:r,lastSeenAt:r};return n.push(i),writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],workspaces:n,activeWorkspaceAlias:o}),clearLegacyConfig(),i}export function ensureWorkspaceRegisteredByPath(e){const r=normalizeWorkspacePath(e),t=(new Date).toISOString(),n=getWhatsAppConfig(),a=[...n?.workspaces??[]],o=a.findIndex(e=>normalizeWorkspacePath(e.path)===r);if(o>=0){const e={...a[o],path:r,lastSeenAt:t};return a[o]=e,writeSecureConfig({...n??{},running:n?.running??!1,allowedNumbers:n?.allowedNumbers??[],workspaces:a,activeWorkspaceAlias:n?.activeWorkspaceAlias??e.alias}),clearLegacyConfig(),e}const i={alias:makeUniqueAlias(a,deriveAliasFromPath(r)),path:r,createdAt:t,lastSeenAt:t};return a.push(i),writeSecureConfig({...n??{},running:n?.running??!1,allowedNumbers:n?.allowedNumbers??[],workspaces:a,activeWorkspaceAlias:n?.activeWorkspaceAlias??i.alias}),clearLegacyConfig(),i}export function setWhatsAppRunning(e){const r=getWhatsAppConfig();writeSecureConfig({...r??{},running:e,allowedNumbers:r?.allowedNumbers??[]}),clearLegacyConfig()}export function setPrimaryRecipient(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[],a=n.includes(r)?n:[...n,r];writeSecureConfig({...t??{},running:t?.running??!1,primaryRecipient:r,allowedNumbers:a}),clearLegacyConfig()}export function addAllowedNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[];n.includes(r)||(writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:[...n,r]}),clearLegacyConfig())}export function removeAllowedNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[];writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:n.filter(e=>e!==r)}),clearLegacyConfig()}export function setSelfIdentity(e,r){const t=getWhatsAppConfig();writeSecureConfig({...t??{},running:t?.running??!1,selfJid:e,selfE164:r,allowedNumbers:t?.allowedNumbers??[]}),clearLegacyConfig()}export function clearWhatsAppConfig(){writeSecureConfig(void 0),clearLegacyConfig()}export function clearLinkedIdentity(){const e=getWhatsAppConfig();writeSecureConfig({...e??{},selfJid:void 0,selfE164:void 0,running:!1,allowedNumbers:e?.allowedNumbers??[]}),clearLegacyConfig()}export function normalizeE164(e){const r=e.trim().replace(/[\s()\-]/g,"");if(!r)return"";if(r.startsWith("+"))return`+${r.slice(1).replace(/\D/g,"")}`;const t=r.replace(/\D/g,"");return t?`+${t}`:""}export function e164ToJid(e){return`${normalizeE164(e).replace(/^\+/,"")}@s.whatsapp.net`}export function jidToE164(e){const r=(e.split(":")[0]??e).split("@")[0]??"";return r&&/^\d+$/.test(r)?`+${r}`:null}function makeUniqueAlias(e,r){const t=new Set(e.map(e=>e.alias));if(!t.has(r))return r;let n=2;for(;t.has(`${r}-${n}`);)n+=1;return`${r}-${n}`}function deriveAliasFromPath(e){return normalizeAlias(r(e)||"workspace")||"workspace"}function normalizeAlias(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"workspace"}function normalizeWorkspacePath(e){const r=n(e);return"win32"===process.platform?r.toLowerCase():r}
1
+ import{homedir as e}from"node:os";import{basename as r,join as t,resolve as n}from"node:path";import{getGlobalConfig as a,saveGlobalConfig as o}from"../utils/config.js";import{getSecureStorage as i}from"../utils/secureStorage/index.js";import{CONTEXT_DIR_NAME as s}from"../utils/configConstants.js";function writeSecureConfig(e){const r=i(),t=r.read()??{};if(!e){const{whatsappBot:e,...n}=t;return void r.update(n)}r.update({...t,whatsappBot:e})}function clearLegacyConfig(){o(e=>{const{whatsappBot:r,...t}=e;return t})}export function getWhatsAppConfig(){const e=i().read()?.whatsappBot;if(e)return e;const r=a().whatsappBot;return r&&(writeSecureConfig(r),clearLegacyConfig()),r}export function getWhatsAppAuthDir(){const r=getWhatsAppConfig()?.authDir?.trim();if(r)return r;const n=process.env.CONTEXT_CONFIG_DIR?.trim()||process.env.CLAUDE_CONFIG_DIR?.trim()||t(e(),s);return t(n,"whatsapp-auth")}export function getAllowedNumbers(){return getWhatsAppConfig()?.allowedNumbers??[]}export function getPrimaryRecipient(){return getWhatsAppConfig()?.primaryRecipient}export function listWhatsAppWorkspaces(){const e=getWhatsAppConfig();return e?.workspaces??[]}export function getActiveWorkspaceAlias(){return getWhatsAppConfig()?.activeWorkspaceAlias}export function getActiveWorkspaceAliasForNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig();return t?.activeWorkspaceByNumber?.[r]??t?.activeWorkspaceAlias}export function getActiveWorkspace(){const e=getWhatsAppConfig(),r=e?.activeWorkspaceAlias;if(r)return(e?.workspaces??[]).find(e=>e.alias===r)}export function getActiveWorkspaceForNumber(e){const r=getActiveWorkspaceAliasForNumber(e);if(r)return listWhatsAppWorkspaces().find(e=>e.alias===r)}export function setActiveWorkspaceAlias(e){const r=normalizeAlias(e),t=getWhatsAppConfig(),n=t?.workspaces??[];return!!n.some(e=>e.alias===r)&&(writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],activeWorkspaceAlias:r,workspaces:n}),clearLegacyConfig(),!0)}export function setActiveWorkspaceAliasForNumber(e,r){const t=normalizeAlias(r),n=normalizeE164(e),a=getWhatsAppConfig(),o=a?.workspaces??[];if(!o.some(e=>e.alias===t)||!n)return!1;const i={...a?.activeWorkspaceByNumber??{}};return i[n]=t,writeSecureConfig({...a??{},running:a?.running??!1,allowedNumbers:a?.allowedNumbers??[],activeWorkspaceByNumber:i,activeWorkspaceAlias:a?.activeWorkspaceAlias??t,workspaces:o}),clearLegacyConfig(),!0}export function ensureCurrentWorkspaceRegistered(){const e=normalizeWorkspacePath(process.cwd()),r=(new Date).toISOString(),t=getWhatsAppConfig(),n=[...t?.workspaces??[]],a=n.findIndex(r=>normalizeWorkspacePath(r.path)===e);if(a>=0){const o={...n[a],lastSeenAt:r,path:e};return n[a]=o,writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],workspaces:n,activeWorkspaceAlias:t?.activeWorkspaceAlias??o.alias}),clearLegacyConfig(),o}const o=makeUniqueAlias(n,deriveAliasFromPath(e)),i={alias:o,path:e,createdAt:r,lastSeenAt:r};return n.push(i),writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:t?.allowedNumbers??[],workspaces:n,activeWorkspaceAlias:o}),clearLegacyConfig(),i}export function ensureWorkspaceRegisteredByPath(e){const r=normalizeWorkspacePath(e),t=(new Date).toISOString(),n=getWhatsAppConfig(),a=[...n?.workspaces??[]],o=a.findIndex(e=>normalizeWorkspacePath(e.path)===r);if(o>=0){const e={...a[o],path:r,lastSeenAt:t};return a[o]=e,writeSecureConfig({...n??{},running:n?.running??!1,allowedNumbers:n?.allowedNumbers??[],workspaces:a,activeWorkspaceAlias:n?.activeWorkspaceAlias??e.alias}),clearLegacyConfig(),e}const i={alias:makeUniqueAlias(a,deriveAliasFromPath(r)),path:r,createdAt:t,lastSeenAt:t};return a.push(i),writeSecureConfig({...n??{},running:n?.running??!1,allowedNumbers:n?.allowedNumbers??[],workspaces:a,activeWorkspaceAlias:n?.activeWorkspaceAlias??i.alias}),clearLegacyConfig(),i}export function setWhatsAppRunning(e){const r=getWhatsAppConfig();writeSecureConfig({...r??{},running:e,allowedNumbers:r?.allowedNumbers??[]}),clearLegacyConfig()}export function setPrimaryRecipient(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[],a=n.includes(r)?n:[...n,r];writeSecureConfig({...t??{},running:t?.running??!1,primaryRecipient:r,allowedNumbers:a}),clearLegacyConfig()}export function addAllowedNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[];n.includes(r)||(writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:[...n,r]}),clearLegacyConfig())}export function removeAllowedNumber(e){const r=normalizeE164(e),t=getWhatsAppConfig(),n=t?.allowedNumbers??[];writeSecureConfig({...t??{},running:t?.running??!1,allowedNumbers:n.filter(e=>e!==r)}),clearLegacyConfig()}export function setSelfIdentity(e,r){const t=getWhatsAppConfig();writeSecureConfig({...t??{},running:t?.running??!1,selfJid:e,selfE164:r,allowedNumbers:t?.allowedNumbers??[]}),clearLegacyConfig()}export function clearWhatsAppConfig(){writeSecureConfig(void 0),clearLegacyConfig()}export function clearLinkedIdentity(){const e=getWhatsAppConfig();writeSecureConfig({...e??{},selfJid:void 0,selfE164:void 0,running:!1,allowedNumbers:e?.allowedNumbers??[]}),clearLegacyConfig()}export function normalizeE164(e){const r=e.trim().replace(/[\s()\-]/g,"");if(!r)return"";if(r.startsWith("+"))return`+${r.slice(1).replace(/\D/g,"")}`;const t=r.replace(/\D/g,"");return t?`+${t}`:""}export function e164ToJid(e){return`${normalizeE164(e).replace(/^\+/,"")}@s.whatsapp.net`}export function jidToE164(e){const r=(e.split(":")[0]??e).split("@")[0]??"";return r&&/^\d+$/.test(r)?`+${r}`:null}function makeUniqueAlias(e,r){const t=new Set(e.map(e=>e.alias));if(!t.has(r))return r;let n=2;for(;t.has(`${r}-${n}`);)n+=1;return`${r}-${n}`}function deriveAliasFromPath(e){return normalizeAlias(r(e)||"workspace")||"workspace"}function normalizeAlias(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"workspace"}function normalizeWorkspacePath(e){const r=n(e);return"win32"===process.platform?r.toLowerCase():r}
@@ -1 +1 @@
1
- import{A as e,Aa as r,Ba as i,C as a,Ca as o,Da as l,Ea as u,J as h,P as p,Q as b,a as g,aa as w,c as d,d as v,f as E,h as f,i as m,j as y,k as O,l as k,m as T,o as C,pa as A,q as I,r as P,s as S,t as M,wa as N,xa as D,ya as W,za as L}from"./chunk-VAB2VXFI.js";var _="Service workers are disabled or not supported by this browser",x=class{serviceWorker;worker;registration;events;constructor(e,r){if(this.serviceWorker=e,e){let i=null,a=new v;this.worker=new d(e=>(null!==i&&e.next(i),a.subscribe(r=>e.next(r))));let s=()=>{let{controller:r}=e;null!==r&&(i=r,a.next(i))};e.addEventListener("controllerchange",s),s(),this.registration=this.worker.pipe(O(()=>e.getRegistration()));let o=new v;this.events=o.asObservable();let c=e=>{let{data:r}=e;r?.type&&o.next(r)};e.addEventListener("message",c),r?.get(b,null,{optional:!0})?.onDestroy(()=>{e.removeEventListener("controllerchange",s),e.removeEventListener("message",c)})}else this.worker=this.events=this.registration=new d(e=>e.error(new k(5601,!1)))}postMessage(e,r){return new Promise(i=>{this.worker.pipe(y(1)).subscribe(a=>{a.postMessage(g({action:e},r)),i()})})}postMessageWithOperation(e,r,i){let a=this.waitForOperationCompleted(i),o=this.postMessage(e,r);return Promise.all([o,a]).then(([,e])=>e)}generateNonce(){return Math.round(1e7*Math.random())}eventsOfType(e){let r;return r="string"==typeof e?r=>r.type===e:r=>e.includes(r.type),this.events.pipe(m(r))}nextEventOfType(e){return this.eventsOfType(e).pipe(y(1))}waitForOperationCompleted(e){return new Promise((r,i)=>{this.eventsOfType("OPERATION_COMPLETED").pipe(m(r=>r.nonce===e),y(1),E(e=>{if(void 0!==e.result)return e.result;throw new Error(e.error)})).subscribe({next:r,error:i})})}get isEnabled(){return!!this.serviceWorker}},V=(()=>{class n{sw;messages;notificationClicks;subscription;get isEnabled(){return this.sw.isEnabled}pushManager=null;subscriptionChanges=new v;constructor(e){if(this.sw=e,!e.isEnabled)return this.messages=f,this.notificationClicks=f,void(this.subscription=f);this.messages=this.sw.eventsOfType("PUSH").pipe(E(e=>e.data)),this.notificationClicks=this.sw.eventsOfType("NOTIFICATION_CLICK").pipe(E(e=>e.data)),this.pushManager=this.sw.registration.pipe(E(e=>e.pushManager));let r=this.pushManager.pipe(O(e=>e.getSubscription()));this.subscription=new d(e=>{let i=r.subscribe(e),a=this.subscriptionChanges.subscribe(e);return()=>{i.unsubscribe(),a.unsubscribe()}})}requestSubscription(e){if(!this.sw.isEnabled||null===this.pushManager)return Promise.reject(new Error(_));let r={userVisibleOnly:!0},i=this.decodeBase64(e.serverPublicKey.replace(/_/g,"/").replace(/-/g,"+")),a=new Uint8Array(new ArrayBuffer(i.length));for(let e=0;e<i.length;e++)a[e]=i.charCodeAt(e);return r.applicationServerKey=a,new Promise((e,i)=>{this.pushManager.pipe(O(e=>e.subscribe(r)),y(1)).subscribe({next:r=>{this.subscriptionChanges.next(r),e(r)},error:i})})}unsubscribe(){if(!this.sw.isEnabled)return Promise.reject(new Error(_));let t=e=>{if(null===e)throw new k(5602,!1);return e.unsubscribe().then(e=>{if(!e)throw new k(5603,!1);this.subscriptionChanges.next(null)})};return new Promise((e,r)=>{this.subscription.pipe(y(1),O(t)).subscribe({next:e,error:r})})}decodeBase64(e){return atob(e)}static ɵfac=function(e){return new(e||n)(P(x))};static ɵprov=C({token:n,factory:n.ɵfac})}return n})(),F=(()=>{class n{sw;versionUpdates;unrecoverable;get isEnabled(){return this.sw.isEnabled}constructor(e){if(this.sw=e,!e.isEnabled)return this.versionUpdates=f,void(this.unrecoverable=f);this.versionUpdates=this.sw.eventsOfType(["VERSION_DETECTED","VERSION_INSTALLATION_FAILED","VERSION_READY","NO_NEW_VERSION_DETECTED"]),this.unrecoverable=this.sw.eventsOfType("UNRECOVERABLE_STATE")}checkForUpdate(){if(!this.sw.isEnabled)return Promise.reject(new Error(_));let e=this.sw.generateNonce();return this.sw.postMessageWithOperation("CHECK_FOR_UPDATES",{nonce:e},e)}activateUpdate(){if(!this.sw.isEnabled)return Promise.reject(new k(5601,!1));let e=this.sw.generateNonce();return this.sw.postMessageWithOperation("ACTIVATE_UPDATE",{nonce:e},e)}static ɵfac=function(e){return new(e||n)(P(x))};static ɵprov=C({token:n,factory:n.ɵfac})}return n})(),R=new I("");function X(){let e=S(U);if(!("serviceWorker"in navigator)||!1===e.enabled)return;let r=S(R),i=S(a),o=S(b);i.runOutsideAngular(()=>{let e=navigator.serviceWorker,s=()=>e.controller?.postMessage({action:"INITIALIZE"});e.addEventListener("controllerchange",s),o.onDestroy(()=>{e.removeEventListener("controllerchange",s)})}),i.runOutsideAngular(()=>{let i,{registrationStrategy:a}=e;if("function"==typeof a)i=new Promise(e=>a().subscribe(()=>e()));else{let[e,...r]=(a||"registerWhenStable:30000").split(":");switch(e){case"registerImmediately":i=Promise.resolve();break;case"registerWithDelay":i=q(+r[0]||0);break;case"registerWhenStable":i=Promise.race([o.whenStable(),q(+r[0])]);break;default:throw new k(5600,!1)}}i.then(()=>{o.destroyed||navigator.serviceWorker.register(r,{scope:e.scope}).catch(e=>console.error(T(5604,!1)))})})}function q(e){return new Promise(r=>setTimeout(r,e))}function ee(e,r){return new x(!1!==e.enabled?navigator.serviceWorker:void 0,r)}var U=class{enabled;scope;registrationStrategy};N(class n{constructor(){this.auth=S(l),this.socket=S(u)}ngOnInit(){this.auth.hasToken()&&this.socket.connect()}static{this.ɵfac=function(e){return new(e||n)}}static{this.ɵcmp=h({type:n,selectors:[["cx-root"]],decls:1,vars:0,template:function(e,r){1&e&&w(0,"router-outlet")},dependencies:[L],encapsulation:2,changeDetection:0})}},{providers:[i([{path:"",redirectTo:"chat",pathMatch:"full"},{path:"login",loadComponent:()=>import("./chunk-NFYBHCXF.js").then(e=>e.LoginComponent)},{path:"chat",canActivate:[()=>{let e=S(l),i=S(r);return!!e.hasToken()||(i.navigate(["/login"]),!1)}],loadComponent:()=>import("./chunk-AMCDNAIG.js").then(e=>e.ChatComponent)},{path:"**",redirectTo:"chat"}],o()),D(W()),function(r,i={}){return M([V,F,{provide:R,useValue:r},{provide:U,useValue:i},{provide:x,useFactory:ee,deps:[U,e]},p(X)])}("ngsw-worker.js",{enabled:!A(),registrationStrategy:"registerWhenStable:30000"})]}).catch(e=>console.error("[webapp] bootstrap error",e));
1
+ import{A as e,Aa as r,Ba as i,C as a,Ca as o,Da as l,Ea as u,J as h,P as p,Q as b,a as g,aa as w,c as d,d as v,f,h as E,i as m,j as y,k,l as O,m as T,o as C,pa as A,q as I,r as P,s as S,t as M,wa as N,xa as D,ya as W,za as L}from"./chunk-VAB2VXFI.js";var _="Service workers are disabled or not supported by this browser",x=class{serviceWorker;worker;registration;events;constructor(e,r){if(this.serviceWorker=e,e){let i=null,a=new v;this.worker=new d(e=>(null!==i&&e.next(i),a.subscribe(r=>e.next(r))));let s=()=>{let{controller:r}=e;null!==r&&(i=r,a.next(i))};e.addEventListener("controllerchange",s),s(),this.registration=this.worker.pipe(k(()=>e.getRegistration()));let o=new v;this.events=o.asObservable();let c=e=>{let{data:r}=e;r?.type&&o.next(r)};e.addEventListener("message",c),r?.get(b,null,{optional:!0})?.onDestroy(()=>{e.removeEventListener("controllerchange",s),e.removeEventListener("message",c)})}else this.worker=this.events=this.registration=new d(e=>e.error(new O(5601,!1)))}postMessage(e,r){return new Promise(i=>{this.worker.pipe(y(1)).subscribe(a=>{a.postMessage(g({action:e},r)),i()})})}postMessageWithOperation(e,r,i){let a=this.waitForOperationCompleted(i),o=this.postMessage(e,r);return Promise.all([o,a]).then(([,e])=>e)}generateNonce(){return Math.round(1e7*Math.random())}eventsOfType(e){let r;return r="string"==typeof e?r=>r.type===e:r=>e.includes(r.type),this.events.pipe(m(r))}nextEventOfType(e){return this.eventsOfType(e).pipe(y(1))}waitForOperationCompleted(e){return new Promise((r,i)=>{this.eventsOfType("OPERATION_COMPLETED").pipe(m(r=>r.nonce===e),y(1),f(e=>{if(void 0!==e.result)return e.result;throw new Error(e.error)})).subscribe({next:r,error:i})})}get isEnabled(){return!!this.serviceWorker}},V=(()=>{class n{sw;messages;notificationClicks;subscription;get isEnabled(){return this.sw.isEnabled}pushManager=null;subscriptionChanges=new v;constructor(e){if(this.sw=e,!e.isEnabled)return this.messages=E,this.notificationClicks=E,void(this.subscription=E);this.messages=this.sw.eventsOfType("PUSH").pipe(f(e=>e.data)),this.notificationClicks=this.sw.eventsOfType("NOTIFICATION_CLICK").pipe(f(e=>e.data)),this.pushManager=this.sw.registration.pipe(f(e=>e.pushManager));let r=this.pushManager.pipe(k(e=>e.getSubscription()));this.subscription=new d(e=>{let i=r.subscribe(e),a=this.subscriptionChanges.subscribe(e);return()=>{i.unsubscribe(),a.unsubscribe()}})}requestSubscription(e){if(!this.sw.isEnabled||null===this.pushManager)return Promise.reject(new Error(_));let r={userVisibleOnly:!0},i=this.decodeBase64(e.serverPublicKey.replace(/_/g,"/").replace(/-/g,"+")),a=new Uint8Array(new ArrayBuffer(i.length));for(let e=0;e<i.length;e++)a[e]=i.charCodeAt(e);return r.applicationServerKey=a,new Promise((e,i)=>{this.pushManager.pipe(k(e=>e.subscribe(r)),y(1)).subscribe({next:r=>{this.subscriptionChanges.next(r),e(r)},error:i})})}unsubscribe(){if(!this.sw.isEnabled)return Promise.reject(new Error(_));let t=e=>{if(null===e)throw new O(5602,!1);return e.unsubscribe().then(e=>{if(!e)throw new O(5603,!1);this.subscriptionChanges.next(null)})};return new Promise((e,r)=>{this.subscription.pipe(y(1),k(t)).subscribe({next:e,error:r})})}decodeBase64(e){return atob(e)}static ɵfac=function(e){return new(e||n)(P(x))};static ɵprov=C({token:n,factory:n.ɵfac})}return n})(),F=(()=>{class n{sw;versionUpdates;unrecoverable;get isEnabled(){return this.sw.isEnabled}constructor(e){if(this.sw=e,!e.isEnabled)return this.versionUpdates=E,void(this.unrecoverable=E);this.versionUpdates=this.sw.eventsOfType(["VERSION_DETECTED","VERSION_INSTALLATION_FAILED","VERSION_READY","NO_NEW_VERSION_DETECTED"]),this.unrecoverable=this.sw.eventsOfType("UNRECOVERABLE_STATE")}checkForUpdate(){if(!this.sw.isEnabled)return Promise.reject(new Error(_));let e=this.sw.generateNonce();return this.sw.postMessageWithOperation("CHECK_FOR_UPDATES",{nonce:e},e)}activateUpdate(){if(!this.sw.isEnabled)return Promise.reject(new O(5601,!1));let e=this.sw.generateNonce();return this.sw.postMessageWithOperation("ACTIVATE_UPDATE",{nonce:e},e)}static ɵfac=function(e){return new(e||n)(P(x))};static ɵprov=C({token:n,factory:n.ɵfac})}return n})(),R=new I("");function X(){let e=S(U);if(!("serviceWorker"in navigator)||!1===e.enabled)return;let r=S(R),i=S(a),o=S(b);i.runOutsideAngular(()=>{let e=navigator.serviceWorker,s=()=>e.controller?.postMessage({action:"INITIALIZE"});e.addEventListener("controllerchange",s),o.onDestroy(()=>{e.removeEventListener("controllerchange",s)})}),i.runOutsideAngular(()=>{let i,{registrationStrategy:a}=e;if("function"==typeof a)i=new Promise(e=>a().subscribe(()=>e()));else{let[e,...r]=(a||"registerWhenStable:30000").split(":");switch(e){case"registerImmediately":i=Promise.resolve();break;case"registerWithDelay":i=q(+r[0]||0);break;case"registerWhenStable":i=Promise.race([o.whenStable(),q(+r[0])]);break;default:throw new O(5600,!1)}}i.then(()=>{o.destroyed||navigator.serviceWorker.register(r,{scope:e.scope}).catch(e=>console.error(T(5604,!1)))})})}function q(e){return new Promise(r=>setTimeout(r,e))}function ee(e,r){return new x(!1!==e.enabled?navigator.serviceWorker:void 0,r)}var U=class{enabled;scope;registrationStrategy};N(class n{constructor(){this.auth=S(l),this.socket=S(u)}ngOnInit(){this.auth.hasToken()&&this.socket.connect()}static{this.ɵfac=function(e){return new(e||n)}}static{this.ɵcmp=h({type:n,selectors:[["cx-root"]],decls:1,vars:0,template:function(e,r){1&e&&w(0,"router-outlet")},dependencies:[L],encapsulation:2,changeDetection:0})}},{providers:[i([{path:"",redirectTo:"chat",pathMatch:"full"},{path:"login",loadComponent:()=>import("./chunk-NFYBHCXF.js").then(e=>e.LoginComponent)},{path:"chat",canActivate:[()=>{let e=S(l),i=S(r);return!!e.hasToken()||(i.navigate(["/login"]),!1)}],loadComponent:()=>import("./chunk-AMCDNAIG.js").then(e=>e.ChatComponent)},{path:"**",redirectTo:"chat"}],o()),D(W()),function(r,i={}){return M([V,F,{provide:R,useValue:r},{provide:U,useValue:i},{provide:x,useFactory:ee,deps:[U,e]},p(X)])}("ngsw-worker.js",{enabled:!A(),registrationStrategy:"registerWhenStable:30000"})]}).catch(e=>console.error("[webapp] bootstrap error",e));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iaforged/context-code",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "description": "Context Code es un asistente de desarrollo para la terminal. Puede revisar tu proyecto, editar archivos, ejecutar comandos y apoyarte en tareas reales de programacion.",
5
5
  "author": "Context AI",
6
6
  "license": "MIT",