claude-code-session-manager 0.104.2 → 0.105.0
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/README.md +15 -3
- package/bin/cli.cjs +0 -5
- package/dist/assets/{AgentLibrary-QPw9gC_o.js → AgentLibrary-BDtr4oI-.js} +1 -1
- package/dist/assets/{Button-8RAbgh1d.js → Button-CuXBCQDp.js} +1 -1
- package/dist/assets/{DataModel-BEkuOZuS.js → DataModel-DmvRipT5.js} +1 -1
- package/dist/assets/{History-BLrNSazM.js → History-B-v9pwwr.js} +1 -1
- package/dist/assets/{Hooks-C8P6wQq1.js → Hooks-S_0oHHlr.js} +1 -1
- package/dist/assets/{Library-CWFvBZw-.js → Library-BcbGJxQ3.js} +1 -1
- package/dist/assets/{ListDetail-B1jVMgzN.js → ListDetail-DtqHqJHt.js} +1 -1
- package/dist/assets/{MarkdownEditor-JrnUbWjW.js → MarkdownEditor-B56sPp_l.js} +1 -1
- package/dist/assets/{McpServers-BGqTxDBm.js → McpServers-D5VnmxAn.js} +1 -1
- package/dist/assets/{Memory-h8bhRNyI.js → Memory-0yGZLyRP.js} +1 -1
- package/dist/assets/{Panel-srf0FT0m.js → Panel-NoAADJwx.js} +1 -1
- package/dist/assets/{Permissions-BA4oWydI.js → Permissions-D2Dh-ee8.js} +1 -1
- package/dist/assets/{Plugins-DTuTQqUL.js → Plugins-kGoUgS9b.js} +2 -2
- package/dist/assets/{ProvenanceBadge-BW18EquH.js → ProvenanceBadge-CHwDFTX7.js} +1 -1
- package/dist/assets/{SaveBar-Cc6UukFa.js → SaveBar-BtKNMIYK.js} +1 -1
- package/dist/assets/{Scheduler-BctPlNL1.js → Scheduler-B0_kMMDs.js} +1 -1
- package/dist/assets/{ScopeSwitcher-DV1OnSuc.js → ScopeSwitcher-BusohWZ5.js} +1 -1
- package/dist/assets/{Settings-Bpyt1RGa.js → Settings-DuRJntaE.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-B28AYo12.js → SkillReferenceGraph-BHOdIpsy.js} +1 -1
- package/dist/assets/{Skills-zXLI5OB_.js → Skills-DSTLZimI.js} +1 -1
- package/dist/assets/{SystemPrompt-DKoN0UAV.js → SystemPrompt-BG7wFWdb.js} +1 -1
- package/dist/assets/{TagLibrary-BkDOa8IM.js → TagLibrary-Bx27aE3_.js} +1 -1
- package/dist/assets/{TiptapBody-CIY1f6mI.js → TiptapBody-j0O8J7YI.js} +1 -1
- package/dist/assets/{Toggle-C139wuAK.js → Toggle-DzVZ6rC7.js} +1 -1
- package/dist/assets/{ViewTabs-CUEfsMOe.js → ViewTabs-BizjuynT.js} +1 -1
- package/dist/assets/{index-D89EUizB.css → index-BLVzrc4k.css} +1 -1
- package/dist/assets/{index-DrvxIbmR.js → index-CLIaYljt.js} +416 -416
- package/dist/assets/{settingsSchema-CAFckLz6.js → settingsSchema-7MRAxaJk.js} +1 -1
- package/dist/assets/useAgentsDirWatch-BUwfU2Kq.js +1 -0
- package/dist/assets/{useConfigDirWatch-QKxfZx9X.js → useConfigDirWatch-ot0VYwQC.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +10 -12
- package/plugins/session-manager-dev/skills/builder/3-publish/SKILL.md +12 -0
- package/plugins/session-manager-dev/skills/develop/SKILL.md +4 -2
- package/scripts/scheduler-mcp-server.cjs +7 -17
- package/src/main/build-info.json +4 -4
- package/src/main/chatRunner.cjs +4 -4
- package/src/main/historyAggregator.cjs +7 -1
- package/src/main/index.cjs +34 -1
- package/src/main/lib/agentPersonaSchema.cjs +8 -1
- package/src/main/lib/appRuntime.cjs +57 -0
- package/src/main/lib/childWithLog.cjs +10 -10
- package/src/main/lib/claudeBin.cjs +40 -16
- package/src/main/lib/cleanEnv.cjs +29 -6
- package/src/main/lib/defaultShell.cjs +36 -0
- package/src/main/lib/definitionOfDone.cjs +6 -2
- package/src/main/lib/delegationReadiness.cjs +13 -5
- package/src/main/lib/gitWorktree.cjs +6 -3
- package/src/main/lib/insideHome.cjs +15 -4
- package/src/main/lib/killTree.cjs +80 -0
- package/src/main/lib/loadGate.cjs +16 -0
- package/src/main/lib/loginShellPath.cjs +82 -0
- package/src/main/lib/machineProfile.cjs +4 -2
- package/src/main/lib/mcpStdioServer.cjs +127 -0
- package/src/main/lib/openExternalApp.cjs +62 -11
- package/src/main/lib/otlpTraceExporter.cjs +147 -0
- package/src/main/lib/prereqs.cjs +145 -0
- package/src/main/lib/procIdentity.cjs +37 -2
- package/src/main/lib/rcaReport.cjs +1 -0
- package/src/main/lib/reaperHelpers.cjs +5 -2
- package/src/main/lib/shippedPersonaSeeds.cjs +10 -0
- package/src/main/lib/timeoutShimScript.cjs +14 -2
- package/src/main/lib/updateCheck.cjs +41 -11
- package/src/main/lib/watchdogHelpers.cjs +23 -3
- package/src/main/lib/winSpawn.cjs +67 -0
- package/src/main/otel.cjs +18 -62
- package/src/main/pty.cjs +32 -11
- package/src/main/runVerify.cjs +42 -2
- package/src/main/scheduler.cjs +105 -20
- package/src/main/seedSchedulerMcp.cjs +11 -2
- package/src/preload/api.d.ts +14 -0
- package/src/preload/index.cjs +2 -0
- package/src/seed/agents/validator.md +2 -2
- package/dist/assets/useAgentsDirWatch-XhQEVlaK.js +0 -1
- package/scripts/postinstall.cjs +0 -52
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aw as b,ah as n,S as N,aN as O}from"./index-DrvxIbmR.js";import{r as K}from"./claude-settings-schema-COigsajx.js";function G({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(J,{...e,node:e.node}):n.jsx(R,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function J({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:k(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function R({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:k(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:k(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",k(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function k(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function V(e,r,s){const a=O(o=>o.files),m=O(o=>o.loadJson),f=O(o=>o.watchFile),x=O(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function B(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:q([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function q(e){return Array.from(new Set(e))}let $=null;function D(){return $||($=B(K)),$}export{G as E,D as s,V as u};
|
|
1
|
+
import{aw as b,ah as n,S as N,aN as O}from"./index-CLIaYljt.js";import{r as K}from"./claude-settings-schema-COigsajx.js";function G({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(J,{...e,node:e.node}):n.jsx(R,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function J({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:k(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function R({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:k(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:k(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",k(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function k(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function V(e,r,s){const a=O(o=>o.files),m=O(o=>o.loadJson),f=O(o=>o.watchFile),x=O(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function B(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:q([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function q(e){return Array.from(new Set(e))}let $=null;function D(){return $||($=B(K)),$}export{G as E,D as s,V as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{aS as u}from"./index-CLIaYljt.js";import{u as a}from"./useConfigDirWatch-ot0VYwQC.js";function r(e,s){const o=u();a([o?`${o}/.claude/agents`:null,e?`${e}/.claude/agents`:null],s)}export{r as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aw as c}from"./index-
|
|
1
|
+
import{aw as c}from"./index-CLIaYljt.js";function s(t){const n=t.replace(/\/{2,}/g,"/");return n.length>1?n.replace(/\/+$/,""):n}function u(t,n){const r=c.useRef(n);r.current=n;const e=Array.from(new Set(t.filter(o=>!!o).map(s))),i=e.slice().sort().join("\0");c.useEffect(()=>{if(e.length===0)return;window.api.config.watch(e);const o=window.api.config.onChanged(a=>{e.includes(a.path)&&r.current()});return()=>{o(),window.api.config.unwatch(e)}},[i])}export{u};
|
package/dist/index.html
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" crossorigin src="./assets/index-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-CLIaYljt.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/monaco-editor-BsIlgG7c.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/monaco-editor-Cu5D__1H.css">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BLVzrc4k.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="bg-bg text-fg font-sans antialiased">
|
|
16
16
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.105.0",
|
|
4
4
|
"description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main/index.cjs",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"bin/",
|
|
12
12
|
".claude-plugin/",
|
|
13
13
|
"plugins/",
|
|
14
|
-
"scripts/postinstall.cjs",
|
|
15
14
|
"scripts/mint-epic.cjs",
|
|
16
15
|
"scripts/scheduler-mcp-server.cjs",
|
|
17
16
|
"scripts/ops-sweep.cjs",
|
|
@@ -38,9 +37,13 @@
|
|
|
38
37
|
"dev:renderer": "vite",
|
|
39
38
|
"dev:electron": "wait-on http://localhost:5173 && SM_DEV=1 electron .",
|
|
40
39
|
"build": "vite build",
|
|
40
|
+
"dist:stage": "npm run build && node scripts/package/stage.cjs",
|
|
41
|
+
"dist:mac": "npm run dist:stage && electron-builder --config electron-builder.yml --mac --publish never",
|
|
42
|
+
"dist:win": "npm run dist:stage && electron-builder --config electron-builder.yml --win --publish never",
|
|
43
|
+
"dist:linux": "npm run dist:stage && electron-builder --config electron-builder.yml --linux --publish never",
|
|
44
|
+
"dist:linux-dir": "npm run dist:stage && electron-builder --config electron-builder.yml --linux dir --publish never",
|
|
41
45
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.main.json",
|
|
42
46
|
"start": "electron .",
|
|
43
|
-
"postinstall": "node scripts/postinstall.cjs",
|
|
44
47
|
"prepack": "node scripts/write-build-info.cjs",
|
|
45
48
|
"prepublishOnly": "node scripts/write-build-info.cjs && vite build",
|
|
46
49
|
"test:unit": "vitest run",
|
|
@@ -49,6 +52,7 @@
|
|
|
49
52
|
"bench:renderer": "vitest run --config vitest.bench.config.ts",
|
|
50
53
|
"test:e2e": "xvfb-run -a playwright test",
|
|
51
54
|
"smoke:darwin": "playwright test tests/smoke/darwin-boot.spec.ts",
|
|
55
|
+
"smoke:packaged": "playwright test tests/smoke/packaged-boot.spec.ts",
|
|
52
56
|
"refresh:vad-assets": "node scripts/refresh-vad-assets.mjs",
|
|
53
57
|
"health": "node src/main/health.cjs",
|
|
54
58
|
"manual:check": "node web/manual/build.mjs --check",
|
|
@@ -121,17 +125,10 @@
|
|
|
121
125
|
},
|
|
122
126
|
"os": [
|
|
123
127
|
"darwin",
|
|
124
|
-
"linux"
|
|
128
|
+
"linux",
|
|
129
|
+
"win32"
|
|
125
130
|
],
|
|
126
131
|
"dependencies": {
|
|
127
|
-
"@electron/rebuild": "4.2.0",
|
|
128
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
129
|
-
"@opentelemetry/api": "^1.9.0",
|
|
130
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
|
|
131
|
-
"@opentelemetry/resources": "^2.11.0",
|
|
132
|
-
"@opentelemetry/sdk-trace-base": "^2.11.0",
|
|
133
|
-
"@opentelemetry/sdk-trace-node": "^2.11.0",
|
|
134
|
-
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
135
132
|
"chokidar": "^4.0.1",
|
|
136
133
|
"electron": "42.11.10",
|
|
137
134
|
"node-pty": "^1.2.0-beta.12",
|
|
@@ -156,6 +153,7 @@
|
|
|
156
153
|
"concurrently": "^9.1.0",
|
|
157
154
|
"dockview-react": "^6.6.1",
|
|
158
155
|
"dompurify": "^3.4.8",
|
|
156
|
+
"electron-builder": "^26.15.3",
|
|
159
157
|
"esbuild": "^0.25.12",
|
|
160
158
|
"jsdom": "^29.1.1",
|
|
161
159
|
"marked": "^14.0.0",
|
|
@@ -92,6 +92,18 @@ to ask for reconfirmation once `builder:gate` has passed.
|
|
|
92
92
|
Confirm the reported version matches `<version>` and `latest` points to it (unless the
|
|
93
93
|
project intentionally publishes to a different dist-tag).
|
|
94
94
|
|
|
95
|
+
11. **Post-publish: verify the installer release.** Pushing `v<version>` (step 8) triggers
|
|
96
|
+
`.github/workflows/release.yml`. Confirm the tag push succeeded, then poll (bounded: at most
|
|
97
|
+
20 tries x 30 s, never open-ended):
|
|
98
|
+
```
|
|
99
|
+
for i in $(seq 1 20); do
|
|
100
|
+
gh release view v<version> --json assets --jq '.assets[].name' && break
|
|
101
|
+
sleep 30
|
|
102
|
+
done
|
|
103
|
+
```
|
|
104
|
+
Check the asset names include a `.dmg`, an `.exe` and an `.AppImage`. Report any that are
|
|
105
|
+
missing (or that the release never appeared); do not retry beyond the bound.
|
|
106
|
+
|
|
95
107
|
## On any step failing
|
|
96
108
|
|
|
97
109
|
Stop at that step. Do not proceed. Report which step failed and its output. If the worktree
|
|
@@ -202,8 +202,10 @@ End every plan with exactly one validate PRD:
|
|
|
202
202
|
depends on). Why: `dependsOn` is walked transitively.
|
|
203
203
|
3. Goal: the plan's slugs and titles.
|
|
204
204
|
4. Criteria: one per PRD, naming its file
|
|
205
|
-
(
|
|
206
|
-
`prds-archived/` beside it once done; find
|
|
205
|
+
(`$SM_PROJECT_ROOT/session-manager-operations/scheduler/epics/<epic-id>/prds/<NN>-<slug>.md`
|
|
206
|
+
while queued, `prds-archived/` (possibly a timestamped subfolder) beside it once done; find
|
|
207
|
+
it by slug. Why: `session-manager-operations/scheduler/` is gitignored, so it is never in
|
|
208
|
+
the validator's job worktree. The last: write and commit
|
|
207
209
|
`session-manager-operations/reviews/validation/<epic-id>/<validate-slug>.md`.
|
|
208
210
|
5. Notes: "Work as the validator persona — the procedure is your system prompt." and a line
|
|
209
211
|
`Base: <sha>`, the SHA from step 11.
|
|
@@ -30,12 +30,7 @@ const { PROC_NAMES, setProcessTitle } = require('../src/main/lib/smProcNames.cjs
|
|
|
30
30
|
setProcessTitle(PROC_NAMES.mcpServer);
|
|
31
31
|
const path = require('node:path');
|
|
32
32
|
const fsp = require('node:fs/promises');
|
|
33
|
-
const {
|
|
34
|
-
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
35
|
-
const {
|
|
36
|
-
ListToolsRequestSchema,
|
|
37
|
-
CallToolRequestSchema,
|
|
38
|
-
} = require('@modelcontextprotocol/sdk/types.js');
|
|
33
|
+
const { createStdioServer } = require('../src/main/lib/mcpStdioServer.cjs');
|
|
39
34
|
const { PRD_WORK_TYPES } = require('../src/main/lib/workTypeLibrary.cjs');
|
|
40
35
|
const { MCP_TOOL_CATALOG, MCP_RECIPES, composeDescription } = require('../src/main/lib/mcpToolCatalog.cjs');
|
|
41
36
|
|
|
@@ -437,13 +432,6 @@ const TOOLS = [
|
|
|
437
432
|
},
|
|
438
433
|
];
|
|
439
434
|
|
|
440
|
-
const server = new Server(
|
|
441
|
-
{ name: 'session-manager-scheduler', version: '1.0.0' },
|
|
442
|
-
{ capabilities: { tools: {} } }
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
446
|
-
|
|
447
435
|
// Named + exported (see module.exports below) so mcpToolCatalog.test.cjs's
|
|
448
436
|
// sibling can exercise session_manager_help's argument handling directly —
|
|
449
437
|
// mocking node:fs/promises + global.fetch to drive adminRequest — without
|
|
@@ -713,11 +701,13 @@ async function handleCallTool(request) {
|
|
|
713
701
|
}
|
|
714
702
|
}
|
|
715
703
|
|
|
716
|
-
server.setRequestHandler(CallToolRequestSchema, handleCallTool);
|
|
717
|
-
|
|
718
704
|
async function main() {
|
|
719
|
-
|
|
720
|
-
|
|
705
|
+
createStdioServer({
|
|
706
|
+
name: 'session-manager-scheduler',
|
|
707
|
+
version: '1.0.0',
|
|
708
|
+
listTools: () => TOOLS,
|
|
709
|
+
callTool: (params) => handleCallTool({ params }),
|
|
710
|
+
}).start();
|
|
721
711
|
}
|
|
722
712
|
|
|
723
713
|
// Guarded so mcpToolCatalog.test.cjs can `require()` this file to read TOOLS
|
package/src/main/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitShortSha": "
|
|
2
|
+
"version": "0.105.0",
|
|
3
|
+
"gitSha": "13d0cde6c719549e850585a834783382050c8bd7",
|
|
4
|
+
"gitShortSha": "13d0cde6",
|
|
5
5
|
"gitBranch": "HEAD",
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-05T18:06:40.040Z",
|
|
7
7
|
"dirty": false
|
|
8
8
|
}
|
package/src/main/chatRunner.cjs
CHANGED
|
@@ -52,6 +52,7 @@ const { extractJson } = require('./lib/extractJson.cjs');
|
|
|
52
52
|
const { classifyPromptTicket } = require('./lib/classifyPromptTicket.cjs');
|
|
53
53
|
const sessionSlots = require('./lib/sessionSlots.cjs');
|
|
54
54
|
const opsErrorLog = require('./lib/opsErrorLog.cjs');
|
|
55
|
+
const { killTree, detachedSpawnOpts } = require('./lib/killTree.cjs');
|
|
55
56
|
const agentModelResolve = require('./lib/agentModelResolve.cjs');
|
|
56
57
|
const { resolveEpicEffort, effortArgs } = require('./lib/agentEffortResolve.cjs');
|
|
57
58
|
const { planEpicSpawn } = require('./lib/epicSpawnPlan.cjs');
|
|
@@ -629,9 +630,8 @@ function executeRun({ tabId, sessionId, prompt, cwd, resume, silent, onSilentRes
|
|
|
629
630
|
if (killed) return;
|
|
630
631
|
killed = true;
|
|
631
632
|
if (!child) return;
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
catch { try { child.kill(sig); } catch { /* already dead */ } }
|
|
633
|
+
// Group kill on POSIX / taskkill /T on win32 (requires detachedSpawnOpts at spawn)
|
|
634
|
+
if (!killTree(child.pid, sig)) { try { child.kill(sig); } catch { /* already dead */ } }
|
|
635
635
|
};
|
|
636
636
|
|
|
637
637
|
const cancelFn = () => {
|
|
@@ -696,7 +696,7 @@ function executeRun({ tabId, sessionId, prompt, cwd, resume, silent, onSilentRes
|
|
|
696
696
|
cwd: execCwd,
|
|
697
697
|
env: childEnv,
|
|
698
698
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
699
|
-
|
|
699
|
+
...detachedSpawnOpts(), // own process group so killTree can SIGTERM descendants
|
|
700
700
|
});
|
|
701
701
|
} catch (err) {
|
|
702
702
|
clearTimeout(killTimer);
|
|
@@ -92,7 +92,12 @@ async function mapWithConcurrency(items, limit, fn) {
|
|
|
92
92
|
|
|
93
93
|
// ── date helpers ──────────────────────────────────────────────────────────────
|
|
94
94
|
|
|
95
|
-
function decodeCwd(encoded) {
|
|
95
|
+
function decodeCwd(encoded, platform = process.platform) {
|
|
96
|
+
// encodeCwd turns `C:\\Users\\Me` into `C--Users-Me`: drive letter, then `--`.
|
|
97
|
+
if (platform === 'win32') {
|
|
98
|
+
const m = /^([A-Za-z])--(.*)$/.exec(encoded);
|
|
99
|
+
if (m) return m[1] + ':\\' + m[2].replace(/-+/g, '\\');
|
|
100
|
+
}
|
|
96
101
|
return '/' + encoded.replace(/-+/g, '/');
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -944,6 +949,7 @@ module.exports = {
|
|
|
944
949
|
remote,
|
|
945
950
|
MODEL_PRICING,
|
|
946
951
|
finalizeClosedDays,
|
|
952
|
+
decodeCwd,
|
|
947
953
|
refreshIntradayToday,
|
|
948
954
|
computeIntradayBuckets,
|
|
949
955
|
// exported for tests
|
package/src/main/index.cjs
CHANGED
|
@@ -11,6 +11,7 @@ const fsp = require('node:fs/promises');
|
|
|
11
11
|
const os = require('node:os');
|
|
12
12
|
const { schemas, validated } = require('./ipcSchemas.cjs');
|
|
13
13
|
const { cleanChildEnv } = require('./lib/cleanEnv.cjs');
|
|
14
|
+
const { applyLoginShellPath } = require('./lib/loginShellPath.cjs');
|
|
14
15
|
const { terminateLosingInstance } = require('./lib/singleInstanceGuard.cjs');
|
|
15
16
|
const { acquireSchedulerOwnership, releaseSchedulerOwnership } = require('./lib/instanceLock.cjs');
|
|
16
17
|
const { manager: ptyManager, registerPtyHandlers } = require('./pty.cjs');
|
|
@@ -101,7 +102,8 @@ const { registerDocEditHandlers, attachWindow: attachDocEditWindow } = require('
|
|
|
101
102
|
const { listExchanges } = require('./exchanges.cjs');
|
|
102
103
|
const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
|
|
103
104
|
const { checkInsideHome, assertInsideHome } = require('./lib/insideHome.cjs');
|
|
104
|
-
const { openInEditor, openFileInEditor, openInFinder, openInTerminal } = require('./lib/openExternalApp.cjs');
|
|
105
|
+
const { openInEditor, openFileInEditor, openInFinder, openInTerminal, runInTerminal } = require('./lib/openExternalApp.cjs');
|
|
106
|
+
const { checkPrereqs } = require('./lib/prereqs.cjs');
|
|
105
107
|
const { rebuildActiveIndex } = require('./lib/activeIndexRebuild.cjs');
|
|
106
108
|
const { activeIndexPath: promptSessionsActiveIndexPath } = require('./lib/epicMint.cjs');
|
|
107
109
|
const { allProjectCwds } = require('./lib/activeSessions.cjs');
|
|
@@ -266,6 +268,8 @@ function writeFirstPaintFailureLog() {
|
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
const { isPackagedApp } = require('./lib/appRuntime.cjs');
|
|
272
|
+
|
|
269
273
|
function resolveNpx() {
|
|
270
274
|
const isWin = process.platform === 'win32';
|
|
271
275
|
try {
|
|
@@ -373,6 +377,14 @@ async function performReboot() {
|
|
|
373
377
|
// Bounded-lifetime marker: the watchdog must not relaunch a second instance
|
|
374
378
|
// during this deliberate exit window (single-instance-lock race).
|
|
375
379
|
upgradeDrain.markRestarting();
|
|
380
|
+
if (isPackagedApp()) {
|
|
381
|
+
// Installer build: no npx — restart the app binary in place.
|
|
382
|
+
logReboot('packaged build: app.relaunch()');
|
|
383
|
+
app.relaunch();
|
|
384
|
+
runShutdownCleanup(); // app.exit bypasses will-quit
|
|
385
|
+
app.exit(0);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
376
388
|
let childOk = false;
|
|
377
389
|
try {
|
|
378
390
|
const child = relaunchViaNpx();
|
|
@@ -715,6 +727,15 @@ ipcMain.handle('app:engage-rules-path', () => process.env.SESSION_MANAGER_ENGAGE
|
|
|
715
727
|
// on disk or the home self-check failed (e.g. macOS /Users symlink mismatch).
|
|
716
728
|
ipcMain.handle('app:claude-bin-status', () => bootClaudeBin);
|
|
717
729
|
ipcMain.handle('app:home-self-check', () => bootHomeSelfCheck);
|
|
730
|
+
ipcMain.handle('app:prereqs', async () => await checkPrereqs());
|
|
731
|
+
// Renderer sends only an id; the command always comes from a fresh main-side probe.
|
|
732
|
+
ipcMain.handle('app:prereqs-run-fix', async (_e, id) => {
|
|
733
|
+
const items = await checkPrereqs();
|
|
734
|
+
const item = typeof id === 'string' ? items.find((i) => i.id === id) : undefined;
|
|
735
|
+
if (!item) throw new Error(`unknown prereq id: ${String(id)}`);
|
|
736
|
+
if (!item.fix || !item.fix.command) throw new Error(`prereq ${id} has no fix command`);
|
|
737
|
+
return runInTerminal({ command: item.fix.command, shell: item.fix.shell });
|
|
738
|
+
});
|
|
718
739
|
// First-boot seeder outcomes ('done' | 'pending' | 'exhausted' per seeder),
|
|
719
740
|
// read live from each seeder's own marker file — see seedStatus.cjs.
|
|
720
741
|
ipcMain.handle('app:seed-status', () => getSeedStatus());
|
|
@@ -1190,6 +1211,18 @@ app.whenReady().then(async () => {
|
|
|
1190
1211
|
onSuspendWhileAlive: () => { stopSystemdInhibit(); startSystemdInhibit(); },
|
|
1191
1212
|
});
|
|
1192
1213
|
|
|
1214
|
+
// A Finder/Dock-launched packaged app inherits a minimal PATH; adopt the
|
|
1215
|
+
// login shell's so node/git/claude resolve like in the user's terminal.
|
|
1216
|
+
// Must run before resolveClaudeBin() and before any pty/scheduler/MCP spawn.
|
|
1217
|
+
if (process.platform !== 'win32' && process.env.SM_SKIP_LOGIN_SHELL_PATH !== '1') {
|
|
1218
|
+
try {
|
|
1219
|
+
const loginPath = await applyLoginShellPath();
|
|
1220
|
+
logs.writeLine({ scope: 'main', level: 'info', message: 'login-shell PATH', meta: loginPath });
|
|
1221
|
+
} catch (err) {
|
|
1222
|
+
logs.writeLine({ scope: 'main', level: 'warn', message: 'login-shell PATH failed', meta: { error: String(err && err.message || err) } });
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1193
1226
|
// Boot-time detection 1: surface `claude` binary resolution so a missing
|
|
1194
1227
|
// install becomes visible to the renderer instead of failing silently on
|
|
1195
1228
|
// first spawn attempt.
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
|
+
const path = require('node:path');
|
|
25
26
|
const { z } = require('zod');
|
|
26
27
|
const { WorkTypeSchema, LEGACY_WORK_TYPES } = require('./workTypeLibrary.cjs');
|
|
27
28
|
const { PERSONA_NAME_RE, ALL_PROJECTS } = require('../agentLibrary.cjs');
|
|
@@ -35,8 +36,13 @@ const BoundedString = z.string().max(2000);
|
|
|
35
36
|
// `projects` entries are either the `'*'` sentinel (every project) or an
|
|
36
37
|
// absolute path — agentLibrary.cjs's openProjects always deals in absolute
|
|
37
38
|
// cwds, and a relative entry could never match one.
|
|
39
|
+
/** @param {string} v @param {NodeJS.Platform} [platform] */
|
|
40
|
+
function isAbsoluteForPlatform(v, platform = process.platform) {
|
|
41
|
+
return (platform === 'win32' ? path.win32 : path.posix).isAbsolute(v);
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
const ProjectEntrySchema = z.string().refine(
|
|
39
|
-
(v) => v === ALL_PROJECTS || v
|
|
45
|
+
(/** @type {string} */ v) => v === ALL_PROJECTS || isAbsoluteForPlatform(v),
|
|
40
46
|
`must be "${ALL_PROJECTS}" or an absolute path`,
|
|
41
47
|
);
|
|
42
48
|
|
|
@@ -95,4 +101,5 @@ function assertValidAgentPersonaSave(persona) {
|
|
|
95
101
|
module.exports = {
|
|
96
102
|
AgentPersonaSaveSchema,
|
|
97
103
|
assertValidAgentPersonaSave,
|
|
104
|
+
isAbsoluteForPlatform,
|
|
98
105
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* appRuntime.cjs — how to run a Node script from an EXTERNAL process.
|
|
3
|
+
*
|
|
4
|
+
* Packaged installer builds may have no system `node`, so scripts handed to
|
|
5
|
+
* Claude Code hooks / MCP registrations run through the app's own binary with
|
|
6
|
+
* ELECTRON_RUN_AS_NODE=1. npx/dev builds keep using `node`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {{ packaged?: boolean }} [opts]
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isPackagedApp(opts) {
|
|
16
|
+
if (opts && typeof opts.packaged === 'boolean') return opts.packaged;
|
|
17
|
+
if (process.env.SM_FORCE_PACKAGED === '1') return true;
|
|
18
|
+
try {
|
|
19
|
+
return require('electron').app?.isPackaged === true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} scriptPath
|
|
27
|
+
* @param {{ packaged?: boolean, execPath?: string }} [opts]
|
|
28
|
+
* @returns {{ command: string, args: string[], env: Record<string, string> }}
|
|
29
|
+
*/
|
|
30
|
+
function nodeSpawnSpec(scriptPath, opts) {
|
|
31
|
+
if (!isPackagedApp(opts)) return { command: 'node', args: [scriptPath], env: {} };
|
|
32
|
+
return {
|
|
33
|
+
command: (opts && opts.execPath) || process.execPath,
|
|
34
|
+
args: [scriptPath],
|
|
35
|
+
env: { ELECTRON_RUN_AS_NODE: '1' },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @param {string} p */
|
|
40
|
+
function quote(p) {
|
|
41
|
+
if (p.includes('"')) throw new Error(`appRuntime: path contains a double quote: ${p}`);
|
|
42
|
+
return `"${p}"`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Shell command string (hook `command` fields). Paths are double-quoted.
|
|
47
|
+
* @param {string} scriptPath
|
|
48
|
+
* @param {{ packaged?: boolean, execPath?: string }} [opts]
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function nodeShellCommand(scriptPath, opts) {
|
|
52
|
+
if (!isPackagedApp(opts)) return `node ${quote(scriptPath)}`;
|
|
53
|
+
const execPath = (opts && opts.execPath) || process.execPath;
|
|
54
|
+
return `ELECTRON_RUN_AS_NODE=1 ${quote(execPath)} ${quote(scriptPath)}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { isPackagedApp, nodeSpawnSpec, nodeShellCommand };
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
const fs = require('node:fs');
|
|
54
54
|
const { spawn, execFile } = require('node:child_process');
|
|
55
|
+
const { killTree } = require('./killTree.cjs');
|
|
55
56
|
|
|
56
57
|
// Injectable for tests (enumeration-failure coverage).
|
|
57
58
|
const deps = { execFile };
|
|
@@ -150,6 +151,7 @@ function openLog(logPath) {
|
|
|
150
151
|
// Async: resolves to the survivors list, rejects on ps failure/timeout. O(P) in
|
|
151
152
|
// the process table, off the main event loop.
|
|
152
153
|
function enumerateProcessGroupSurvivors(pgid) {
|
|
154
|
+
// ps -eo is Linux-only; on win32 (and macOS) report the empty/unknown result without spawning ps.
|
|
153
155
|
if (process.platform !== 'linux') return Promise.resolve([]);
|
|
154
156
|
if (!pgid || pgid <= 1) return Promise.resolve([]);
|
|
155
157
|
return new Promise((resolve, reject) => {
|
|
@@ -182,12 +184,14 @@ function sweepChildProcessGroup(child, detached, safeLog) {
|
|
|
182
184
|
if (!child || !child.pid || child.pid <= 1) return null;
|
|
183
185
|
// Defensive: never target the Session Manager process's own group.
|
|
184
186
|
if (child.pid === process.pid) return null;
|
|
187
|
+
// win32 has no process groups; taskkill on an already-exited pid risks hitting a reused pid.
|
|
188
|
+
if (process.platform === 'win32') return null;
|
|
185
189
|
|
|
186
190
|
const pid = child.pid;
|
|
187
191
|
const kill = () => {
|
|
188
|
-
|
|
192
|
+
killTree(pid, 'SIGTERM'); // ESRCH on an empty group is the normal case
|
|
189
193
|
const t = setTimeout(() => {
|
|
190
|
-
|
|
194
|
+
killTree(pid, 'SIGKILL');
|
|
191
195
|
}, POST_EXIT_GROUP_SWEEP_GRACE_MS);
|
|
192
196
|
if (t.unref) t.unref();
|
|
193
197
|
};
|
|
@@ -219,13 +223,9 @@ function withChildAndLog({ fd, logPath, safeLog, closeFd, spawn: spawnSpec, watc
|
|
|
219
223
|
killTree(signal) {
|
|
220
224
|
const c = ctx.child;
|
|
221
225
|
if (!c) return false;
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
catch {
|
|
226
|
-
try { process.kill(c.pid, signal); return true; }
|
|
227
|
-
catch { return false; /* already dead */ }
|
|
228
|
-
}
|
|
226
|
+
// Whole process group on POSIX / taskkill /T on win32 (requires detachedSpawnOpts at spawn);
|
|
227
|
+
// killTree falls back to the bare pid if the process is not a group leader.
|
|
228
|
+
return killTree(c.pid, signal, { log: safeLog });
|
|
229
229
|
},
|
|
230
230
|
addTimer(t) { extraTimers.push(t); },
|
|
231
231
|
};
|
|
@@ -315,7 +315,7 @@ function withChildAndLog({ fd, logPath, safeLog, closeFd, spawn: spawnSpec, watc
|
|
|
315
315
|
const cancel = () => {
|
|
316
316
|
clearAllTimers();
|
|
317
317
|
// Best-effort kill; ignore errors (process may already be gone).
|
|
318
|
-
|
|
318
|
+
if (!killTree(child.pid, 'SIGKILL', { log: safeLog })) {
|
|
319
319
|
try { child.kill('SIGKILL'); } catch { /* */ }
|
|
320
320
|
}
|
|
321
321
|
};
|
|
@@ -17,28 +17,52 @@ const os = require('node:os');
|
|
|
17
17
|
|
|
18
18
|
let cached = null;
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @param {{ platform?: string, env?: Record<string, string|undefined>, homedir?: string,
|
|
22
|
+
* accessSync?: (p: string, mode?: number) => void }} [opts] injectable for tests;
|
|
23
|
+
* passing any opts bypasses the process-lifetime cache.
|
|
24
|
+
*/
|
|
25
|
+
function resolveClaudeBin(opts) {
|
|
21
26
|
// Explicit override wins and is never cached — lets operators pin a binary
|
|
22
27
|
// and lets tests point the runner at a controllable stub process.
|
|
23
28
|
if (process.env.SM_CLAUDE_BIN) return process.env.SM_CLAUDE_BIN;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const injected = !!opts;
|
|
30
|
+
if (!injected && cached) return cached;
|
|
31
|
+
const platform = (opts && opts.platform) || process.platform;
|
|
32
|
+
const env = (opts && opts.env) || process.env;
|
|
33
|
+
const home = (opts && opts.homedir) || os.homedir();
|
|
34
|
+
const accessSync = (opts && opts.accessSync) || fs.accessSync;
|
|
35
|
+
let candidates;
|
|
36
|
+
let mode;
|
|
37
|
+
if (platform === 'win32') {
|
|
38
|
+
const w = path.win32;
|
|
39
|
+
candidates = [];
|
|
40
|
+
if (env.USERPROFILE) candidates.push(w.join(env.USERPROFILE, '.local', 'bin', 'claude.exe')); // native installer
|
|
41
|
+
if (env.APPDATA) candidates.push(w.join(env.APPDATA, 'npm', 'claude.cmd'));
|
|
42
|
+
if (env.LOCALAPPDATA) candidates.push(w.join(env.LOCALAPPDATA, 'Programs', 'claude', 'claude.exe'));
|
|
43
|
+
mode = fs.constants.F_OK; // X_OK is meaningless on Windows
|
|
44
|
+
} else {
|
|
45
|
+
// Merged candidate list — was forked in scheduler vs pluginInstall before.
|
|
46
|
+
candidates = [
|
|
47
|
+
path.join(home, '.claude', 'local', 'claude'), // Claude Code bundled install
|
|
48
|
+
path.join(home, '.local', 'bin', 'claude'), // user pip-style install
|
|
49
|
+
path.join(home, '.npm-global', 'bin', 'claude'), // user npm-global
|
|
50
|
+
'/usr/local/bin/claude',
|
|
51
|
+
'/opt/homebrew/bin/claude',
|
|
52
|
+
'/usr/bin/claude',
|
|
53
|
+
];
|
|
54
|
+
mode = fs.constants.X_OK;
|
|
55
|
+
}
|
|
35
56
|
for (const c of candidates) {
|
|
36
|
-
try {
|
|
57
|
+
try { accessSync(c, mode); if (!injected) cached = c; return c; } catch { /* */ }
|
|
37
58
|
}
|
|
38
|
-
cached = 'claude';
|
|
39
|
-
return
|
|
59
|
+
if (!injected) cached = 'claude';
|
|
60
|
+
return 'claude';
|
|
40
61
|
}
|
|
41
62
|
|
|
63
|
+
/** Test hook — forget the cached resolution. */
|
|
64
|
+
function __resetForTests() { cached = null; }
|
|
65
|
+
|
|
42
66
|
// ---------- process naming ----------
|
|
43
67
|
//
|
|
44
68
|
// Every `claude` spawn goes through claudeSpawnTarget so GNOME System Monitor
|
|
@@ -127,4 +151,4 @@ function resetClaudeVersionCache() {
|
|
|
127
151
|
versionCache = { at: 0, value: null, inflight: null };
|
|
128
152
|
}
|
|
129
153
|
|
|
130
|
-
module.exports = { resolveClaudeBin, claudeSpawnTarget, probeClaudeVersion, resetClaudeVersionCache };
|
|
154
|
+
module.exports = { resolveClaudeBin, __resetForTests, claudeSpawnTarget, probeClaudeVersion, resetClaudeVersionCache };
|