@iaforged/context-code 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/bridge/bridgeMain.js +1 -1
- package/dist/src/bridge/types.js +1 -1
- package/dist/src/cli/handlers/auth.js +1 -1
- package/dist/src/cli/handlers/mcp.js +1 -1
- package/dist/src/cli/print.js +1 -1
- package/dist/src/commands/copy/copy.js +1 -1
- package/dist/src/commands/heartbeat/heartbeat.js +1 -0
- package/dist/src/commands/heartbeat/index.js +1 -0
- package/dist/src/commands/insights.js +1 -1
- package/dist/src/commands/install-github-app/ExistingWorkflowStep.js +1 -1
- package/dist/src/commands/install-github-app/InstallAppStep.js +1 -1
- package/dist/src/commands/install-github-app/OAuthFlowStep.js +1 -1
- package/dist/src/commands/install-github-app/SuccessStep.js +1 -1
- package/dist/src/commands/install-github-app/setupGitHubActions.js +1 -1
- package/dist/src/commands/login-openai/index.js +1 -1
- package/dist/src/commands/mobile/mobile.js +1 -1
- package/dist/src/commands/remote-setup/remote-setup.js +1 -1
- package/dist/src/commands/swarm-auto/swarmAuto.js +1 -1
- package/dist/src/commands/timeline/timeline.js +1 -1
- package/dist/src/commands/webapp/webapp.js +1 -1
- package/dist/src/commands.js +1 -1
- package/dist/src/components/ApproveApiKey.js +1 -1
- package/dist/src/components/ChannelDowngradeDialog.js +1 -1
- package/dist/src/components/ClaudeCodeHint/PluginHintMenu.js +1 -1
- package/dist/src/components/ConsoleOAuthFlow.js +1 -1
- package/dist/src/components/CostThresholdDialog.js +1 -1
- package/dist/src/components/CtrlOToExpand.js +1 -1
- package/dist/src/components/CustomSelect/select.js +1 -1
- package/dist/src/components/DesktopHandoff.js +1 -1
- package/dist/src/components/DesktopUpsell/DesktopUpsellStartup.js +1 -1
- package/dist/src/components/ExportDialog.js +1 -1
- package/dist/src/components/Feedback.js +1 -1
- package/dist/src/components/FeedbackSurvey/FeedbackSurvey.js +1 -1
- package/dist/src/components/FeedbackSurvey/FeedbackSurveyView.js +1 -1
- package/dist/src/components/IdeAutoConnectDialog.js +1 -1
- package/dist/src/components/IdeOnboardingDialog.js +1 -1
- package/dist/src/components/IdleReturnDialog.js +1 -1
- package/dist/src/components/InterruptedByUser.js +1 -1
- package/dist/src/components/InvalidConfigDialog.js +1 -1
- package/dist/src/components/InvalidSettingsDialog.js +1 -1
- package/dist/src/components/LanguagePicker.js +1 -1
- package/dist/src/components/LogSelector.js +1 -1
- package/dist/src/components/LogoV2/ChannelsNotice.js +1 -1
- package/dist/src/components/LogoV2/LogoV2.js +1 -1
- package/dist/src/components/LogoV2/feedConfigs.js +1 -1
- package/dist/src/components/LspRecommendation/LspRecommendationMenu.js +1 -1
- package/dist/src/components/MCPServerApprovalDialog.js +1 -1
- package/dist/src/components/MCPServerDesktopImportDialog.js +1 -1
- package/dist/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.js +1 -1
- package/dist/src/components/MessageSelector.js +1 -1
- package/dist/src/components/OutputStylePicker.js +1 -1
- package/dist/src/components/PromptInput/PromptInput.js +1 -1
- package/dist/src/components/RemoteCallout.js +1 -1
- package/dist/src/components/ResumeTask.js +1 -1
- package/dist/src/components/Settings/Config.js +1 -1
- package/dist/src/components/ShowInIDEPrompt.js +1 -1
- package/dist/src/components/Stats.js +1 -1
- package/dist/src/components/TagTabs.js +1 -1
- package/dist/src/components/TeleportError.js +1 -1
- package/dist/src/components/TeleportResumeWrapper.js +1 -1
- package/dist/src/components/TextInput.js +1 -1
- package/dist/src/components/agents/new-agent-creation/wizard-steps/MethodStep.js +1 -1
- package/dist/src/components/design-system/KeyboardShortcutHint.js +1 -1
- package/dist/src/components/diff/DiffDetailView.js +1 -1
- package/dist/src/components/grove/Grove.js +1 -1
- package/dist/src/components/hooks/SelectEventMode.js +1 -1
- package/dist/src/components/hooks/SelectHookMode.js +1 -1
- package/dist/src/components/hooks/SelectMatcherMode.js +1 -1
- package/dist/src/components/hooks/ViewHookMode.js +1 -1
- package/dist/src/components/mcp/MCPAgentServerMenu.js +1 -1
- package/dist/src/components/mcp/utils/reconnectHelpers.js +1 -1
- package/dist/src/components/memory/MemoryFileSelector.js +1 -1
- package/dist/src/components/messages/AssistantTextMessage.js +1 -1
- package/dist/src/components/messages/PlanApprovalMessage.js +1 -1
- package/dist/src/components/messages/ShutdownMessage.js +1 -1
- package/dist/src/components/messages/SystemAPIErrorMessage.js +1 -1
- package/dist/src/components/messages/UserPlanMessage.js +1 -1
- package/dist/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.js +1 -1
- package/dist/src/components/permissions/ComputerUseApproval/ComputerUseApproval.js +1 -1
- package/dist/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.js +1 -1
- package/dist/src/components/permissions/FilePermissionDialog/FilePermissionDialog.js +1 -1
- package/dist/src/components/permissions/FilePermissionDialog/permissionOptions.js +1 -1
- package/dist/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.js +1 -1
- package/dist/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js +1 -1
- package/dist/src/components/permissions/PermissionRequest.js +1 -1
- package/dist/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.js +1 -1
- package/dist/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.js +1 -1
- package/dist/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.js +1 -1
- package/dist/src/components/permissions/rules/AddPermissionRules.js +1 -1
- package/dist/src/components/permissions/rules/AddWorkspaceDirectory.js +1 -1
- package/dist/src/components/permissions/rules/PermissionRuleList.js +1 -1
- package/dist/src/components/permissions/rules/RemoveWorkspaceDirectory.js +1 -1
- package/dist/src/components/sandbox/SandboxConfigTab.js +1 -1
- package/dist/src/components/sandbox/SandboxOverridesTab.js +1 -1
- package/dist/src/components/tasks/RemoteSessionDetailDialog.js +1 -1
- package/dist/src/constants/outputStyles.js +1 -1
- package/dist/src/entrypoints/cli.js +1 -1
- package/dist/src/hooks/notifs/useCanSwitchToExistingSubscription.js +1 -1
- package/dist/src/main.js +1 -1
- package/dist/src/projectOnboardingState.js +1 -1
- package/dist/src/screens/REPL.js +1 -1
- package/dist/src/server/channelServer.js +1 -0
- package/dist/src/server/channelServer.test.js +1 -0
- package/dist/src/services/api/errors.js +1 -1
- package/dist/src/services/api/filesApi.js +1 -1
- package/dist/src/services/api/index.js +1 -1
- package/dist/src/services/tips/tipRegistry.js +1 -1
- package/dist/src/tasks/RemoteAgentTask/RemoteAgentTask.js +1 -1
- package/dist/src/tools/AgentTool/AgentTool.js +1 -1
- package/dist/src/tools/ConfigTool/supportedSettings.js +1 -1
- package/dist/src/tools/EnterWorktreeTool/EnterWorktreeTool.js +1 -1
- package/dist/src/tools/ExitWorktreeTool/ExitWorktreeTool.js +1 -1
- package/dist/src/tools/WebFetchTool/WebFetchTool.js +1 -1
- package/dist/src/utils/computerUse/cleanup.js +1 -1
- package/dist/src/utils/computerUse/wrapper.js +1 -1
- package/dist/src/utils/fileHistory.js +1 -1
- package/dist/src/utils/heartbeat.js +1 -0
- package/dist/src/utils/model/providers.js +1 -1
- package/dist/src/utils/permissions/filesystem.js +1 -1
- package/dist/src/utils/permissions/permissions.js +1 -1
- package/dist/src/utils/processUserInput/processSlashCommand.js +1 -1
- package/dist/src/utils/releaseNoteTranslations.js +1 -1
- package/dist/src/utils/shell/shellToolUtils.js +1 -1
- package/dist/src/utils/sideQuery.js +1 -1
- package/dist/src/utils/sshMcp/common.js +1 -0
- package/dist/src/utils/sshMcp/setup.js +1 -0
- package/dist/src/utils/sshMcp/sshMcpServer.js +1 -0
- package/dist/src/utils/sshMcp/sshMcpServer.test.js +1 -0
- package/dist/src/utils/teleport.js +1 -1
- package/dist/src/webapp/server.js +1 -1
- package/dist/sshMcpServer.js +1 -0
- package/dist/sshMcpServer.test.js +1 -0
- package/dist/webapp/chunk-AMCDNAIG.js +1 -0
- package/dist/webapp/{chunk-YUEYJPXQ.js → chunk-NFYBHCXF.js} +1 -1
- package/dist/webapp/{chunk-DFKSSBHI.js → chunk-OJZNEHPP.js} +1 -1
- package/dist/webapp/chunk-VAB2VXFI.js +1 -0
- package/dist/webapp/index.html +2 -2
- package/dist/webapp/main-MTQLKGXD.js +1 -0
- package/dist/webapp/ngsw.json +14 -14
- package/dist/webapp/styles-DIKEDJBH.css +1 -0
- package/package.json +2 -1
- package/dist/webapp/chunk-4SRNXNLW.js +0 -1
- package/dist/webapp/chunk-SIHYW6PA.js +0 -1
- package/dist/webapp/main-LBNRQBXX.js +0 -1
- package/dist/webapp/styles-FUPULZDX.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MACRO as e}from"../recovery/bunBundleShim.js";import{execFileSync as t}from"child_process";import{diffLines as n}from"diff";import{constants as s}from"fs";import{copyFile as o,mkdir as i,mkdtemp as a,readdir as r,readFile as c,rm as l,unlink as d,writeFile as p}from"fs/promises";import{tmpdir as u}from"os";import{extname as m,join as f}from"path";import{queryWithModel as g}from"../services/api/claude.js";import{AGENT_TOOL_NAME as h,LEGACY_AGENT_TOOL_NAME as _}from"../tools/AgentTool/constants.js";import{getClaudeConfigHomeDir as v}from"../utils/envUtils.js";import{toError as b}from"../utils/errors.js";import{execFileNoThrow as x}from"../utils/execFileNoThrow.js";import{logError as y}from"../utils/log.js";import{extractTextContent as w}from"../utils/messages.js";import{getDefaultOpusModel as k}from"../utils/model/model.js";import{getProjectsDir as C,getSessionFilesWithMtime as $,getSessionIdFromLog as T,loadAllLogsFromSessionFile as S}from"../utils/sessionStorage.js";import{jsonParse as E,jsonStringify as A}from"../utils/slowOperations.js";import{countCharInString as I}from"../utils/stringUtils.js";import{asSystemPrompt as z}from"../utils/systemPromptType.js";import{escapeXmlAttr as j}from"../utils/xml.js";function getAnalysisModel(){return k()}const O="ant"===process.env.USER_TYPE?async()=>{const{stdout:e,code:t}=await x("coder",["list","-o","json"],{timeout:3e4});if(0!==t)return[];try{return E(e).filter(e=>"running"===e.latest_build?.status).map(e=>e.name)}catch{return[]}}:async()=>[],D="ant"===process.env.USER_TYPE?async e=>{const{stdout:t,code:n}=await x("ssh",[`${e}.coder`,'find /root/.claude/projects -name "*.jsonl" 2>/dev/null | wc -l'],{timeout:3e4});return 0!==n?0:parseInt(t.trim(),10)||0}:async()=>0,N="ant"===process.env.USER_TYPE?async(e,t)=>{const n={copied:0,skipped:0},c=await a(f(u(),"claude-hs-"));try{if(0!==(await x("scp",["-rq",`${e}.coder:/root/.claude/projects/`,c],{timeout:3e5})).code)return n;const a=f(c,"projects");let l;try{l=await r(a,{withFileTypes:!0})}catch{return n}await Promise.all(l.map(async c=>{const l=c.name,d=f(a,l);if(!c.isDirectory())return;const p=f(t,`${l}__${e}`);try{await i(p,{recursive:!0})}catch{}let u;try{u=await r(d,{withFileTypes:!0})}catch{return}await Promise.all(u.map(async e=>{const t=e.name;if(!t.endsWith(".jsonl"))return;const i=f(d,t),a=f(p,t);try{await o(i,a,s.COPYFILE_EXCL),n.copied++}catch{n.skipped++}}))}))}finally{try{await l(c,{recursive:!0,force:!0})}catch{}}return n}:async()=>({copied:0,skipped:0}),P="ant"===process.env.USER_TYPE?async e=>{const t=await O(),n=[];let s=0,o=0;const i=await Promise.all(t.map(async t=>{const n=await D(t);if(n>0){const{copied:s,skipped:o}=await N(t,e);return{name:t,sessionCount:n,copied:s,skipped:o}}return{name:t,sessionCount:n,copied:0,skipped:0}}));for(const e of i)n.push({name:e.name,sessionCount:e.sessionCount}),s+=e.copied,o+=e.skipped;return{hosts:n,totalCopied:s,totalSkipped:o}}:async()=>({hosts:[],totalCopied:0,totalSkipped:0}),R={".ts":"TypeScript",".tsx":"TypeScript",".js":"JavaScript",".jsx":"JavaScript",".py":"Python",".rb":"Ruby",".go":"Go",".rs":"Rust",".java":"Java",".md":"Markdown",".json":"JSON",".yaml":"YAML",".yml":"YAML",".sh":"Shell",".css":"CSS",".html":"HTML"},M={debug_investigate:"Debug/Investigate",implement_feature:"Implement Feature",fix_bug:"Fix Bug",write_script_tool:"Write Script/Tool",refactor_code:"Refactor Code",configure_system:"Configure System",create_pr_commit:"Create PR/Commit",analyze_data:"Analyze Data",understand_codebase:"Understand Codebase",write_tests:"Write Tests",write_docs:"Write Docs",deploy_infra:"Deploy/Infra",warmup_minimal:"Cache Warmup",fast_accurate_search:"Fast/Accurate Search",correct_code_edits:"Correct Code Edits",good_explanations:"Good Explanations",proactive_help:"Proactive Help",multi_file_changes:"Multi-file Changes",handled_complexity:"Multi-file Changes",good_debugging:"Good Debugging",misunderstood_request:"Misunderstood Request",wrong_approach:"Wrong Approach",buggy_code:"Buggy Code",user_rejected_action:"User Rejected Action",claude_got_blocked:"Claude Got Blocked",user_stopped_early:"User Stopped Early",wrong_file_or_location:"Wrong File/Location",excessive_changes:"Excessive Changes",slow_or_verbose:"Slow/Verbose",tool_failed:"Tool Failed",user_unclear:"User Unclear",external_issue:"External Issue",frustrated:"Frustrated",dissatisfied:"Dissatisfied",likely_satisfied:"Likely Satisfied",satisfied:"Satisfied",happy:"Happy",unsure:"Unsure",neutral:"Neutral",delighted:"Delighted",single_task:"Single Task",multi_task:"Multi Task",iterative_refinement:"Iterative Refinement",exploration:"Exploration",quick_question:"Quick Question",fully_achieved:"Fully Achieved",mostly_achieved:"Mostly Achieved",partially_achieved:"Partially Achieved",not_achieved:"Not Achieved",unclear_from_transcript:"Unclear",unhelpful:"Unhelpful",slightly_helpful:"Slightly Helpful",moderately_helpful:"Moderately Helpful",very_helpful:"Very Helpful",essential:"Essential"};function getDataDir(){return f(v(),"usage-data")}function getFacetsDir(){return f(getDataDir(),"facets")}function getSessionMetaDir(){return f(getDataDir(),"session-meta")}function getLanguageFromPath(e){const t=m(e).toLowerCase();return R[t]||null}function hasValidDates(e){return!Number.isNaN(e.created.getTime())&&!Number.isNaN(e.modified.getTime())}function logToSessionMeta(e){const t=function(e){const t={},s={};let o=0,i=0,a=0,r=0,c=0;const l=[];let d=0;const p={};let u=!1,m=0,f=0;const g=new Set,v=[],b=[];let x=!1,y=!1,w=!1,k=null;for(const C of e.messages){const e=C.timestamp;if("assistant"===C.type&&C.message){e&&(k=e);const c=C.message.usage;c&&(a+=c.input_tokens||0,r+=c.output_tokens||0);const l=C.message.content;if(Array.isArray(l))for(const e of l)if("tool_use"===e.type&&"name"in e){const a=e.name;t[a]=(t[a]||0)+1,a!==h&&a!==_||(u=!0),a.startsWith("mcp__")&&(x=!0),"WebSearch"===a&&(y=!0),"WebFetch"===a&&(w=!0);const r=e.input;if(r){const e=r.file_path||"";if(e){const t=getLanguageFromPath(e);t&&(s[t]=(s[t]||0)+1),"Edit"!==a&&"Write"!==a||g.add(e)}if("Edit"===a){const e=r.old_string||"",t=r.new_string||"";for(const s of n(e,t))s.added&&(m+=s.count||0),s.removed&&(f+=s.count||0)}if("Write"===a){const e=r.content||"";e&&(m+=I(e,"\n")+1)}const t=r.command||"";t.includes("git commit")&&o++,t.includes("git push")&&i++}}}if("user"===C.type&&C.message){const t=C.message.content;let n=!1;if("string"==typeof t&&t.trim())n=!0;else if(Array.isArray(t))for(const e of t)if("text"===e.type&&"text"in e){n=!0;break}if(n){if(e)try{const t=new Date(e).getHours();v.push(t),b.push(e)}catch{}if(k&&e){const t=new Date(k).getTime(),n=(new Date(e).getTime()-t)/1e3;n>2&&n<3600&&l.push(n)}}if(Array.isArray(t))for(const e of t)if("tool_result"===e.type&&"content"in e&&e.is_error){d++;const t=e.content;let n="Other";if("string"==typeof t){const e=t.toLowerCase();e.includes("exit code")?n="Command Failed":e.includes("rejected")||e.includes("doesn't want")?n="User Rejected":e.includes("string to replace not found")||e.includes("no changes")?n="Edit Failed":e.includes("modified since read")?n="File Changed":e.includes("exceeds maximum")||e.includes("too large")?n="File Too Large":(e.includes("file not found")||e.includes("does not exist"))&&(n="File Not Found")}p[n]=(p[n]||0)+1}if("string"==typeof t)t.includes("[Request interrupted by user")&&c++;else if(Array.isArray(t))for(const e of t)if("text"===e.type&&"text"in e&&e.text.includes("[Request interrupted by user")){c++;break}}}return{toolCounts:t,languages:s,gitCommits:o,gitPushes:i,inputTokens:a,outputTokens:r,userInterruptions:c,userResponseTimes:l,toolErrors:d,toolErrorCategories:p,usesTaskAgent:u,usesMcp:x,usesWebSearch:y,usesWebFetch:w,linesAdded:m,linesRemoved:f,filesModified:g,messageHours:v,userMessageTimestamps:b}}(e),s=T(e)||"unknown",o=e.created.toISOString(),i=Math.round((e.modified.getTime()-e.created.getTime())/1e3/60);let a=0,r=0;for(const t of e.messages)if("assistant"===t.type&&r++,"user"===t.type&&t.message){const e=t.message.content;let n=!1;if("string"==typeof e&&e.trim())n=!0;else if(Array.isArray(e))for(const t of e)if("text"===t.type&&"text"in t){n=!0;break}n&&a++}return{session_id:s,project_path:e.projectPath||"",start_time:o,duration_minutes:i,user_message_count:a,assistant_message_count:r,tool_counts:t.toolCounts,languages:t.languages,git_commits:t.gitCommits,git_pushes:t.gitPushes,input_tokens:t.inputTokens,output_tokens:t.outputTokens,first_prompt:e.firstPrompt||"",summary:e.summary,user_interruptions:t.userInterruptions,user_response_times:t.userResponseTimes,tool_errors:t.toolErrors,tool_error_categories:t.toolErrorCategories,uses_task_agent:t.usesTaskAgent,uses_mcp:t.usesMcp,uses_web_search:t.usesWebSearch,uses_web_fetch:t.usesWebFetch,lines_added:t.linesAdded,lines_removed:t.linesRemoved,files_modified:t.filesModified.size,message_hours:t.messageHours,user_message_timestamps:t.userMessageTimestamps}}export function deduplicateSessionBranches(e){const t=new Map;for(const n of e){const e=n.meta.session_id,s=t.get(e);(!s||n.meta.user_message_count>s.meta.user_message_count||n.meta.user_message_count===s.meta.user_message_count&&n.meta.duration_minutes>s.meta.duration_minutes)&&t.set(e,n)}return[...t.values()]}async function summarizeTranscriptChunk(e){try{const t=await g({systemPrompt:z([]),userPrompt:"Summarize this portion of a Context Code session transcript. Focus on:\n1. What the user asked for\n2. What Claude did (tools used, files modified)\n3. Any friction or issues\n4. The outcome\n\nKeep it concise - 3-5 sentences. Preserve specific details like file names, error messages, and user feedback.\n\nTRANSCRIPT CHUNK:\n"+e,signal:(new AbortController).signal,options:{model:getAnalysisModel(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:500}});return w(t.message.content)||e.slice(0,2e3)}catch{return e.slice(0,2e3)}}async function formatTranscriptWithSummarization(e){const t=function(e){const t=[],n=logToSessionMeta(e);t.push(`Session: ${n.session_id.slice(0,8)}`),t.push(`Date: ${n.start_time}`),t.push(`Project: ${n.project_path}`),t.push(`Duration: ${n.duration_minutes} min`),t.push("");for(const n of e.messages)if("user"===n.type&&n.message){const e=n.message.content;if("string"==typeof e)t.push(`[User]: ${e.slice(0,500)}`);else if(Array.isArray(e))for(const n of e)"text"===n.type&&"text"in n&&t.push(`[User]: ${n.text.slice(0,500)}`)}else if("assistant"===n.type&&n.message){const e=n.message.content;if(Array.isArray(e))for(const n of e)"text"===n.type&&"text"in n?t.push(`[Assistant]: ${n.text.slice(0,300)}`):"tool_use"===n.type&&"name"in n&&t.push(`[Tool: ${n.name}]`)}return t.join("\n")}(e);if(t.length<=3e4)return t;const n=[];for(let e=0;e<t.length;e+=25e3)n.push(t.slice(e,e+25e3));const s=await Promise.all(n.map(summarizeTranscriptChunk)),o=logToSessionMeta(e);return[`Session: ${o.session_id.slice(0,8)}`,`Date: ${o.start_time}`,`Project: ${o.project_path}`,`Duration: ${o.duration_minutes} min`,`[Long session - ${n.length} parts summarized]`,""].join("\n")+s.join("\n\n---\n\n")}async function loadCachedFacets(e){const t=f(getFacetsDir(),`${e}.json`);try{const e=await c(t,{encoding:"utf-8"}),n=E(e);if(!isValidSessionFacets(n)){try{await d(t)}catch{}return null}return n}catch{return null}}async function saveFacets(e){try{await i(getFacetsDir(),{recursive:!0})}catch{}const t=f(getFacetsDir(),`${e.session_id}.json`);await p(t,A(e,null,2),{encoding:"utf-8",mode:384})}async function loadCachedSessionMeta(e){const t=f(getSessionMetaDir(),`${e}.json`);try{const e=await c(t,{encoding:"utf-8"});return E(e)}catch{return null}}async function saveSessionMeta(e){try{await i(getSessionMetaDir(),{recursive:!0})}catch{}const t=f(getSessionMetaDir(),`${e.session_id}.json`);await p(t,A(e,null,2),{encoding:"utf-8",mode:384})}async function extractFacetsFromAPI(e,t){try{const n=`Analyze this Context Code session and extract structured facets.\n\nCRITICAL GUIDELINES:\n\n1. **goal_categories**: Count ONLY what the USER explicitly asked for.\n - DO NOT count Claude's autonomous codebase exploration\n - DO NOT count work Claude decided to do on its own\n - ONLY count when user says "can you...", "please...", "I need...", "let's..."\n\n2. **user_satisfaction_counts**: Base ONLY on explicit user signals.\n - "Yay!", "great!", "perfect!" → happy\n - "thanks", "looks good", "that works" → satisfied\n - "ok, now let's..." (continuing without complaint) → likely_satisfied\n - "that's not right", "try again" → dissatisfied\n - "this is broken", "I give up" → frustrated\n\n3. **friction_counts**: Be specific about what went wrong.\n - misunderstood_request: Claude interpreted incorrectly\n - wrong_approach: Right goal, wrong solution method\n - buggy_code: Code didn't work correctly\n - user_rejected_action: User said no/stop to a tool call\n - excessive_changes: Over-engineered or changed too much\n\n4. If very short or just warmup, use warmup_minimal for goal_category\n\nSESSION:\n${await formatTranscriptWithSummarization(e)}\n\nRESPOND WITH ONLY A VALID JSON OBJECT matching this schema:\n{\n "underlying_goal": "What the user fundamentally wanted to achieve",\n "goal_categories": {"category_name": count, ...},\n "outcome": "fully_achieved|mostly_achieved|partially_achieved|not_achieved|unclear_from_transcript",\n "user_satisfaction_counts": {"level": count, ...},\n "claude_helpfulness": "unhelpful|slightly_helpful|moderately_helpful|very_helpful|essential",\n "session_type": "single_task|multi_task|iterative_refinement|exploration|quick_question",\n "friction_counts": {"friction_type": count, ...},\n "friction_detail": "One sentence describing friction or empty",\n "primary_success": "none|fast_accurate_search|correct_code_edits|good_explanations|proactive_help|multi_file_changes|good_debugging",\n "brief_summary": "One sentence: what user wanted and whether they got it"\n}`,s=await g({systemPrompt:z([]),userPrompt:n,signal:(new AbortController).signal,options:{model:getAnalysisModel(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:4096}}),o=w(s.message.content).match(/\{[\s\S]*\}/);if(!o)return null;const i=E(o[0]);if(!isValidSessionFacets(i))return null;return{...i,session_id:t}}catch(e){return y(new Error(`Facet extraction failed: ${b(e).message}`)),null}}export function detectMultiClauding(e){const t=[];for(const n of e)for(const e of n.user_message_timestamps)try{const s=new Date(e).getTime();t.push({ts:s,sessionId:n.session_id})}catch{}t.sort((e,t)=>e.ts-t.ts);const n=new Set,s=new Set;let o=0;const i=new Map;for(let e=0;e<t.length;e++){const a=t[e];for(;o<e&&a.ts-t[o].ts>18e5;){const e=t[o];i.get(e.sessionId)===o&&i.delete(e.sessionId),o++}const r=i.get(a.sessionId);if(void 0!==r)for(let o=r+1;o<e;o++){const e=t[o];if(e.sessionId!==a.sessionId){const o=[a.sessionId,e.sessionId].sort().join(":");n.add(o),s.add(`${t[r].ts}:${a.sessionId}`),s.add(`${e.ts}:${e.sessionId}`),s.add(`${a.ts}:${a.sessionId}`);break}}i.set(a.sessionId,e)}const a=new Set;for(const e of n){const[t,n]=e.split(":");t&&a.add(t),n&&a.add(n)}return{overlap_events:n.size,sessions_involved:a.size,user_messages_during:s.size}}const L=[{name:"project_areas",prompt:'Analyze this Context Code usage data and identify project areas.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "areas": [\n {"name": "Area name", "session_count": N, "description": "2-3 sentences about what was worked on and how Context Code was used."}\n ]\n}\n\nInclude 4-5 areas. Skip internal CC operations.',maxTokens:8192},{name:"interaction_style",prompt:'Analyze this Context Code usage data and describe the user\'s interaction style.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "narrative": "2-3 paragraphs analyzing HOW the user interacts with Context Code. Use second person \'you\'. Describe patterns: iterate quickly vs detailed upfront specs? Interrupt often or let Claude run? Include specific examples. Use **bold** for key insights.",\n "key_pattern": "One sentence summary of most distinctive interaction style"\n}',maxTokens:8192},{name:"what_works",prompt:'Analyze this Context Code usage data and identify what\'s working well for this user. Use second person ("you").\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence of context",\n "impressive_workflows": [\n {"title": "Short title (3-6 words)", "description": "2-3 sentences describing the impressive workflow or approach. Use \'you\' not \'the user\'."}\n ]\n}\n\nInclude 3 impressive workflows.',maxTokens:8192},{name:"friction_analysis",prompt:'Analyze this Context Code usage data and identify friction points for this user. Use second person ("you").\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence summarizing friction patterns",\n "categories": [\n {"category": "Concrete category name", "description": "1-2 sentences explaining this category and what could be done differently. Use \'you\' not \'the user\'.", "examples": ["Specific example with consequence", "Another example"]}\n ]\n}\n\nInclude 3 friction categories with 2 examples each.',maxTokens:8192},{name:"suggestions",prompt:'Analyze this Context Code usage data and suggest improvements.\n\n## CC FEATURES REFERENCE (pick from these for features_to_try):\n1. **MCP Servers**: Connect Claude to external tools, databases, and APIs via Model Context Protocol.\n - How to use: Run `claude mcp add <server-name> -- <command>`\n - Good for: database queries, Slack integration, GitHub issue lookup, connecting to internal APIs\n\n2. **Custom Skills**: Reusable prompts you define as markdown files that run with a single /command.\n - How to use: Create `.claude/skills/commit/SKILL.md` with instructions. Then type `/commit` to run it.\n - Good for: repetitive workflows - /commit, /review, /test, /deploy, /pr, or complex multi-step workflows\n\n3. **Hooks**: Shell commands that auto-run at specific lifecycle events.\n - How to use: Add to `.claude/settings.json` under "hooks" key.\n - Good for: auto-formatting code, running type checks, enforcing conventions\n\n4. **Headless Mode**: Run Claude non-interactively from scripts and CI/CD.\n - How to use: `claude -p "fix lint errors" --allowedTools "Edit,Read,Bash"`\n - Good for: CI/CD integration, batch code fixes, automated reviews\n\n5. **Task Agents**: Claude spawns focused sub-agents for complex exploration or parallel work.\n - How to use: Claude auto-invokes when helpful, or ask "use an agent to explore X"\n - Good for: codebase exploration, understanding complex systems\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "claude_md_additions": [\n {"addition": "A specific line or block to add to CLAUDE.md based on workflow patterns. E.g., \'Always run tests after modifying auth-related files\'", "why": "1 sentence explaining why this would help based on actual sessions", "prompt_scaffold": "Instructions for where to add this in CLAUDE.md. E.g., \'Add under ## Testing section\'"}\n ],\n "features_to_try": [\n {"feature": "Feature name from CC FEATURES REFERENCE above", "one_liner": "What it does", "why_for_you": "Why this would help YOU based on your sessions", "example_code": "Actual command or config to copy"}\n ],\n "usage_patterns": [\n {"title": "Short title", "suggestion": "1-2 sentence summary", "detail": "3-4 sentences explaining how this applies to YOUR work", "copyable_prompt": "A specific prompt to copy and try"}\n ]\n}\n\nIMPORTANT for claude_md_additions: PRIORITIZE instructions that appear MULTIPLE TIMES in the user data. If user told Claude the same thing in 2+ sessions (e.g., \'always run tests\', \'use TypeScript\'), that\'s a PRIME candidate - they shouldn\'t have to repeat themselves.\n\nIMPORTANT for features_to_try: Pick 2-3 from the CC FEATURES REFERENCE above. Include 2-3 items for each category.',maxTokens:8192},{name:"on_the_horizon",prompt:'Analyze this Context Code usage data and identify future opportunities.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence about evolving AI-assisted development",\n "opportunities": [\n {"title": "Short title (4-8 words)", "whats_possible": "2-3 ambitious sentences about autonomous workflows", "how_to_try": "1-2 sentences mentioning relevant tooling", "copyable_prompt": "Detailed prompt to try"}\n ]\n}\n\nInclude 3 opportunities. Think BIG - autonomous workflows, parallel agents, iterating against tests.',maxTokens:8192},..."ant"===process.env.USER_TYPE?[{name:"cc_team_improvements",prompt:'Analyze this Context Code usage data and suggest product improvements for the CC team.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "improvements": [\n {"title": "Product/tooling improvement", "detail": "3-4 sentences describing the improvement", "evidence": "3-4 sentences with specific session examples"}\n ]\n}\n\nInclude 2-3 improvements based on friction patterns observed.',maxTokens:8192},{name:"model_behavior_improvements",prompt:'Analyze this Context Code usage data and suggest model behavior improvements.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "improvements": [\n {"title": "Model behavior change", "detail": "3-4 sentences describing what the model should do differently", "evidence": "3-4 sentences with specific examples"}\n ]\n}\n\nInclude 2-3 improvements based on friction patterns observed.',maxTokens:8192}]:[],{name:"fun_ending",prompt:'Analyze this Context Code usage data and find a memorable moment.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "headline": "A memorable QUALITATIVE moment from the transcripts - not a statistic. Something human, funny, or surprising.",\n "detail": "Brief context about when/where this happened"\n}\n\nFind something genuinely interesting or amusing from the session summaries.',maxTokens:8192}];async function generateSectionInsight(e,t){try{const n=await g({systemPrompt:z([]),userPrompt:e.prompt+"\n\nDATA:\n"+t,signal:(new AbortController).signal,options:{model:k(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:e.maxTokens}}),s=w(n.message.content);if(s){const t=s.match(/\{[\s\S]*\}/);if(t)try{return{name:e.name,result:E(t[0])}}catch{return{name:e.name,result:null}}}return{name:e.name,result:null}}catch(t){return y(new Error(`${e.name} failed: ${b(t).message}`)),{name:e.name,result:null}}}function escapeHtmlWithBold(e){return j(e).replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>")}const U=["frustrated","dissatisfied","likely_satisfied","satisfied","happy","unsure"],F=["not_achieved","partially_achieved","mostly_achieved","fully_achieved","unclear_from_transcript"];function generateBarChart(e,t,n=6,s){let o;if(o=s?s.filter(t=>t in e&&(e[t]??0)>0).map(t=>[t,e[t]??0]):Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,n),0===o.length)return'<p class="empty">No data</p>';const i=Math.max(...o.map(e=>e[1]));return o.map(([e,n])=>{const s=n/i*100,o=M[e]||e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase());return`<div class="bar-row">\n <div class="bar-label">${j(o)}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${s}%;background:${t}"></div></div>\n <div class="bar-value">${n}</div>\n </div>`}).join("\n")}function generateHtmlReport(e,t){const n=t.at_a_glance,s=n?`\n <div class="at-a-glance">\n <div class="glance-title">At a Glance</div>\n <div class="glance-sections">\n ${n.whats_working?`<div class="glance-section"><strong>What's working:</strong> ${escapeHtmlWithBold(n.whats_working)} <a href="#section-wins" class="see-more">Impressive Things You Did →</a></div>`:""}\n ${n.whats_hindering?`<div class="glance-section"><strong>What's hindering you:</strong> ${escapeHtmlWithBold(n.whats_hindering)} <a href="#section-friction" class="see-more">Where Things Go Wrong →</a></div>`:""}\n ${n.quick_wins?`<div class="glance-section"><strong>Quick wins to try:</strong> ${escapeHtmlWithBold(n.quick_wins)} <a href="#section-features" class="see-more">Features to Try →</a></div>`:""}\n ${n.ambitious_workflows?`<div class="glance-section"><strong>Ambitious workflows:</strong> ${escapeHtmlWithBold(n.ambitious_workflows)} <a href="#section-horizon" class="see-more">On the Horizon →</a></div>`:""}\n </div>\n </div>\n `:"",o=t.project_areas?.areas||[],i=o.length>0?`\n <h2 id="section-work">What You Work On</h2>\n <div class="project-areas">\n ${o.map(e=>`\n <div class="project-area">\n <div class="area-header">\n <span class="area-name">${j(e.name)}</span>\n <span class="area-count">~${e.session_count} sessions</span>\n </div>\n <div class="area-desc">${j(e.description)}</div>\n </div>\n `).join("")}\n </div>\n `:"",a=t.interaction_style,r=a?.narrative?`\n <h2 id="section-usage">How You Use Context Code</h2>\n <div class="narrative">\n ${c=a.narrative,c?c.split("\n\n").map(e=>{let t=j(e);return t=t.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),t=t.replace(/^- /gm,"• "),t=t.replace(/\n/g,"<br>"),`<p>${t}</p>`}).join("\n"):""}\n ${a.key_pattern?`<div class="key-insight"><strong>Key pattern:</strong> ${j(a.key_pattern)}</div>`:""}\n </div>\n `:"";var c;const l=t.what_works,d=l?.impressive_workflows&&l.impressive_workflows.length>0?`\n <h2 id="section-wins">Impressive Things You Did</h2>\n ${l.intro?`<p class="section-intro">${j(l.intro)}</p>`:""}\n <div class="big-wins">\n ${l.impressive_workflows.map(e=>`\n <div class="big-win">\n <div class="big-win-title">${j(e.title||"")}</div>\n <div class="big-win-desc">${j(e.description||"")}</div>\n </div>\n `).join("")}\n </div>\n `:"",p=t.friction_analysis,u=p?.categories&&p.categories.length>0?`\n <h2 id="section-friction">Where Things Go Wrong</h2>\n ${p.intro?`<p class="section-intro">${j(p.intro)}</p>`:""}\n <div class="friction-categories">\n ${p.categories.map(e=>`\n <div class="friction-category">\n <div class="friction-title">${j(e.category||"")}</div>\n <div class="friction-desc">${j(e.description||"")}</div>\n ${e.examples?`<ul class="friction-examples">${e.examples.map(e=>`<li>${j(e)}</li>`).join("")}</ul>`:""}\n </div>\n `).join("")}\n </div>\n `:"",m=t.suggestions,f=m?`\n ${m.claude_md_additions&&m.claude_md_additions.length>0?`\n <h2 id="section-features">Existing CC Features to Try</h2>\n <div class="claude-md-section">\n <h3>Suggested CLAUDE.md Additions</h3>\n <p style="font-size: 12px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code to add it to your CLAUDE.md.</p>\n <div class="claude-md-actions">\n <button class="copy-all-btn" onclick="copyAllCheckedClaudeMd()">Copy All Checked</button>\n </div>\n ${m.claude_md_additions.map((e,t)=>`\n <div class="claude-md-item">\n <input type="checkbox" id="cmd-${t}" class="cmd-checkbox" checked data-text="${j(e.prompt_scaffold||e.where||"Add to CLAUDE.md")}\\n\\n${j(e.addition)}">\n <label for="cmd-${t}">\n <code class="cmd-code">${j(e.addition)}</code>\n <button class="copy-btn" onclick="copyCmdItem(${t})">Copy</button>\n </label>\n <div class="cmd-why">${j(e.why)}</div>\n </div>\n `).join("")}\n </div>\n `:""}\n ${m.features_to_try&&m.features_to_try.length>0?`\n <p style="font-size: 13px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code and it'll set it up for you.</p>\n <div class="features-section">\n ${m.features_to_try.map(e=>`\n <div class="feature-card">\n <div class="feature-title">${j(e.feature||"")}</div>\n <div class="feature-oneliner">${j(e.one_liner||"")}</div>\n <div class="feature-why"><strong>Why for you:</strong> ${j(e.why_for_you||"")}</div>\n ${e.example_code?`\n <div class="feature-examples">\n <div class="feature-example">\n <div class="example-code-row">\n <code class="example-code">${j(e.example_code)}</code>\n <button class="copy-btn" onclick="copyText(this)">Copy</button>\n </div>\n </div>\n </div>\n `:""}\n </div>\n `).join("")}\n </div>\n `:""}\n ${m.usage_patterns&&m.usage_patterns.length>0?`\n <h2 id="section-patterns">New Ways to Use Context Code</h2>\n <p style="font-size: 13px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code and it'll walk you through it.</p>\n <div class="patterns-section">\n ${m.usage_patterns.map(e=>`\n <div class="pattern-card">\n <div class="pattern-title">${j(e.title||"")}</div>\n <div class="pattern-summary">${j(e.suggestion||"")}</div>\n ${e.detail?`<div class="pattern-detail">${j(e.detail)}</div>`:""}\n ${e.copyable_prompt?`\n <div class="copyable-prompt-section">\n <div class="prompt-label">Paste into Context Code:</div>\n <div class="copyable-prompt-row">\n <code class="copyable-prompt">${j(e.copyable_prompt)}</code>\n <button class="copy-btn" onclick="copyText(this)">Copy</button>\n </div>\n </div>\n `:""}\n </div>\n `).join("")}\n </div>\n `:""}\n `:"",g=t.on_the_horizon,h=g?.opportunities&&g.opportunities.length>0?`\n <h2 id="section-horizon">On the Horizon</h2>\n ${g.intro?`<p class="section-intro">${j(g.intro)}</p>`:""}\n <div class="horizon-section">\n ${g.opportunities.map(e=>`\n <div class="horizon-card">\n <div class="horizon-title">${j(e.title||"")}</div>\n <div class="horizon-possible">${j(e.whats_possible||"")}</div>\n ${e.how_to_try?`<div class="horizon-tip"><strong>Getting started:</strong> ${j(e.how_to_try)}</div>`:""}\n ${e.copyable_prompt?`<div class="pattern-prompt"><div class="prompt-label">Paste into Context Code:</div><code>${j(e.copyable_prompt)}</code><button class="copy-btn" onclick="copyText(this)">Copy</button></div>`:""}\n </div>\n `).join("")}\n </div>\n `:"",_="ant"===process.env.USER_TYPE&&t.cc_team_improvements?.improvements||[],v="ant"===process.env.USER_TYPE&&t.model_behavior_improvements?.improvements||[],b=_.length>0||v.length>0?`\n <h2 id="section-feedback" class="feedback-header">Closing the Loop: Feedback for Other Teams</h2>\n <p class="feedback-intro">Suggestions for the CC product and model teams based on your usage patterns. Click to expand.</p>\n ${_.length>0?`\n <div class="collapsible-section">\n <div class="collapsible-header" onclick="toggleCollapsible(this)">\n <span class="collapsible-arrow">▶</span>\n <h3>Product Improvements for CC Team</h3>\n </div>\n <div class="collapsible-content">\n <div class="suggestions-section">\n ${_.map(e=>`\n <div class="feedback-card team-card">\n <div class="feedback-title">${j(e.title||"")}</div>\n <div class="feedback-detail">${j(e.detail||"")}</div>\n ${e.evidence?`<div class="feedback-evidence"><em>Evidence:</em> ${j(e.evidence)}</div>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `:""}\n ${v.length>0?`\n <div class="collapsible-section">\n <div class="collapsible-header" onclick="toggleCollapsible(this)">\n <span class="collapsible-arrow">▶</span>\n <h3>Model Behavior Improvements</h3>\n </div>\n <div class="collapsible-content">\n <div class="suggestions-section">\n ${v.map(e=>`\n <div class="feedback-card model-card">\n <div class="feedback-title">${j(e.title||"")}</div>\n <div class="feedback-detail">${j(e.detail||"")}</div>\n ${e.evidence?`<div class="feedback-evidence"><em>Evidence:</em> ${j(e.evidence)}</div>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `:""}\n `:"",x=t.fun_ending,y=x?.headline?`\n <div class="fun-ending">\n <div class="fun-headline">"${j(x.headline)}"</div>\n ${x.detail?`<div class="fun-detail">${j(x.detail)}</div>`:""}\n </div>\n `:"",w=`\n function toggleCollapsible(header) {\n header.classList.toggle('open');\n const content = header.nextElementSibling;\n content.classList.toggle('open');\n }\n function copyText(btn) {\n const code = btn.previousElementSibling;\n navigator.clipboard.writeText(code.textContent).then(() => {\n btn.textContent = 'Copied!';\n setTimeout(() => { btn.textContent = 'Copy'; }, 2000);\n });\n }\n function copyCmdItem(idx) {\n const checkbox = document.getElementById('cmd-' + idx);\n if (checkbox) {\n const text = checkbox.dataset.text;\n navigator.clipboard.writeText(text).then(() => {\n const btn = checkbox.nextElementSibling.querySelector('.copy-btn');\n if (btn) { btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy'; }, 2000); }\n });\n }\n }\n function copyAllCheckedClaudeMd() {\n const checkboxes = document.querySelectorAll('.cmd-checkbox:checked');\n const texts = [];\n checkboxes.forEach(cb => {\n if (cb.dataset.text) { texts.push(cb.dataset.text); }\n });\n const combined = texts.join('\\n');\n const btn = document.querySelector('.copy-all-btn');\n if (btn) {\n navigator.clipboard.writeText(combined).then(() => {\n btn.textContent = 'Copied ' + texts.length + ' items!';\n btn.classList.add('copied');\n setTimeout(() => { btn.textContent = 'Copy All Checked'; btn.classList.remove('copied'); }, 2000);\n });\n }\n }\n // Timezone selector for time of day chart (data is from our own analytics, not user input)\n const rawHourCounts = ${function(e){const t={};for(const n of e)t[n]=(t[n]||0)+1;return A(t)}(e.message_hours)};\n function updateHourHistogram(offsetFromPT) {\n const periods = [\n { label: "Morning (6-12)", range: [6,7,8,9,10,11] },\n { label: "Afternoon (12-18)", range: [12,13,14,15,16,17] },\n { label: "Evening (18-24)", range: [18,19,20,21,22,23] },\n { label: "Night (0-6)", range: [0,1,2,3,4,5] }\n ];\n const adjustedCounts = {};\n for (const [hour, count] of Object.entries(rawHourCounts)) {\n const newHour = (parseInt(hour) + offsetFromPT + 24) % 24;\n adjustedCounts[newHour] = (adjustedCounts[newHour] || 0) + count;\n }\n const periodCounts = periods.map(p => ({\n label: p.label,\n count: p.range.reduce((sum, h) => sum + (adjustedCounts[h] || 0), 0)\n }));\n const maxCount = Math.max(...periodCounts.map(p => p.count)) || 1;\n const container = document.getElementById('hour-histogram');\n container.textContent = '';\n periodCounts.forEach(p => {\n const row = document.createElement('div');\n row.className = 'bar-row';\n const label = document.createElement('div');\n label.className = 'bar-label';\n label.textContent = p.label;\n const track = document.createElement('div');\n track.className = 'bar-track';\n const fill = document.createElement('div');\n fill.className = 'bar-fill';\n fill.style.width = (p.count / maxCount) * 100 + '%';\n fill.style.background = '#8b5cf6';\n track.appendChild(fill);\n const value = document.createElement('div');\n value.className = 'bar-value';\n value.textContent = p.count;\n row.appendChild(label);\n row.appendChild(track);\n row.appendChild(value);\n container.appendChild(row);\n });\n }\n document.getElementById('timezone-select').addEventListener('change', function() {\n const customInput = document.getElementById('custom-offset');\n if (this.value === 'custom') {\n customInput.style.display = 'inline-block';\n customInput.focus();\n } else {\n customInput.style.display = 'none';\n updateHourHistogram(parseInt(this.value));\n }\n });\n document.getElementById('custom-offset').addEventListener('change', function() {\n const offset = parseInt(this.value) + 8;\n updateHourHistogram(offset);\n });\n `;return`<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <title>Context Code Insights</title>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">\n <style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: #f8fafc; color: #334155; line-height: 1.65; padding: 48px 24px; }\n .container { max-width: 800px; margin: 0 auto; }\n h1 { font-size: 32px; font-weight: 700; color: #0f172a; margin-bottom: 8px; }\n h2 { font-size: 20px; font-weight: 600; color: #0f172a; margin-top: 48px; margin-bottom: 16px; }\n .subtitle { color: #64748b; font-size: 15px; margin-bottom: 32px; }\n .nav-toc { display: flex; flex-wrap: wrap; gap: 8px; margin: 24px 0 32px 0; padding: 16px; background: white; border-radius: 8px; border: 1px solid #e2e8f0; }\n .nav-toc a { font-size: 12px; color: #64748b; text-decoration: none; padding: 6px 12px; border-radius: 6px; background: #f1f5f9; transition: all 0.15s; }\n .nav-toc a:hover { background: #e2e8f0; color: #334155; }\n .stats-row { display: flex; gap: 24px; margin-bottom: 40px; padding: 20px 0; border-top: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; flex-wrap: wrap; }\n .stat { text-align: center; }\n .stat-value { font-size: 24px; font-weight: 700; color: #0f172a; }\n .stat-label { font-size: 11px; color: #64748b; text-transform: uppercase; }\n .at-a-glance { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #f59e0b; border-radius: 12px; padding: 20px 24px; margin-bottom: 32px; }\n .glance-title { font-size: 16px; font-weight: 700; color: #92400e; margin-bottom: 16px; }\n .glance-sections { display: flex; flex-direction: column; gap: 12px; }\n .glance-section { font-size: 14px; color: #78350f; line-height: 1.6; }\n .glance-section strong { color: #92400e; }\n .see-more { color: #b45309; text-decoration: none; font-size: 13px; white-space: nowrap; }\n .see-more:hover { text-decoration: underline; }\n .project-areas { display: flex; flex-direction: column; gap: 12px; margin-bottom: 32px; }\n .project-area { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; }\n .area-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }\n .area-name { font-weight: 600; font-size: 15px; color: #0f172a; }\n .area-count { font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px; }\n .area-desc { font-size: 14px; color: #475569; line-height: 1.5; }\n .narrative { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; margin-bottom: 24px; }\n .narrative p { margin-bottom: 12px; font-size: 14px; color: #475569; line-height: 1.7; }\n .key-insight { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 12px 16px; margin-top: 12px; font-size: 14px; color: #166534; }\n .section-intro { font-size: 14px; color: #64748b; margin-bottom: 16px; }\n .big-wins { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }\n .big-win { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 16px; }\n .big-win-title { font-weight: 600; font-size: 15px; color: #166534; margin-bottom: 8px; }\n .big-win-desc { font-size: 14px; color: #15803d; line-height: 1.5; }\n .friction-categories { display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px; }\n .friction-category { background: #fef2f2; border: 1px solid #fca5a5; border-radius: 8px; padding: 16px; }\n .friction-title { font-weight: 600; font-size: 15px; color: #991b1b; margin-bottom: 6px; }\n .friction-desc { font-size: 13px; color: #7f1d1d; margin-bottom: 10px; }\n .friction-examples { margin: 0 0 0 20px; font-size: 13px; color: #334155; }\n .friction-examples li { margin-bottom: 4px; }\n .claude-md-section { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 16px; margin-bottom: 20px; }\n .claude-md-section h3 { font-size: 14px; font-weight: 600; color: #1e40af; margin: 0 0 12px 0; }\n .claude-md-actions { margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #dbeafe; }\n .copy-all-btn { background: #2563eb; color: white; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; cursor: pointer; font-weight: 500; transition: all 0.2s; }\n .copy-all-btn:hover { background: #1d4ed8; }\n .copy-all-btn.copied { background: #16a34a; }\n .claude-md-item { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; padding: 10px 0; border-bottom: 1px solid #dbeafe; }\n .claude-md-item:last-child { border-bottom: none; }\n .cmd-checkbox { margin-top: 2px; }\n .cmd-code { background: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; color: #1e40af; border: 1px solid #bfdbfe; font-family: monospace; display: block; white-space: pre-wrap; word-break: break-word; flex: 1; }\n .cmd-why { font-size: 12px; color: #64748b; width: 100%; padding-left: 24px; margin-top: 4px; }\n .features-section, .patterns-section { display: flex; flex-direction: column; gap: 12px; margin: 16px 0; }\n .feature-card { background: #f0fdf4; border: 1px solid #86efac; border-radius: 8px; padding: 16px; }\n .pattern-card { background: #f0f9ff; border: 1px solid #7dd3fc; border-radius: 8px; padding: 16px; }\n .feature-title, .pattern-title { font-weight: 600; font-size: 15px; color: #0f172a; margin-bottom: 6px; }\n .feature-oneliner { font-size: 14px; color: #475569; margin-bottom: 8px; }\n .pattern-summary { font-size: 14px; color: #475569; margin-bottom: 8px; }\n .feature-why, .pattern-detail { font-size: 13px; color: #334155; line-height: 1.5; }\n .feature-examples { margin-top: 12px; }\n .feature-example { padding: 8px 0; border-top: 1px solid #d1fae5; }\n .feature-example:first-child { border-top: none; }\n .example-desc { font-size: 13px; color: #334155; margin-bottom: 6px; }\n .example-code-row { display: flex; align-items: flex-start; gap: 8px; }\n .example-code { flex: 1; background: #f1f5f9; padding: 8px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; overflow-x: auto; white-space: pre-wrap; }\n .copyable-prompt-section { margin-top: 12px; padding-top: 12px; border-top: 1px solid #e2e8f0; }\n .copyable-prompt-row { display: flex; align-items: flex-start; gap: 8px; }\n .copyable-prompt { flex: 1; background: #f8fafc; padding: 10px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; border: 1px solid #e2e8f0; white-space: pre-wrap; line-height: 1.5; }\n .feature-code { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; display: flex; align-items: flex-start; gap: 8px; }\n .feature-code code { flex: 1; font-family: monospace; font-size: 12px; color: #334155; white-space: pre-wrap; }\n .pattern-prompt { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; }\n .pattern-prompt code { font-family: monospace; font-size: 12px; color: #334155; display: block; white-space: pre-wrap; margin-bottom: 8px; }\n .prompt-label { font-size: 11px; font-weight: 600; text-transform: uppercase; color: #64748b; margin-bottom: 6px; }\n .copy-btn { background: #e2e8f0; border: none; border-radius: 4px; padding: 4px 8px; font-size: 11px; cursor: pointer; color: #475569; flex-shrink: 0; }\n .copy-btn:hover { background: #cbd5e1; }\n .charts-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin: 24px 0; }\n .chart-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; }\n .chart-title { font-size: 12px; font-weight: 600; color: #64748b; text-transform: uppercase; margin-bottom: 12px; }\n .bar-row { display: flex; align-items: center; margin-bottom: 6px; }\n .bar-label { width: 100px; font-size: 11px; color: #475569; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .bar-track { flex: 1; height: 6px; background: #f1f5f9; border-radius: 3px; margin: 0 8px; }\n .bar-fill { height: 100%; border-radius: 3px; }\n .bar-value { width: 28px; font-size: 11px; font-weight: 500; color: #64748b; text-align: right; }\n .empty { color: #94a3b8; font-size: 13px; }\n .horizon-section { display: flex; flex-direction: column; gap: 16px; }\n .horizon-card { background: linear-gradient(135deg, #faf5ff 0%, #f5f3ff 100%); border: 1px solid #c4b5fd; border-radius: 8px; padding: 16px; }\n .horizon-title { font-weight: 600; font-size: 15px; color: #5b21b6; margin-bottom: 8px; }\n .horizon-possible { font-size: 14px; color: #334155; margin-bottom: 10px; line-height: 1.5; }\n .horizon-tip { font-size: 13px; color: #6b21a8; background: rgba(255,255,255,0.6); padding: 8px 12px; border-radius: 4px; }\n .feedback-header { margin-top: 48px; color: #64748b; font-size: 16px; }\n .feedback-intro { font-size: 13px; color: #94a3b8; margin-bottom: 16px; }\n .feedback-section { margin-top: 16px; }\n .feedback-section h3 { font-size: 14px; font-weight: 600; color: #475569; margin-bottom: 12px; }\n .feedback-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; margin-bottom: 12px; }\n .feedback-card.team-card { background: #eff6ff; border-color: #bfdbfe; }\n .feedback-card.model-card { background: #faf5ff; border-color: #e9d5ff; }\n .feedback-title { font-weight: 600; font-size: 14px; color: #0f172a; margin-bottom: 6px; }\n .feedback-detail { font-size: 13px; color: #475569; line-height: 1.5; }\n .feedback-evidence { font-size: 12px; color: #64748b; margin-top: 8px; }\n .fun-ending { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #fbbf24; border-radius: 12px; padding: 24px; margin-top: 40px; text-align: center; }\n .fun-headline { font-size: 18px; font-weight: 600; color: #78350f; margin-bottom: 8px; }\n .fun-detail { font-size: 14px; color: #92400e; }\n .collapsible-section { margin-top: 16px; }\n .collapsible-header { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 12px 0; border-bottom: 1px solid #e2e8f0; }\n .collapsible-header h3 { margin: 0; font-size: 14px; font-weight: 600; color: #475569; }\n .collapsible-arrow { font-size: 12px; color: #94a3b8; transition: transform 0.2s; }\n .collapsible-content { display: none; padding-top: 16px; }\n .collapsible-content.open { display: block; }\n .collapsible-header.open .collapsible-arrow { transform: rotate(90deg); }\n @media (max-width: 640px) { .charts-row { grid-template-columns: 1fr; } .stats-row { justify-content: center; } }\n </style>\n</head>\n<body>\n <div class="container">\n <h1>Context Code Insights</h1>\n <p class="subtitle">${e.total_messages.toLocaleString()} messages across ${e.total_sessions} sessions${e.total_sessions_scanned&&e.total_sessions_scanned>e.total_sessions?` (${e.total_sessions_scanned.toLocaleString()} total)`:""} | ${e.date_range.start} to ${e.date_range.end}</p>\n\n ${s}\n\n <nav class="nav-toc">\n <a href="#section-work">What You Work On</a>\n <a href="#section-usage">How You Use CC</a>\n <a href="#section-wins">Impressive Things</a>\n <a href="#section-friction">Where Things Go Wrong</a>\n <a href="#section-features">Features to Try</a>\n <a href="#section-patterns">New Usage Patterns</a>\n <a href="#section-horizon">On the Horizon</a>\n <a href="#section-feedback">Team Feedback</a>\n </nav>\n\n <div class="stats-row">\n <div class="stat"><div class="stat-value">${e.total_messages.toLocaleString()}</div><div class="stat-label">Messages</div></div>\n <div class="stat"><div class="stat-value">+${e.total_lines_added.toLocaleString()}/-${e.total_lines_removed.toLocaleString()}</div><div class="stat-label">Lines</div></div>\n <div class="stat"><div class="stat-value">${e.total_files_modified}</div><div class="stat-label">Files</div></div>\n <div class="stat"><div class="stat-value">${e.days_active}</div><div class="stat-label">Days</div></div>\n <div class="stat"><div class="stat-value">${e.messages_per_day}</div><div class="stat-label">Msgs/Day</div></div>\n </div>\n\n ${i}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">What You Wanted</div>\n ${generateBarChart(e.goal_categories,"#2563eb")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Top Tools Used</div>\n ${generateBarChart(e.tool_counts,"#0891b2")}\n </div>\n </div>\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">Languages</div>\n ${generateBarChart(e.languages,"#10b981")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Session Types</div>\n ${generateBarChart(e.session_types||{},"#8b5cf6")}\n </div>\n </div>\n\n ${r}\n\n \x3c!-- Response Time Distribution --\x3e\n <div class="chart-card" style="margin: 24px 0;">\n <div class="chart-title">User Response Time Distribution</div>\n ${function(e){if(0===e.length)return'<p class="empty">No response time data</p>';const t={"2-10s":0,"10-30s":0,"30s-1m":0,"1-2m":0,"2-5m":0,"5-15m":0,">15m":0};for(const n of e)n<10?t["2-10s"]=(t["2-10s"]??0)+1:n<30?t["10-30s"]=(t["10-30s"]??0)+1:n<60?t["30s-1m"]=(t["30s-1m"]??0)+1:n<120?t["1-2m"]=(t["1-2m"]??0)+1:n<300?t["2-5m"]=(t["2-5m"]??0)+1:n<900?t["5-15m"]=(t["5-15m"]??0)+1:t[">15m"]=(t[">15m"]??0)+1;const n=Math.max(...Object.values(t));return 0===n?'<p class="empty">No response time data</p>':Object.entries(t).map(([e,t])=>`<div class="bar-row">\n <div class="bar-label">${e}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${t/n*100}%;background:#6366f1"></div></div>\n <div class="bar-value">${t}</div>\n </div>`).join("\n")}(e.user_response_times)}\n <div style="font-size: 12px; color: #64748b; margin-top: 8px;">\n Median: ${e.median_response_time.toFixed(1)}s • Average: ${e.avg_response_time.toFixed(1)}s\n </div>\n </div>\n\n \x3c!-- Multi-clauding Section (matching Python reference) --\x3e\n <div class="chart-card" style="margin: 24px 0;">\n <div class="chart-title">Multi-Clauding (Parallel Sessions)</div>\n ${0===e.multi_clauding.overlap_events?'\n <p style="font-size: 14px; color: #64748b; padding: 8px 0;">\n No parallel session usage detected. You typically work with one Context Code session at a time.\n </p>\n ':`\n <div style="display: flex; gap: 24px; margin: 12px 0;">\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.multi_clauding.overlap_events}</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Overlap Events</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.multi_clauding.sessions_involved}</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Sessions Involved</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.total_messages>0?Math.round(100*e.multi_clauding.user_messages_during/e.total_messages):0}%</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Of Messages</div>\n </div>\n </div>\n <p style="font-size: 13px; color: #475569; margin-top: 12px;">\n You run multiple Context Code sessions simultaneously. Multi-clauding is detected when sessions\n overlap in time, suggesting parallel workflows.\n </p>\n `}\n </div>\n\n \x3c!-- Time of Day & Tool Errors --\x3e\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title" style="display: flex; align-items: center; gap: 12px;">\n User Messages by Time of Day\n <select id="timezone-select" style="font-size: 12px; padding: 4px 8px; border-radius: 4px; border: 1px solid #e2e8f0;">\n <option value="0">PT (UTC-8)</option>\n <option value="3">ET (UTC-5)</option>\n <option value="8">London (UTC)</option>\n <option value="9">CET (UTC+1)</option>\n <option value="17">Tokyo (UTC+9)</option>\n <option value="custom">Custom offset...</option>\n </select>\n <input type="number" id="custom-offset" placeholder="UTC offset" style="display: none; width: 80px; font-size: 12px; padding: 4px; border-radius: 4px; border: 1px solid #e2e8f0;">\n </div>\n ${function(e){if(0===e.length)return'<p class="empty">No time data</p>';const t={};for(const n of e)t[n]=(t[n]||0)+1;const n=[{label:"Morning (6-12)",range:[6,7,8,9,10,11]},{label:"Afternoon (12-18)",range:[12,13,14,15,16,17]},{label:"Evening (18-24)",range:[18,19,20,21,22,23]},{label:"Night (0-6)",range:[0,1,2,3,4,5]}].map(e=>({label:e.label,count:e.range.reduce((e,n)=>e+(t[n]||0),0)})),s=Math.max(...n.map(e=>e.count))||1;return`<div id="hour-histogram">${n.map(e=>`\n <div class="bar-row">\n <div class="bar-label">${e.label}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${e.count/s*100}%;background:#8b5cf6"></div></div>\n <div class="bar-value">${e.count}</div>\n </div>`).join("\n")}</div>`}(e.message_hours)}\n </div>\n <div class="chart-card">\n <div class="chart-title">Tool Errors Encountered</div>\n ${Object.keys(e.tool_error_categories).length>0?generateBarChart(e.tool_error_categories,"#dc2626"):'<p class="empty">No tool errors</p>'}\n </div>\n </div>\n\n ${d}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">What Helped Most (Claude's Capabilities)</div>\n ${generateBarChart(e.success,"#16a34a")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Outcomes</div>\n ${generateBarChart(e.outcomes,"#8b5cf6",6,F)}\n </div>\n </div>\n\n ${u}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">Primary Friction Types</div>\n ${generateBarChart(e.friction,"#dc2626")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Inferred Satisfaction (model-estimated)</div>\n ${generateBarChart(e.satisfaction,"#eab308",6,U)}\n </div>\n </div>\n\n ${f}\n\n ${h}\n\n ${y}\n\n ${b}\n </div>\n <script>${w}<\/script>\n</body>\n</html>`}export function buildExportData(t,n,s,o){const i=void 0!==e?e.VERSION:"unknown",a=o?.hosts.filter(e=>e.sessionCount>0).map(e=>e.name),r={total:s.size,goal_categories:{},outcomes:{},satisfaction:{},friction:{}};for(const e of s.values()){for(const[t,n]of safeEntries(e.goal_categories))n>0&&(r.goal_categories[t]=(r.goal_categories[t]||0)+n);r.outcomes[e.outcome]=(r.outcomes[e.outcome]||0)+1;for(const[t,n]of safeEntries(e.user_satisfaction_counts))n>0&&(r.satisfaction[t]=(r.satisfaction[t]||0)+n);for(const[t,n]of safeEntries(e.friction_counts))n>0&&(r.friction[t]=(r.friction[t]||0)+n)}return{metadata:{username:process.env.SAFEUSER||process.env.USER||"unknown",generated_at:(new Date).toISOString(),claude_code_version:i,date_range:t.date_range,session_count:t.total_sessions,...a&&a.length>0&&{remote_hosts_collected:a}},aggregated_data:t,insights:n,facets_summary:r}}export async function generateUsageReport(e){let t;if("ant"===process.env.USER_TYPE&&e?.collectRemote){const e=f(v(),"projects"),{hosts:n,totalCopied:s}=await P(e);t={hosts:n,totalCopied:s}}const n=await async function(){const e=C();let t;try{t=await r(e,{withFileTypes:!0})}catch{return[]}const n=t.filter(e=>e.isDirectory()).map(t=>f(e,t.name)),s=[];for(let e=0;e<n.length;e++){const t=await $(n[e]);for(const[e,n]of t)s.push({sessionId:e,path:n.path,mtime:n.mtime,size:n.size});e%10==9&&await new Promise(e=>setImmediate(e))}return s.sort((e,t)=>t.mtime-e.mtime),s}(),s=n.length;let o=[];const a=[];for(let e=0;e<n.length;e+=50){const t=n.slice(e,e+50),s=await Promise.all(t.map(async e=>({sessionInfo:e,cached:await loadCachedSessionMeta(e.sessionId)})));for(const{sessionInfo:e,cached:t}of s)t?o.push(t):a.length<200&&a.push(e)}const c=new Map,isMetaSession=e=>{for(const t of e.messages.slice(0,5))if("user"===t.type&&t.message){const e=t.message.content;if("string"==typeof e&&(e.includes("RESPOND WITH ONLY A VALID JSON OBJECT")||e.includes("record_facets")))return!0}return!1};for(let e=0;e<a.length;e+=10){const t=a.slice(e,e+10),n=await Promise.all(t.map(async e=>{try{return await S(e.path)}catch{return[]}})),s=[];for(const e of n)for(const t of e){if(isMetaSession(t)||!hasValidDates(t))continue;const e=logToSessionMeta(t);o.push(e),s.push(e),c.set(e.session_id,t)}await Promise.all(s.map(e=>saveSessionMeta(e)))}const l=new Map;for(const e of o){const t=l.get(e.session_id);(!t||e.user_message_count>t.user_message_count||e.user_message_count===t.user_message_count&&e.duration_minutes>t.duration_minutes)&&l.set(e.session_id,e)}const d=new Set(l.keys());o=[...l.values()];for(const e of c.keys())d.has(e)||c.delete(e);o.sort((e,t)=>t.start_time.localeCompare(e.start_time));const u=o.filter(e=>!(e.user_message_count<2)&&!(e.duration_minutes<1)),m=new Map,g=[],h=await Promise.all(u.map(async e=>({sessionId:e.session_id,cached:await loadCachedFacets(e.session_id)})));for(const{sessionId:e,cached:t}of h)if(t)m.set(e,t);else{const t=c.get(e);t&&g.length<50&&g.push({log:t,sessionId:e})}for(let e=0;e<g.length;e+=50){const t=g.slice(e,e+50),n=await Promise.all(t.map(async({log:e,sessionId:t})=>({sessionId:t,newFacets:await extractFacetsFromAPI(e,t)}))),s=[];for(const{sessionId:e,newFacets:t}of n)t&&(m.set(e,t),s.push(t));await Promise.all(s.map(e=>saveFacets(e)))}const isMinimalSession=e=>{const t=m.get(e);if(!t)return!1;const n=t.goal_categories,s=(o=n,o?Object.keys(o):[]).filter(e=>(n[e]??0)>0);var o;return 1===s.length&&"warmup_minimal"===s[0]},_=u.filter(e=>!isMinimalSession(e.session_id)),b=new Map;for(const[e,t]of m)isMinimalSession(e)||b.set(e,t);const x=function(e,t){const n={total_sessions:e.length,sessions_with_facets:t.size,date_range:{start:"",end:""},total_messages:0,total_duration_hours:0,total_input_tokens:0,total_output_tokens:0,tool_counts:{},languages:{},git_commits:0,git_pushes:0,projects:{},goal_categories:{},outcomes:{},satisfaction:{},helpfulness:{},session_types:{},friction:{},success:{},session_summaries:[],total_interruptions:0,total_tool_errors:0,tool_error_categories:{},user_response_times:[],median_response_time:0,avg_response_time:0,sessions_using_task_agent:0,sessions_using_mcp:0,sessions_using_web_search:0,sessions_using_web_fetch:0,total_lines_added:0,total_lines_removed:0,total_files_modified:0,days_active:0,messages_per_day:0,message_hours:[],multi_clauding:{overlap_events:0,sessions_involved:0,user_messages_during:0}},s=[],o=[],i=[];for(const a of e){s.push(a.start_time),n.total_messages+=a.user_message_count,n.total_duration_hours+=a.duration_minutes/60,n.total_input_tokens+=a.input_tokens,n.total_output_tokens+=a.output_tokens,n.git_commits+=a.git_commits,n.git_pushes+=a.git_pushes,n.total_interruptions+=a.user_interruptions,n.total_tool_errors+=a.tool_errors;for(const[e,t]of Object.entries(a.tool_error_categories))n.tool_error_categories[e]=(n.tool_error_categories[e]||0)+t;o.push(...a.user_response_times),a.uses_task_agent&&n.sessions_using_task_agent++,a.uses_mcp&&n.sessions_using_mcp++,a.uses_web_search&&n.sessions_using_web_search++,a.uses_web_fetch&&n.sessions_using_web_fetch++,n.total_lines_added+=a.lines_added,n.total_lines_removed+=a.lines_removed,n.total_files_modified+=a.files_modified,i.push(...a.message_hours);for(const[e,t]of Object.entries(a.tool_counts))n.tool_counts[e]=(n.tool_counts[e]||0)+t;for(const[e,t]of Object.entries(a.languages))n.languages[e]=(n.languages[e]||0)+t;a.project_path&&(n.projects[a.project_path]=(n.projects[a.project_path]||0)+1);const e=t.get(a.session_id);if(e){for(const[t,s]of safeEntries(e.goal_categories))s>0&&(n.goal_categories[t]=(n.goal_categories[t]||0)+s);n.outcomes[e.outcome]=(n.outcomes[e.outcome]||0)+1;for(const[t,s]of safeEntries(e.user_satisfaction_counts))s>0&&(n.satisfaction[t]=(n.satisfaction[t]||0)+s);n.helpfulness[e.claude_helpfulness]=(n.helpfulness[e.claude_helpfulness]||0)+1,n.session_types[e.session_type]=(n.session_types[e.session_type]||0)+1;for(const[t,s]of safeEntries(e.friction_counts))s>0&&(n.friction[t]=(n.friction[t]||0)+s);"none"!==e.primary_success&&(n.success[e.primary_success]=(n.success[e.primary_success]||0)+1)}n.session_summaries.length<50&&n.session_summaries.push({id:a.session_id.slice(0,8),date:a.start_time.split("T")[0]||"",summary:a.summary||a.first_prompt.slice(0,100),goal:e?.underlying_goal})}if(s.sort(),n.date_range.start=s[0]?.split("T")[0]||"",n.date_range.end=s[s.length-1]?.split("T")[0]||"",n.user_response_times=o,o.length>0){const e=[...o].sort((e,t)=>e-t);n.median_response_time=e[Math.floor(e.length/2)]||0,n.avg_response_time=o.reduce((e,t)=>e+t,0)/o.length}const a=new Set(s.map(e=>e.split("T")[0]));return n.days_active=a.size,n.messages_per_day=n.days_active>0?Math.round(n.total_messages/n.days_active*10)/10:0,n.message_hours=i,n.multi_clauding=detectMultiClauding(e),n}(_,b);x.total_sessions_scanned=s;const y=await async function(e,t){const n=Array.from(t.values()).slice(0,50).map(e=>`- ${e.brief_summary} (${e.outcome}, ${e.claude_helpfulness})`).join("\n"),s=Array.from(t.values()).filter(e=>e.friction_detail).slice(0,20).map(e=>`- ${e.friction_detail}`).join("\n"),o=Array.from(t.values()).flatMap(e=>e.user_instructions_to_claude||[]).slice(0,15).map(e=>`- ${e}`).join("\n"),i=A({sessions:e.total_sessions,analyzed:e.sessions_with_facets,date_range:e.date_range,messages:e.total_messages,hours:Math.round(e.total_duration_hours),commits:e.git_commits,top_tools:Object.entries(e.tool_counts).sort((e,t)=>t[1]-e[1]).slice(0,8),top_goals:Object.entries(e.goal_categories).sort((e,t)=>t[1]-e[1]).slice(0,8),outcomes:e.outcomes,satisfaction:e.satisfaction,friction:e.friction,success:e.success,languages:e.languages},null,2)+"\n\nSESSION SUMMARIES:\n"+n+"\n\nFRICTION DETAILS:\n"+s+"\n\nUSER INSTRUCTIONS TO CLAUDE:\n"+(o||"None captured"),a=await Promise.all(L.map(e=>generateSectionInsight(e,i))),r={};for(const{name:e,result:t}of a)t&&(r[e]=t);const c=r.project_areas?.areas?.map(e=>`- ${e.name}: ${e.description}`).join("\n")||"",l=r.what_works?.impressive_workflows?.map(e=>`- ${e.title}: ${e.description}`).join("\n")||"",d=r.friction_analysis?.categories?.map(e=>`- ${e.category}: ${e.description}`).join("\n")||"",p=r.suggestions?.features_to_try?.map(e=>`- ${e.feature}: ${e.one_liner}`).join("\n")||"",u=r.suggestions?.usage_patterns?.map(e=>`- ${e.title}: ${e.suggestion}`).join("\n")||"",m=r.on_the_horizon?.opportunities?.map(e=>`- ${e.title}: ${e.whats_possible}`).join("\n")||"",f={name:"at_a_glance",prompt:`You're writing an "At a Glance" summary for a Context Code usage insights report for Context Code users. The goal is to help them understand their usage and improve how they can use Claude better, especially as models improve.\n\nUse this 4-part structure:\n\n1. **What's working** - What is the user's unique style of interacting with Claude and what are some impactful things they've done? You can include one or two details, but keep it high level since things might not be fresh in the user's memory. Don't be fluffy or overly complimentary. Also, don't focus on the tool calls they use.\n\n2. **What's hindering you** - Split into (a) Claude's fault (misunderstandings, wrong approaches, bugs) and (b) user-side friction (not providing enough context, environment issues -- ideally more general than just one project). Be honest but constructive.\n\n3. **Quick wins to try** - Specific Context Code features they could try from the examples below, or a workflow technique if you think it's really compelling. (Avoid stuff like "Ask Claude to confirm before taking actions" or "Type out more context up front" which are less compelling.)\n\n4. **Ambitious workflows for better models** - As we move to much more capable models over the next 3-6 months, what should they prepare for? What workflows that seem impossible now will become possible? Draw from the appropriate section below.\n\nKeep each section to 2-3 not-too-long sentences. Don't overwhelm the user. Don't mention specific numerical stats or underlined_categories from the session data below. Use a coaching tone.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "whats_working": "(refer to instructions above)",\n "whats_hindering": "(refer to instructions above)",\n "quick_wins": "(refer to instructions above)",\n "ambitious_workflows": "(refer to instructions above)"\n}\n\nSESSION DATA:\n${i}\n\n## Project Areas (what user works on)\n${c}\n\n## Big Wins (impressive accomplishments)\n${l}\n\n## Friction Categories (where things go wrong)\n${d}\n\n## Features to Try\n${p}\n\n## Usage Patterns to Adopt\n${u}\n\n## On the Horizon (ambitious workflows for better models)\n${m}`,maxTokens:8192},g=await generateSectionInsight(f,"");return g.result&&(r.at_a_glance=g.result),r}(x,m),w=generateHtmlReport(x,y);try{await i(getDataDir(),{recursive:!0})}catch{}const k=f(getDataDir(),"report.html");return await p(k,w,{encoding:"utf-8",mode:384}),{insights:y,htmlPath:k,data:x,remoteStats:t,facets:b}}function safeEntries(e){return e?Object.entries(e):[]}const W={type:"prompt",name:"insights",description:"Generate a report analyzing your Context Code sessions",contentLength:0,progressMessage:"analyzing your sessions",source:"builtin",async getPromptForCommand(e){let n=!1,s=[],o=!1;"ant"===process.env.USER_TYPE&&(n=e?.includes("--homespaces")??!1,s=await O(),o=s.length>0,n&&o&&console.error(`Collecting sessions from ${s.length} homespace(s): ${s.join(", ")}...`));const{insights:i,htmlPath:a,data:r,remoteStats:c}=await generateUsageReport({collectRemote:n});let l=`file://${a}`,d="";if("ant"===process.env.USER_TYPE){const e=(new Date).toISOString().replace(/[-:]/g,"").replace("T","_").slice(0,15),n=`${process.env.SAFEUSER||process.env.USER||"unknown"}_insights_${e}.html`,s=`s3://anthropic-serve/atamkin/cc-user-reports/${n}`,o=`https://s3-frontend.infra.ant.dev/anthropic-serve/atamkin/cc-user-reports/${n}`;l=o;try{t("ff",["cp",a,s],{timeout:6e4,stdio:"pipe"})}catch{l=`file://${a}`,d=`\nAutomatic upload failed. Are you on the boron namespace? Try \`use-bo\` and ensure you've run \`sso\`.\nTo share, run: ff cp ${a} ${s}\nThen access at: ${o}`}}const p=[r.total_sessions_scanned&&r.total_sessions_scanned>r.total_sessions?`${r.total_sessions_scanned.toLocaleString()} sessions total · ${r.total_sessions} analyzed`:`${r.total_sessions} sessions`,`${r.total_messages.toLocaleString()} messages`,`${Math.round(r.total_duration_hours)}h`,`${r.git_commits} commits`].join(" · ");let u="";if("ant"===process.env.USER_TYPE)if(c&&c.totalCopied>0){const e=c.hosts.filter(e=>e.sessionCount>0).map(e=>e.name).join(", ");u=`\n_Collected ${c.totalCopied} new sessions from: ${e}_\n`}else!n&&o&&(u=`\n_Tip: Run \`/insights --homespaces\` to include sessions from your ${s.length} running homespace(s)_\n`);const m=i.at_a_glance,f=m?`## At a Glance\n\n${m.whats_working?`**What's working:** ${m.whats_working} See _Impressive Things You Did_.`:""}\n\n${m.whats_hindering?`**What's hindering you:** ${m.whats_hindering} See _Where Things Go Wrong_.`:""}\n\n${m.quick_wins?`**Quick wins to try:** ${m.quick_wins} See _Features to Try_.`:""}\n\n${m.ambitious_workflows?`**Ambitious workflows:** ${m.ambitious_workflows} See _On the Horizon_.`:""}`:"_No insights generated_",g=`${`# Context Code Insights\n\n${p}\n${r.date_range.start} to ${r.date_range.end}\n${u}\n`}${f}\n\nYour full shareable insights report is ready: ${l}${d}`;return[{type:"text",text:`The user just ran /insights to generate a usage report analyzing their Context Code sessions.\n\nHere is the full insights data:\n${A(i,null,2)}\n\nReport URL: ${l}\nHTML file: ${a}\nFacets directory: ${getFacetsDir()}\n\nHere is what the user sees:\n${g}\n\nNow output the following message exactly:\n\n<message>\nYour shareable insights report is ready:\n${l}${d}\n\nWant to dig into any section or try one of the suggestions?\n</message>`}]}};function isValidSessionFacets(e){if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.underlying_goal&&"string"==typeof t.outcome&&"string"==typeof t.brief_summary&&null!==t.goal_categories&&"object"==typeof t.goal_categories&&null!==t.user_satisfaction_counts&&"object"==typeof t.user_satisfaction_counts&&null!==t.friction_counts&&"object"==typeof t.friction_counts}export default W;
|
|
1
|
+
import{MACRO as e}from"../recovery/bunBundleShim.js";import{execFileSync as t}from"child_process";import{diffLines as n}from"diff";import{constants as s}from"fs";import{copyFile as o,mkdir as i,mkdtemp as a,readdir as r,readFile as c,rm as l,unlink as d,writeFile as p}from"fs/promises";import{tmpdir as u}from"os";import{extname as m,join as f}from"path";import{queryWithModel as g}from"../services/api/claude.js";import{AGENT_TOOL_NAME as h,LEGACY_AGENT_TOOL_NAME as _}from"../tools/AgentTool/constants.js";import{getClaudeConfigHomeDir as v}from"../utils/envUtils.js";import{toError as b}from"../utils/errors.js";import{execFileNoThrow as x}from"../utils/execFileNoThrow.js";import{logError as y}from"../utils/log.js";import{extractTextContent as w}from"../utils/messages.js";import{getDefaultOpusModel as k}from"../utils/model/model.js";import{getProjectsDir as C,getSessionFilesWithMtime as $,getSessionIdFromLog as T,loadAllLogsFromSessionFile as S}from"../utils/sessionStorage.js";import{jsonParse as E,jsonStringify as A}from"../utils/slowOperations.js";import{countCharInString as I}from"../utils/stringUtils.js";import{asSystemPrompt as z}from"../utils/systemPromptType.js";import{escapeXmlAttr as j}from"../utils/xml.js";function getAnalysisModel(){return k()}const O="ant"===process.env.USER_TYPE?async()=>{const{stdout:e,code:t}=await x("coder",["list","-o","json"],{timeout:3e4});if(0!==t)return[];try{return E(e).filter(e=>"running"===e.latest_build?.status).map(e=>e.name)}catch{return[]}}:async()=>[],D="ant"===process.env.USER_TYPE?async e=>{const{stdout:t,code:n}=await x("ssh",[`${e}.coder`,'find /root/.claude/projects -name "*.jsonl" 2>/dev/null | wc -l'],{timeout:3e4});return 0!==n?0:parseInt(t.trim(),10)||0}:async()=>0,N="ant"===process.env.USER_TYPE?async(e,t)=>{const n={copied:0,skipped:0},c=await a(f(u(),"claude-hs-"));try{if(0!==(await x("scp",["-rq",`${e}.coder:/root/.claude/projects/`,c],{timeout:3e5})).code)return n;const a=f(c,"projects");let l;try{l=await r(a,{withFileTypes:!0})}catch{return n}await Promise.all(l.map(async c=>{const l=c.name,d=f(a,l);if(!c.isDirectory())return;const p=f(t,`${l}__${e}`);try{await i(p,{recursive:!0})}catch{}let u;try{u=await r(d,{withFileTypes:!0})}catch{return}await Promise.all(u.map(async e=>{const t=e.name;if(!t.endsWith(".jsonl"))return;const i=f(d,t),a=f(p,t);try{await o(i,a,s.COPYFILE_EXCL),n.copied++}catch{n.skipped++}}))}))}finally{try{await l(c,{recursive:!0,force:!0})}catch{}}return n}:async()=>({copied:0,skipped:0}),P="ant"===process.env.USER_TYPE?async e=>{const t=await O(),n=[];let s=0,o=0;const i=await Promise.all(t.map(async t=>{const n=await D(t);if(n>0){const{copied:s,skipped:o}=await N(t,e);return{name:t,sessionCount:n,copied:s,skipped:o}}return{name:t,sessionCount:n,copied:0,skipped:0}}));for(const e of i)n.push({name:e.name,sessionCount:e.sessionCount}),s+=e.copied,o+=e.skipped;return{hosts:n,totalCopied:s,totalSkipped:o}}:async()=>({hosts:[],totalCopied:0,totalSkipped:0}),R={".ts":"TypeScript",".tsx":"TypeScript",".js":"JavaScript",".jsx":"JavaScript",".py":"Python",".rb":"Ruby",".go":"Go",".rs":"Rust",".java":"Java",".md":"Markdown",".json":"JSON",".yaml":"YAML",".yml":"YAML",".sh":"Shell",".css":"CSS",".html":"HTML"},M={debug_investigate:"Debug/Investigate",implement_feature:"Implement Feature",fix_bug:"Fix Bug",write_script_tool:"Write Script/Tool",refactor_code:"Refactor Code",configure_system:"Configure System",create_pr_commit:"Create PR/Commit",analyze_data:"Analyze Data",understand_codebase:"Understand Codebase",write_tests:"Write Tests",write_docs:"Write Docs",deploy_infra:"Deploy/Infra",warmup_minimal:"Cache Warmup",fast_accurate_search:"Fast/Accurate Search",correct_code_edits:"Correct Code Edits",good_explanations:"Good Explanations",proactive_help:"Proactive Help",multi_file_changes:"Multi-file Changes",handled_complexity:"Multi-file Changes",good_debugging:"Good Debugging",misunderstood_request:"Misunderstood Request",wrong_approach:"Wrong Approach",buggy_code:"Buggy Code",user_rejected_action:"User Rejected Action",claude_got_blocked:"Claude Got Blocked",user_stopped_early:"User Stopped Early",wrong_file_or_location:"Wrong File/Location",excessive_changes:"Excessive Changes",slow_or_verbose:"Slow/Verbose",tool_failed:"Tool Failed",user_unclear:"User Unclear",external_issue:"External Issue",frustrated:"Frustrated",dissatisfied:"Dissatisfied",likely_satisfied:"Likely Satisfied",satisfied:"Satisfied",happy:"Happy",unsure:"Unsure",neutral:"Neutral",delighted:"Delighted",single_task:"Single Task",multi_task:"Multi Task",iterative_refinement:"Iterative Refinement",exploration:"Exploration",quick_question:"Quick Question",fully_achieved:"Fully Achieved",mostly_achieved:"Mostly Achieved",partially_achieved:"Partially Achieved",not_achieved:"Not Achieved",unclear_from_transcript:"Unclear",unhelpful:"Unhelpful",slightly_helpful:"Slightly Helpful",moderately_helpful:"Moderately Helpful",very_helpful:"Very Helpful",essential:"Essential"};function getDataDir(){return f(v(),"usage-data")}function getFacetsDir(){return f(getDataDir(),"facets")}function getSessionMetaDir(){return f(getDataDir(),"session-meta")}function getLanguageFromPath(e){const t=m(e).toLowerCase();return R[t]||null}function hasValidDates(e){return!Number.isNaN(e.created.getTime())&&!Number.isNaN(e.modified.getTime())}function logToSessionMeta(e){const t=function(e){const t={},s={};let o=0,i=0,a=0,r=0,c=0;const l=[];let d=0;const p={};let u=!1,m=0,f=0;const g=new Set,v=[],b=[];let x=!1,y=!1,w=!1,k=null;for(const C of e.messages){const e=C.timestamp;if("assistant"===C.type&&C.message){e&&(k=e);const c=C.message.usage;c&&(a+=c.input_tokens||0,r+=c.output_tokens||0);const l=C.message.content;if(Array.isArray(l))for(const e of l)if("tool_use"===e.type&&"name"in e){const a=e.name;t[a]=(t[a]||0)+1,a!==h&&a!==_||(u=!0),a.startsWith("mcp__")&&(x=!0),"WebSearch"===a&&(y=!0),"WebFetch"===a&&(w=!0);const r=e.input;if(r){const e=r.file_path||"";if(e){const t=getLanguageFromPath(e);t&&(s[t]=(s[t]||0)+1),"Edit"!==a&&"Write"!==a||g.add(e)}if("Edit"===a){const e=r.old_string||"",t=r.new_string||"";for(const s of n(e,t))s.added&&(m+=s.count||0),s.removed&&(f+=s.count||0)}if("Write"===a){const e=r.content||"";e&&(m+=I(e,"\n")+1)}const t=r.command||"";t.includes("git commit")&&o++,t.includes("git push")&&i++}}}if("user"===C.type&&C.message){const t=C.message.content;let n=!1;if("string"==typeof t&&t.trim())n=!0;else if(Array.isArray(t))for(const e of t)if("text"===e.type&&"text"in e){n=!0;break}if(n){if(e)try{const t=new Date(e).getHours();v.push(t),b.push(e)}catch{}if(k&&e){const t=new Date(k).getTime(),n=(new Date(e).getTime()-t)/1e3;n>2&&n<3600&&l.push(n)}}if(Array.isArray(t))for(const e of t)if("tool_result"===e.type&&"content"in e&&e.is_error){d++;const t=e.content;let n="Other";if("string"==typeof t){const e=t.toLowerCase();e.includes("exit code")?n="Command Failed":e.includes("rejected")||e.includes("doesn't want")?n="User Rejected":e.includes("string to replace not found")||e.includes("no changes")?n="Edit Failed":e.includes("modified since read")?n="File Changed":e.includes("exceeds maximum")||e.includes("too large")?n="File Too Large":(e.includes("file not found")||e.includes("does not exist"))&&(n="File Not Found")}p[n]=(p[n]||0)+1}if("string"==typeof t)t.includes("[Request interrupted by user")&&c++;else if(Array.isArray(t))for(const e of t)if("text"===e.type&&"text"in e&&e.text.includes("[Request interrupted by user")){c++;break}}}return{toolCounts:t,languages:s,gitCommits:o,gitPushes:i,inputTokens:a,outputTokens:r,userInterruptions:c,userResponseTimes:l,toolErrors:d,toolErrorCategories:p,usesTaskAgent:u,usesMcp:x,usesWebSearch:y,usesWebFetch:w,linesAdded:m,linesRemoved:f,filesModified:g,messageHours:v,userMessageTimestamps:b}}(e),s=T(e)||"unknown",o=e.created.toISOString(),i=Math.round((e.modified.getTime()-e.created.getTime())/1e3/60);let a=0,r=0;for(const t of e.messages)if("assistant"===t.type&&r++,"user"===t.type&&t.message){const e=t.message.content;let n=!1;if("string"==typeof e&&e.trim())n=!0;else if(Array.isArray(e))for(const t of e)if("text"===t.type&&"text"in t){n=!0;break}n&&a++}return{session_id:s,project_path:e.projectPath||"",start_time:o,duration_minutes:i,user_message_count:a,assistant_message_count:r,tool_counts:t.toolCounts,languages:t.languages,git_commits:t.gitCommits,git_pushes:t.gitPushes,input_tokens:t.inputTokens,output_tokens:t.outputTokens,first_prompt:e.firstPrompt||"",summary:e.summary,user_interruptions:t.userInterruptions,user_response_times:t.userResponseTimes,tool_errors:t.toolErrors,tool_error_categories:t.toolErrorCategories,uses_task_agent:t.usesTaskAgent,uses_mcp:t.usesMcp,uses_web_search:t.usesWebSearch,uses_web_fetch:t.usesWebFetch,lines_added:t.linesAdded,lines_removed:t.linesRemoved,files_modified:t.filesModified.size,message_hours:t.messageHours,user_message_timestamps:t.userMessageTimestamps}}export function deduplicateSessionBranches(e){const t=new Map;for(const n of e){const e=n.meta.session_id,s=t.get(e);(!s||n.meta.user_message_count>s.meta.user_message_count||n.meta.user_message_count===s.meta.user_message_count&&n.meta.duration_minutes>s.meta.duration_minutes)&&t.set(e,n)}return[...t.values()]}async function summarizeTranscriptChunk(e){try{const t=await g({systemPrompt:z([]),userPrompt:"Summarize this portion of a Context Code session transcript. Focus on:\n1. What the user asked for\n2. What Claude did (tools used, files modified)\n3. Any friction or issues\n4. The outcome\n\nKeep it concise - 3-5 sentences. Preserve specific details like file names, error messages, and user feedback.\n\nTRANSCRIPT CHUNK:\n"+e,signal:(new AbortController).signal,options:{model:getAnalysisModel(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:500}});return w(t.message.content)||e.slice(0,2e3)}catch{return e.slice(0,2e3)}}async function formatTranscriptWithSummarization(e){const t=function(e){const t=[],n=logToSessionMeta(e);t.push(`Session: ${n.session_id.slice(0,8)}`),t.push(`Date: ${n.start_time}`),t.push(`Project: ${n.project_path}`),t.push(`Duration: ${n.duration_minutes} min`),t.push("");for(const n of e.messages)if("user"===n.type&&n.message){const e=n.message.content;if("string"==typeof e)t.push(`[User]: ${e.slice(0,500)}`);else if(Array.isArray(e))for(const n of e)"text"===n.type&&"text"in n&&t.push(`[User]: ${n.text.slice(0,500)}`)}else if("assistant"===n.type&&n.message){const e=n.message.content;if(Array.isArray(e))for(const n of e)"text"===n.type&&"text"in n?t.push(`[Assistant]: ${n.text.slice(0,300)}`):"tool_use"===n.type&&"name"in n&&t.push(`[Tool: ${n.name}]`)}return t.join("\n")}(e);if(t.length<=3e4)return t;const n=[];for(let e=0;e<t.length;e+=25e3)n.push(t.slice(e,e+25e3));const s=await Promise.all(n.map(summarizeTranscriptChunk)),o=logToSessionMeta(e);return[`Session: ${o.session_id.slice(0,8)}`,`Date: ${o.start_time}`,`Project: ${o.project_path}`,`Duration: ${o.duration_minutes} min`,`[Long session - ${n.length} parts summarized]`,""].join("\n")+s.join("\n\n---\n\n")}async function loadCachedFacets(e){const t=f(getFacetsDir(),`${e}.json`);try{const e=await c(t,{encoding:"utf-8"}),n=E(e);if(!isValidSessionFacets(n)){try{await d(t)}catch{}return null}return n}catch{return null}}async function saveFacets(e){try{await i(getFacetsDir(),{recursive:!0})}catch{}const t=f(getFacetsDir(),`${e.session_id}.json`);await p(t,A(e,null,2),{encoding:"utf-8",mode:384})}async function loadCachedSessionMeta(e){const t=f(getSessionMetaDir(),`${e}.json`);try{const e=await c(t,{encoding:"utf-8"});return E(e)}catch{return null}}async function saveSessionMeta(e){try{await i(getSessionMetaDir(),{recursive:!0})}catch{}const t=f(getSessionMetaDir(),`${e.session_id}.json`);await p(t,A(e,null,2),{encoding:"utf-8",mode:384})}async function extractFacetsFromAPI(e,t){try{const n=`Analyze this Context Code session and extract structured facets.\n\nCRITICAL GUIDELINES:\n\n1. **goal_categories**: Count ONLY what the USER explicitly asked for.\n - DO NOT count Claude's autonomous codebase exploration\n - DO NOT count work Claude decided to do on its own\n - ONLY count when user says "can you...", "please...", "I need...", "let's..."\n\n2. **user_satisfaction_counts**: Base ONLY on explicit user signals.\n - "Yay!", "great!", "perfect!" → happy\n - "thanks", "looks good", "that works" → satisfied\n - "ok, now let's..." (continuing without complaint) → likely_satisfied\n - "that's not right", "try again" → dissatisfied\n - "this is broken", "I give up" → frustrated\n\n3. **friction_counts**: Be specific about what went wrong.\n - misunderstood_request: Claude interpreted incorrectly\n - wrong_approach: Right goal, wrong solution method\n - buggy_code: Code didn't work correctly\n - user_rejected_action: User said no/stop to a tool call\n - excessive_changes: Over-engineered or changed too much\n\n4. If very short or just warmup, use warmup_minimal for goal_category\n\nSESSION:\n${await formatTranscriptWithSummarization(e)}\n\nRESPOND WITH ONLY A VALID JSON OBJECT matching this schema:\n{\n "underlying_goal": "What the user fundamentally wanted to achieve",\n "goal_categories": {"category_name": count, ...},\n "outcome": "fully_achieved|mostly_achieved|partially_achieved|not_achieved|unclear_from_transcript",\n "user_satisfaction_counts": {"level": count, ...},\n "claude_helpfulness": "unhelpful|slightly_helpful|moderately_helpful|very_helpful|essential",\n "session_type": "single_task|multi_task|iterative_refinement|exploration|quick_question",\n "friction_counts": {"friction_type": count, ...},\n "friction_detail": "One sentence describing friction or empty",\n "primary_success": "none|fast_accurate_search|correct_code_edits|good_explanations|proactive_help|multi_file_changes|good_debugging",\n "brief_summary": "One sentence: what user wanted and whether they got it"\n}`,s=await g({systemPrompt:z([]),userPrompt:n,signal:(new AbortController).signal,options:{model:getAnalysisModel(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:4096}}),o=w(s.message.content).match(/\{[\s\S]*\}/);if(!o)return null;const i=E(o[0]);if(!isValidSessionFacets(i))return null;return{...i,session_id:t}}catch(e){return y(new Error(`Facet extraction failed: ${b(e).message}`)),null}}export function detectMultiClauding(e){const t=[];for(const n of e)for(const e of n.user_message_timestamps)try{const s=new Date(e).getTime();t.push({ts:s,sessionId:n.session_id})}catch{}t.sort((e,t)=>e.ts-t.ts);const n=new Set,s=new Set;let o=0;const i=new Map;for(let e=0;e<t.length;e++){const a=t[e];for(;o<e&&a.ts-t[o].ts>18e5;){const e=t[o];i.get(e.sessionId)===o&&i.delete(e.sessionId),o++}const r=i.get(a.sessionId);if(void 0!==r)for(let o=r+1;o<e;o++){const e=t[o];if(e.sessionId!==a.sessionId){const o=[a.sessionId,e.sessionId].sort().join(":");n.add(o),s.add(`${t[r].ts}:${a.sessionId}`),s.add(`${e.ts}:${e.sessionId}`),s.add(`${a.ts}:${a.sessionId}`);break}}i.set(a.sessionId,e)}const a=new Set;for(const e of n){const[t,n]=e.split(":");t&&a.add(t),n&&a.add(n)}return{overlap_events:n.size,sessions_involved:a.size,user_messages_during:s.size}}const L=[{name:"project_areas",prompt:'Analyze this Context Code usage data and identify project areas.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "areas": [\n {"name": "Area name", "session_count": N, "description": "2-3 sentences about what was worked on and how Context Code was used."}\n ]\n}\n\nInclude 4-5 areas. Skip internal CC operations.',maxTokens:8192},{name:"interaction_style",prompt:'Analyze this Context Code usage data and describe the user\'s interaction style.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "narrative": "2-3 paragraphs analyzing HOW the user interacts with Context Code. Use second person \'you\'. Describe patterns: iterate quickly vs detailed upfront specs? Interrupt often or let Claude run? Include specific examples. Use **bold** for key insights.",\n "key_pattern": "One sentence summary of most distinctive interaction style"\n}',maxTokens:8192},{name:"what_works",prompt:'Analyze this Context Code usage data and identify what\'s working well for this user. Use second person ("you").\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence of context",\n "impressive_workflows": [\n {"title": "Short title (3-6 words)", "description": "2-3 sentences describing the impressive workflow or approach. Use \'you\' not \'the user\'."}\n ]\n}\n\nInclude 3 impressive workflows.',maxTokens:8192},{name:"friction_analysis",prompt:'Analyze this Context Code usage data and identify friction points for this user. Use second person ("you").\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence summarizing friction patterns",\n "categories": [\n {"category": "Concrete category name", "description": "1-2 sentences explaining this category and what could be done differently. Use \'you\' not \'the user\'.", "examples": ["Specific example with consequence", "Another example"]}\n ]\n}\n\nInclude 3 friction categories with 2 examples each.',maxTokens:8192},{name:"suggestions",prompt:'Analyze this Context Code usage data and suggest improvements.\n\n## CC FEATURES REFERENCE (pick from these for features_to_try):\n1. **MCP Servers**: Connect Claude to external tools, databases, and APIs via Model Context Protocol.\n - How to use: Run `claude mcp add <server-name> -- <command>`\n - Good for: database queries, Slack integration, GitHub issue lookup, connecting to internal APIs\n\n2. **Custom Skills**: Reusable prompts you define as markdown files that run with a single /command.\n - How to use: Create `.claude/skills/commit/SKILL.md` with instructions. Then type `/commit` to run it.\n - Good for: repetitive workflows - /commit, /review, /test, /deploy, /pr, or complex multi-step workflows\n\n3. **Hooks**: Shell commands that auto-run at specific lifecycle events.\n - How to use: Add to `.claude/settings.json` under "hooks" key.\n - Good for: auto-formatting code, running type checks, enforcing conventions\n\n4. **Headless Mode**: Run Claude non-interactively from scripts and CI/CD.\n - How to use: `claude -p "fix lint errors" --allowedTools "Edit,Read,Bash"`\n - Good for: CI/CD integration, batch code fixes, automated reviews\n\n5. **Task Agents**: Claude spawns focused sub-agents for complex exploration or parallel work.\n - How to use: Claude auto-invokes when helpful, or ask "use an agent to explore X"\n - Good for: codebase exploration, understanding complex systems\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "claude_md_additions": [\n {"addition": "A specific line or block to add to CLAUDE.md based on workflow patterns. E.g., \'Always run tests after modifying auth-related files\'", "why": "1 sentence explaining why this would help based on actual sessions", "prompt_scaffold": "Instructions for where to add this in CLAUDE.md. E.g., \'Add under ## Testing section\'"}\n ],\n "features_to_try": [\n {"feature": "Feature name from CC FEATURES REFERENCE above", "one_liner": "What it does", "why_for_you": "Why this would help YOU based on your sessions", "example_code": "Actual command or config to copy"}\n ],\n "usage_patterns": [\n {"title": "Short title", "suggestion": "1-2 sentence summary", "detail": "3-4 sentences explaining how this applies to YOUR work", "copyable_prompt": "A specific prompt to copy and try"}\n ]\n}\n\nIMPORTANT for claude_md_additions: PRIORITIZE instructions that appear MULTIPLE TIMES in the user data. If user told Claude the same thing in 2+ sessions (e.g., \'always run tests\', \'use TypeScript\'), that\'s a PRIME candidate - they shouldn\'t have to repeat themselves.\n\nIMPORTANT for features_to_try: Pick 2-3 from the CC FEATURES REFERENCE above. Include 2-3 items for each category.',maxTokens:8192},{name:"on_the_horizon",prompt:'Analyze this Context Code usage data and identify future opportunities.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "intro": "1 sentence about evolving AI-assisted development",\n "opportunities": [\n {"title": "Short title (4-8 words)", "whats_possible": "2-3 ambitious sentences about autonomous workflows", "how_to_try": "1-2 sentences mentioning relevant tooling", "copyable_prompt": "Detailed prompt to try"}\n ]\n}\n\nInclude 3 opportunities. Think BIG - autonomous workflows, parallel agents, iterating against tests.',maxTokens:8192},..."ant"===process.env.USER_TYPE?[{name:"cc_team_improvements",prompt:'Analyze this Context Code usage data and suggest product improvements for the CC team.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "improvements": [\n {"title": "Product/tooling improvement", "detail": "3-4 sentences describing the improvement", "evidence": "3-4 sentences with specific session examples"}\n ]\n}\n\nInclude 2-3 improvements based on friction patterns observed.',maxTokens:8192},{name:"model_behavior_improvements",prompt:'Analyze this Context Code usage data and suggest model behavior improvements.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "improvements": [\n {"title": "Model behavior change", "detail": "3-4 sentences describing what the model should do differently", "evidence": "3-4 sentences with specific examples"}\n ]\n}\n\nInclude 2-3 improvements based on friction patterns observed.',maxTokens:8192}]:[],{name:"fun_ending",prompt:'Analyze this Context Code usage data and find a memorable moment.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "headline": "A memorable QUALITATIVE moment from the transcripts - not a statistic. Something human, funny, or surprising.",\n "detail": "Brief context about when/where this happened"\n}\n\nFind something genuinely interesting or amusing from the session summaries.',maxTokens:8192}];async function generateSectionInsight(e,t){try{const n=await g({systemPrompt:z([]),userPrompt:e.prompt+"\n\nDATA:\n"+t,signal:(new AbortController).signal,options:{model:k(),querySource:"insights",agents:[],isNonInteractiveSession:!0,hasAppendSystemPrompt:!1,mcpTools:[],maxOutputTokensOverride:e.maxTokens}}),s=w(n.message.content);if(s){const t=s.match(/\{[\s\S]*\}/);if(t)try{return{name:e.name,result:E(t[0])}}catch{return{name:e.name,result:null}}}return{name:e.name,result:null}}catch(t){return y(new Error(`${e.name} failed: ${b(t).message}`)),{name:e.name,result:null}}}function escapeHtmlWithBold(e){return j(e).replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>")}const U=["frustrated","dissatisfied","likely_satisfied","satisfied","happy","unsure"],F=["not_achieved","partially_achieved","mostly_achieved","fully_achieved","unclear_from_transcript"];function generateBarChart(e,t,n=6,s){let o;if(o=s?s.filter(t=>t in e&&(e[t]??0)>0).map(t=>[t,e[t]??0]):Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,n),0===o.length)return'<p class="empty">No data</p>';const i=Math.max(...o.map(e=>e[1]));return o.map(([e,n])=>{const s=n/i*100,o=M[e]||e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase());return`<div class="bar-row">\n <div class="bar-label">${j(o)}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${s}%;background:${t}"></div></div>\n <div class="bar-value">${n}</div>\n </div>`}).join("\n")}function generateHtmlReport(e,t){const n=t.at_a_glance,s=n?`\n <div class="at-a-glance">\n <div class="glance-title">At a Glance</div>\n <div class="glance-sections">\n ${n.whats_working?`<div class="glance-section"><strong>What's working:</strong> ${escapeHtmlWithBold(n.whats_working)} <a href="#section-wins" class="see-more">Impressive Things You Did →</a></div>`:""}\n ${n.whats_hindering?`<div class="glance-section"><strong>What's hindering you:</strong> ${escapeHtmlWithBold(n.whats_hindering)} <a href="#section-friction" class="see-more">Where Things Go Wrong →</a></div>`:""}\n ${n.quick_wins?`<div class="glance-section"><strong>Quick wins to try:</strong> ${escapeHtmlWithBold(n.quick_wins)} <a href="#section-features" class="see-more">Features to Try →</a></div>`:""}\n ${n.ambitious_workflows?`<div class="glance-section"><strong>Ambitious workflows:</strong> ${escapeHtmlWithBold(n.ambitious_workflows)} <a href="#section-horizon" class="see-more">On the Horizon →</a></div>`:""}\n </div>\n </div>\n `:"",o=t.project_areas?.areas||[],i=o.length>0?`\n <h2 id="section-work">What You Work On</h2>\n <div class="project-areas">\n ${o.map(e=>`\n <div class="project-area">\n <div class="area-header">\n <span class="area-name">${j(e.name)}</span>\n <span class="area-count">~${e.session_count} sessions</span>\n </div>\n <div class="area-desc">${j(e.description)}</div>\n </div>\n `).join("")}\n </div>\n `:"",a=t.interaction_style,r=a?.narrative?`\n <h2 id="section-usage">How You Use Context Code</h2>\n <div class="narrative">\n ${c=a.narrative,c?c.split("\n\n").map(e=>{let t=j(e);return t=t.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),t=t.replace(/^- /gm,"• "),t=t.replace(/\n/g,"<br>"),`<p>${t}</p>`}).join("\n"):""}\n ${a.key_pattern?`<div class="key-insight"><strong>Key pattern:</strong> ${j(a.key_pattern)}</div>`:""}\n </div>\n `:"";var c;const l=t.what_works,d=l?.impressive_workflows&&l.impressive_workflows.length>0?`\n <h2 id="section-wins">Impressive Things You Did</h2>\n ${l.intro?`<p class="section-intro">${j(l.intro)}</p>`:""}\n <div class="big-wins">\n ${l.impressive_workflows.map(e=>`\n <div class="big-win">\n <div class="big-win-title">${j(e.title||"")}</div>\n <div class="big-win-desc">${j(e.description||"")}</div>\n </div>\n `).join("")}\n </div>\n `:"",p=t.friction_analysis,u=p?.categories&&p.categories.length>0?`\n <h2 id="section-friction">Where Things Go Wrong</h2>\n ${p.intro?`<p class="section-intro">${j(p.intro)}</p>`:""}\n <div class="friction-categories">\n ${p.categories.map(e=>`\n <div class="friction-category">\n <div class="friction-title">${j(e.category||"")}</div>\n <div class="friction-desc">${j(e.description||"")}</div>\n ${e.examples?`<ul class="friction-examples">${e.examples.map(e=>`<li>${j(e)}</li>`).join("")}</ul>`:""}\n </div>\n `).join("")}\n </div>\n `:"",m=t.suggestions,f=m?`\n ${m.claude_md_additions&&m.claude_md_additions.length>0?`\n <h2 id="section-features">Existing CC Features to Try</h2>\n <div class="claude-md-section">\n <h3>Suggested CLAUDE.md Additions</h3>\n <p style="font-size: 12px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code to add it to your CLAUDE.md.</p>\n <div class="claude-md-actions">\n <button class="copy-all-btn" onclick="copyAllCheckedClaudeMd()">Copy All Checked</button>\n </div>\n ${m.claude_md_additions.map((e,t)=>`\n <div class="claude-md-item">\n <input type="checkbox" id="cmd-${t}" class="cmd-checkbox" checked data-text="${j(e.prompt_scaffold||e.where||"Add to CLAUDE.md")}\\n\\n${j(e.addition)}">\n <label for="cmd-${t}">\n <code class="cmd-code">${j(e.addition)}</code>\n <button class="copy-btn" onclick="copyCmdItem(${t})">Copy</button>\n </label>\n <div class="cmd-why">${j(e.why)}</div>\n </div>\n `).join("")}\n </div>\n `:""}\n ${m.features_to_try&&m.features_to_try.length>0?`\n <p style="font-size: 13px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code and it'll set it up for you.</p>\n <div class="features-section">\n ${m.features_to_try.map(e=>`\n <div class="feature-card">\n <div class="feature-title">${j(e.feature||"")}</div>\n <div class="feature-oneliner">${j(e.one_liner||"")}</div>\n <div class="feature-why"><strong>Why for you:</strong> ${j(e.why_for_you||"")}</div>\n ${e.example_code?`\n <div class="feature-examples">\n <div class="feature-example">\n <div class="example-code-row">\n <code class="example-code">${j(e.example_code)}</code>\n <button class="copy-btn" onclick="copyText(this)">Copy</button>\n </div>\n </div>\n </div>\n `:""}\n </div>\n `).join("")}\n </div>\n `:""}\n ${m.usage_patterns&&m.usage_patterns.length>0?`\n <h2 id="section-patterns">New Ways to Use Context Code</h2>\n <p style="font-size: 13px; color: #64748b; margin-bottom: 12px;">Just copy this into Context Code and it'll walk you through it.</p>\n <div class="patterns-section">\n ${m.usage_patterns.map(e=>`\n <div class="pattern-card">\n <div class="pattern-title">${j(e.title||"")}</div>\n <div class="pattern-summary">${j(e.suggestion||"")}</div>\n ${e.detail?`<div class="pattern-detail">${j(e.detail)}</div>`:""}\n ${e.copyable_prompt?`\n <div class="copyable-prompt-section">\n <div class="prompt-label">Paste into Context Code:</div>\n <div class="copyable-prompt-row">\n <code class="copyable-prompt">${j(e.copyable_prompt)}</code>\n <button class="copy-btn" onclick="copyText(this)">Copy</button>\n </div>\n </div>\n `:""}\n </div>\n `).join("")}\n </div>\n `:""}\n `:"",g=t.on_the_horizon,h=g?.opportunities&&g.opportunities.length>0?`\n <h2 id="section-horizon">On the Horizon</h2>\n ${g.intro?`<p class="section-intro">${j(g.intro)}</p>`:""}\n <div class="horizon-section">\n ${g.opportunities.map(e=>`\n <div class="horizon-card">\n <div class="horizon-title">${j(e.title||"")}</div>\n <div class="horizon-possible">${j(e.whats_possible||"")}</div>\n ${e.how_to_try?`<div class="horizon-tip"><strong>Getting started:</strong> ${j(e.how_to_try)}</div>`:""}\n ${e.copyable_prompt?`<div class="pattern-prompt"><div class="prompt-label">Paste into Context Code:</div><code>${j(e.copyable_prompt)}</code><button class="copy-btn" onclick="copyText(this)">Copy</button></div>`:""}\n </div>\n `).join("")}\n </div>\n `:"",_="ant"===process.env.USER_TYPE&&t.cc_team_improvements?.improvements||[],v="ant"===process.env.USER_TYPE&&t.model_behavior_improvements?.improvements||[],b=_.length>0||v.length>0?`\n <h2 id="section-feedback" class="feedback-header">Closing the Loop: Feedback for Other Teams</h2>\n <p class="feedback-intro">Suggestions for the CC product and model teams based on your usage patterns. Click to expand.</p>\n ${_.length>0?`\n <div class="collapsible-section">\n <div class="collapsible-header" onclick="toggleCollapsible(this)">\n <span class="collapsible-arrow">▶</span>\n <h3>Product Improvements for CC Team</h3>\n </div>\n <div class="collapsible-content">\n <div class="suggestions-section">\n ${_.map(e=>`\n <div class="feedback-card team-card">\n <div class="feedback-title">${j(e.title||"")}</div>\n <div class="feedback-detail">${j(e.detail||"")}</div>\n ${e.evidence?`<div class="feedback-evidence"><em>Evidence:</em> ${j(e.evidence)}</div>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `:""}\n ${v.length>0?`\n <div class="collapsible-section">\n <div class="collapsible-header" onclick="toggleCollapsible(this)">\n <span class="collapsible-arrow">▶</span>\n <h3>Model Behavior Improvements</h3>\n </div>\n <div class="collapsible-content">\n <div class="suggestions-section">\n ${v.map(e=>`\n <div class="feedback-card model-card">\n <div class="feedback-title">${j(e.title||"")}</div>\n <div class="feedback-detail">${j(e.detail||"")}</div>\n ${e.evidence?`<div class="feedback-evidence"><em>Evidence:</em> ${j(e.evidence)}</div>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `:""}\n `:"",x=t.fun_ending,y=x?.headline?`\n <div class="fun-ending">\n <div class="fun-headline">"${j(x.headline)}"</div>\n ${x.detail?`<div class="fun-detail">${j(x.detail)}</div>`:""}\n </div>\n `:"",w=`\n function toggleCollapsible(header) {\n header.classList.toggle('open');\n const content = header.nextElementSibling;\n content.classList.toggle('open');\n }\n function copyText(btn) {\n const code = btn.previousElementSibling;\n navigator.clipboard.writeText(code.textContent).then(() => {\n btn.textContent = 'Copied!';\n setTimeout(() => { btn.textContent = 'Copy'; }, 2000);\n });\n }\n function copyCmdItem(idx) {\n const checkbox = document.getElementById('cmd-' + idx);\n if (checkbox) {\n const text = checkbox.dataset.text;\n navigator.clipboard.writeText(text).then(() => {\n const btn = checkbox.nextElementSibling.querySelector('.copy-btn');\n if (btn) { btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy'; }, 2000); }\n });\n }\n }\n function copyAllCheckedClaudeMd() {\n const checkboxes = document.querySelectorAll('.cmd-checkbox:checked');\n const texts = [];\n checkboxes.forEach(cb => {\n if (cb.dataset.text) { texts.push(cb.dataset.text); }\n });\n const combined = texts.join('\\n');\n const btn = document.querySelector('.copy-all-btn');\n if (btn) {\n navigator.clipboard.writeText(combined).then(() => {\n btn.textContent = 'Copied ' + texts.length + ' items!';\n btn.classList.add('copied');\n setTimeout(() => { btn.textContent = 'Copy All Checked'; btn.classList.remove('copied'); }, 2000);\n });\n }\n }\n // Timezone selector for time of day chart (data is from our own analytics, not user input)\n const rawHourCounts = ${function(e){const t={};for(const n of e)t[n]=(t[n]||0)+1;return A(t)}(e.message_hours)};\n function updateHourHistogram(offsetFromPT) {\n const periods = [\n { label: "Morning (6-12)", range: [6,7,8,9,10,11] },\n { label: "Afternoon (12-18)", range: [12,13,14,15,16,17] },\n { label: "Evening (18-24)", range: [18,19,20,21,22,23] },\n { label: "Night (0-6)", range: [0,1,2,3,4,5] }\n ];\n const adjustedCounts = {};\n for (const [hour, count] of Object.entries(rawHourCounts)) {\n const newHour = (parseInt(hour) + offsetFromPT + 24) % 24;\n adjustedCounts[newHour] = (adjustedCounts[newHour] || 0) + count;\n }\n const periodCounts = periods.map(p => ({\n label: p.label,\n count: p.range.reduce((sum, h) => sum + (adjustedCounts[h] || 0), 0)\n }));\n const maxCount = Math.max(...periodCounts.map(p => p.count)) || 1;\n const container = document.getElementById('hour-histogram');\n container.textContent = '';\n periodCounts.forEach(p => {\n const row = document.createElement('div');\n row.className = 'bar-row';\n const label = document.createElement('div');\n label.className = 'bar-label';\n label.textContent = p.label;\n const track = document.createElement('div');\n track.className = 'bar-track';\n const fill = document.createElement('div');\n fill.className = 'bar-fill';\n fill.style.width = (p.count / maxCount) * 100 + '%';\n fill.style.background = '#8b5cf6';\n track.appendChild(fill);\n const value = document.createElement('div');\n value.className = 'bar-value';\n value.textContent = p.count;\n row.appendChild(label);\n row.appendChild(track);\n row.appendChild(value);\n container.appendChild(row);\n });\n }\n document.getElementById('timezone-select').addEventListener('change', function() {\n const customInput = document.getElementById('custom-offset');\n if (this.value === 'custom') {\n customInput.style.display = 'inline-block';\n customInput.focus();\n } else {\n customInput.style.display = 'none';\n updateHourHistogram(parseInt(this.value));\n }\n });\n document.getElementById('custom-offset').addEventListener('change', function() {\n const offset = parseInt(this.value) + 8;\n updateHourHistogram(offset);\n });\n `;return`<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <title>Context Code Insights</title>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">\n <style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: #f8fafc; color: #334155; line-height: 1.65; padding: 48px 24px; }\n .container { max-width: 800px; margin: 0 auto; }\n h1 { font-size: 32px; font-weight: 700; color: #0f172a; margin-bottom: 8px; }\n h2 { font-size: 20px; font-weight: 600; color: #0f172a; margin-top: 48px; margin-bottom: 16px; }\n .subtitle { color: #64748b; font-size: 15px; margin-bottom: 32px; }\n .nav-toc { display: flex; flex-wrap: wrap; gap: 8px; margin: 24px 0 32px 0; padding: 16px; background: white; border-radius: 8px; border: 1px solid #e2e8f0; }\n .nav-toc a { font-size: 12px; color: #64748b; text-decoration: none; padding: 6px 12px; border-radius: 6px; background: #f1f5f9; transition: all 0.15s; }\n .nav-toc a:hover { background: #e2e8f0; color: #334155; }\n .stats-row { display: flex; gap: 24px; margin-bottom: 40px; padding: 20px 0; border-top: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; flex-wrap: wrap; }\n .stat { text-align: center; }\n .stat-value { font-size: 24px; font-weight: 700; color: #0f172a; }\n .stat-label { font-size: 11px; color: #64748b; text-transform: uppercase; }\n .at-a-glance { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #f59e0b; border-radius: 12px; padding: 20px 24px; margin-bottom: 32px; }\n .glance-title { font-size: 16px; font-weight: 700; color: #92400e; margin-bottom: 16px; }\n .glance-sections { display: flex; flex-direction: column; gap: 12px; }\n .glance-section { font-size: 14px; color: #78350f; line-height: 1.6; }\n .glance-section strong { color: #92400e; }\n .see-more { color: #b45309; text-decoration: none; font-size: 13px; white-space: nowrap; }\n .see-more:hover { text-decoration: underline; }\n .project-areas { display: flex; flex-direction: column; gap: 12px; margin-bottom: 32px; }\n .project-area { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; }\n .area-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }\n .area-name { font-weight: 600; font-size: 15px; color: #0f172a; }\n .area-count { font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px; }\n .area-desc { font-size: 14px; color: #475569; line-height: 1.5; }\n .narrative { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; margin-bottom: 24px; }\n .narrative p { margin-bottom: 12px; font-size: 14px; color: #475569; line-height: 1.7; }\n .key-insight { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 12px 16px; margin-top: 12px; font-size: 14px; color: #166534; }\n .section-intro { font-size: 14px; color: #64748b; margin-bottom: 16px; }\n .big-wins { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }\n .big-win { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 16px; }\n .big-win-title { font-weight: 600; font-size: 15px; color: #166534; margin-bottom: 8px; }\n .big-win-desc { font-size: 14px; color: #15803d; line-height: 1.5; }\n .friction-categories { display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px; }\n .friction-category { background: #fef2f2; border: 1px solid #fca5a5; border-radius: 8px; padding: 16px; }\n .friction-title { font-weight: 600; font-size: 15px; color: #991b1b; margin-bottom: 6px; }\n .friction-desc { font-size: 13px; color: #7f1d1d; margin-bottom: 10px; }\n .friction-examples { margin: 0 0 0 20px; font-size: 13px; color: #334155; }\n .friction-examples li { margin-bottom: 4px; }\n .claude-md-section { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 16px; margin-bottom: 20px; }\n .claude-md-section h3 { font-size: 14px; font-weight: 600; color: #1e40af; margin: 0 0 12px 0; }\n .claude-md-actions { margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #dbeafe; }\n .copy-all-btn { background: #2563eb; color: white; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; cursor: pointer; font-weight: 500; transition: all 0.2s; }\n .copy-all-btn:hover { background: #1d4ed8; }\n .copy-all-btn.copied { background: #16a34a; }\n .claude-md-item { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; padding: 10px 0; border-bottom: 1px solid #dbeafe; }\n .claude-md-item:last-child { border-bottom: none; }\n .cmd-checkbox { margin-top: 2px; }\n .cmd-code { background: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; color: #1e40af; border: 1px solid #bfdbfe; font-family: monospace; display: block; white-space: pre-wrap; word-break: break-word; flex: 1; }\n .cmd-why { font-size: 12px; color: #64748b; width: 100%; padding-left: 24px; margin-top: 4px; }\n .features-section, .patterns-section { display: flex; flex-direction: column; gap: 12px; margin: 16px 0; }\n .feature-card { background: #f0fdf4; border: 1px solid #86efac; border-radius: 8px; padding: 16px; }\n .pattern-card { background: #f0f9ff; border: 1px solid #7dd3fc; border-radius: 8px; padding: 16px; }\n .feature-title, .pattern-title { font-weight: 600; font-size: 15px; color: #0f172a; margin-bottom: 6px; }\n .feature-oneliner { font-size: 14px; color: #475569; margin-bottom: 8px; }\n .pattern-summary { font-size: 14px; color: #475569; margin-bottom: 8px; }\n .feature-why, .pattern-detail { font-size: 13px; color: #334155; line-height: 1.5; }\n .feature-examples { margin-top: 12px; }\n .feature-example { padding: 8px 0; border-top: 1px solid #d1fae5; }\n .feature-example:first-child { border-top: none; }\n .example-desc { font-size: 13px; color: #334155; margin-bottom: 6px; }\n .example-code-row { display: flex; align-items: flex-start; gap: 8px; }\n .example-code { flex: 1; background: #f1f5f9; padding: 8px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; overflow-x: auto; white-space: pre-wrap; }\n .copyable-prompt-section { margin-top: 12px; padding-top: 12px; border-top: 1px solid #e2e8f0; }\n .copyable-prompt-row { display: flex; align-items: flex-start; gap: 8px; }\n .copyable-prompt { flex: 1; background: #f8fafc; padding: 10px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; border: 1px solid #e2e8f0; white-space: pre-wrap; line-height: 1.5; }\n .feature-code { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; display: flex; align-items: flex-start; gap: 8px; }\n .feature-code code { flex: 1; font-family: monospace; font-size: 12px; color: #334155; white-space: pre-wrap; }\n .pattern-prompt { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; }\n .pattern-prompt code { font-family: monospace; font-size: 12px; color: #334155; display: block; white-space: pre-wrap; margin-bottom: 8px; }\n .prompt-label { font-size: 11px; font-weight: 600; text-transform: uppercase; color: #64748b; margin-bottom: 6px; }\n .copy-btn { background: #e2e8f0; border: none; border-radius: 4px; padding: 4px 8px; font-size: 11px; cursor: pointer; color: #475569; flex-shrink: 0; }\n .copy-btn:hover { background: #cbd5e1; }\n .charts-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin: 24px 0; }\n .chart-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; }\n .chart-title { font-size: 12px; font-weight: 600; color: #64748b; text-transform: uppercase; margin-bottom: 12px; }\n .bar-row { display: flex; align-items: center; margin-bottom: 6px; }\n .bar-label { width: 100px; font-size: 11px; color: #475569; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .bar-track { flex: 1; height: 6px; background: #f1f5f9; border-radius: 3px; margin: 0 8px; }\n .bar-fill { height: 100%; border-radius: 3px; }\n .bar-value { width: 28px; font-size: 11px; font-weight: 500; color: #64748b; text-align: right; }\n .empty { color: #94a3b8; font-size: 13px; }\n .horizon-section { display: flex; flex-direction: column; gap: 16px; }\n .horizon-card { background: linear-gradient(135deg, #faf5ff 0%, #f5f3ff 100%); border: 1px solid #c4b5fd; border-radius: 8px; padding: 16px; }\n .horizon-title { font-weight: 600; font-size: 15px; color: #5b21b6; margin-bottom: 8px; }\n .horizon-possible { font-size: 14px; color: #334155; margin-bottom: 10px; line-height: 1.5; }\n .horizon-tip { font-size: 13px; color: #6b21a8; background: rgba(255,255,255,0.6); padding: 8px 12px; border-radius: 4px; }\n .feedback-header { margin-top: 48px; color: #64748b; font-size: 16px; }\n .feedback-intro { font-size: 13px; color: #94a3b8; margin-bottom: 16px; }\n .feedback-section { margin-top: 16px; }\n .feedback-section h3 { font-size: 14px; font-weight: 600; color: #475569; margin-bottom: 12px; }\n .feedback-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; margin-bottom: 12px; }\n .feedback-card.team-card { background: #eff6ff; border-color: #bfdbfe; }\n .feedback-card.model-card { background: #faf5ff; border-color: #e9d5ff; }\n .feedback-title { font-weight: 600; font-size: 14px; color: #0f172a; margin-bottom: 6px; }\n .feedback-detail { font-size: 13px; color: #475569; line-height: 1.5; }\n .feedback-evidence { font-size: 12px; color: #64748b; margin-top: 8px; }\n .fun-ending { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #fbbf24; border-radius: 12px; padding: 24px; margin-top: 40px; text-align: center; }\n .fun-headline { font-size: 18px; font-weight: 600; color: #78350f; margin-bottom: 8px; }\n .fun-detail { font-size: 14px; color: #92400e; }\n .collapsible-section { margin-top: 16px; }\n .collapsible-header { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 12px 0; border-bottom: 1px solid #e2e8f0; }\n .collapsible-header h3 { margin: 0; font-size: 14px; font-weight: 600; color: #475569; }\n .collapsible-arrow { font-size: 12px; color: #94a3b8; transition: transform 0.2s; }\n .collapsible-content { display: none; padding-top: 16px; }\n .collapsible-content.open { display: block; }\n .collapsible-header.open .collapsible-arrow { transform: rotate(90deg); }\n @media (max-width: 640px) { .charts-row { grid-template-columns: 1fr; } .stats-row { justify-content: center; } }\n </style>\n</head>\n<body>\n <div class="container">\n <h1>Context Code Insights</h1>\n <p class="subtitle">${e.total_messages.toLocaleString()} messages across ${e.total_sessions} sessions${e.total_sessions_scanned&&e.total_sessions_scanned>e.total_sessions?` (${e.total_sessions_scanned.toLocaleString()} total)`:""} | ${e.date_range.start} to ${e.date_range.end}</p>\n\n ${s}\n\n <nav class="nav-toc">\n <a href="#section-work">What You Work On</a>\n <a href="#section-usage">How You Use CC</a>\n <a href="#section-wins">Impressive Things</a>\n <a href="#section-friction">Where Things Go Wrong</a>\n <a href="#section-features">Features to Try</a>\n <a href="#section-patterns">New Usage Patterns</a>\n <a href="#section-horizon">On the Horizon</a>\n <a href="#section-feedback">Team Feedback</a>\n </nav>\n\n <div class="stats-row">\n <div class="stat"><div class="stat-value">${e.total_messages.toLocaleString()}</div><div class="stat-label">Messages</div></div>\n <div class="stat"><div class="stat-value">+${e.total_lines_added.toLocaleString()}/-${e.total_lines_removed.toLocaleString()}</div><div class="stat-label">Lines</div></div>\n <div class="stat"><div class="stat-value">${e.total_files_modified}</div><div class="stat-label">Files</div></div>\n <div class="stat"><div class="stat-value">${e.days_active}</div><div class="stat-label">Days</div></div>\n <div class="stat"><div class="stat-value">${e.messages_per_day}</div><div class="stat-label">Msgs/Day</div></div>\n </div>\n\n ${i}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">What You Wanted</div>\n ${generateBarChart(e.goal_categories,"#2563eb")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Top Tools Used</div>\n ${generateBarChart(e.tool_counts,"#0891b2")}\n </div>\n </div>\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">Languages</div>\n ${generateBarChart(e.languages,"#10b981")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Session Types</div>\n ${generateBarChart(e.session_types||{},"#8b5cf6")}\n </div>\n </div>\n\n ${r}\n\n \x3c!-- Response Time Distribution --\x3e\n <div class="chart-card" style="margin: 24px 0;">\n <div class="chart-title">User Response Time Distribution</div>\n ${function(e){if(0===e.length)return'<p class="empty">No response time data</p>';const t={"2-10s":0,"10-30s":0,"30s-1m":0,"1-2m":0,"2-5m":0,"5-15m":0,">15m":0};for(const n of e)n<10?t["2-10s"]=(t["2-10s"]??0)+1:n<30?t["10-30s"]=(t["10-30s"]??0)+1:n<60?t["30s-1m"]=(t["30s-1m"]??0)+1:n<120?t["1-2m"]=(t["1-2m"]??0)+1:n<300?t["2-5m"]=(t["2-5m"]??0)+1:n<900?t["5-15m"]=(t["5-15m"]??0)+1:t[">15m"]=(t[">15m"]??0)+1;const n=Math.max(...Object.values(t));return 0===n?'<p class="empty">No response time data</p>':Object.entries(t).map(([e,t])=>`<div class="bar-row">\n <div class="bar-label">${e}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${t/n*100}%;background:#6366f1"></div></div>\n <div class="bar-value">${t}</div>\n </div>`).join("\n")}(e.user_response_times)}\n <div style="font-size: 12px; color: #64748b; margin-top: 8px;">\n Median: ${e.median_response_time.toFixed(1)}s • Average: ${e.avg_response_time.toFixed(1)}s\n </div>\n </div>\n\n \x3c!-- Multi-clauding Section (matching Python reference) --\x3e\n <div class="chart-card" style="margin: 24px 0;">\n <div class="chart-title">Multi-Clauding (Parallel Sessions)</div>\n ${0===e.multi_clauding.overlap_events?'\n <p style="font-size: 14px; color: #64748b; padding: 8px 0;">\n No parallel session usage detected. You typically work with one Context Code session at a time.\n </p>\n ':`\n <div style="display: flex; gap: 24px; margin: 12px 0;">\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.multi_clauding.overlap_events}</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Overlap Events</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.multi_clauding.sessions_involved}</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Sessions Involved</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: 700; color: #7c3aed;">${e.total_messages>0?Math.round(100*e.multi_clauding.user_messages_during/e.total_messages):0}%</div>\n <div style="font-size: 11px; color: #64748b; text-transform: uppercase;">Of Messages</div>\n </div>\n </div>\n <p style="font-size: 13px; color: #475569; margin-top: 12px;">\n You run multiple Context Code sessions simultaneously. Multi-clauding is detected when sessions\n overlap in time, suggesting parallel workflows.\n </p>\n `}\n </div>\n\n \x3c!-- Time of Day & Tool Errors --\x3e\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title" style="display: flex; align-items: center; gap: 12px;">\n User Messages by Time of Day\n <select id="timezone-select" style="font-size: 12px; padding: 4px 8px; border-radius: 4px; border: 1px solid #e2e8f0;">\n <option value="0">PT (UTC-8)</option>\n <option value="3">ET (UTC-5)</option>\n <option value="8">London (UTC)</option>\n <option value="9">CET (UTC+1)</option>\n <option value="17">Tokyo (UTC+9)</option>\n <option value="custom">Custom offset...</option>\n </select>\n <input type="number" id="custom-offset" placeholder="UTC offset" style="display: none; width: 80px; font-size: 12px; padding: 4px; border-radius: 4px; border: 1px solid #e2e8f0;">\n </div>\n ${function(e){if(0===e.length)return'<p class="empty">No time data</p>';const t={};for(const n of e)t[n]=(t[n]||0)+1;const n=[{label:"Morning (6-12)",range:[6,7,8,9,10,11]},{label:"Afternoon (12-18)",range:[12,13,14,15,16,17]},{label:"Evening (18-24)",range:[18,19,20,21,22,23]},{label:"Night (0-6)",range:[0,1,2,3,4,5]}].map(e=>({label:e.label,count:e.range.reduce((e,n)=>e+(t[n]||0),0)})),s=Math.max(...n.map(e=>e.count))||1;return`<div id="hour-histogram">${n.map(e=>`\n <div class="bar-row">\n <div class="bar-label">${e.label}</div>\n <div class="bar-track"><div class="bar-fill" style="width:${e.count/s*100}%;background:#8b5cf6"></div></div>\n <div class="bar-value">${e.count}</div>\n </div>`).join("\n")}</div>`}(e.message_hours)}\n </div>\n <div class="chart-card">\n <div class="chart-title">Tool Errors Encountered</div>\n ${Object.keys(e.tool_error_categories).length>0?generateBarChart(e.tool_error_categories,"#dc2626"):'<p class="empty">No tool errors</p>'}\n </div>\n </div>\n\n ${d}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">What Helped Most (Claude's Capabilities)</div>\n ${generateBarChart(e.success,"#16a34a")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Outcomes</div>\n ${generateBarChart(e.outcomes,"#8b5cf6",6,F)}\n </div>\n </div>\n\n ${u}\n\n <div class="charts-row">\n <div class="chart-card">\n <div class="chart-title">Primary Friction Types</div>\n ${generateBarChart(e.friction,"#dc2626")}\n </div>\n <div class="chart-card">\n <div class="chart-title">Inferred Satisfaction (model-estimated)</div>\n ${generateBarChart(e.satisfaction,"#eab308",6,U)}\n </div>\n </div>\n\n ${f}\n\n ${h}\n\n ${y}\n\n ${b}\n </div>\n <script>${w}<\/script>\n</body>\n</html>`}export function buildExportData(t,n,s,o){const i=void 0!==e?e.VERSION:"unknown",a=o?.hosts.filter(e=>e.sessionCount>0).map(e=>e.name),r={total:s.size,goal_categories:{},outcomes:{},satisfaction:{},friction:{}};for(const e of s.values()){for(const[t,n]of safeEntries(e.goal_categories))n>0&&(r.goal_categories[t]=(r.goal_categories[t]||0)+n);r.outcomes[e.outcome]=(r.outcomes[e.outcome]||0)+1;for(const[t,n]of safeEntries(e.user_satisfaction_counts))n>0&&(r.satisfaction[t]=(r.satisfaction[t]||0)+n);for(const[t,n]of safeEntries(e.friction_counts))n>0&&(r.friction[t]=(r.friction[t]||0)+n)}return{metadata:{username:process.env.SAFEUSER||process.env.USER||"unknown",generated_at:(new Date).toISOString(),claude_code_version:i,date_range:t.date_range,session_count:t.total_sessions,...a&&a.length>0&&{remote_hosts_collected:a}},aggregated_data:t,insights:n,facets_summary:r}}export async function generateUsageReport(e){let t;if("ant"===process.env.USER_TYPE&&e?.collectRemote){const e=f(v(),"projects"),{hosts:n,totalCopied:s}=await P(e);t={hosts:n,totalCopied:s}}const n=await async function(){const e=C();let t;try{t=await r(e,{withFileTypes:!0})}catch{return[]}const n=t.filter(e=>e.isDirectory()).map(t=>f(e,t.name)),s=[];for(let e=0;e<n.length;e++){const t=await $(n[e]);for(const[e,n]of t)s.push({sessionId:e,path:n.path,mtime:n.mtime,size:n.size});e%10==9&&await new Promise(e=>setImmediate(e))}return s.sort((e,t)=>t.mtime-e.mtime),s}(),s=n.length;let o=[];const a=[];for(let e=0;e<n.length;e+=50){const t=n.slice(e,e+50),s=await Promise.all(t.map(async e=>({sessionInfo:e,cached:await loadCachedSessionMeta(e.sessionId)})));for(const{sessionInfo:e,cached:t}of s)t?o.push(t):a.length<200&&a.push(e)}const c=new Map,isMetaSession=e=>{for(const t of e.messages.slice(0,5))if("user"===t.type&&t.message){const e=t.message.content;if("string"==typeof e&&(e.includes("RESPOND WITH ONLY A VALID JSON OBJECT")||e.includes("record_facets")))return!0}return!1};for(let e=0;e<a.length;e+=10){const t=a.slice(e,e+10),n=await Promise.all(t.map(async e=>{try{return await S(e.path)}catch{return[]}})),s=[];for(const e of n)for(const t of e){if(isMetaSession(t)||!hasValidDates(t))continue;const e=logToSessionMeta(t);o.push(e),s.push(e),c.set(e.session_id,t)}await Promise.all(s.map(e=>saveSessionMeta(e)))}const l=new Map;for(const e of o){const t=l.get(e.session_id);(!t||e.user_message_count>t.user_message_count||e.user_message_count===t.user_message_count&&e.duration_minutes>t.duration_minutes)&&l.set(e.session_id,e)}const d=new Set(l.keys());o=[...l.values()];for(const e of c.keys())d.has(e)||c.delete(e);o.sort((e,t)=>t.start_time.localeCompare(e.start_time));const u=o.filter(e=>!(e.user_message_count<2)&&!(e.duration_minutes<1)),m=new Map,g=[],h=await Promise.all(u.map(async e=>({sessionId:e.session_id,cached:await loadCachedFacets(e.session_id)})));for(const{sessionId:e,cached:t}of h)if(t)m.set(e,t);else{const t=c.get(e);t&&g.length<50&&g.push({log:t,sessionId:e})}for(let e=0;e<g.length;e+=50){const t=g.slice(e,e+50),n=await Promise.all(t.map(async({log:e,sessionId:t})=>({sessionId:t,newFacets:await extractFacetsFromAPI(e,t)}))),s=[];for(const{sessionId:e,newFacets:t}of n)t&&(m.set(e,t),s.push(t));await Promise.all(s.map(e=>saveFacets(e)))}const isMinimalSession=e=>{const t=m.get(e);if(!t)return!1;const n=t.goal_categories,s=(o=n,o?Object.keys(o):[]).filter(e=>(n[e]??0)>0);var o;return 1===s.length&&"warmup_minimal"===s[0]},_=u.filter(e=>!isMinimalSession(e.session_id)),b=new Map;for(const[e,t]of m)isMinimalSession(e)||b.set(e,t);const x=function(e,t){const n={total_sessions:e.length,sessions_with_facets:t.size,date_range:{start:"",end:""},total_messages:0,total_duration_hours:0,total_input_tokens:0,total_output_tokens:0,tool_counts:{},languages:{},git_commits:0,git_pushes:0,projects:{},goal_categories:{},outcomes:{},satisfaction:{},helpfulness:{},session_types:{},friction:{},success:{},session_summaries:[],total_interruptions:0,total_tool_errors:0,tool_error_categories:{},user_response_times:[],median_response_time:0,avg_response_time:0,sessions_using_task_agent:0,sessions_using_mcp:0,sessions_using_web_search:0,sessions_using_web_fetch:0,total_lines_added:0,total_lines_removed:0,total_files_modified:0,days_active:0,messages_per_day:0,message_hours:[],multi_clauding:{overlap_events:0,sessions_involved:0,user_messages_during:0}},s=[],o=[],i=[];for(const a of e){s.push(a.start_time),n.total_messages+=a.user_message_count,n.total_duration_hours+=a.duration_minutes/60,n.total_input_tokens+=a.input_tokens,n.total_output_tokens+=a.output_tokens,n.git_commits+=a.git_commits,n.git_pushes+=a.git_pushes,n.total_interruptions+=a.user_interruptions,n.total_tool_errors+=a.tool_errors;for(const[e,t]of Object.entries(a.tool_error_categories))n.tool_error_categories[e]=(n.tool_error_categories[e]||0)+t;o.push(...a.user_response_times),a.uses_task_agent&&n.sessions_using_task_agent++,a.uses_mcp&&n.sessions_using_mcp++,a.uses_web_search&&n.sessions_using_web_search++,a.uses_web_fetch&&n.sessions_using_web_fetch++,n.total_lines_added+=a.lines_added,n.total_lines_removed+=a.lines_removed,n.total_files_modified+=a.files_modified,i.push(...a.message_hours);for(const[e,t]of Object.entries(a.tool_counts))n.tool_counts[e]=(n.tool_counts[e]||0)+t;for(const[e,t]of Object.entries(a.languages))n.languages[e]=(n.languages[e]||0)+t;a.project_path&&(n.projects[a.project_path]=(n.projects[a.project_path]||0)+1);const e=t.get(a.session_id);if(e){for(const[t,s]of safeEntries(e.goal_categories))s>0&&(n.goal_categories[t]=(n.goal_categories[t]||0)+s);n.outcomes[e.outcome]=(n.outcomes[e.outcome]||0)+1;for(const[t,s]of safeEntries(e.user_satisfaction_counts))s>0&&(n.satisfaction[t]=(n.satisfaction[t]||0)+s);n.helpfulness[e.claude_helpfulness]=(n.helpfulness[e.claude_helpfulness]||0)+1,n.session_types[e.session_type]=(n.session_types[e.session_type]||0)+1;for(const[t,s]of safeEntries(e.friction_counts))s>0&&(n.friction[t]=(n.friction[t]||0)+s);"none"!==e.primary_success&&(n.success[e.primary_success]=(n.success[e.primary_success]||0)+1)}n.session_summaries.length<50&&n.session_summaries.push({id:a.session_id.slice(0,8),date:a.start_time.split("T")[0]||"",summary:a.summary||a.first_prompt.slice(0,100),goal:e?.underlying_goal})}if(s.sort(),n.date_range.start=s[0]?.split("T")[0]||"",n.date_range.end=s[s.length-1]?.split("T")[0]||"",n.user_response_times=o,o.length>0){const e=[...o].sort((e,t)=>e-t);n.median_response_time=e[Math.floor(e.length/2)]||0,n.avg_response_time=o.reduce((e,t)=>e+t,0)/o.length}const a=new Set(s.map(e=>e.split("T")[0]));return n.days_active=a.size,n.messages_per_day=n.days_active>0?Math.round(n.total_messages/n.days_active*10)/10:0,n.message_hours=i,n.multi_clauding=detectMultiClauding(e),n}(_,b);x.total_sessions_scanned=s;const y=await async function(e,t){const n=Array.from(t.values()).slice(0,50).map(e=>`- ${e.brief_summary} (${e.outcome}, ${e.claude_helpfulness})`).join("\n"),s=Array.from(t.values()).filter(e=>e.friction_detail).slice(0,20).map(e=>`- ${e.friction_detail}`).join("\n"),o=Array.from(t.values()).flatMap(e=>e.user_instructions_to_claude||[]).slice(0,15).map(e=>`- ${e}`).join("\n"),i=A({sessions:e.total_sessions,analyzed:e.sessions_with_facets,date_range:e.date_range,messages:e.total_messages,hours:Math.round(e.total_duration_hours),commits:e.git_commits,top_tools:Object.entries(e.tool_counts).sort((e,t)=>t[1]-e[1]).slice(0,8),top_goals:Object.entries(e.goal_categories).sort((e,t)=>t[1]-e[1]).slice(0,8),outcomes:e.outcomes,satisfaction:e.satisfaction,friction:e.friction,success:e.success,languages:e.languages},null,2)+"\n\nSESSION SUMMARIES:\n"+n+"\n\nFRICTION DETAILS:\n"+s+"\n\nUSER INSTRUCTIONS TO CLAUDE:\n"+(o||"None captured"),a=await Promise.all(L.map(e=>generateSectionInsight(e,i))),r={};for(const{name:e,result:t}of a)t&&(r[e]=t);const c=r.project_areas?.areas?.map(e=>`- ${e.name}: ${e.description}`).join("\n")||"",l=r.what_works?.impressive_workflows?.map(e=>`- ${e.title}: ${e.description}`).join("\n")||"",d=r.friction_analysis?.categories?.map(e=>`- ${e.category}: ${e.description}`).join("\n")||"",p=r.suggestions?.features_to_try?.map(e=>`- ${e.feature}: ${e.one_liner}`).join("\n")||"",u=r.suggestions?.usage_patterns?.map(e=>`- ${e.title}: ${e.suggestion}`).join("\n")||"",m=r.on_the_horizon?.opportunities?.map(e=>`- ${e.title}: ${e.whats_possible}`).join("\n")||"",f={name:"at_a_glance",prompt:`You're writing an "At a Glance" summary for a Context Code usage insights report for Context Code users. The goal is to help them understand their usage and improve how they can use Claude better, especially as models improve.\n\nUse this 4-part structure:\n\n1. **What's working** - What is the user's unique style of interacting with Claude and what are some impactful things they've done? You can include one or two details, but keep it high level since things might not be fresh in the user's memory. Don't be fluffy or overly complimentary. Also, don't focus on the tool calls they use.\n\n2. **What's hindering you** - Split into (a) Claude's fault (misunderstandings, wrong approaches, bugs) and (b) user-side friction (not providing enough context, environment issues -- ideally more general than just one project). Be honest but constructive.\n\n3. **Quick wins to try** - Specific Context Code features they could try from the examples below, or a workflow technique if you think it's really compelling. (Avoid stuff like "Ask Claude to confirm before taking actions" or "Type out more context up front" which are less compelling.)\n\n4. **Ambitious workflows for better models** - As we move to much more capable models over the next 3-6 months, what should they prepare for? What workflows that seem impossible now will become possible? Draw from the appropriate section below.\n\nKeep each section to 2-3 not-too-long sentences. Don't overwhelm the user. Don't mention specific numerical stats or underlined_categories from the session data below. Use a coaching tone.\n\nRESPOND WITH ONLY A VALID JSON OBJECT:\n{\n "whats_working": "(refer to instructions above)",\n "whats_hindering": "(refer to instructions above)",\n "quick_wins": "(refer to instructions above)",\n "ambitious_workflows": "(refer to instructions above)"\n}\n\nSESSION DATA:\n${i}\n\n## Project Areas (what user works on)\n${c}\n\n## Big Wins (impressive accomplishments)\n${l}\n\n## Friction Categories (where things go wrong)\n${d}\n\n## Features to Try\n${p}\n\n## Usage Patterns to Adopt\n${u}\n\n## On the Horizon (ambitious workflows for better models)\n${m}`,maxTokens:8192},g=await generateSectionInsight(f,"");return g.result&&(r.at_a_glance=g.result),r}(x,m),w=generateHtmlReport(x,y);try{await i(getDataDir(),{recursive:!0})}catch{}const k=f(getDataDir(),"report.html");return await p(k,w,{encoding:"utf-8",mode:384}),{insights:y,htmlPath:k,data:x,remoteStats:t,facets:b}}function safeEntries(e){return e?Object.entries(e):[]}const W={type:"prompt",name:"insights",description:"Generar un informe analizando tus sesiones de Context Code",contentLength:0,progressMessage:"analyzing your sessions",source:"builtin",async getPromptForCommand(e){let n=!1,s=[],o=!1;"ant"===process.env.USER_TYPE&&(n=e?.includes("--homespaces")??!1,s=await O(),o=s.length>0,n&&o&&console.error(`Collecting sessions from ${s.length} homespace(s): ${s.join(", ")}...`));const{insights:i,htmlPath:a,data:r,remoteStats:c}=await generateUsageReport({collectRemote:n});let l=`file://${a}`,d="";if("ant"===process.env.USER_TYPE){const e=(new Date).toISOString().replace(/[-:]/g,"").replace("T","_").slice(0,15),n=`${process.env.SAFEUSER||process.env.USER||"unknown"}_insights_${e}.html`,s=`s3://anthropic-serve/atamkin/cc-user-reports/${n}`,o=`https://s3-frontend.infra.ant.dev/anthropic-serve/atamkin/cc-user-reports/${n}`;l=o;try{t("ff",["cp",a,s],{timeout:6e4,stdio:"pipe"})}catch{l=`file://${a}`,d=`\nAutomatic upload failed. Are you on the boron namespace? Try \`use-bo\` and ensure you've run \`sso\`.\nTo share, run: ff cp ${a} ${s}\nThen access at: ${o}`}}const p=[r.total_sessions_scanned&&r.total_sessions_scanned>r.total_sessions?`${r.total_sessions_scanned.toLocaleString()} sessions total · ${r.total_sessions} analyzed`:`${r.total_sessions} sessions`,`${r.total_messages.toLocaleString()} messages`,`${Math.round(r.total_duration_hours)}h`,`${r.git_commits} commits`].join(" · ");let u="";if("ant"===process.env.USER_TYPE)if(c&&c.totalCopied>0){const e=c.hosts.filter(e=>e.sessionCount>0).map(e=>e.name).join(", ");u=`\n_Collected ${c.totalCopied} new sessions from: ${e}_\n`}else!n&&o&&(u=`\n_Tip: Run \`/insights --homespaces\` to include sessions from your ${s.length} running homespace(s)_\n`);const m=i.at_a_glance,f=m?`## At a Glance\n\n${m.whats_working?`**What's working:** ${m.whats_working} See _Impressive Things You Did_.`:""}\n\n${m.whats_hindering?`**What's hindering you:** ${m.whats_hindering} See _Where Things Go Wrong_.`:""}\n\n${m.quick_wins?`**Quick wins to try:** ${m.quick_wins} See _Features to Try_.`:""}\n\n${m.ambitious_workflows?`**Ambitious workflows:** ${m.ambitious_workflows} See _On the Horizon_.`:""}`:"_No insights generated_",g=`${`# Context Code Insights\n\n${p}\n${r.date_range.start} to ${r.date_range.end}\n${u}\n`}${f}\n\nYour full shareable insights report is ready: ${l}${d}`;return[{type:"text",text:`The user just ran /insights to generate a usage report analyzing their Context Code sessions.\n\nHere is the full insights data:\n${A(i,null,2)}\n\nReport URL: ${l}\nHTML file: ${a}\nFacets directory: ${getFacetsDir()}\n\nHere is what the user sees:\n${g}\n\nNow output the following message exactly:\n\n<message>\nYour shareable insights report is ready:\n${l}${d}\n\nWant to dig into any section or try one of the suggestions?\n</message>`}]}};function isValidSessionFacets(e){if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.underlying_goal&&"string"==typeof t.outcome&&"string"==typeof t.brief_summary&&null!==t.goal_categories&&"object"==typeof t.goal_categories&&null!==t.user_satisfaction_counts&&"object"==typeof t.user_satisfaction_counts&&null!==t.friction_counts&&"object"==typeof t.friction_counts}export default W;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as l}from"react/compiler-runtime";import{Select as t}from"../../components/CustomSelect/index.js";import{Box as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as l}from"react/compiler-runtime";import{Select as t}from"../../components/CustomSelect/index.js";import{Box as r,Text as i}from"../../ink.js";export function ExistingWorkflowStep(n){const c=l(16),{repoName:a,onSelectAction:m}=n;let s;c[0]===Symbol.for("react.memo_cache_sentinel")?(s=[{label:"Update workflow file with latest version",value:"update"},{label:"Skip workflow update (configure secrets only)",value:"skip"},{label:"Exit without making changes",value:"exit"}],c[0]=s):s=c[0];const d=s;let u;c[1]!==m?(u=e=>{m(e)},c[1]=m,c[2]=u):u=c[2];const h=u;let f;c[3]!==m?(f=()=>{m("exit")},c[3]=m,c[4]=f):f=c[4];const p=f;let x,w,b,g,k,y;return c[5]===Symbol.for("react.memo_cache_sentinel")?(x=e(i,{bold:!0,children:"Existing Workflow Found"}),c[5]=x):x=c[5],c[6]!==a?(w=o(r,{flexDirection:"column",marginBottom:1,children:[x,o(i,{dimColor:!0,children:["Repository: ",a]})]}),c[6]=a,c[7]=w):w=c[7],c[8]===Symbol.for("react.memo_cache_sentinel")?(b=o(r,{flexDirection:"column",marginBottom:1,children:[o(i,{children:["Ya existe un archivo de workflow Context en"," ",e(i,{color:"claude",children:".github/workflows/claude.yml"})]}),e(i,{dimColor:!0,children:"¿Qué quieres hacer?"})]}),c[8]=b):b=c[8],c[9]!==p||c[10]!==h?(g=e(r,{flexDirection:"column",children:e(t,{options:d,onChange:h,onCancel:p})}),c[9]=p,c[10]=h,c[11]=g):g=c[11],c[12]===Symbol.for("react.memo_cache_sentinel")?(k=e(r,{marginTop:1,children:o(i,{dimColor:!0,children:["View the latest workflow template at:"," ",e(i,{color:"claude",children:"https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml"})]})}),c[12]=k):k=c[12],c[13]!==w||c[14]!==g?(y=o(r,{flexDirection:"column",borderStyle:"round",borderDimColor:!0,paddingX:1,children:[w,b,g,k]}),c[13]=w,c[14]=g,c[15]=y):y=c[15],y}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as r}from"react/compiler-runtime";import
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as r}from"react/compiler-runtime";import n from"figures";import{GITHUB_ACTION_SETUP_DOCS_URL as t}from"../../constants/github-app.js";import{Box as i,Text as l}from"../../ink.js";import{useKeybinding as c}from"../../keybindings/useKeybinding.js";export function InstallAppStep(m){const a=r(12),{repoUrl:s,onSubmit:d}=m;let p,h,u,b,f,y,g,_,S,x;return a[0]===Symbol.for("react.memo_cache_sentinel")?(p={context:"Confirmation"},a[0]=p):p=a[0],c("confirm:yes",d,p),a[1]===Symbol.for("react.memo_cache_sentinel")?(h=e(i,{flexDirection:"column",marginBottom:1,children:e(l,{bold:!0,children:"Install the Claude GitHub App"})}),a[1]=h):h=a[1],a[2]===Symbol.for("react.memo_cache_sentinel")?(u=e(i,{marginBottom:1,children:e(l,{children:"Abriendo navegador para instalar la app Context para GitHub…"})}),a[2]=u):u=a[2],a[3]===Symbol.for("react.memo_cache_sentinel")?(b=e(i,{marginBottom:1,children:e(l,{children:"If your browser doesn't open automatically, visit:"})}),a[3]=b):b=a[3],a[4]===Symbol.for("react.memo_cache_sentinel")?(f=e(i,{marginBottom:1,children:e(l,{underline:!0,children:"https://github.com/apps/claude"})}),a[4]=f):f=a[4],a[5]!==s?(y=e(i,{marginBottom:1,children:o(l,{children:["Please install the app for repository: ",e(l,{bold:!0,children:s})]})}),a[5]=s,a[6]=y):y=a[6],a[7]===Symbol.for("react.memo_cache_sentinel")?(g=e(i,{marginBottom:1,children:e(l,{dimColor:!0,children:"Important: Make sure to grant access to this specific repository"})}),a[7]=g):g=a[7],a[8]===Symbol.for("react.memo_cache_sentinel")?(_=e(i,{children:o(l,{bold:!0,color:"permission",children:["Press Enter once you've installed the app",n.ellipsis]})}),a[8]=_):_=a[8],a[9]===Symbol.for("react.memo_cache_sentinel")?(S=e(i,{marginTop:1,children:o(l,{dimColor:!0,children:["Having trouble? See manual setup instructions at:"," ",e(l,{color:"claude",children:t})]})}),a[9]=S):S=a[9],a[10]!==y?(x=o(i,{flexDirection:"column",borderStyle:"round",borderDimColor:!0,paddingX:1,children:[h,u,b,f,y,g,_,S]}),a[10]=y,a[11]=x):x=a[11],x}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useCallback as r,useEffect as o,useRef as n,useState as i}from"react";import{logEvent as s}from"../../services/analytics/index.js";import{KeyboardShortcutHint as c}from"../../components/design-system/KeyboardShortcutHint.js";import{Spinner as a}from"../../components/Spinner.js";import l from"../../components/TextInput.js";import{useTerminalSize as u}from"../../hooks/useTerminalSize.js";import{setClipboard as d}from"../../ink/termio/osc.js";import{Box as m,Link as h,Text as g}from"../../ink.js";import{OAuthService as p}from"../../services/oauth/index.js";import{saveOAuthTokensIfNeeded as f}from"../../utils/auth.js";import{logError as y}from"../../utils/log.js";const _="Paste code here if prompted > ";export function OAuthFlowStep({onSuccess:
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useCallback as r,useEffect as o,useRef as n,useState as i}from"react";import{logEvent as s}from"../../services/analytics/index.js";import{KeyboardShortcutHint as c}from"../../components/design-system/KeyboardShortcutHint.js";import{Spinner as a}from"../../components/Spinner.js";import l from"../../components/TextInput.js";import{useTerminalSize as u}from"../../hooks/useTerminalSize.js";import{setClipboard as d}from"../../ink/termio/osc.js";import{Box as m,Link as h,Text as g}from"../../ink.js";import{OAuthService as p}from"../../services/oauth/index.js";import{saveOAuthTokensIfNeeded as f}from"../../utils/auth.js";import{logError as y}from"../../utils/log.js";const _="Paste code here if prompted > ";export function OAuthFlowStep({onSuccess:x,onCancel:C}){const[w,k]=i({state:"starting"}),[T]=i(()=>new p),[b,j]=i(""),[A,D]=i(0),[v,S]=i(!1),[R,I]=i(!1),O=n(new Set),P=n(void 0),B=u(),H=Math.max(50,B.columns-30-4);const E=r(async()=>{O.current.forEach(e=>clearTimeout(e)),O.current.clear();try{const e=await T.startOAuthFlow(async e=>{k({state:"waiting_for_login",url:e});const t=setTimeout(S,3e3,!0);O.current.add(t)},{loginWithClaudeAi:!0,inferenceOnly:!0,expiresIn:31536e3});k({state:"processing"}),f(e);const t=setTimeout((e,t,r,o)=>{e({state:"success",token:t});const n=setTimeout(r,1e3,t);o.current.add(n)},100,k,e.accessToken,x,O);O.current.add(t)}catch(e){const t=e.message;k({state:"error",message:t,toRetry:{state:"starting"}}),y(e),s("tengu_oauth_error",{error:t})}},[T,x]);return o(()=>{"starting"===w.state&&E()},[w.state,E]),o(()=>{if("about_to_retry"===w.state){const e=setTimeout((e,t,r)=>{t("waiting_for_login"===e.state),r(e)},500,w.nextState,S,k);O.current.add(e)}},[w]),o(()=>{"c"===b&&"waiting_for_login"===w.state&&v&&!R&&(d(w.url).then(e=>{e&&process.stdout.write(e),I(!0),clearTimeout(P.current),P.current=setTimeout(I,2e3,!1)}),j(""))},[b,w,v,R]),o(()=>{const e=O.current;return()=>{T.cleanup(),e.forEach(e=>clearTimeout(e)),e.clear(),clearTimeout(P.current)}},[T]),t(m,{flexDirection:"column",gap:1,tabIndex:0,autoFocus:!0,onKeyDown:function(e){"error"===w.state&&(e.preventDefault(),"return"===e.key&&w.toRetry?(j(""),D(0),k({state:"about_to_retry",nextState:w.toRetry})):C())},children:["starting"===w.state&&t(m,{flexDirection:"column",gap:1,paddingBottom:1,children:[e(g,{bold:!0,children:"Create Authentication Token"}),e(g,{dimColor:!0,children:"Creating a long-lived token for GitHub Actions"})]}),"success"!==w.state&&"starting"!==w.state&&"processing"!==w.state&&t(m,{flexDirection:"column",gap:1,paddingBottom:1,children:[e(g,{bold:!0,children:"Create Authentication Token"}),e(g,{dimColor:!0,children:"Creating a long-lived token for GitHub Actions"})]},"header"),"waiting_for_login"===w.state&&v&&t(m,{flexDirection:"column",gap:1,paddingBottom:1,children:[t(m,{paddingX:1,children:[t(g,{dimColor:!0,children:["Browser didn't open? Use the url below to sign in"," "]}),e(g,R?{color:"success",children:"(Copied!)"}:{dimColor:!0,children:e(c,{shortcut:"c",action:"copy",parens:!0})})]}),e(h,{url:w.url,children:e(g,{dimColor:!0,children:w.url})})]},"urlToCopy"),e(m,{paddingLeft:1,flexDirection:"column",gap:1,children:function(){switch(w.state){case"starting":return t(m,{children:[e(a,{}),e(g,{children:"Starting authentication…"})]});case"waiting_for_login":return t(m,{flexDirection:"column",gap:1,children:[!v&&t(m,{children:[e(a,{}),e(g,{children:"Abriendo navegador para iniciar sesión con tu cuenta Context…"})]}),v&&t(m,{children:[e(g,{children:_}),e(l,{value:b,onChange:j,onSubmit:e=>async function(e,t){try{const[r,o]=e.split("#");if(!r||!o)return void k({state:"error",message:"Invalid code. Please make sure the full code was copied",toRetry:{state:"waiting_for_login",url:t}});s("tengu_oauth_manual_entry",{}),T.handleManualAuthCodeInput({authorizationCode:r,state:o})}catch(e){y(e),k({state:"error",message:e.message,toRetry:{state:"waiting_for_login",url:t}})}}(e,w.url),cursorOffset:A,onChangeCursorOffset:D,columns:H})]})]});case"processing":return t(m,{children:[e(a,{}),e(g,{children:"Processing authentication…"})]});case"success":return t(m,{flexDirection:"column",gap:1,children:[e(g,{color:"success",children:"✓ Authentication token created successfully!"}),e(g,{dimColor:!0,children:"Using token for GitHub Actions setup…"})]});case"error":return t(m,{flexDirection:"column",gap:1,children:[t(g,{color:"error",children:["OAuth error: ",w.message]}),w.toRetry?e(g,{dimColor:!0,children:"Press Enter to try again, or any other key to cancel"}):e(g,{dimColor:!0,children:"Press any key to return to API key selection"})]});case"about_to_retry":return e(m,{flexDirection:"column",gap:1,children:e(g,{color:"permission",children:"Retrying…"})});default:return null}}()})]})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as r,Fragment as i}from"react/jsx-runtime";import{c}from"react/compiler-runtime";import{Box as
|
|
1
|
+
import{jsx as e,jsxs as r,Fragment as i}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{Box as c,Text as o}from"../../ink.js";export function SuccessStep(a){const s=n(21),{secretExists:t,useExistingSecret:l,secretName:d,skipWorkflow:m}=a,h=void 0!==m&&m;let p,u,f,g,x,b,S,y,k;return s[0]===Symbol.for("react.memo_cache_sentinel")?(p=r(c,{flexDirection:"column",marginBottom:1,children:[e(o,{bold:!0,children:"Install GitHub App"}),e(o,{dimColor:!0,children:"Success"})]}),s[0]=p):p=s[0],s[1]!==h?(u=!h&&e(o,{color:"success",children:"✓ GitHub Actions workflow created!"}),s[1]=h,s[2]=u):u=s[2],s[3]!==t||s[4]!==l?(f=t&&l&&e(c,{marginTop:1,children:e(o,{color:"success",children:"✓ Using existing ANTHROPIC_API_KEY secret"})}),s[3]=t,s[4]=l,s[5]=f):f=s[5],s[6]!==t||s[7]!==d||s[8]!==l?(g=(!t||!l)&&e(c,{marginTop:1,children:r(o,{color:"success",children:["✓ API key saved as ",d," secret"]})}),s[6]=t,s[7]=d,s[8]=l,s[9]=g):g=s[9],s[10]===Symbol.for("react.memo_cache_sentinel")?(x=e(c,{marginTop:1,children:e(o,{children:"Siguientes pasos:"})}),s[10]=x):x=s[10],s[11]!==h?(b=r(i,h?{children:[e(o,{children:"1. Instala la app Context para GitHub si aún no lo has hecho"}),e(o,{children:"2. Tu archivo de workflow se mantuvo sin cambios"}),e(o,{children:"3. La API key está configurada y lista para usar"})]}:{children:[e(o,{children:"1. Se creó una página de PR pre-completada"}),e(o,{children:"2. Instala la app Context para GitHub si aún no lo has hecho"}),e(o,{children:"3. Haz merge del PR para habilitar la asistencia de Context en PRs"})]}),s[11]=h,s[12]=b):b=s[12],s[13]!==u||s[14]!==f||s[15]!==g||s[16]!==b?(S=r(c,{flexDirection:"column",borderStyle:"round",paddingX:1,children:[p,u,f,g,x,b]}),s[13]=u,s[14]=f,s[15]=g,s[16]=b,s[17]=S):S=s[17],s[18]===Symbol.for("react.memo_cache_sentinel")?(y=e(c,{marginLeft:3,children:e(o,{dimColor:!0,children:"Press any key to exit"})}),s[18]=y):y=s[18],s[19]!==S?(k=r(i,{children:[S,y]}),s[19]=S,s[20]=k):k=s[20],k}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logEvent as e}from"../../services/analytics/index.js";import{saveGlobalConfig as t}from"../../utils/config.js";import{CODE_REVIEW_PLUGIN_WORKFLOW_CONTENT as o,PR_BODY as s,PR_TITLE as r,WORKFLOW_CONTENT as i}from"../../constants/github-app.js";import{openBrowser as a}from"../../utils/browser.js";import{execFileNoThrow as n}from"../../utils/execFileNoThrow.js";import{logError as c}from"../../utils/log.js";async function createWorkflowFile(t,o,s,r,i,a,c){const d=await n("gh",["api",`repos/${t}/contents/${s}`,"--jq",".sha"]);let _=null;0===d.code&&(_=d.stdout.trim());let u=r;"CLAUDE_CODE_OAUTH_TOKEN"===i?u=r.replace(/anthropic_api_key: \$\{\{ secrets\.ANTHROPIC_API_KEY \}\}/g,"claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}"):"ANTHROPIC_API_KEY"!==i&&(u=r.replace(/anthropic_api_key: \$\{\{ secrets\.ANTHROPIC_API_KEY \}\}/g,`anthropic_api_key: \${{ secrets.${i} }}`));const l=["api","--method","PUT",`repos/${t}/contents/${s}`,"-f","message="+(_?`"Update ${a}"`:`"${a}"`),"-f",`content=${Buffer.from(u).toString("base64")}`,"-f",`branch=${o}`];_&&l.push("-f",`sha=${_}`);const h=await n("gh",l);if(0!==h.code){if(h.stderr.includes("422")&&h.stderr.includes("sha"))throw e("tengu_setup_github_actions_failed",{reason:"failed_to_create_workflow_file",exit_code:h.code,...c}),new Error(`Failed to create workflow file ${s}: A Claude workflow file already exists in this repository. Please remove it first or update it manually.`);e("tengu_setup_github_actions_failed",{reason:"failed_to_create_workflow_file",exit_code:h.code,...c});const t="\n\nNeed help? Common issues:\n· Permission denied → Run: gh auth refresh -h github.com -s repo,workflow\n· Not authorized → Ensure you have admin access to the repository\n· For manual setup → Visit: https://github.com/anthropics/claude-code-action";throw new Error(`Failed to create workflow file ${s}: ${h.stderr}${t}`)}}export async function setupGitHubActions(d,_,u,l,h=!1,f,p,w){try{e("tengu_setup_github_actions_started",{skip_workflow:h,has_api_key:!!_,using_default_secret_name:"ANTHROPIC_API_KEY"===u,selected_claude_workflow:f.includes("claude"),selected_claude_review_workflow:f.includes("claude-review"),...w});const c=await n("gh",["api",`repos/${d}`,"--jq",".id"]);if(0!==c.code)throw e("tengu_setup_github_actions_failed",{reason:"repo_not_found",exit_code:c.code,...w}),new Error(`Failed to access repository ${d}: ${c.stderr}`);const g=await n("gh",["api",`repos/${d}`,"--jq",".default_branch"]);if(0!==g.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_get_default_branch",exit_code:g.code,...w}),new Error(`Failed to get default branch: ${g.stderr}`);const m=g.stdout.trim(),$=await n("gh",["api",`repos/${d}/git/ref/heads/${m}`,"--jq",".object.sha"]);if(0!==$.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_get_branch_sha",exit_code:$.code,...w}),new Error(`Failed to get branch SHA: ${$.stderr}`);const b=$.stdout.trim();let k=null;if(!h){l(),k=`add-claude-github-actions-${Date.now()}`;const t=await n("gh",["api","--method","POST",`repos/${d}/git/refs`,"-f",`ref=refs/heads/${k}`,"-f",`sha=${b}`]);if(0!==t.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_create_branch",exit_code:t.code,...w}),new Error(`Failed to create branch: ${t.stderr}`);l();const s=[];f.includes("claude")&&s.push({path:".github/workflows/claude.yml",content:i,message:"
|
|
1
|
+
import{logEvent as e}from"../../services/analytics/index.js";import{saveGlobalConfig as t}from"../../utils/config.js";import{CODE_REVIEW_PLUGIN_WORKFLOW_CONTENT as o,PR_BODY as s,PR_TITLE as r,WORKFLOW_CONTENT as i}from"../../constants/github-app.js";import{openBrowser as a}from"../../utils/browser.js";import{execFileNoThrow as n}from"../../utils/execFileNoThrow.js";import{logError as c}from"../../utils/log.js";async function createWorkflowFile(t,o,s,r,i,a,c){const d=await n("gh",["api",`repos/${t}/contents/${s}`,"--jq",".sha"]);let _=null;0===d.code&&(_=d.stdout.trim());let u=r;"CLAUDE_CODE_OAUTH_TOKEN"===i?u=r.replace(/anthropic_api_key: \$\{\{ secrets\.ANTHROPIC_API_KEY \}\}/g,"claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}"):"ANTHROPIC_API_KEY"!==i&&(u=r.replace(/anthropic_api_key: \$\{\{ secrets\.ANTHROPIC_API_KEY \}\}/g,`anthropic_api_key: \${{ secrets.${i} }}`));const l=["api","--method","PUT",`repos/${t}/contents/${s}`,"-f","message="+(_?`"Update ${a}"`:`"${a}"`),"-f",`content=${Buffer.from(u).toString("base64")}`,"-f",`branch=${o}`];_&&l.push("-f",`sha=${_}`);const h=await n("gh",l);if(0!==h.code){if(h.stderr.includes("422")&&h.stderr.includes("sha"))throw e("tengu_setup_github_actions_failed",{reason:"failed_to_create_workflow_file",exit_code:h.code,...c}),new Error(`Failed to create workflow file ${s}: A Claude workflow file already exists in this repository. Please remove it first or update it manually.`);e("tengu_setup_github_actions_failed",{reason:"failed_to_create_workflow_file",exit_code:h.code,...c});const t="\n\nNeed help? Common issues:\n· Permission denied → Run: gh auth refresh -h github.com -s repo,workflow\n· Not authorized → Ensure you have admin access to the repository\n· For manual setup → Visit: https://github.com/anthropics/claude-code-action";throw new Error(`Failed to create workflow file ${s}: ${h.stderr}${t}`)}}export async function setupGitHubActions(d,_,u,l,h=!1,f,p,w){try{e("tengu_setup_github_actions_started",{skip_workflow:h,has_api_key:!!_,using_default_secret_name:"ANTHROPIC_API_KEY"===u,selected_claude_workflow:f.includes("claude"),selected_claude_review_workflow:f.includes("claude-review"),...w});const c=await n("gh",["api",`repos/${d}`,"--jq",".id"]);if(0!==c.code)throw e("tengu_setup_github_actions_failed",{reason:"repo_not_found",exit_code:c.code,...w}),new Error(`Failed to access repository ${d}: ${c.stderr}`);const g=await n("gh",["api",`repos/${d}`,"--jq",".default_branch"]);if(0!==g.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_get_default_branch",exit_code:g.code,...w}),new Error(`Failed to get default branch: ${g.stderr}`);const m=g.stdout.trim(),$=await n("gh",["api",`repos/${d}/git/ref/heads/${m}`,"--jq",".object.sha"]);if(0!==$.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_get_branch_sha",exit_code:$.code,...w}),new Error(`Failed to get branch SHA: ${$.stderr}`);const b=$.stdout.trim();let k=null;if(!h){l(),k=`add-claude-github-actions-${Date.now()}`;const t=await n("gh",["api","--method","POST",`repos/${d}/git/refs`,"-f",`ref=refs/heads/${k}`,"-f",`sha=${b}`]);if(0!==t.code)throw e("tengu_setup_github_actions_failed",{reason:"failed_to_create_branch",exit_code:t.code,...w}),new Error(`Failed to create branch: ${t.stderr}`);l();const s=[];f.includes("claude")&&s.push({path:".github/workflows/claude.yml",content:i,message:"workflow Asistente de PR de Context"}),f.includes("claude-review")&&s.push({path:".github/workflows/claude-code-review.yml",content:o,message:"Context Code Review workflow"});for(const e of s)await createWorkflowFile(d,k,e.path,e.content,u,e.message,w)}if(l(),_){const t=await n("gh",["secret","set",u,"--body",_,"--repo",d]);if(0!==t.code){e("tengu_setup_github_actions_failed",{reason:"failed_to_set_api_key_secret",exit_code:t.code,...w});const o="\n\nNeed help? Common issues:\n· Permission denied → Run: gh auth refresh -h github.com -s repo\n· Not authorized → Ensure you have admin access to the repository\n· For manual setup → Visit: https://github.com/anthropics/claude-code-action";throw new Error(`Failed to set API key secret: ${t.stderr||"Unknown error"}${o}`)}}if(!h&&k){l();const e=`https://github.com/${d}/compare/${m}...${k}?quick_pull=1&title=${encodeURIComponent(r)}&body=${encodeURIComponent(s)}`;await a(e)}e("tengu_setup_github_actions_completed",{skip_workflow:h,has_api_key:!!_,auth_type:p,using_default_secret_name:"ANTHROPIC_API_KEY"===u,selected_claude_workflow:f.includes("claude"),selected_claude_review_workflow:f.includes("claude-review"),...w}),t(e=>({...e,githubActionSetupCount:(e.githubActionSetupCount??0)+1}))}catch(t){throw t&&t instanceof Error&&t.message.includes("Failed to")||e("tengu_setup_github_actions_failed",{reason:"unexpected_error",...w}),t instanceof Error&&c(t),t}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default{type:"local-jsx",name:"login-openai",aliases:["iniciar-sesion-openai"],description:"Iniciar sesión con OpenAI / Codex (mantiene la sesión de
|
|
1
|
+
export default{type:"local-jsx",name:"login-openai",aliases:["iniciar-sesion-openai"],description:"Iniciar sesión con OpenAI / Codex (mantiene la sesión de Context activa)",isEnabled:()=>!0,load:()=>import("./login-openai.js")};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as r}from"react/compiler-runtime";import{toString as n}from"qrcode";import{useEffect as t,useState as i}from"react";import{Pane as c}from"../../components/design-system/Pane.js";import{Box as l,Text as a}from"../../ink.js";import{useKeybinding as m}from"../../keybindings/useKeybinding.js";const s={ios:{url:"https://apps.apple.com/app/claude-by-anthropic/id6473753684"},android:{url:"https://play.google.com/store/apps/details?id=com.anthropic.claude"}};function MobileQRCode(d){const p=r(52),{onDone:f}=d,[u,h]=i("ios");let y;p[0]===Symbol.for("react.memo_cache_sentinel")?(y={ios:"",android:""},p[0]=y):y=p[0];const[_,b]=i(y),{url:g}=s[u],S=_[u];let x,C,k;p[1]===Symbol.for("react.memo_cache_sentinel")?(x=()=>{(async function(){const[e,o]=await Promise.all([n(s.ios.url,{type:"utf8",errorCorrectionLevel:"L"}),n(s.android.url,{type:"utf8",errorCorrectionLevel:"L"})]);b({ios:e,android:o})})().catch(_temp)},C=[],p[1]=x,p[2]=C):(x=p[1],C=p[2]),t(x,C),p[3]!==f?(k=()=>{f()},p[3]=f,p[4]=k):k=p[4];const D=k;let j,v;p[5]===Symbol.for("react.memo_cache_sentinel")?(j={context:"Confirmation"},p[5]=j):j=p[5],m("confirm:no",D,j),p[6]!==f?(v=function(e){if("q"===e.key||e.ctrl&&"c"===e.key)return e.preventDefault(),void f();"tab"!==e.key&&"left"!==e.key&&"right"!==e.key||(e.preventDefault(),h(_temp2))},p[6]=f,p[7]=v):v=p[7];const L=v;let w,q,K,M,P,Q,R,A,F,I,O;if(p[8]!==L||p[9]!==S){const o=S.split("\n").filter(_temp3);q=c,w=l,R="column",A=0,F=!0,K=L,p[19]===Symbol.for("react.memo_cache_sentinel")?(M=e(a,{children:" "}),P=e(a,{children:" "}),p[19]=M,p[20]=P):(M=p[19],P=p[20]),Q=o.map(_temp4),p[8]=L,p[9]=S,p[10]=w,p[11]=q,p[12]=K,p[13]=M,p[14]=P,p[15]=Q,p[16]=R,p[17]=A,p[18]=F}else w=p[10],q=p[11],K=p[12],M=p[13],P=p[14],Q=p[15],R=p[16],A=p[17],F=p[18];p[21]===Symbol.for("react.memo_cache_sentinel")?(I=e(a,{children:" "}),O=e(a,{children:" "}),p[21]=I,p[22]=O):(I=p[21],O=p[22]);const z="ios"===u,B="ios"===u;let E,G;p[23]!==z||p[24]!==B?(E=e(a,{bold:z,underline:B,children:"iOS"}),p[23]=z,p[24]=B,p[25]=E):E=p[25],p[26]===Symbol.for("react.memo_cache_sentinel")?(G=e(a,{dimColor:!0,children:" / "}),p[26]=G):G=p[26];const H="android"===u,J="android"===u;let N,T,U,V,W,X,Y;return p[27]!==H||p[28]!==J?(N=e(a,{bold:H,underline:J,children:"Android"}),p[27]=H,p[28]=J,p[29]=N):N=p[29],p[30]!==E||p[31]!==N?(T=o(a,{children:[E,G,N]}),p[30]=E,p[31]=N,p[32]=T):T=p[32],p[33]===Symbol.for("react.memo_cache_sentinel")?(U=e(a,{dimColor:!0,children:"(tab para cambiar, esc para cerrar)"}),p[33]=U):U=p[33],p[34]!==T?(V=o(l,{flexDirection:"row",gap:2,children:[T,U]}),p[34]=T,p[35]=V):V=p[35],p[36]!==g?(W=e(a,{dimColor:!0,children:g}),p[36]=g,p[37]=W):W=p[37],p[38]!==w||p[39]!==K||p[40]!==M||p[41]!==P||p[42]!==Q||p[43]!==V||p[44]!==W||p[45]!==R||p[46]!==A||p[47]!==F?(X=o(w,{flexDirection:R,tabIndex:A,autoFocus:F,onKeyDown:K,children:[M,P,Q,I,O,V,W]}),p[38]=w,p[39]=K,p[40]=M,p[41]=P,p[42]=Q,p[43]=V,p[44]=W,p[45]=R,p[46]=A,p[47]=F,p[48]=X):X=p[48],p[49]!==q||p[50]!==X?(Y=e(q,{children:X}),p[49]=q,p[50]=X,p[51]=Y):Y=p[51],Y}function _temp4(o,r){return e(a,{children:o},r)}function _temp3(e){return e.length>0}function _temp2(e){return"ios"===e?"android":"ios"}function _temp(){}export async function call(o){return e(MobileQRCode,{onDone:o})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as t,jsxs as e}from"react/jsx-runtime";import{execa as n}from"execa";import{useEffect as o,useState as
|
|
1
|
+
import{jsx as t,jsxs as e}from"react/jsx-runtime";import{execa as n}from"execa";import{useEffect as o,useState as s}from"react";import{Select as i}from"../../components/CustomSelect/index.js";import{Dialog as r}from"../../components/design-system/Dialog.js";import{LoadingState as a}from"../../components/design-system/LoadingState.js";import{Box as u,Text as c}from"../../ink.js";import{logEvent as l}from"../../services/analytics/index.js";import{openBrowser as d}from"../../utils/browser.js";import{getGhAuthStatus as m}from"../../utils/github/ghAuthStatus.js";import{createDefaultEnvironment as g,getCodeWebUrl as h,importGithubToken as _,isSignedIn as p,RedactedGithubToken as f}from"./api.js";function Web({onDone:b}){const[k,C]=s({name:"checking"});o(()=>{l("tengu_remote_setup_started",{}),async function(){if(!await p())return{status:"not_signed_in"};const t=await m();if("not_installed"===t)return{status:"gh_not_installed"};if("not_authenticated"===t)return{status:"gh_not_authenticated"};const{stdout:e}=await n("gh",["auth","token"],{stdout:"pipe",stderr:"ignore",timeout:5e3,reject:!1}),o=e.trim();return o?{status:"has_gh_token",token:new f(o)}:{status:"gh_not_authenticated"}}().then(async t=>{switch(t.status){case"not_signed_in":return l("tengu_remote_setup_result",{result:"not_signed_in"}),void b("Not signed in to Claude. Run /login first.");case"gh_not_installed":case"gh_not_authenticated":{const e=`${h()}/onboarding?step=alt-auth`;return await d(e),l("tengu_remote_setup_result",{result:t.status}),void b("gh_not_installed"===t.status?`GitHub CLI not found. Install it via https://cli.github.com/, then run \`gh auth login\`, or connect GitHub on the web: ${e}`:`GitHub CLI not authenticated. Run \`gh auth login\` and try again, or connect GitHub on the web: ${e}`)}case"has_gh_token":C({name:"confirm",token:t.token})}})},[]);const handleCancel=()=>{l("tengu_remote_setup_result",{result:"cancelled"}),b()};if("checking"===k.name)return t(a,{message:"Verificando estado de sesión…"});if("uploading"===k.name)return t(a,{message:"Conectando GitHub con Context…"});const w=k.token;return e(r,{title:"¿Conectar Context en la web con GitHub?",onCancel:handleCancel,hideInputGuide:!0,children:[e(u,{flexDirection:"column",children:[t(c,{children:"Context en la web requiere conectarse a tu cuenta de GitHub para clonar y hacer push del código en tu nombre."}),t(c,{dimColor:!0,children:"Tus credenciales locales se usan para autenticarse con GitHub"})]}),t(i,{options:[{label:"Continue",value:"send"},{label:"Cancel",value:"cancel"}],onChange:t=>{"send"===t?(async t=>{C({name:"uploading"});const e=await _(t);if(!e.ok)return l("tengu_remote_setup_result",{result:"import_failed",error_kind:e.error.kind}),void b(function(t,e){switch(t.kind){case"not_signed_in":return`Login failed. Please visit ${e} and login using the GitHub App`;case"invalid_token":return"GitHub rejected that token. Run `gh auth login` and try again.";case"server":return`Server error (${t.status}). Try again in a moment.`;case"network":return"Couldn't reach the server. Check your connection."}}(e.error,h()));await g();const n=h();await d(n),l("tengu_remote_setup_result",{result:"success"}),b(`Connected as ${e.result.github_username}. Opened ${n}`)})(w):handleCancel()},onCancel:handleCancel})]})}export async function call(e){return t(Web,{onDone:e})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e}from"react/jsx-runtime";import{Text as r}from"../../ink.js";import{OPEN_SWARM_BLUEPRINTS as i}from"../../core/agents/blueprints.js";import{createTeam as o,createTeamUnit as a,upsertProviderWorkspace as t,updateTeam as n}from"../../utils/orchestration/store/index.js";import{createAgent as d,setAgentRoleByName as s,setAgentOrchestratorByName as c}from"../agent/agentStore.js";import{listProviderProfiles as p}from"../../utils/model/providerProfiles.js";import{getSecureStorage as l}from"../../utils/secureStorage/index.js";export const call=async(m,u,f)=>{const v=f?.trim()||"auto-swarm",h=p(),g=l().read()??{},A=[],getAuthMethod=e=>g.providerProfileApiKeys?.[e.id]?.trim()?"API Key de Perfil":g.providerProfileOauth?.[e.id]?.accessToken?.trim()?"OAuth de Perfil":g.providerApiKeys?.[e.provider]?.trim()?"API Key Global":"claude"===e.provider&&g.claudeAiOauth?.accessToken?.trim()?"OAuth Global (
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{Text as r}from"../../ink.js";import{OPEN_SWARM_BLUEPRINTS as i}from"../../core/agents/blueprints.js";import{createTeam as o,createTeamUnit as a,upsertProviderWorkspace as t,updateTeam as n}from"../../utils/orchestration/store/index.js";import{createAgent as d,setAgentRoleByName as s,setAgentOrchestratorByName as c}from"../agent/agentStore.js";import{listProviderProfiles as p}from"../../utils/model/providerProfiles.js";import{getSecureStorage as l}from"../../utils/secureStorage/index.js";export const call=async(m,u,f)=>{const v=f?.trim()||"auto-swarm",h=p(),g=l().read()??{},A=[],getAuthMethod=e=>g.providerProfileApiKeys?.[e.id]?.trim()?"API Key de Perfil":g.providerProfileOauth?.[e.id]?.accessToken?.trim()?"OAuth de Perfil":g.providerApiKeys?.[e.provider]?.trim()?"API Key Global":"claude"===e.provider&&g.claudeAiOauth?.accessToken?.trim()?"OAuth Global (Context)":"openai"===e.provider&&g.openAiOauth?.accessToken?.trim()?"OAuth Global (OpenAI)":"Desconocido",$=h.filter(e=>{const r=!!g.providerProfileApiKeys?.[e.id]?.trim(),i=!!g.providerProfileOauth?.[e.id]?.accessToken?.trim(),o=!!g.providerApiKeys?.[e.provider]?.trim(),a="claude"===e.provider&&!!g.claudeAiOauth?.accessToken?.trim()||"openai"===e.provider&&!!g.openAiOauth?.accessToken?.trim();return r||i||o||a});if(0===$.length)return m(`No hay perfiles autenticados detectados. \nPerfiles encontrados: ${h.map(e=>`${e.provider}/${e.name}`).join(", ")}\nUsa /provider login primero para conectar al menos un servicio.`),null;A.push("Perfiles autenticados detectados:");for(const e of $)A.push(` - ${e.provider}/${e.name} (vía ${getAuthMethod(e)})`);const getBestProfileForBlueprint=e=>{const r=$.find(e=>"claude"===e.provider),o=$.find(e=>"minimax"===e.provider),a=$.find(e=>"zai"===e.provider),t=$.find(e=>"ollama"===e.provider),n=$.find(e=>"openai"===e.provider),d=$.find(e=>"gemini-api"===e.provider||"gemini-google"===e.provider),s=[o,a,d,n,t,r].filter(e=>!!e);switch(e){case"orchestrator":return o??n??r??$[0];case"analyst":return o??a??n??$[0];case"researcher":return d??n??o??$[0];case"docs":return a??o??r??$[0];case"image":case"video":case"slides":return o??n??$[0];default:return s[i.findIndex(r=>r.id===e)%s.length]??$[0]}};A.push(`\nGenerando configuracion automatica multi-proveedor para "${v}"...`);try{const e=await o({name:v,isEnabled:!0});for(const r of i){const i=getBestProfileForBlueprint(r.id),o=await t({provider:i.provider,isEnabled:!0}),p=await d({provider:i.provider,name:`${v}-${r.id}`,profileName:i.name});await s(i.provider,p.name,r.id),"orchestrator"===r.id&&(await c(i.provider,p.name,!0),await n(e.id,{globalOrchestratorAgentId:p.id})),await a({teamId:e.id,unitName:r.id,workspaceId:o.id,leadAgentId:p.id,selectionMode:"manual",required:!0}),A.push(` ✔ Agente [${r.id}] -> ${i.provider}/${i.name}`)}A.push(`\n¡Equipo Swarm "${v}" creado con exito!`),A.push(`Lanzalo con: /orchestrate ${v} <objetivo>`),m(A.join("\n"))}catch(e){A.push(`\n❌ Error en auto-configuracion: ${e.message}`),m(A.join("\n"))}return e(r,{color:"cyan",children:"Configuracion de enjambre inteligente completada."})};
|