claude-code-session-manager 0.100.0 → 0.101.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.
Files changed (49) hide show
  1. package/dist/assets/DataModel-CXaH2GED.js +1 -0
  2. package/dist/assets/{History-Dr4Jr_5z.js → History-Cerii-HA.js} +2 -2
  3. package/dist/assets/{Hooks-NhrvH3JP.js → Hooks-aXMb5LDy.js} +3 -3
  4. package/dist/assets/{Library-Dv_EtIMr.js → Library-WrJFKEOJ.js} +1 -1
  5. package/dist/assets/{MarkdownEditor-DTKDWV_n.js → MarkdownEditor-D1BMCXS7.js} +1 -1
  6. package/dist/assets/{McpServers-M034G9B3.js → McpServers-Cf0davLd.js} +2 -2
  7. package/dist/assets/{Memory-DzF1B4gx.js → Memory-Dn6NHWPe.js} +3 -3
  8. package/dist/assets/Permissions-CTVuFPjg.js +3 -0
  9. package/dist/assets/{Plugins-Bd3ef35w.js → Plugins-C2TyF8Mn.js} +2 -2
  10. package/dist/assets/ProvenanceBadge-F_jwHSwE.js +1 -0
  11. package/dist/assets/{SaveBar-rimJem-Y.js → SaveBar-DA1KVJST.js} +1 -1
  12. package/dist/assets/{Scheduler-DjgFJl2w.js → Scheduler-Bm6Q_hlu.js} +8 -8
  13. package/dist/assets/{ScopeSwitcher-CnAzTBrl.js → ScopeSwitcher-BygZlJca.js} +1 -1
  14. package/dist/assets/Settings-BYCiv5KV.js +3 -0
  15. package/dist/assets/{SkillReferenceGraph-B9aZDecM.js → SkillReferenceGraph-CcLnl9np.js} +1 -1
  16. package/dist/assets/{Skills-DRXd18gu.js → Skills-ChffsdTD.js} +2 -2
  17. package/dist/assets/{SystemPrompt-ahy5-Fwe.js → SystemPrompt-dNkfHE_M.js} +1 -1
  18. package/dist/assets/{TagLibrary-C_tToDeL.js → TagLibrary-jVlEuB0m.js} +1 -1
  19. package/dist/assets/{TiptapBody-DvdefS6K.js → TiptapBody-6_cdwcP0.js} +1 -1
  20. package/dist/assets/{Toggle-6Jl5Njc6.js → Toggle-CVXqGpAP.js} +1 -1
  21. package/dist/assets/ViewTabs-DO1YVbpc.js +1 -0
  22. package/dist/assets/{index-Bta-hwud.css → index-D89EUizB.css} +1 -1
  23. package/dist/assets/{index-vx8O73l8.js → index-DbCQPWWY.js} +571 -571
  24. package/dist/assets/{settingsSchema-D6iRguNV.js → settingsSchema-Wrvn-Yz1.js} +1 -1
  25. package/dist/index.html +2 -2
  26. package/package.json +1 -1
  27. package/scripts/scheduler-mcp-server.cjs +28 -0
  28. package/src/main/build-info.json +4 -4
  29. package/src/main/chatRunner.cjs +4 -3
  30. package/src/main/index.cjs +17 -1
  31. package/src/main/ipcSchemas.cjs +8 -0
  32. package/src/main/lib/builtinMacros.cjs +39 -0
  33. package/src/main/lib/macroLibrary.cjs +102 -8
  34. package/src/main/lib/mcpToolCatalog.cjs +23 -0
  35. package/src/main/lib/opsOwnership.cjs +5 -4
  36. package/src/main/lib/projectHomeAdminRoutes.cjs +136 -21
  37. package/src/main/lib/shippedPersonaSeeds.cjs +47 -0
  38. package/src/main/lib/smfileHeaders.cjs +66 -0
  39. package/src/main/projectPages.cjs +43 -13
  40. package/src/main/seedAgentPersonas.cjs +1 -1
  41. package/src/preload/api.d.ts +12 -0
  42. package/src/seed/agents/architect.md +2 -1
  43. package/src/seed/agents/demo-video-builder.md +84 -0
  44. package/src/seed/agents/dev-lead.md +2 -1
  45. package/src/seed/agents/project-home-builder.md +66 -36
  46. package/dist/assets/DataModel-HdR0AxFq.js +0 -1
  47. package/dist/assets/Permissions-DrgbSGWl.js +0 -3
  48. package/dist/assets/ProvenanceBadge-DcBHT1Iy.js +0 -1
  49. package/dist/assets/Settings-Dc1F_sSg.js +0 -3
@@ -1 +1 @@
1
- import{ay as b,aj as n,S as N,aP as O,aw as K}from"./index-vx8O73l8.js";function T({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 G(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 V(){return $||($=B(K)),$}export{T as E,V as s,G as u};
1
+ import{ax as b,ai as n,S as N,aO as O,av as K}from"./index-DbCQPWWY.js";function T({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 G(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 V(){return $||($=B(K)),$}export{T as E,V as s,G as 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-vx8O73l8.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-DbCQPWWY.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/monaco-editor-BW5C4Iv1.js">
12
12
  <link rel="stylesheet" crossorigin href="./assets/monaco-editor-BTnBOi8r.css">
13
- <link rel="stylesheet" crossorigin href="./assets/index-Bta-hwud.css">
13
+ <link rel="stylesheet" crossorigin href="./assets/index-D89EUizB.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.100.0",
3
+ "version": "0.101.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",
@@ -12,6 +12,7 @@
12
12
  * feedback_open_session({ ... }) -> POST /admin/feedback/open-session
13
13
  *
14
14
  * project_home_write({ cwd?, html }) -> POST /admin/project-home/write
15
+ * project_demo_video_write({ cwd?, html }) -> POST /admin/project-home/demo-video/write
15
16
  *
16
17
  * This is a separate process from the Electron app — it only ever reaches
17
18
  * it over the token-authed loopback HTTP API in admin-api.json, never by
@@ -375,6 +376,25 @@ const TOOLS = [
375
376
  required: ['html'],
376
377
  },
377
378
  },
379
+ {
380
+ name: 'project_demo_video_write',
381
+ description: descriptionFor('project_demo_video_write'),
382
+ inputSchema: {
383
+ type: 'object',
384
+ properties: {
385
+ cwd: { type: 'string', description: "Optional: absolute path to the target project. Defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted." },
386
+ html: {
387
+ type: 'string',
388
+ description: 'The complete self-contained HTML/JS document for a 30-second-or-less demo video (<= 2MB). '
389
+ + 'It is rejected for: being empty; <script src>, <link href=http*>, @import, or url(http*) (no remote references); '
390
+ + 'fetch/XMLHttpRequest/WebSocket/EventSource/sendBeacon/dynamic import/importScripts/<iframe>/<object>/<embed>/window.open/'
391
+ + 'any http(s)/// src or href (no network-capable APIs — the document is CSP-fenced after it is written); '
392
+ + 'or missing a <meta name="sm-demo-duration" content="N"> tag with 5 <= N <= 30.',
393
+ },
394
+ },
395
+ required: ['html'],
396
+ },
397
+ },
378
398
  {
379
399
  name: 'session_manager_help',
380
400
  description: descriptionFor('session_manager_help'),
@@ -599,6 +619,14 @@ async function handleCallTool(request) {
599
619
  const result = await adminRequest('POST', '/admin/project-home/write', { cwd, html: args.html });
600
620
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
601
621
  }
622
+ if (name === 'project_demo_video_write') {
623
+ if (!args || typeof args.html !== 'string' || args.html.length === 0) {
624
+ return errorResult('missing required argument: html');
625
+ }
626
+ const cwd = resolveCwdArg(args);
627
+ const result = await adminRequest('POST', '/admin/project-home/demo-video/write', { cwd, html: args.html });
628
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
629
+ }
602
630
  if (name === 'session_manager_help') {
603
631
  const toolName = args && typeof args.tool === 'string' ? args.tool : null;
604
632
  const topic = args && typeof args.topic === 'string' ? args.topic : null;
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.100.0",
3
- "gitSha": "e2cdd120c1125c4310e8bae3ed007d1d6e55a850",
4
- "gitShortSha": "e2cdd120",
2
+ "version": "0.101.0",
3
+ "gitSha": "c57a278f1ec1bf0c508ff6a1fcc260195febd6e3",
4
+ "gitShortSha": "c57a278f",
5
5
  "gitBranch": "HEAD",
6
- "builtAt": "2026-10-03T03:50:40.894Z",
6
+ "builtAt": "2026-10-03T17:17:40.599Z",
7
7
  "dirty": false
8
8
  }
@@ -30,7 +30,7 @@
30
30
  * chat:run:queued { tabId, sessionId, position } — waiting behind a busy lane
31
31
  * chat:run:started { tabId, sessionId }
32
32
  * chat:run:output { tabId, delta }
33
- * chat:run:tool-use { tabId, id, kind, label }
33
+ * chat:run:tool-use { tabId, id, kind, label, detail }
34
34
  * chat:run:complete { tabId, sessionId, finalMessage }
35
35
  * chat:run:needs-input { tabId, sessionId, questions, raw }
36
36
  * chat:run:error { tabId, sessionId, message }
@@ -736,9 +736,10 @@ function executeRun({ tabId, sessionId, prompt, cwd, resume, silent, onSilentRes
736
736
  if (!silent) broadcast('chat:run:output', { tabId, delta: block.text });
737
737
  } else if (block.type === 'tool_use' && typeof block.name === 'string') {
738
738
  const classified = classifyToolUse(block);
739
- recentToolUses.push({ ...classified, detail: describeToolUseInput(block) });
739
+ const detail = describeToolUseInput(block);
740
+ recentToolUses.push({ ...classified, detail });
740
741
  if (recentToolUses.length > RECENT_TOOL_USE_LIMIT) recentToolUses.shift();
741
- if (!silent) broadcast('chat:run:tool-use', { tabId, id: block.id, ...classified });
742
+ if (!silent) broadcast('chat:run:tool-use', { tabId, id: block.id, ...classified, detail });
742
743
  }
743
744
  }
744
745
  } else if (event.type === 'user') {
@@ -33,6 +33,7 @@ const { createAdminHttp } = require('./lib/localAdminHttp.cjs');
33
33
  const prdCreate = require('./lib/prdCreate.cjs');
34
34
  const prdAdminRoutes = require('./lib/prdAdminRoutes.cjs');
35
35
  const projectHomeAdminRoutes = require('./lib/projectHomeAdminRoutes.cjs');
36
+ const { smfileResponseHeaders, shouldBlockFrameNavigation } = require('./lib/smfileHeaders.cjs');
36
37
  const { appendAuditEvent } = require('./lib/auditLog.cjs');
37
38
  const chatRunner = require('./chatRunner.cjs');
38
39
  const promptSessionEvents = require('./promptSessionEvents.cjs');
@@ -455,6 +456,21 @@ function createWindow() {
455
456
  configMgr.releaseWatchesForSender(mainWindow.webContents.id);
456
457
  });
457
458
 
459
+ // CSP (frame-src/connect-src) does not govern a subframe navigating itself —
460
+ // a demo-video document could still exfiltrate data via meta refresh or
461
+ // `location.href = 'https://…'`. Block any such navigation out of an
462
+ // smfile:// demo-video subframe.
463
+ mainWindow.webContents.on('will-frame-navigate', (details) => {
464
+ if (shouldBlockFrameNavigation({
465
+ isMainFrame: details.isMainFrame,
466
+ currentUrl: details.frame?.url,
467
+ targetUrl: details.url,
468
+ })) {
469
+ details.preventDefault();
470
+ console.warn('[main] blocked will-frame-navigate to', details.url);
471
+ }
472
+ });
473
+
458
474
  mainWindow.webContents.on('context-menu', (_e, params) => {
459
475
  const items = [];
460
476
  if (params.editFlags.canCopy) items.push({ label: 'Copy', role: 'copy' });
@@ -1276,7 +1292,7 @@ app.whenReady().then(async () => {
1276
1292
  const buf = await fs.promises.readFile(realPath);
1277
1293
  const ext = path.extname(realPath).toLowerCase();
1278
1294
  const type = SMFILE_MIME[ext] || 'application/octet-stream';
1279
- return new Response(buf, { headers: { 'content-type': type } });
1295
+ return new Response(buf, { headers: smfileResponseHeaders(realPath, type) });
1280
1296
  } catch (err) {
1281
1297
  return new Response(`smfile error: ${err && err.message}`, { status: 404 });
1282
1298
  }
@@ -730,6 +730,13 @@ const projectHomeAdminWriteBody = z.object({
730
730
  html: z.string(),
731
731
  }).strict();
732
732
 
733
+ // Mirrors projectHomeAdminWriteBody; content limits (network-check rejections,
734
+ // size cap, duration meta) live in projectHomeAdminRoutes.cjs's validateDemoVideoHtml.
735
+ const projectDemoVideoAdminWriteBody = z.object({
736
+ cwd: z.string().min(1).max(4096).optional(),
737
+ html: z.string(),
738
+ }).strict();
739
+
733
740
  const projectBriefSetPin = z.object({
734
741
  cwd: z.string().min(1).max(4096),
735
742
  block: PROJECT_BRIEF_BLOCK,
@@ -1132,6 +1139,7 @@ module.exports = {
1132
1139
  projectBriefCwd,
1133
1140
  projectPagesCwd,
1134
1141
  projectHomeAdminWriteBody,
1142
+ projectDemoVideoAdminWriteBody,
1135
1143
  projectBriefSetPin,
1136
1144
  projectBriefUpdate,
1137
1145
  promptSessionTranscriptAppend,
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * builtinMacros.cjs — the machine-local Macro library's seeded built-ins.
5
+ *
6
+ * These drive Project Home's one-click actions through the same (agent, tag,
7
+ * prompt) hot-key framework as user-created macros. `macroLibrary.cjs`'s
8
+ * `ensureBuiltins` seeds any entry here that is missing from macros.json, and
9
+ * upgrades an unedited stored copy whose `builtinVersion` is behind this file.
10
+ *
11
+ * Plain Node, no Electron imports.
12
+ */
13
+
14
+ const BUILTIN_MACROS = Object.freeze([
15
+ Object.freeze({
16
+ id: 'builtin-project-home',
17
+ label: 'Project Home',
18
+ agentName: 'project-home-builder',
19
+ tag: 'project-home-builder',
20
+ surface: 'project-home',
21
+ projects: ['*'],
22
+ builtinVersion: 1,
23
+ prompt:
24
+ "Generate this project's Project Home page: one self-contained HTML overview (what it is, who it is for, key features, structure, how to run it, key commands), grounded only in what you read in the repository. Save it with a single project_home_write call.",
25
+ }),
26
+ Object.freeze({
27
+ id: 'builtin-demo-video',
28
+ label: 'Demo Video',
29
+ agentName: 'demo-video-builder',
30
+ tag: 'project-home-builder',
31
+ surface: 'project-home',
32
+ projects: ['*'],
33
+ builtinVersion: 1,
34
+ prompt:
35
+ 'Generate a 30-second demo video for this project as one self-contained HTML/JavaScript animation that presents its main goals and key features, grounded only in what you read in the repository. Save it with a single project_demo_video_write call.',
36
+ }),
37
+ ]);
38
+
39
+ module.exports = { BUILTIN_MACROS };
@@ -43,6 +43,7 @@ const { EpicTagSchema } = require('./promptSessionSchema.cjs');
43
43
  const { WORK_TYPES } = require('./workTypeLibrary.cjs');
44
44
  const { PERSONA_NAME_RE } = require('./personaMerge.cjs');
45
45
  const { schedulerHome } = require('./schedulerPaths.cjs');
46
+ const { BUILTIN_MACROS } = require('./builtinMacros.cjs');
46
47
 
47
48
  const ALL_PROJECTS = '*';
48
49
  const STORE_VERSION = 1;
@@ -57,6 +58,7 @@ const AbsolutePathSchema = z.string().max(1024).refine(
57
58
  );
58
59
  const MacroProjectEntrySchema = z.union([z.literal(ALL_PROJECTS), AbsolutePathSchema]);
59
60
  const MacroProjectsSchema = z.array(MacroProjectEntrySchema).max(200);
61
+ const MacroSurfaceSchema = z.enum(['sessions', 'project-home']);
60
62
 
61
63
  const MacroSchema = z.object({
62
64
  id: MacroIdSchema,
@@ -65,6 +67,9 @@ const MacroSchema = z.object({
65
67
  tag: EpicTagSchema,
66
68
  prompt: MacroPromptSchema,
67
69
  projects: MacroProjectsSchema,
70
+ surface: MacroSurfaceSchema.default('sessions'),
71
+ builtin: z.boolean().optional(),
72
+ builtinVersion: z.number().int().optional(),
68
73
  createdAt: z.string().min(1).max(64),
69
74
  updatedAt: z.string().min(1).max(64),
70
75
  });
@@ -75,7 +80,11 @@ const MacroSaveSchema = z.object({
75
80
  agentName: MacroAgentNameSchema,
76
81
  tag: EpicTagSchema,
77
82
  prompt: MacroPromptSchema,
78
- projects: MacroProjectsSchema.default([]),
83
+ // Omitted (not merely `[]`) means "leave projects unchanged" on update, so a
84
+ // label/prompt-only edit of a macro (builtin or otherwise) can't silently
85
+ // wipe its visibility back to no projects. `undefined` on create means [].
86
+ projects: MacroProjectsSchema.optional(),
87
+ surface: MacroSurfaceSchema.optional(),
79
88
  });
80
89
 
81
90
  const MacroDeleteSchema = z.object({ id: MacroIdSchema });
@@ -185,6 +194,73 @@ async function writeStore(filePath, macros) {
185
194
  await writeJsonAtomic(filePath, { version: STORE_VERSION, macros });
186
195
  }
187
196
 
197
+ function resolveBuiltins(opts) {
198
+ return opts && Object.prototype.hasOwnProperty.call(opts, 'builtins') ? opts.builtins : BUILTIN_MACROS;
199
+ }
200
+
201
+ /**
202
+ * Pure. Seeds any `builtins` entry missing from `macros` (as `builtin: true`),
203
+ * and upgrades a stored built-in that is behind the shipped `builtinVersion`
204
+ * AND was never user-edited (`updatedAt === createdAt`). A user-edited
205
+ * built-in is left untouched. Returns `{ macros, changed }`.
206
+ */
207
+ function ensureBuiltins(macros, builtins) {
208
+ if (!builtins || builtins.length === 0) return { macros, changed: false };
209
+ let changed = false;
210
+ const next = macros.slice();
211
+ const now = new Date().toISOString();
212
+ for (const b of builtins) {
213
+ const idx = next.findIndex((m) => m.id === b.id);
214
+ if (idx === -1) {
215
+ next.push({
216
+ id: b.id,
217
+ label: b.label,
218
+ agentName: b.agentName,
219
+ tag: b.tag,
220
+ prompt: b.prompt,
221
+ projects: b.projects.slice(),
222
+ surface: b.surface,
223
+ builtin: true,
224
+ builtinVersion: b.builtinVersion,
225
+ createdAt: now,
226
+ updatedAt: now,
227
+ });
228
+ changed = true;
229
+ continue;
230
+ }
231
+ const stored = next[idx];
232
+ const storedVersion = stored.builtinVersion || 0;
233
+ const neverEdited = stored.updatedAt === stored.createdAt;
234
+ if (storedVersion < b.builtinVersion && neverEdited) {
235
+ next[idx] = {
236
+ ...stored,
237
+ label: b.label,
238
+ agentName: b.agentName,
239
+ tag: b.tag,
240
+ prompt: b.prompt,
241
+ surface: b.surface,
242
+ builtinVersion: b.builtinVersion,
243
+ updatedAt: stored.createdAt,
244
+ };
245
+ changed = true;
246
+ }
247
+ }
248
+ return { macros: next, changed };
249
+ }
250
+
251
+ /** Apply builtin seeding/upgrades, writing only when something changed. */
252
+ async function applyBuiltins(filePath, macros, opts) {
253
+ const { macros: next, changed } = ensureBuiltins(macros, resolveBuiltins(opts));
254
+ if (!changed) return { macros, failed: false };
255
+ try {
256
+ await writeStore(filePath, next);
257
+ } catch (err) {
258
+ console.warn('[macroLibrary] could not persist built-in macro seeding:', err && err.message);
259
+ return { macros, failed: false };
260
+ }
261
+ return { macros: next, failed: false };
262
+ }
263
+
188
264
  // --- migration --------------------------------------------------------------
189
265
 
190
266
  function isValidProject(p) {
@@ -246,7 +322,7 @@ async function migrateFromPersonas(opts) {
246
322
  */
247
323
  async function loadStore(filePath, opts) {
248
324
  const cur = await readStore(filePath);
249
- if (cur.state === 'ok') return { macros: cur.macros, failed: false };
325
+ if (cur.state === 'ok') return applyBuiltins(filePath, cur.macros, opts);
250
326
  if (cur.state === 'unreadable') return { macros: [], failed: true };
251
327
  if (cur.state === 'corrupt') {
252
328
  const quarantine = `${filePath}.corrupt-${Date.now()}`;
@@ -263,7 +339,7 @@ async function loadStore(filePath, opts) {
263
339
  console.warn('[macroLibrary] could not write fresh macros.json:', err && err.message);
264
340
  return { macros: [], failed: true };
265
341
  }
266
- return { macros: [], failed: false };
342
+ return applyBuiltins(filePath, [], opts);
267
343
  }
268
344
  // missing: migrate once
269
345
  const seeded = await migrateFromPersonas(opts);
@@ -274,7 +350,7 @@ async function loadStore(filePath, opts) {
274
350
  console.warn('[macroLibrary] could not write migrated macros.json:', err && err.message);
275
351
  return { macros: seeded, failed: true };
276
352
  }
277
- return { macros: seeded, failed: false };
353
+ return applyBuiltins(filePath, seeded, opts);
278
354
  }
279
355
 
280
356
  // --- public API -------------------------------------------------------------
@@ -283,8 +359,11 @@ async function loadStore(filePath, opts) {
283
359
  async function listMacros(opts = {}) {
284
360
  const filePath = resolvePath(opts);
285
361
  const first = await readStore(filePath);
286
- if (first.state === 'ok') return first.macros;
287
- // Missing/corrupt: repair inside the write chain so racing callers don't double-seed.
362
+ if (first.state === 'ok') {
363
+ const { changed } = ensureBuiltins(first.macros, resolveBuiltins(opts));
364
+ if (!changed) return first.macros;
365
+ }
366
+ // Missing/corrupt/needs-seeding: repair inside the write chain so racing callers don't double-seed.
288
367
  return serialize(async () => (await loadStore(filePath, opts)).macros);
289
368
  }
290
369
 
@@ -305,14 +384,24 @@ async function saveMacro(input, opts = {}) {
305
384
  return serialize(async () => {
306
385
  const macros = await loadForWrite(filePath, opts);
307
386
  const now = new Date().toISOString();
308
- const projects = normalizeProjects(parsed.projects);
309
387
  let saved;
310
388
  if (parsed.id) {
311
389
  const idx = macros.findIndex((m) => m.id === parsed.id);
312
390
  if (idx === -1) throw new Error(`macro not found: ${parsed.id}`);
313
- saved = { ...macros[idx], label: parsed.label, agentName: parsed.agentName, tag: parsed.tag, prompt: parsed.prompt, projects, updatedAt: now };
391
+ const projects = parsed.projects !== undefined ? normalizeProjects(parsed.projects) : macros[idx].projects;
392
+ saved = {
393
+ ...macros[idx],
394
+ label: parsed.label,
395
+ agentName: parsed.agentName,
396
+ tag: parsed.tag,
397
+ prompt: parsed.prompt,
398
+ projects,
399
+ surface: parsed.surface ?? macros[idx].surface,
400
+ updatedAt: now,
401
+ };
314
402
  macros[idx] = saved;
315
403
  } else {
404
+ const projects = parsed.projects !== undefined ? normalizeProjects(parsed.projects) : [];
316
405
  saved = {
317
406
  id: newId(parsed.label, new Set(macros.map((m) => m.id))),
318
407
  label: parsed.label,
@@ -320,6 +409,7 @@ async function saveMacro(input, opts = {}) {
320
409
  tag: parsed.tag,
321
410
  prompt: parsed.prompt,
322
411
  projects,
412
+ surface: parsed.surface ?? 'sessions',
323
413
  createdAt: now,
324
414
  updatedAt: now,
325
415
  };
@@ -336,6 +426,8 @@ async function deleteMacro(input, opts = {}) {
336
426
  const filePath = resolvePath(opts);
337
427
  return serialize(async () => {
338
428
  const macros = await loadForWrite(filePath, opts);
429
+ const target = macros.find((m) => m.id === id);
430
+ if (target && target.builtin) throw new Error('built-in macros cannot be deleted');
339
431
  const next = macros.filter((m) => m.id !== id);
340
432
  if (next.length !== macros.length) await writeStore(filePath, next);
341
433
  return { ok: true };
@@ -361,6 +453,7 @@ async function setMacroProject(input, opts = {}) {
361
453
  if (enabled) return { ok: true, macro };
362
454
  return { ok: false, error: 'macro is shown in every project' };
363
455
  }
456
+ if (macro.builtin && !enabled) return { ok: false, error: 'built-in macro visibility cannot be narrowed' };
364
457
  const has = macro.projects.includes(target);
365
458
  if (enabled === has) return { ok: true, macro };
366
459
  const projects = enabled ? [...macro.projects, target] : macro.projects.filter((p) => p !== target);
@@ -383,4 +476,5 @@ module.exports = {
383
476
  deleteMacro,
384
477
  setMacroProject,
385
478
  defaultFilePath,
479
+ ensureBuiltins,
386
480
  };
@@ -270,6 +270,29 @@ const MCP_TOOL_CATALOG = [
270
270
  exampleArgs: { cwd: '/home/bilko/Projects/session-manager', html: '<!DOCTYPE html><html><body><h1>Example</h1></body></html>' },
271
271
  notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted.",
272
272
  },
273
+ {
274
+ name: 'project_demo_video_write',
275
+ group: 'project-home',
276
+ purpose: "Write this project's 30-second demo video — ONE self-contained HTML/JS document — to "
277
+ + "session-manager-operations/project-pages/demo-video/index.html via the session-manager app's admin API. "
278
+ + 'The only write path for the demo video.',
279
+ whenToUse: 'Use when asked to generate or refresh the demo video. Compose the whole document yourself '
280
+ + '(inline <style>/<script>, data: URIs for assets, CSS/JS-driven animation) from what is really true about '
281
+ + 'the project, declare its length with <meta name="sm-demo-duration" content="N"> (5-30 seconds), then '
282
+ + 'pass the full document as `html`.',
283
+ whenNotToUse: 'Never fabricate content — every claim must trace to something concrete in the project. The '
284
+ + 'document must be self-contained and non-network-capable: the call is rejected for empty input, more than '
285
+ + '2MB, any <script src>, <link href="http…">, @import, or url(http…) (no remote references); any '
286
+ + 'fetch/XMLHttpRequest/WebSocket/EventSource/sendBeacon/dynamic import/importScripts/<iframe>/<object>/'
287
+ + '<embed>/window.open/http(s)-pointing src or href (no network-capable APIs); or a missing/out-of-range '
288
+ + 'sm-demo-duration meta tag.',
289
+ exampleArgs: {
290
+ cwd: '/home/bilko/Projects/session-manager',
291
+ html: '<!DOCTYPE html><html><head><meta name="sm-demo-duration" content="30"></head><body><h1>Example</h1></body></html>',
292
+ },
293
+ notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted. "
294
+ + 'The document is CSP-stamped after it is written, so the viewer sandbox is the real network fence — these checks are defence in depth.',
295
+ },
273
296
  {
274
297
  name: 'session_manager_help',
275
298
  group: 'help',
@@ -48,10 +48,11 @@ const OWNERS = Object.freeze({
48
48
  'scheduler': 'scheduler',
49
49
  // Project Home owns the synthesized Brief (generate + hand-edit).
50
50
  'project-brief': 'project-home',
51
- // Project Home owns project-pages/home.html, written only via the
52
- // project_home_write MCP tool -> /admin/project-home/write route
53
- // (config.cjs's writeTextAtomic). See
54
- // session-manager-operations/project-pages/README.md.
51
+ // Project Home owns project-pages/home.html and project-pages/demo-video/
52
+ // index.html, written only via the project_home_write ->
53
+ // /admin/project-home/write and project_demo_video_write ->
54
+ // /admin/project-home/demo-video/write routes (config.cjs's
55
+ // writeTextAtomic). See session-manager-operations/project-pages/README.md.
55
56
  'project-pages': 'project-home',
56
57
  // Structured per-tab error log lines (JSONL), tagged for tracing/analysis.
57
58
  'logs': 'logs',