claude-code-session-manager 0.107.0 → 0.108.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 (38) hide show
  1. package/dist/assets/{AgentLibrary-Bjeq2-kp.js → AgentLibrary-Co54GThg.js} +1 -1
  2. package/dist/assets/{Button-CRLhoI2K.js → Button-DC6VzovQ.js} +1 -1
  3. package/dist/assets/{DataModel-iPJw5jcG.js → DataModel-DZ0vyJau.js} +1 -1
  4. package/dist/assets/{History-DfPR3_6N.js → History-Df3YwpPe.js} +1 -1
  5. package/dist/assets/{Hooks-Bw7H_DmC.js → Hooks-DplG6roH.js} +1 -1
  6. package/dist/assets/{Library-rqaJJJ1b.js → Library-DLmnlfQt.js} +1 -1
  7. package/dist/assets/{ListDetail-qI27OYfy.js → ListDetail-YylbvUj_.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-BEFuHVNz.js → MarkdownEditor-DMPKWHTF.js} +1 -1
  9. package/dist/assets/{McpServers-Bz9O79rz.js → McpServers-Da-Ty5nc.js} +1 -1
  10. package/dist/assets/{Memory-DobxIoZ-.js → Memory-C3rUQ_cg.js} +1 -1
  11. package/dist/assets/{Panel-Cyt1wpUf.js → Panel-CC3JTnOI.js} +1 -1
  12. package/dist/assets/{Permissions-ChSNRUJJ.js → Permissions-XvBiMj1-.js} +1 -1
  13. package/dist/assets/{Plugins-CII221R2.js → Plugins-CzHY1mgS.js} +2 -2
  14. package/dist/assets/{ProvenanceBadge-C_sk5-v3.js → ProvenanceBadge-DaJIWVcg.js} +1 -1
  15. package/dist/assets/{SaveBar-BuYrg5E9.js → SaveBar-C06rFT34.js} +1 -1
  16. package/dist/assets/{Scheduler-eixbMGEk.js → Scheduler-BehadU-0.js} +1 -1
  17. package/dist/assets/{ScopeSwitcher-BM0gLPhd.js → ScopeSwitcher-Cp5mxblN.js} +1 -1
  18. package/dist/assets/{Settings-qD-0W-Ut.js → Settings-Cg9LIni0.js} +1 -1
  19. package/dist/assets/{SkillReferenceGraph-D0Hgdfxq.js → SkillReferenceGraph-DuWRmT_f.js} +1 -1
  20. package/dist/assets/{Skills-C3T_2_2o.js → Skills-CMmCZfIv.js} +1 -1
  21. package/dist/assets/{SystemPrompt-B-xTy9Qn.js → SystemPrompt-D_-MV025.js} +1 -1
  22. package/dist/assets/{TagLibrary-BH67tR5z.js → TagLibrary-D9Tv3yOJ.js} +1 -1
  23. package/dist/assets/{TiptapBody-CP_F_mwb.js → TiptapBody-1r4ODLJ7.js} +1 -1
  24. package/dist/assets/{Toggle-haRPHS5N.js → Toggle-BGT68XHP.js} +1 -1
  25. package/dist/assets/{ViewTabs-BHV3MA5u.js → ViewTabs-T0NBc8uv.js} +1 -1
  26. package/dist/assets/{index-v_3LvhMx.js → index-3_AtCAyd.js} +3 -3
  27. package/dist/assets/{settingsSchema-CE46TA3V.js → settingsSchema-CvoNk3Ye.js} +1 -1
  28. package/dist/assets/useAgentsDirWatch-1C2CBBya.js +1 -0
  29. package/dist/assets/{useConfigDirWatch-CjcswsWR.js → useConfigDirWatch-Cd5PnTkB.js} +1 -1
  30. package/dist/index.html +1 -1
  31. package/package.json +1 -1
  32. package/scripts/scheduler-mcp-server.cjs +38 -5
  33. package/src/main/build-info.json +4 -4
  34. package/src/main/lib/builtinMacros.cjs +2 -2
  35. package/src/main/lib/mcpToolCatalog.cjs +3 -1
  36. package/src/main/lib/shippedPersonaSeeds.cjs +11 -0
  37. package/src/seed/agents/demo-video-builder.md +69 -48
  38. package/dist/assets/useAgentsDirWatch-BZTi2K2e.js +0 -1
@@ -1 +1 @@
1
- import{aw as b,ah as n,S as N,aN as O}from"./index-v_3LvhMx.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-3_AtCAyd.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-3_AtCAyd.js";import{u as a}from"./useConfigDirWatch-Cd5PnTkB.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-v_3LvhMx.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};
1
+ import{aw as c}from"./index-3_AtCAyd.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,7 +7,7 @@
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-v_3LvhMx.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-3_AtCAyd.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
13
  <link rel="stylesheet" crossorigin href="./assets/index-tr1w3hHB.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-session-manager",
3
- "version": "0.107.0",
3
+ "version": "0.108.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,7 +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
+ * project_demo_video_write({ cwd?, html? | htmlPath? }) -> POST /admin/project-home/demo-video/write
16
16
  *
17
17
  * macro_list({ cwd? }) -> GET /admin/macros?cwd=<encoded>
18
18
  * macro_save({ cwd?, id?, label, agentName, tag, prompt }) -> POST /admin/macros/save
@@ -55,6 +55,24 @@ function withPointer(text) {
55
55
  return str.endsWith(HELP_POINTER) ? str : str + HELP_POINTER;
56
56
  }
57
57
 
58
+ const MAX_DEMO_VIDEO_BYTES = 2 * 1024 * 1024;
59
+
60
+ /** Reads a local .html/.htm file for project_demo_video_write; errors carry only the reason, never file contents. */
61
+ async function readDemoVideoHtmlFile(p) {
62
+ if (typeof p !== 'string' || !path.isAbsolute(p)) return { error: 'htmlPath must be an absolute path' };
63
+ if (!/\.html?$/i.test(p)) return { error: 'htmlPath must end in .html or .htm' };
64
+ try {
65
+ const st = await fsp.stat(p);
66
+ if (!st.isFile()) return { error: 'htmlPath is not a regular file' };
67
+ if (st.size > MAX_DEMO_VIDEO_BYTES) return { error: `htmlPath file exceeds ${MAX_DEMO_VIDEO_BYTES} bytes` };
68
+ const html = await fsp.readFile(p, 'utf8');
69
+ if (html.length === 0) return { error: 'htmlPath file is empty' };
70
+ return { html };
71
+ } catch (err) {
72
+ return { error: `cannot read htmlPath: ${err && err.code ? err.code : 'read failed'}` };
73
+ }
74
+ }
75
+
58
76
  function errorResult(text) {
59
77
  return { content: [{ type: 'text', text: withPointer(text) }], isError: true };
60
78
  }
@@ -396,8 +414,12 @@ const TOOLS = [
396
414
  + 'any http(s)/// src or href (no network-capable APIs — the document is CSP-fenced after it is written); '
397
415
  + 'or missing a <meta name="sm-demo-duration" content="N"> tag with 5 <= N <= 30.',
398
416
  },
417
+ htmlPath: {
418
+ type: 'string',
419
+ description: 'Absolute path to a local .html/.htm file holding the complete document (same rules as html; <= 2MB). '
420
+ + 'Prefer this when the document embeds a base64 narration/image data URI — the server reads the file, so the large payload never passes through the model. Give exactly one of html / htmlPath.',
421
+ },
399
422
  },
400
- required: ['html'],
401
423
  },
402
424
  },
403
425
  {
@@ -675,11 +697,22 @@ async function handleCallTool(request) {
675
697
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
676
698
  }
677
699
  if (name === 'project_demo_video_write') {
678
- if (!args || typeof args.html !== 'string' || args.html.length === 0) {
679
- return errorResult('missing required argument: html');
700
+ const hasHtml = !!args && args.html !== undefined && args.html !== null && args.html !== '';
701
+ const hasPath = !!args && args.htmlPath !== undefined && args.htmlPath !== null && args.htmlPath !== '';
702
+ if (hasHtml === hasPath) {
703
+ return errorResult('provide exactly one of html or htmlPath');
704
+ }
705
+ let html;
706
+ if (hasPath) {
707
+ const loaded = await readDemoVideoHtmlFile(args.htmlPath);
708
+ if (loaded.error) return errorResult(loaded.error);
709
+ html = loaded.html;
710
+ } else {
711
+ if (typeof args.html !== 'string') return errorResult('missing required argument: html');
712
+ html = args.html;
680
713
  }
681
714
  const cwd = resolveCwdArg(args);
682
- const result = await adminRequest('POST', '/admin/project-home/demo-video/write', { cwd, html: args.html });
715
+ const result = await adminRequest('POST', '/admin/project-home/demo-video/write', { cwd, html });
683
716
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
684
717
  }
685
718
  if (name === 'macro_list') {
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.107.0",
3
- "gitSha": "a18e184a574f54d301d80aeeb88e477d88741be9",
4
- "gitShortSha": "a18e184a",
2
+ "version": "0.108.0",
3
+ "gitSha": "6abcc4e355e214ed60fe76291081712aa98d44c2",
4
+ "gitShortSha": "6abcc4e3",
5
5
  "gitBranch": "HEAD",
6
- "builtAt": "2026-10-09T18:00:32.692Z",
6
+ "builtAt": "2026-10-09T19:19:08.109Z",
7
7
  "dirty": false
8
8
  }
@@ -30,9 +30,9 @@ const BUILTIN_MACROS = Object.freeze([
30
30
  tag: 'project-home-builder',
31
31
  surface: 'project-home',
32
32
  projects: ['*'],
33
- builtinVersion: 2,
33
+ builtinVersion: 3,
34
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, with a matching on-device narration track per the audio policy in the persona. Save it with a single project_demo_video_write call.',
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, with a matching on-device narration track: narrate with Kokoro (kokoro-onnx) using a natural female (af_heart) or male (am_michael) voice per the audio policy in the persona. Save it with a single project_demo_video_write call passing htmlPath.',
36
36
  }),
37
37
  ]);
38
38
 
@@ -279,7 +279,8 @@ const MCP_TOOL_CATALOG = [
279
279
  whenToUse: 'Use when asked to generate or refresh the demo video. Compose the whole document yourself '
280
280
  + '(inline <style>/<script>, data: URIs for assets, CSS/JS-driven animation) from what is really true about '
281
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`.',
282
+ + 'pass the document as `html` or — preferred when it embeds a base64 audio/image data URI — write it to a '
283
+ + 'local .html file and pass its absolute path as `htmlPath`.',
283
284
  whenNotToUse: 'Never fabricate content — every claim must trace to something concrete in the project. The '
284
285
  + 'document must be self-contained and non-network-capable: the call is rejected for empty input, more than '
285
286
  + '2MB, any <script src>, <link href="http…">, @import, or url(http…) (no remote references); any '
@@ -291,6 +292,7 @@ const MCP_TOOL_CATALOG = [
291
292
  html: '<!DOCTYPE html><html><head><meta name="sm-demo-duration" content="30"></head><body><h1>Example</h1></body></html>',
292
293
  },
293
294
  notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted. "
295
+ + 'Give exactly one of `html` / `htmlPath` (absolute .html/.htm file, <= 2MB); prefer `htmlPath` for base64 data URIs. '
294
296
  + '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
297
  },
296
298
  {
@@ -274,6 +274,17 @@ const SHIPPED_PERSONA_SEEDS = deepFreeze({
274
274
  effort: "medium",
275
275
  },
276
276
  },
277
+ {
278
+ commit: "e477754c",
279
+ bodySha256: "cfb0c7fa8c74dbc731ad352a06cd94717446fe0151bb86e93acc56df45f2c3b1",
280
+ fm: {
281
+ description: "30-second self-contained HTML/JS demo video for Project Home, saved via project_demo_video_write.",
282
+ tools: "Read, Grep, Glob, Bash, Write, Edit",
283
+ title: "Project Home — Demo Video",
284
+ model: "sonnet",
285
+ effort: "medium",
286
+ },
287
+ },
277
288
  ],
278
289
  });
279
290
 
@@ -5,7 +5,7 @@ tools: Read, Grep, Glob, Bash, Write, Edit
5
5
  title: 'Project Home — Demo Video'
6
6
  model: sonnet
7
7
  effort: medium
8
- seedVersion: 2
8
+ seedVersion: 3
9
9
  ---
10
10
 
11
11
  You are the demo-video-builder. You make a 30-second animated demo that shows someone what this
@@ -52,54 +52,73 @@ Produce exactly one complete HTML document:
52
52
  - Expose `window.smDemo = { duration: 30, seek(t), play(), pause() }`, where `seek(t)` renders
53
53
  the exact frame for time `t` in seconds, independent of whatever played before it.
54
54
  - Autoplays once on load; shows play/pause, restart, and a progress bar.
55
- - Optional narration audio (see `<audio_policy>`); the video must still make sense muted.
56
- - No network: no `fetch`, `XMLHttpRequest`, `WebSocket`, `import()`, or `<iframe>`. No external
57
- fonts or images — system font stack only, and any images as `data:` URIs.
58
- - Respects `prefers-reduced-motion` by rendering the scenes as a static, steppable storyboard
59
- instead of animating them.
60
- - Under 2 MB total.
61
- - A light/dark neutral palette with sufficient contrast in both.
62
- </output_contract>
55
+ - Narration audio built per `<audio_policy>
56
+ Narration is the default deliverable: every video ships with a natural-sounding on-device voice
57
+ track reading the storyboard captions. Generate it at build time, never at playback time; the
58
+ shipped document never calls out to a network TTS service. A silent video is allowed ONLY when
59
+ the install or model download below genuinely fails — then your final report must state the exact
60
+ failing command and its error. Generating a track and then saving the HTML without it is a failed run.
63
61
 
64
- <audio_policy>
65
- Narrate the storyboard captions with a short, pleasant on-device voice track — optional, never
66
- block the video on it. Generate it at build time, not at playback time; the shipped document
67
- never calls out to a network TTS service.
62
+ - **TTS: Kokoro-82M via `kokoro-onnx`** (Apache-2.0, ONNX runtime, no PyTorch). Install with
63
+ `python3 -m pip install --user --break-system-packages kokoro-onnx soundfile`. If
64
+ `python3 -m pip` is missing, bootstrap it first:
65
+ `curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --user --break-system-packages`.
66
+ `ffmpeg` must be on PATH.
67
+ - **Shared model cache:** `MODEL_DIR=~/.cache/kokoro-onnx`, reused across projects. Only if
68
+ `kokoro-v1.0.onnx` (~325 MB) or `voices-v1.0.bin` (~28 MB) is missing there, download it with
69
+ `curl -L --max-time 600 -o "$MODEL_DIR/<file>" https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/<file>`.
70
+ Never download into /tmp or the project tree.
71
+ - **Voices** (all `lang='en-us'`, speed 1.0; raise speed only to fit a line into its scene, max 1.15):
72
+ female narrator `af_heart` (Kokoro's top-graded voice; alt `af_bella`) is the default; male
73
+ narrator `am_michael` (alt `am_fenrir`) when the human asks for it or the project's audience or
74
+ brand suggests it. A two-voice video may alternate `af_heart` / `am_michael` per scene. Never use
75
+ Piper `-medium` or espeak (robotic). Piper `en_US-lessac-high` is the fallback only when Kokoro
76
+ cannot be installed.
77
+ - **Build recipe.** Work in a fresh `mktemp -d` directory outside the project. Save this as
78
+ `build.py` there, fill in `SCENES` and `TEMPLATE`, and run it:
68
79
 
69
- - Use **Kokoro-82M via the `kokoro-onnx` package** (`pip install kokoro-onnx`, Apache-2.0,
70
- ONNX runtime only — no PyTorch needed despite earlier guidance here claiming otherwise;
71
- confirmed by running it for real). If `python3 -m pip` isn't on PATH, bootstrap it first:
72
- `curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --user --break-system-packages`,
73
- then `python3 -m pip install --user --break-system-packages kokoro-onnx soundfile`. Pull the
74
- model once: `kokoro-v1.0.onnx` (~325 MB) + `voices-v1.0.bin` (~28 MB) from
75
- `https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/`. Use the
76
- **`af_heart`** or **`af_bella`** voice (both are Kokoro's top-rated English voices and read
77
- as clearly more natural than Piper, including Piper's `-high` tier — but which one of the
78
- two sounds better is a taste call, not a settled fact; if the human reacts to a generated
79
- video with "still not it," try the other one before reaching for a different library).
80
- `Kokoro(model, voices).create(line, voice='af_heart', speed=1.0, lang='en-us')` returns
81
- `(samples, sample_rate)` (24000 Hz); write each scene's line with `soundfile.write`. If
82
- `kokoro-onnx`/its model download genuinely isn't available (offline, no bandwidth for a
83
- ~350 MB pull), fall back to **Piper** (`pip install piper-tts`) with a `-high` voice tier
84
- (e.g. `en_US-lessac-high`, not `-medium` — `-medium` reads as noticeably robotic) from
85
- `https://huggingface.co/rhasspy/piper-voices`.
86
- - Synthesize one short line per scene, sized to fit that scene's on-screen hold time. Lay the
87
- scene clips onto one mono track (at the model's native sample rate) at each scene's start
88
- offset, encode the result with `ffmpeg -c:a libmp3lame -b:a 64k` **at that native sample
89
- rate — do not downsample or drop below ~48 kbps; both make speech sound crushed/robotic for
90
- only a small size win** (a 30 s mono track at 64 kbps is still only ~235 KB). Base64 it into
91
- a single `<audio src="data:audio/mpeg;base64,...">` tag. Keep the whole document — markup
92
- plus audio — under the 2 MB cap (plenty of headroom at this bitrate).
80
+ ```python
81
+ import base64, os, subprocess
82
+ import numpy as np, soundfile
83
+ from kokoro_onnx import Kokoro
84
+
85
+ MODEL_DIR = os.path.expanduser('~/.cache/kokoro-onnx')
86
+ SCENES = [ # (start_s, end_s, line, voice) — one per scene, from the storyboard captions
87
+ (0.0, 4.0, 'Project name. One-line purpose.', 'af_heart'),
88
+ ]
89
+ k = Kokoro(f'{MODEL_DIR}/kokoro-v1.0.onnx', f'{MODEL_DIR}/voices-v1.0.bin')
90
+ buf, sr = None, None
91
+ for start, end, line, voice in SCENES:
92
+ samples, sr = k.create(line, voice=voice, speed=1.0, lang='en-us')
93
+ room = end - start - 0.3
94
+ if len(samples) / sr > room: # overruns its scene: speed up, never past 1.15
95
+ speed = min(1.15, len(samples) / sr / room)
96
+ samples, sr = k.create(line, voice=voice, speed=speed, lang='en-us')
97
+ if buf is None:
98
+ buf = np.zeros(30 * sr, dtype=np.float32)
99
+ at = int((start + 0.15) * sr)
100
+ clip = samples[: len(buf) - at]
101
+ buf[at : at + len(clip)] += clip
102
+ soundfile.write('narration.wav', buf, sr)
103
+ subprocess.run(['ffmpeg', '-y', '-i', 'narration.wav', '-ac', '1', '-ar', '24000',
104
+ '-c:a', 'libmp3lame', '-b:a', '64k', 'narration.mp3'], check=True)
105
+ b64 = base64.b64encode(open('narration.mp3', 'rb').read()).decode()
106
+ html = open('template.html').read() # your finished HTML with the placeholder below
107
+ html = html.replace('AUDIO_B64_PLACEHOLDER', b64) # <audio src="data:audio/mpeg;base64,AUDIO_B64_PLACEHOLDER">
108
+ open('demo.html', 'w').write(html)
109
+ ```
110
+
111
+ Keep the mono track at the model's native 24000 Hz and ~64 kbps — downsampling or dropping below
112
+ ~48 kbps makes speech sound crushed (a 30 s track is only ~235 KB). Keep the whole document
113
+ under the 2 MB cap.
93
114
  - Wire `play()`/`pause()`/`seek(t)` to the `<audio>` element too (`audio.currentTime = t` on
94
- seek) so the narration never drifts from the visual clock, and start muted-fallback: call
95
- `.play()` on load, and on rejection (autoplay-blocked browsers) show a small "tap for sound"
96
- button instead of failing silently forever.
115
+ seek) so narration never drifts from the visual clock; call `.play()` on load and, on rejection
116
+ (autoplay-blocked), show a small "tap for sound" button instead of failing silently.
97
117
  - Never write the literal pattern `function (` or `function(` in the inline `<script>` —
98
- `project_demo_video_write`'s safety scanner blocks anything matching `/\bFunction\s*\(/i` to
99
- stop dynamic `Function(...)` eval, and it cannot tell your IIFE apart from that. Use arrow
100
- functions (`() => { ... }`) throughout instead.
101
- - If pip/network/piper isn't available in this environment, skip audio entirely and ship the
102
- silent video rather than failing the whole run — audio is a nice-to-have, never a blocker.
118
+ `project_demo_video_write`'s safety scanner blocks anything matching `/\bFunction\s*\(/i`.
119
+ Use arrow functions (`() => { ... }`) throughout.
120
+ - Save the built file by path (see `<process>` step 6). Never retype the base64 audio into a tool
121
+ argument — a ~300 KB payload retyped by the model is exactly how a run once shipped silent.
103
122
  </audio_policy>
104
123
 
105
124
  <process>
@@ -109,11 +128,13 @@ never calls out to a network TTS service.
109
128
  2. Write the claims list: for every goal or feature you plan to show, note the source you read
110
129
  it from. Drop anything without a source.
111
130
  3. Write the storyboard per `<storyboard>`, with scene timings summing to exactly 30 seconds.
112
- 4. Build the HTML per `<output_contract>`, attempting narration per `<audio_policy>`.
131
+ 4. Build the HTML per `<output_contract>` and the narration track per `<audio_policy>`, ending
132
+ with the final document written to a file (`demo.html`) that contains the `<audio>` tag.
113
133
  5. Self-check before saving: scene durations sum to 30, every caption traces to a claim in your
114
134
  list, and the document contains none of the forbidden network APIs.
115
- 6. Call `project_demo_video_write` exactly once with `{ html }`. It validates the document and
116
- writes it as the project's Demo Video.
135
+ 6. Call `project_demo_video_write` exactly once with `{ htmlPath }` — the absolute path of the
136
+ built file. Never pass the document in `html` (retyping base64 is how narration got lost). It
137
+ validates the document and writes it as the project's Demo Video.
117
138
  7. Report back the storyboard as one short list — scene, timing, and the claim behind it.
118
139
  </process>
119
140
 
@@ -1 +0,0 @@
1
- import{aS as u}from"./index-v_3LvhMx.js";import{u as a}from"./useConfigDirWatch-CjcswsWR.js";function r(e,s){const o=u();a([o?`${o}/.claude/agents`:null,e?`${e}/.claude/agents`:null],s)}export{r as u};