claude-code-session-manager 0.122.1 → 0.123.1

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 (93) hide show
  1. package/dist/assets/{AgentLibrary-C2qUX9WU.js → AgentLibrary-T14aM8ie.js} +2 -2
  2. package/dist/assets/{Button-ssBzvFha.js → Button-Dz1kV8Z_.js} +1 -1
  3. package/dist/assets/DataModel-DdYRn2Ec.js +1 -0
  4. package/dist/assets/{History-BRdRp1J_.js → History-zQPvqwE7.js} +2 -2
  5. package/dist/assets/{Hooks--hiGPBQy.js → Hooks-BTrbT742.js} +3 -3
  6. package/dist/assets/{Library-0XYvkhh2.js → Library-BwQ6GRHR.js} +7 -7
  7. package/dist/assets/ListDetail-CunxAhRC.js +1 -0
  8. package/dist/assets/{McpServers-CCjHSwFm.js → McpServers-DpoyxXaO.js} +2 -2
  9. package/dist/assets/{Memory-C7RXoVO4.js → Memory-KDtGYMc3.js} +8 -8
  10. package/dist/assets/{Panel-C92O_aAH.js → Panel-xXmUBo8l.js} +1 -1
  11. package/dist/assets/{Permissions-DuTJ1FUb.js → Permissions-lIzTUrW2.js} +3 -3
  12. package/dist/assets/{Plugins-DSXsP4hM.js → Plugins-BA1Zqk69.js} +2 -2
  13. package/dist/assets/{ProvenanceBadge-CWdDkU1m.js → ProvenanceBadge-CmeJG2Kw.js} +1 -1
  14. package/dist/assets/{SaveBar-ob2dJJjH.js → SaveBar-BTJg_9Uc.js} +1 -1
  15. package/dist/assets/{Scheduler-Du4HgPKY.js → Scheduler-CISTDNMU.js} +7 -7
  16. package/dist/assets/{ScopeSwitcher-Dv_6M_Ci.js → ScopeSwitcher-C6NWvzoj.js} +1 -1
  17. package/dist/assets/Settings-BBoGJ34v.js +4 -0
  18. package/dist/assets/{SkillReferenceGraph-Dx1Qs8Y0.js → SkillReferenceGraph-IJv60QAJ.js} +1 -1
  19. package/dist/assets/Skills-L6nGcNF1.js +2 -0
  20. package/dist/assets/SystemPrompt-Cm7I2gJz.js +1 -0
  21. package/dist/assets/TagLibrary-BxPwoPE6.js +1 -0
  22. package/dist/assets/{TiptapBody-Cihe5er3.js → TiptapBody-CR-Zr3Pz.js} +1 -1
  23. package/dist/assets/{Toggle-CpYVC8bF.js → Toggle-BFhlpZ-n.js} +1 -1
  24. package/dist/assets/{ViewTabs-C3FN6P_C.js → ViewTabs-CuGlZ9Ed.js} +1 -1
  25. package/dist/assets/{historyTotals-C4Rs37Cd.js → historyTotals-Dxv9Fzrs.js} +1 -1
  26. package/dist/assets/{index-Dd7UQabV.css → index-BnKaYERU.css} +1 -1
  27. package/dist/assets/{index-S-F5jp_h.js → index-Dx8owDeQ.js} +151 -151
  28. package/dist/assets/{settingsSchema-BZcdzHTQ.js → settingsSchema-DaqFSAQH.js} +1 -1
  29. package/dist/assets/{skillFrontmatter-EfNERt4b.js → skillFrontmatter-BIJddxVB.js} +1 -1
  30. package/dist/assets/{useAgentsDirWatch-BfbrFvB3.js → useAgentsDirWatch-1hjZ4knk.js} +1 -1
  31. package/dist/index.html +2 -2
  32. package/package.json +3 -3
  33. package/plugins/session-manager-dev/skills/develop/SKILL.md +29 -25
  34. package/scripts/scheduler-mcp-server.cjs +124 -74
  35. package/src/main/agentLibrary.cjs +8 -3
  36. package/src/main/build-info.json +4 -4
  37. package/src/main/crashDiagnostics.cjs +24 -0
  38. package/src/main/historyAggregator.cjs +41 -7
  39. package/src/main/index.cjs +13 -3
  40. package/src/main/ipcSchemas.cjs +30 -22
  41. package/src/main/lib/activeSessions.cjs +93 -32
  42. package/src/main/lib/epic/delegationReadiness.cjs +2 -0
  43. package/src/main/lib/epic/delegationSelfHeal.cjs +77 -0
  44. package/src/main/lib/epic/epicMint.cjs +1 -1
  45. package/src/main/lib/epic/epicStandingInstructions.cjs +73 -0
  46. package/src/main/lib/epic/guardSpawnSettings.cjs +76 -0
  47. package/src/main/lib/feedback-remote/crossProjectFeedback.cjs +9 -2
  48. package/src/main/lib/feedback-remote/remoteClientSessions.cjs +3 -11
  49. package/src/main/lib/feedback-remote/remoteE2eCrypto.cjs +1 -1
  50. package/src/main/lib/git/gitWorktree.cjs +9 -4
  51. package/src/main/lib/git/worktreeDeps.cjs +13 -2
  52. package/src/main/lib/git/worktreeSetup.cjs +212 -0
  53. package/src/main/lib/mcpToolCatalog.cjs +31 -2
  54. package/src/main/lib/persona-macro/agentPersonaSchema.cjs +4 -0
  55. package/src/main/lib/persona-macro/shippedPersonaSeeds.cjs +23 -0
  56. package/src/main/lib/prd/prdAgentType.cjs +28 -0
  57. package/src/main/lib/prd/prdBodyPatch.cjs +138 -0
  58. package/src/main/lib/prd/prdCreate.cjs +205 -32
  59. package/src/main/lib/prd/prdGateFiles.cjs +31 -0
  60. package/src/main/lib/scheduler/definitionOfDone.cjs +24 -3
  61. package/src/main/lib/scheduler/queueStore.cjs +5 -4
  62. package/src/main/lib/scheduler/scheduleJobSchema.cjs +1 -0
  63. package/src/main/lib/scheduler/schedulerBatch.cjs +43 -5
  64. package/src/main/lib/scheduler/schedulerPaths.cjs +2 -0
  65. package/src/main/lib/scheduler/watchdogHelpers.cjs +114 -22
  66. package/src/main/lib/telemetry/mainHeapSnapshot.cjs +69 -0
  67. package/src/main/lib/telemetry/telemetryBoot.cjs +25 -3
  68. package/src/main/lib/telemetry/telemetryClient.cjs +12 -0
  69. package/src/main/lib/telemetry/telemetryCounters.cjs +25 -2
  70. package/src/main/pty.cjs +0 -53
  71. package/src/main/runVerify.cjs +38 -11
  72. package/src/main/scheduler/prdParser.cjs +3 -0
  73. package/src/main/scheduler/prdPaths.cjs +48 -2
  74. package/src/main/scheduler.cjs +124 -29
  75. package/src/main/seedDevPlugin.cjs +1 -1
  76. package/src/main/seedSchedulerMcp.cjs +1 -1
  77. package/src/main/templates/PRD_AUTHORING.md +8 -4
  78. package/src/main/webRemote.cjs +38 -132
  79. package/src/preload/api.d.ts +4 -52
  80. package/src/preload/index.cjs +0 -24
  81. package/src/seed/agents/dev-lead.md +2 -1
  82. package/src/seed/agents/validator.md +2 -1
  83. package/dist/assets/DataModel-CENoTvc2.js +0 -1
  84. package/dist/assets/ListDetail-CkKXAM59.js +0 -1
  85. package/dist/assets/RemoteMachines-BFmbIGpW.js +0 -3
  86. package/dist/assets/Settings-DMoI0cqB.js +0 -4
  87. package/dist/assets/Skills-DYWQTQB1.js +0 -2
  88. package/dist/assets/SystemPrompt-Df0IfOuI.js +0 -1
  89. package/dist/assets/TagLibrary-BuMsGx-2.js +0 -1
  90. package/dist/assets/useRemotePeers-CKVakQPc.js +0 -1
  91. package/src/main/lib/feedback-remote/relayClientAuth.cjs +0 -133
  92. package/src/main/remoteClient.cjs +0 -334
  93. package/src/main/remoteClientIpc.cjs +0 -133
@@ -1 +1 @@
1
- import{aF as b,ap as n,S as N,a_ as O}from"./index-S-F5jp_h.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(_,{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&&I(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 _({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 I(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{aB as b,al as n,S as N,aW as O}from"./index-Dx8owDeQ.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:B(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 B(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 M(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 $||($=M(K)),$}export{G as E,D as s,V as u};
@@ -1,4 +1,4 @@
1
- import{ap as k,F as E}from"./index-S-F5jp_h.js";function M({value:t,onChange:n,path:e,readOnly:i}){return k.jsx(E,{path:`file://${e}`,defaultLanguage:"markdown",value:t,onChange:s=>n(s??""),theme:"vs-dark",options:{minimap:{enabled:!1},fontSize:13,fontFamily:"JetBrains Mono, ui-monospace, Menlo, monospace",lineNumbers:"on",renderLineHighlight:"line",scrollBeyondLastLine:!1,tabSize:2,wordWrap:"on",readOnly:i,automaticLayout:!0}})}const f=/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/,u="disable-model-invocation",g=/^\s*disable-model-invocation\s*:/;function j(t){const n=t.match(f);if(!n)return!1;for(const e of n[1].split(/\r?\n/)){if(!g.test(e))continue;const i=e.slice(e.indexOf(":")+1).trim().toLowerCase();return i==="true"||i==="yes"}return!1}function S(t){const n=t.trim();return n.length>=2&&(n[0]==='"'&&n[n.length-1]==='"'||n[0]==="'"&&n[n.length-1]==="'")?n.slice(1,-1):n}function h(t){return t.length-t.trimStart().length}function v(t){const n=t.match(f);if(!n)return{name:null,description:null,body:t};const e=n[1].split(/\r?\n/),i=n[2]??"";let s=null,r=null;for(let o=0;o<e.length;o++){const m=e[o],d=m.match(/^(\s*)(name|description)\s*:(.*)$/);if(!d)continue;const[,,y,$]=d,l=$.trim(),b=h(m);let c;if(l===""||l===">-"||l===">"||l==="|-"||l==="|"){const p=[];let a=o+1;for(;a<e.length&&e[a].trim()!==""&&h(e[a])>b;)p.push(e[a].trim()),a++;c=p.join(" ")}else c=S(l);y==="name"?s=c:r=c}return{name:s,description:r,body:i}}function w(t,n){const e=t.match(f);if(!e){if(!n)return t;const o=t.startsWith(`
1
+ import{al as k,F as E}from"./index-Dx8owDeQ.js";function M({value:t,onChange:n,path:e,readOnly:i}){return k.jsx(E,{path:`file://${e}`,defaultLanguage:"markdown",value:t,onChange:s=>n(s??""),theme:"vs-dark",options:{minimap:{enabled:!1},fontSize:13,fontFamily:"JetBrains Mono, ui-monospace, Menlo, monospace",lineNumbers:"on",renderLineHighlight:"line",scrollBeyondLastLine:!1,tabSize:2,wordWrap:"on",readOnly:i,automaticLayout:!0}})}const f=/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/,u="disable-model-invocation",g=/^\s*disable-model-invocation\s*:/;function j(t){const n=t.match(f);if(!n)return!1;for(const e of n[1].split(/\r?\n/)){if(!g.test(e))continue;const i=e.slice(e.indexOf(":")+1).trim().toLowerCase();return i==="true"||i==="yes"}return!1}function S(t){const n=t.trim();return n.length>=2&&(n[0]==='"'&&n[n.length-1]==='"'||n[0]==="'"&&n[n.length-1]==="'")?n.slice(1,-1):n}function h(t){return t.length-t.trimStart().length}function v(t){const n=t.match(f);if(!n)return{name:null,description:null,body:t};const e=n[1].split(/\r?\n/),i=n[2]??"";let s=null,r=null;for(let o=0;o<e.length;o++){const m=e[o],d=m.match(/^(\s*)(name|description)\s*:(.*)$/);if(!d)continue;const[,,y,$]=d,l=$.trim(),b=h(m);let c;if(l===""||l===">-"||l===">"||l==="|-"||l==="|"){const p=[];let a=o+1;for(;a<e.length&&e[a].trim()!==""&&h(e[a])>b;)p.push(e[a].trim()),a++;c=p.join(" ")}else c=S(l);y==="name"?s=c:r=c}return{name:s,description:r,body:i}}function w(t,n){const e=t.match(f);if(!e){if(!n)return t;const o=t.startsWith(`
2
2
  `)?"":`
3
3
  `;return`---
4
4
  ${u}: true
@@ -1 +1 @@
1
- import{W as e,b4 as a,a$ as n}from"./index-S-F5jp_h.js";const c=new Set(e.map(t=>t.tag));function l(t){return t.filter(o=>c.has(o))}function s(t,o={}){return{name:t.name,originalName:t.name,description:t.description??"",tools:t.tools,model:t.model??"inherit",effort:t.effort??void 0,color:t.color??"",tags:l(t.tags),projects:t.projects,action:t.action??void 0,actionLabel:t.actionLabel??void 0,title:t.title??void 0,body:t.body,...o}}function d(t,o){const i=a();n([i?`${i}/.claude/agents`:null,t?`${t}/.claude/agents`:null],o)}export{s as a,l as t,d as u};
1
+ import{V as e,b0 as a,aX as n}from"./index-Dx8owDeQ.js";const c=new Set(e.map(t=>t.tag));function l(t){return t.filter(o=>c.has(o))}function s(t,o={}){return{name:t.name,originalName:t.name,description:t.description??"",tools:t.tools,model:t.model??"inherit",effort:t.effort??void 0,color:t.color??"",tags:l(t.tags),projects:t.projects,action:t.action??void 0,actionLabel:t.actionLabel??void 0,title:t.title??void 0,body:t.body,...o}}function d(t,o){const i=a();n([i?`${i}/.claude/agents`:null,t?`${t}/.claude/agents`:null],o)}export{s as a,l as t,d 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-S-F5jp_h.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-Dx8owDeQ.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/monaco-editor-BsIlgG7c.js">
12
12
  <link rel="stylesheet" crossorigin href="./assets/monaco-editor-Cu5D__1H.css">
13
- <link rel="stylesheet" crossorigin href="./assets/index-Dd7UQabV.css">
13
+ <link rel="stylesheet" crossorigin href="./assets/index-BnKaYERU.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.122.1",
3
+ "version": "0.123.1",
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",
@@ -125,7 +125,7 @@
125
125
  "ip-address": "^10.7.1",
126
126
  "qs": "^6.16.0",
127
127
  "tar": "^7.5.21",
128
- "undici": "^7.29.1"
128
+ "undici": "^7.30.1"
129
129
  },
130
130
  "os": [
131
131
  "darwin",
@@ -134,7 +134,7 @@
134
134
  ],
135
135
  "dependencies": {
136
136
  "chokidar": "^4.0.1",
137
- "electron": "42.11.10",
137
+ "electron": "42.11.18",
138
138
  "electron-updater": "^6.8.9",
139
139
  "node-pty": "^1.2.0-beta.12",
140
140
  "ws": "^8.18.0",
@@ -31,7 +31,7 @@ new work. **report** = your final message to the human.
31
31
  4. No match: stop and say so. Tell the human to open an Epic with the New Epic card and run
32
32
  /develop there. Never mint an Epic or write a PRD without one. Why: only a human creates
33
33
  an Epic.
34
- 2. **Tag-aware default.** On a `feature` or `bug` Epic, PRDs are the expected path once scope
34
+ 2. **Tag-aware default.** On a `feature`, `bug` or `art` Epic, PRDs are the expected path once scope
35
35
  is clear; start unasked. On a `discussion` Epic it stays available, but wait until the
36
36
  human settles that code is wanted.
37
37
  3. **Never hand-implement in this session**, even when the plan is agreed. Why: this session
@@ -69,7 +69,7 @@ failure modes have different fixes:
69
69
  error is not this case: fix the input and retry. **STOP. Do not write any PRD file.** Tell the
70
70
  human to start the app, then retry the same call. Why: a PRD file the API did not write is
71
71
  quarantined and never runs.
72
- - **(c) Create errors with "did not answer" or "Connection closed".** It may still have been written; retry the same call with the same slug (it returns the existing PRD with `alreadyExisted: true`) and never re-issue it under a new slug.
72
+ - **(c) Create errors with "did not answer" or "Connection closed".** It may still have been written; retry the same call with the same slug (it returns the existing PRD with `alreadyExisted: true`, or per entry `applied`) and never re-issue it under a new slug. `scheduler_get_prd` accepts the bare slug.
73
73
  - **(b) Tool ABSENT from your tool list.** The `session-manager-scheduler` MCP server is not
74
74
  registered: a **misconfiguration**, not an offline app.
75
75
  **STOP. Do not write any PRD file.** Tell the human. The app registers the server at user
@@ -89,10 +89,11 @@ failure modes have different fixes:
89
89
  (empty, max, concurrent, malformed, permission, failure); interaction effects (what depends
90
90
  on your change); integration (reuse the existing schema, store, API, primitives);
91
91
  validation (for UI: which screenshot, light and dark, proves which criterion).
92
- 4. **Run a completeness pass** if the ask exceeds one or two PRDs or spans subsystems: give one
93
- Explore or general-purpose sub-agent the ask verbatim, your draft list and the lenses; ask
94
- what is missing. Fold in real gaps, drop vague ones. Repeat at most once. It reviews; you
95
- author.
92
+ 4. **Run a completeness pass** if the ask exceeds one or two PRDs or spans subsystems: start it as a
93
+ background sub-agent as soon as the draft list exists (Explore or general-purpose; the ask
94
+ verbatim, your draft list, the lenses; ask what is missing) and keep exploring while it runs.
95
+ The planner turn is hard-killed at 30 minutes with a warning at 24; late gaps become sibling
96
+ PRDs. Fold in real gaps, drop vague ones. Repeat at most once. It reviews; you author.
96
97
  5. **Tests and security go inside the feature's own PRD** as criteria (security when it
97
98
  touches input, auth or data), never as follow-ups. Why: TDD needs the test with the code;
98
99
  security is decided while writing it. Also check quality (performance, error handling).
@@ -103,7 +104,8 @@ failure modes have different fixes:
103
104
  pass. Each goal's first sentence names its type, in chain order: `primitive` (new helper
104
105
  and its test), `wire` (adopt it at named call sites), `behavior` (one function's logic and
105
106
  its test), `migration` (mechanical change), `doc`, `validate`.
106
- 7. **Keep each PRD small**: at most 3 edited files, 1 new file, 6 criteria. The validate PRD is
107
+ 7. **Keep each PRD small**: at most 3 edited files, 1 new file, 6 criteria. Test files edited or added in lockstep with
108
+ the PRD's own code do not count toward the 3-edited / 1-new file limit. The validate PRD is
107
109
  exempt: it has one criterion per PRD, plus the record. A new helper and its first caller are
108
110
  two PRDs. Most PRDs take 5–10 executor minutes; split anything expected
109
111
  to take over 15. Do not write an estimate. Why: the kill budget floors at 45 minutes, so a
@@ -116,15 +118,16 @@ failure modes have different fixes:
116
118
  the test file and tests. Commands go in `gate`. No open-ended "grep X and update" lines.
117
119
  10. **Show the plan once**: the plan goal line, the stage view (for example `Stage 1: A ∥ B →
118
120
  Stage 2: C → validate`), and a table (#, PRD, files, gate, dependsOn) — not PRD
119
- drafts. Queue at once when the Epic is `feature` or `bug` and scope is clear; otherwise ask
121
+ drafts. Queue at once when the Epic is `feature`, `bug` or `art` and scope is clear; otherwise ask
120
122
  one approval question, once.
121
- 11. Before the first `scheduler_create_prd` call, run `git -C "$SM_PROJECT_ROOT" rev-parse HEAD`
122
- and keep the SHA. **Walk the pre-queue checklist** (last section of PRD_AUTHORING.md), then
123
- queue each PRD with `scheduler_create_prd`, the validate PRD last.
123
+ 11. Before queueing, run `git -C "$SM_PROJECT_ROOT" rev-parse HEAD` and keep the SHA.
124
+ **Walk the pre-queue checklist** (last section of PRD_AUTHORING.md), then queue the whole
125
+ plan, validate PRD included, in one `scheduler_create_prds` call; `dependsOn` may name other
126
+ entries of that call. Use one `scheduler_create_prd` per PRD only if the batch tool is absent.
124
127
  12. **Read the warnings each call returns before the next call.** Warnings do not block. If a
125
- gate or files warning shows a real mistake, archive that PRD with `scheduler_archive_prd` and
126
- queue it again under a new slug, before you queue the PRDs that depend on it. Why: a new
127
- call checks the gate and files again; `scheduler_update_prd` does not check them. Never
128
+ gate or files warning shows a real mistake, fix it with `scheduler_update_prd`: its `gate`,
129
+ `files` and `acceptanceCriteria` fields are validated and re-rendered. Why: a full body
130
+ replacement is for prose only and is not re-checked. Never
128
131
  change a PRD only to silence a warning. The validate PRD's none-gate warning and its size
129
132
  warning (8 or more criteria) are expected. Then post one short message: each PRD's number and slug, the validate slug, the
130
133
  warnings you kept, and that no per-PRD check will come from this session.
@@ -136,7 +139,7 @@ failure modes have different fixes:
136
139
  | --- | --- |
137
140
  | `title`, `goal`, `acceptanceCriteria`, `implementationNotes`, `outOfScope` | Steps 6–9. Goal: 2–4 sentences. |
138
141
  | `gate`, `files` | **REQUIRED.** Rules below. The tool refuses a call without them. |
139
- | `estimateMinutes` | Omit. Pass one only for a run that legitimately needs more than the 45-minute kill-budget floor. |
142
+ | `estimateMinutes` | Omit (a persona's `defaultEstimateMinutes` applies). Pass one only for a run needing more than the 45-minute floor. |
140
143
  | `sourcePromptId` | Always `<epic-id>`. Never rely on the server's fallback. |
141
144
  | `cwd` | The Epic's project root, as an absolute path or `~/…`. The API always uses the Epic's project, whatever you pass. To work in another project, open an Epic there. |
142
145
  | `dependsOn` | Slugs that must finish first. The only ordering tool. |
@@ -174,7 +177,9 @@ the API rejects that.
174
177
  is red, scope the gate to the PRD's own test files, never a suite that is already red, and
175
178
  say in the PRD notes which suite was red and why it was scoped out. Why: a gate that is red
176
179
  on the base parks the job as `sentinel_fail` whatever the executor does.
177
- 11. Never put a step only a human or the passage of time can complete (host cron install,
180
+ 11. A gate command must not create or modify tracked files; write outputs to the per-gate TMPDIR
181
+ or a gitignored path. Why: a dirtied tree fails the clean-tree check.
182
+ 12. Never put a step only a human or the passage of time can complete (host cron install,
178
183
  waiting for a live post, a live payment, an account dashboard action) in a gate or
179
184
  criterion. The executor ends with `SCHEDULER_VERDICT: NEEDS_HUMAN <question>` for those, and
180
185
  the PRD notes say so. Why: no executor can pass such a gate, so the job parks as
@@ -187,8 +192,8 @@ the API rejects that.
187
192
  1. 1–50 repo-relative paths. A folder ends with `/`.
188
193
  2. No absolute paths, no `~` at the start, no `..` or `.` segments, no `*` or `?`, no `\` or
189
194
  backticks, no leading `-`. Use `/` between folders.
190
- 3. PRDs that can run at the same time must not share a file. If two PRDs touch the same file,
191
- chain them with `dependsOn`.
195
+ 3. The scheduler never runs two PRDs with overlapping files at once (held as `files-overlap`);
196
+ `dependsOn` is for real output or order dependencies only.
192
197
 
193
198
  ### What a headless run cannot do
194
199
 
@@ -213,10 +218,9 @@ End every plan with exactly one validate PRD:
213
218
  2. `dependsOn`: every other slug in the plan. Past the cap of 100, only the sinks (PRDs nothing
214
219
  depends on). Why: `dependsOn` is walked transitively.
215
220
  3. Goal: the plan's slugs and titles.
216
- 4. Criteria: one per PRD, naming its file
217
- (`$SM_PROJECT_ROOT/session-manager-operations/scheduler/epics/<epic-id>/prds/<NN>-<slug>.md`
218
- while queued, `prds-archived/` (possibly a timestamped subfolder) beside it once done; find
219
- it by slug. Why: `session-manager-operations/scheduler/` is gitignored, so it is never in
221
+ 4. Criteria: one per PRD, citing it by slug
222
+ (under `$SM_PROJECT_ROOT/session-manager-operations/scheduler/epics/<epic-id>/`; find it by
223
+ slug under prds/ or prds-archived/, possibly a timestamped subfolder. Why: `session-manager-operations/scheduler/` is gitignored, so it is never in
220
224
  the validator's job worktree. The last: write and commit
221
225
  `session-manager-operations/reviews/validation/<epic-id>/<validate-slug>.md`.
222
226
  5. Notes: "Work as the validator persona — the procedure is your system prompt." and a line
@@ -225,7 +229,7 @@ End every plan with exactly one validate PRD:
225
229
  itself, and a REFUTED PRD is still a successful validation.
226
230
 
227
231
  The validator prints `VALIDATION: <slug> VERIFIED` or `VALIDATION: <slug> REFUTED — <reason>`
228
- per PRD. Queue the validate PRD last: `dependsOn` accepts only slugs that already exist. The
232
+ per PRD. Queue the validate PRD in the same `scheduler_create_prds` call as the plan (its `dependsOn` may name that call's entries); with single creates it goes last: in a single `scheduler_create_prd`, `dependsOn` accepts only slugs that already exist. The
229
233
  scheduler sends no per-PRD validation prompt. When the Epic has no pending or running PRD for
230
234
  `SM_EPIC_SETTLE_SECONDS` (default 120), it queues one `validate-sweep-*` validator for completed
231
235
  PRDs no validator checked, then sends one PLAN SETTLED digest. Several validators in one Epic
@@ -260,8 +264,8 @@ Rules:
260
264
  reports it, and the fix wave covers it.
261
265
  2. Its spec is wrong: fix it with `scheduler_update_prd`, then call `scheduler_reset_job`
262
266
  (add `force: true` if it is skipped). Always edit first. Why: a reset job can start on
263
- the next tick. The tool does not check the new body, so copy the `# Gate` and
264
- `# Files` sections unchanged unless they are what is wrong.
267
+ the next tick. Fix `gate`, `files` and `acceptanceCriteria` through their validated,
268
+ re-rendered fields; use a full body replacement only for prose.
265
269
  3. Its spec is right: call `scheduler_reset_job` (add `force: true` if it is skipped).
266
270
  4. Another PRD or a human already did the work: confirm it in the tree, then call
267
271
  `scheduler_archive_prd`. Archiving marks it completed and frees the PRDs that depend on
@@ -158,6 +158,56 @@ async function fetchReadiness() {
158
158
  }
159
159
  }
160
160
 
161
+ const PRD_BATCH_MAX = 40; // mirrors prdCreate.cjs / ipcSchemas.cjs (the server enforces it)
162
+
163
+ const CREATE_PRD_ITEM_SCHEMA = {
164
+ type: 'object',
165
+ properties: {
166
+ title: { type: 'string', description: 'One-line human-readable title' },
167
+ cwd: { type: 'string', description: 'Absolute path to the target project (where claude -p will run). Optional inside an Epic session — the server resolves the real project from the calling session (originClaudeSessionId/sourcePromptId) when omitted.' },
168
+ estimateMinutes: { type: 'number', description: 'Optional; normally omit. An omitted estimate falls back to the executing persona\'s defaultEstimateMinutes, then to the 45-minute kill-budget floor. Pass one only for work that legitimately needs longer (budget = 3x the estimate, capped at 180 minutes).' },
169
+ goal: { type: 'string', description: '2-4 sentences: what the executor will build and why' },
170
+ acceptanceCriteria: {
171
+ type: 'array', items: { type: 'string' },
172
+ description: 'Each entry is one verifiable checklist line',
173
+ },
174
+ implementationNotes: { type: 'string', description: 'File paths, patterns, and constraints the executor needs' },
175
+ gate: {
176
+ type: 'array', items: { type: 'string' },
177
+ description: 'REQUIRED. Commands that prove this PRD is done. The scheduler re-runs them without a shell. One command per entry, run in order; each must exit 0. Start each && step with `timeout <seconds>`. Join steps with &&. Outside single quotes, no | < > ; & ` $ \\ * ? [ ] ( ) { } or !. Use ["none"], exactly, only for docs or config with no runnable check.',
178
+ },
179
+ files: {
180
+ type: 'array', items: { type: 'string' },
181
+ description: 'REQUIRED. Repo-relative files or folders (end a folder with /) this PRD may change. No absolute paths, .. or . segments, globs, backslashes or backticks. The scheduler never runs two PRDs with overlapping files at once (dispatch holds the later one), so do not chain PRDs with dependsOn just to avoid a file clash; dependsOn is for real output or order dependencies.',
182
+ },
183
+ outOfScope: { type: 'array', items: { type: 'string' }, description: 'Optional: what NOT to build' },
184
+ slug: { type: 'string', description: 'Optional kebab-case slug; derived from title if omitted' },
185
+ parallelGroup: { type: 'number', description: 'DEPRECATED and ignored (PRD 832): numbers are strictly unique per project; use dependsOn for ordering' },
186
+ dependsOn: { type: 'array', items: { type: 'string' }, description: 'Optional: PRD slugs that must complete before this one becomes eligible (replaces the retired shared-NN-parallel convention)' },
187
+ disposition: {
188
+ type: 'string',
189
+ enum: ['append', 'new-head'],
190
+ description: 'Required when this PRD would land as a new root (no dependsOn given) in an Epic that already has incomplete PRDs: "append" depends this wave on the existing chain\'s current terminal PRD(s); "new-head" leaves it independent, eligible to run in parallel. Omit when the Epic has no incomplete PRDs yet, or when dependsOn already places this PRD explicitly.',
191
+ },
192
+ sourcePromptId: { type: 'string', description: "Effectively required: an EXISTING, already-human-approved Epic's promptSessionId (the id shown in the Epics list / active-index.json sessions key) to join — NOT a PromptTicket.id (those are distinct fields). The server never mints a new Epic; if this is omitted and no Epic can be resolved (via the SM_CHAT_SESSION_ID fallback below), the write is refused with an error telling the caller to create/approve an Epic first." },
193
+ sourceTabId: { type: 'string', description: 'Optional: tab id (claudeSessionId) this PRD was queued from, so the scheduler can route a completion status prompt back to it' },
194
+ tag: {
195
+ type: 'string',
196
+ enum: PRD_WORK_TYPES,
197
+ description: 'Optional: the WORK TYPE of THIS PRD — independent of the parent Epic\'s own tag. An Epic is the plan; a PRD is one unit of work inside it, and a single plan may legitimately contain several different work types. Never derived or inherited from the Epic.',
198
+ },
199
+ agentType: {
200
+ type: 'string',
201
+ description: 'Optional: WHO executes this PRD — the persona name (a file at ~/.claude/agents/<name>.md, e.g. "dev-lead") the executor should adopt. Distinct from `tag` (what kind of work); this is who does it. Defaults to "dev-lead" when omitted. Rejected at write time if the name does not resolve to a readable persona file.',
202
+ },
203
+ quietMachine: {
204
+ type: 'boolean',
205
+ description: 'Optional: set true only when this PRD\'s acceptance criteria are wall-clock/timing measurements (frame time, performance fences) that CPU contention from concurrent jobs would invalidate. The scheduler dispatches it only once zero other jobs are running machine-wide, and holds every other job off the whole slot pool for its run — a whole-machine exclusive lease, not a per-project one. If the machine never goes quiet within the configured wait window (default 30 minutes), it dispatches anyway and is marked degraded. Opt-in only; omit for ordinary PRDs.',
206
+ },
207
+ },
208
+ required: ['title', 'goal', 'acceptanceCriteria', 'implementationNotes', 'gate', 'files'],
209
+ };
210
+
161
211
  const TOOLS = [
162
212
  {
163
213
  name: 'scheduler_reset_job',
@@ -195,52 +245,23 @@ const TOOLS = [
195
245
  {
196
246
  name: 'scheduler_create_prd',
197
247
  description: descriptionFor('scheduler_create_prd'),
248
+ inputSchema: CREATE_PRD_ITEM_SCHEMA,
249
+ },
250
+ {
251
+ name: 'scheduler_create_prds',
252
+ description: descriptionFor('scheduler_create_prds'),
198
253
  inputSchema: {
199
254
  type: 'object',
200
255
  properties: {
201
- title: { type: 'string', description: 'One-line human-readable title' },
202
- cwd: { type: 'string', description: 'Absolute path to the target project (where claude -p will run). Optional inside an Epic session — the server resolves the real project from the calling session (originClaudeSessionId/sourcePromptId) when omitted.' },
203
- estimateMinutes: { type: 'number', description: 'Optional; normally omit. A run without one gets the 45-minute kill-budget floor. Pass one only for work that legitimately needs longer (budget = 3x the estimate, capped at 180 minutes).' },
204
- goal: { type: 'string', description: '2-4 sentences: what the executor will build and why' },
205
- acceptanceCriteria: {
206
- type: 'array', items: { type: 'string' },
207
- description: 'Each entry is one verifiable checklist line',
208
- },
209
- implementationNotes: { type: 'string', description: 'File paths, patterns, and constraints the executor needs' },
210
- gate: {
211
- type: 'array', items: { type: 'string' },
212
- description: 'REQUIRED. Commands that prove this PRD is done. The scheduler re-runs them without a shell. One command per entry, run in order; each must exit 0. Start each && step with `timeout <seconds>`. Join steps with &&. Outside single quotes, no | < > ; & ` $ \\ * ? [ ] ( ) { } or !. Use ["none"], exactly, only for docs or config with no runnable check.',
213
- },
214
- files: {
215
- type: 'array', items: { type: 'string' },
216
- description: 'REQUIRED. Repo-relative files or folders (end a folder with /) this PRD may change. No absolute paths, .. or . segments, globs, backslashes or backticks. PRDs that run at the same time must not share files; chain them with dependsOn instead.',
217
- },
218
- outOfScope: { type: 'array', items: { type: 'string' }, description: 'Optional: what NOT to build' },
219
- slug: { type: 'string', description: 'Optional kebab-case slug; derived from title if omitted' },
220
- parallelGroup: { type: 'number', description: 'DEPRECATED and ignored (PRD 832): numbers are strictly unique per project; use dependsOn for ordering' },
221
- dependsOn: { type: 'array', items: { type: 'string' }, description: 'Optional: PRD slugs that must complete before this one becomes eligible (replaces the retired shared-NN-parallel convention)' },
222
- disposition: {
223
- type: 'string',
224
- enum: ['append', 'new-head'],
225
- description: 'Required when this PRD would land as a new root (no dependsOn given) in an Epic that already has incomplete PRDs: "append" depends this wave on the existing chain\'s current terminal PRD(s); "new-head" leaves it independent, eligible to run in parallel. Omit when the Epic has no incomplete PRDs yet, or when dependsOn already places this PRD explicitly.',
226
- },
227
- sourcePromptId: { type: 'string', description: "Effectively required: an EXISTING, already-human-approved Epic's promptSessionId (the id shown in the Epics list / active-index.json sessions key) to join — NOT a PromptTicket.id (those are distinct fields). The server never mints a new Epic; if this is omitted and no Epic can be resolved (via the SM_CHAT_SESSION_ID fallback below), the write is refused with an error telling the caller to create/approve an Epic first." },
228
- sourceTabId: { type: 'string', description: 'Optional: tab id (claudeSessionId) this PRD was queued from, so the scheduler can route a completion status prompt back to it' },
229
- tag: {
230
- type: 'string',
231
- enum: PRD_WORK_TYPES,
232
- description: 'Optional: the WORK TYPE of THIS PRD — independent of the parent Epic\'s own tag. An Epic is the plan; a PRD is one unit of work inside it, and a single plan may legitimately contain several different work types. Never derived or inherited from the Epic.',
233
- },
234
- agentType: {
235
- type: 'string',
236
- description: 'Optional: WHO executes this PRD — the persona name (a file at ~/.claude/agents/<name>.md, e.g. "dev-lead") the executor should adopt. Distinct from `tag` (what kind of work); this is who does it. Defaults to "dev-lead" when omitted. Rejected at write time if the name does not resolve to a readable persona file.',
237
- },
238
- quietMachine: {
239
- type: 'boolean',
240
- description: 'Optional: set true only when this PRD\'s acceptance criteria are wall-clock/timing measurements (frame time, performance fences) that CPU contention from concurrent jobs would invalidate. The scheduler dispatches it only once zero other jobs are running machine-wide, and holds every other job off the whole slot pool for its run — a whole-machine exclusive lease, not a per-project one. If the machine never goes quiet within the configured wait window (default 30 minutes), it dispatches anyway and is marked degraded. Opt-in only; omit for ordinary PRDs.',
256
+ prds: {
257
+ type: 'array',
258
+ minItems: 1,
259
+ maxItems: PRD_BATCH_MAX,
260
+ items: CREATE_PRD_ITEM_SCHEMA,
261
+ description: `1 to ${PRD_BATCH_MAX} PRDs, each exactly the scheduler_create_prd input. All-or-nothing: if any entry is invalid nothing is written. dependsOn may name other entries of this same call by bare slug.`,
241
262
  },
242
263
  },
243
- required: ['title', 'goal', 'acceptanceCriteria', 'implementationNotes', 'gate', 'files'],
264
+ required: ['prds'],
244
265
  },
245
266
  },
246
267
  {
@@ -295,7 +316,10 @@ const TOOLS = [
295
316
  disposition: { type: 'string', enum: ['append', 'new-head'], description: 'Optional: patches the recorded wave-authoring decision — see scheduler_create_prd' },
296
317
  },
297
318
  },
298
- body: { type: 'string', description: 'Optional: full replacement body (everything after the frontmatter)' },
319
+ body: { type: 'string', description: 'Optional: full replacement body (everything after the frontmatter). Cannot be combined with gate/files/acceptanceCriteria.' },
320
+ gate: { type: 'array', items: { type: 'string' }, description: 'Optional: replaces the # Gate section with these commands (validated and re-rendered like scheduler_create_prd). Not with body.' },
321
+ files: { type: 'array', items: { type: 'string' }, description: 'Optional: replaces the # Files section with these paths (validated and re-rendered). Not with body.' },
322
+ acceptanceCriteria: { type: 'array', items: { type: 'string' }, description: 'Optional: replaces the # Acceptance criteria checklist; one string per criterion. Not with body.' },
299
323
  },
300
324
  required: ['slug'],
301
325
  },
@@ -552,6 +576,36 @@ const TOOLS = [
552
576
  // sibling can exercise session_manager_help's argument handling directly —
553
577
  // mocking node:fs/promises + global.fetch to drive adminRequest — without
554
578
  // booting a stdio transport.
579
+ // Shared by scheduler_create_prd and scheduler_create_prds: the headless-job
580
+ // refusal (issue #11 C1) and the origin-forwarding fields. Returns an error
581
+ // result to bail with, or null.
582
+ function headlessQueueRefusal(toolName) {
583
+ // A headless scheduled executor performs its own acceptance criteria and
584
+ // never queues follow-on work. scheduler.cjs stamps SM_SCHEDULER_JOB_SLUG on
585
+ // every job spawn and SM_SCHEDULER_JOB_MAY_QUEUE=1 only for decomposition
586
+ // personas (architect).
587
+ if (process.env.SM_SCHEDULER_JOB_SLUG && process.env.SM_SCHEDULER_JOB_MAY_QUEUE !== '1') {
588
+ return errorResult(
589
+ `refused: ${toolName} was called from inside headless scheduled job '${process.env.SM_SCHEDULER_JOB_SLUG}'. `
590
+ + 'A PRD executor performs its own acceptance criteria directly and never queues follow-on work (self-queue failure class). '
591
+ + 'If more work is genuinely needed, say so in your final result text — the authoring Epic decides whether to queue it.',
592
+ );
593
+ }
594
+ return null;
595
+ }
596
+
597
+ // Adds originClaudeSessionId (SM_CHAT_SESSION_ID fallback when sourcePromptId
598
+ // is absent) and originProjectRoot (SM_PROJECT_ROOT hint) to a create payload.
599
+ function withCreateOrigin(payload) {
600
+ if (!payload.sourcePromptId && process.env.SM_CHAT_SESSION_ID) {
601
+ payload.originClaudeSessionId = process.env.SM_CHAT_SESSION_ID;
602
+ }
603
+ if (process.env.SM_PROJECT_ROOT) {
604
+ payload.originProjectRoot = process.env.SM_PROJECT_ROOT;
605
+ }
606
+ return payload;
607
+ }
608
+
555
609
  async function handleCallTool(request) {
556
610
  const { name, arguments: args } = request.params;
557
611
  try {
@@ -643,21 +697,8 @@ async function handleCallTool(request) {
643
697
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
644
698
  }
645
699
  if (name === 'scheduler_create_prd') {
646
- // Hard refusal at the authoring boundary (issue #11, list C1): a
647
- // headless scheduled executor performs its own acceptance criteria and
648
- // never queues follow-on work — PRD 460 called /develop from inside its
649
- // run, spawned a duplicate PRD, and exited 0 having done nothing. The
650
- // standards prose already says so; this makes the harness enforce it.
651
- // scheduler.cjs stamps SM_SCHEDULER_JOB_SLUG on every job spawn and
652
- // SM_SCHEDULER_JOB_MAY_QUEUE=1 only for personas whose whole job is
653
- // decomposition (architect), so a planning PRD still can.
654
- if (process.env.SM_SCHEDULER_JOB_SLUG && process.env.SM_SCHEDULER_JOB_MAY_QUEUE !== '1') {
655
- return errorResult(
656
- `refused: scheduler_create_prd was called from inside headless scheduled job '${process.env.SM_SCHEDULER_JOB_SLUG}'. `
657
- + 'A PRD executor performs its own acceptance criteria directly and never queues follow-on work (self-queue failure class). '
658
- + 'If more work is genuinely needed, say so in your final result text — the authoring Epic decides whether to queue it.',
659
- );
660
- }
700
+ const refusal = headlessQueueRefusal('scheduler_create_prd');
701
+ if (refusal) return refusal;
661
702
  // Required-fields refusal (gate-and-files-in-PRD-body unit): checked
662
703
  // right after the headless-refusal check above, before any payload
663
704
  // forwarding — 74% of parked jobs had no gate the scheduler could
@@ -670,23 +711,9 @@ async function handleCallTool(request) {
670
711
  + 'Add both and call again.',
671
712
  );
672
713
  }
673
- // If the caller (the model) didn't pass sourcePromptId, forward this
674
- // process's own SM_CHAT_SESSION_ID (set by chatRunner.cjs on the
675
- // parent claude -p process this MCP server inherited its env from) so
676
- // the admin route can auto-resolve it to the calling Epic and join
677
- // that Epic instead of silently minting an unrelated sibling one — see
678
- // prdCreate.cjs's resolveSourcePromptIdFromClaudeSession.
679
- const payload = { ...args };
680
- if (!payload.sourcePromptId && process.env.SM_CHAT_SESSION_ID) {
681
- payload.originClaudeSessionId = process.env.SM_CHAT_SESSION_ID;
682
- }
683
- // SM_PROJECT_ROOT (chatRunner.cjs/pty.cjs/scheduler.cjs's job spawn) —
684
- // a trusted hint for the real project cwd, forwarded so the admin route
685
- // can resolve it even when the caller omitted cwd or passed a worktree
686
- // pwd. See projectRootResolve.cjs's resolveProjectContext.
687
- if (process.env.SM_PROJECT_ROOT) {
688
- payload.originProjectRoot = process.env.SM_PROJECT_ROOT;
689
- }
714
+ // Forwards SM_CHAT_SESSION_ID / SM_PROJECT_ROOT so the admin route resolves the
715
+ // calling Epic and real project (prdCreate.cjs, projectRootResolve.cjs).
716
+ const payload = withCreateOrigin({ ...args });
690
717
  const result = await adminRequest('POST', '/admin/scheduler/create-prd', payload);
691
718
  // Never say "queued" — this tool only writes the PRD file; the queue
692
719
  // row is derived by the scheduler's next reconcile pass, not by this
@@ -712,6 +739,29 @@ async function handleCallTool(request) {
712
739
  }
713
740
  return { content: [{ type: 'text', text }] };
714
741
  }
742
+ if (name === 'scheduler_create_prds') {
743
+ const refusal = headlessQueueRefusal('scheduler_create_prds');
744
+ if (refusal) return refusal;
745
+ const prds = Array.isArray(args?.prds) ? args.prds : null;
746
+ if (!prds || prds.length === 0 || prds.length > PRD_BATCH_MAX) {
747
+ return errorResult(`scheduler_create_prds needs "prds": an array of 1 to ${PRD_BATCH_MAX} scheduler_create_prd inputs.`);
748
+ }
749
+ const payload = withCreateOrigin({ ...args });
750
+ const result = await adminRequest('POST', '/admin/scheduler/create-prds', payload);
751
+ if (result?.ok === false) {
752
+ const errs = Array.isArray(result.errors) ? result.errors : [{ index: -1, slug: null, error: result.error ?? 'batch refused' }];
753
+ const lines = errs.map((e) => `- [${e.index}] ${e.slug ?? '(no slug)'}: ${e.error}`);
754
+ return errorResult(`scheduler_create_prds refused the batch — nothing was written (all-or-nothing). Fix every entry below and call again:\n${lines.join('\n')}`);
755
+ }
756
+ const written = Array.isArray(result?.prds) ? result.prds : [];
757
+ let text = `${written.length} PRD file(s) written (the queue rows are derived on the next scheduler reconcile pass, not by this call):\n`
758
+ + written.map((p) => `${p.slug} -> ${p.nn}`).join('\n');
759
+ const warned = written.filter((p) => Array.isArray(p.warnings) && p.warnings.length > 0);
760
+ if (warned.length > 0) {
761
+ text += `\nWarnings:\n${warned.map((p) => p.warnings.map((w) => `- ${p.slug}: ${w}`).join('\n')).join('\n')}`;
762
+ }
763
+ return { content: [{ type: 'text', text }] };
764
+ }
715
765
  if (name === 'feedback_list_projects') {
716
766
  const result = await adminRequest('GET', '/admin/feedback/targets');
717
767
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
@@ -72,7 +72,7 @@ function decodeLine(s) {
72
72
  * unchanged (not Claude-Code-visible either) so a save from the Agent
73
73
  * Library editor never erases the stamp a hand-edit should keep intact.
74
74
  */
75
- function serializePersona({ name, description, tools, model, effort, color, tags, projects, action, actionLabel, title, seedVersion, body }) {
75
+ function serializePersona({ name, description, tools, model, effort, color, tags, projects, action, actionLabel, title, seedVersion, executorOnly, body }) {
76
76
  const lines = ['---', `name: ${name}`];
77
77
  if (description) lines.push(`description: ${description}`);
78
78
  if (tools && tools.length) lines.push(`tools: ${tools.join(', ')}`);
@@ -85,6 +85,7 @@ function serializePersona({ name, description, tools, model, effort, color, tags
85
85
  if (actionLabel) lines.push(`actionLabel: ${encodeLine(actionLabel)}`);
86
86
  if (title) lines.push(`title: ${title}`);
87
87
  if (typeof seedVersion === 'number' && Number.isFinite(seedVersion)) lines.push(`seedVersion: ${seedVersion}`);
88
+ if (executorOnly === true) lines.push('executorOnly: true');
88
89
  lines.push('---', '');
89
90
  return lines.join('\n') + (body || '').trim() + '\n';
90
91
  }
@@ -149,6 +150,7 @@ async function savePersona({
149
150
  actionLabel,
150
151
  title,
151
152
  seedVersion,
153
+ executorOnly,
152
154
  body,
153
155
  projectName,
154
156
  globalDir = path.join(os.homedir(), '.claude', 'agents'),
@@ -173,20 +175,22 @@ async function savePersona({
173
175
  let resolvedProjects = projects;
174
176
  let resolvedAction = action;
175
177
  let resolvedActionLabel = actionLabel;
176
- if ([seedVersion, title, projects, action, actionLabel].some((v) => v === undefined)) {
178
+ let resolvedExecutorOnly = executorOnly;
179
+ if ([seedVersion, title, projects, action, actionLabel, executorOnly].some((v) => v === undefined)) {
177
180
  try {
178
181
  const { fm } = splitFrontmatter(fsSync.readFileSync(target, 'utf8'));
179
182
  if (resolvedSeedVersion === undefined) {
180
183
  const existingVersion = Number(fm.seedVersion);
181
184
  if (Number.isFinite(existingVersion)) resolvedSeedVersion = existingVersion;
182
185
  }
186
+ if (resolvedExecutorOnly === undefined && String(fm.executorOnly).trim() === 'true') resolvedExecutorOnly = true;
183
187
  if (resolvedTitle === undefined && fm.title) resolvedTitle = fm.title;
184
188
  if (resolvedProjects === undefined && fm.projects) resolvedProjects = parseTools(fm.projects);
185
189
  if (resolvedAction === undefined && fm.action) resolvedAction = decodeLine(fm.action);
186
190
  if (resolvedActionLabel === undefined && fm.actionLabel) resolvedActionLabel = decodeLine(fm.actionLabel);
187
191
  } catch { /* no existing file (or unreadable) — nothing to carry over */ }
188
192
  }
189
- const text = serializePersona({ name, description, tools, model, effort, color, tags, projects: resolvedProjects, action: resolvedAction, actionLabel: resolvedActionLabel, title: resolvedTitle, seedVersion: resolvedSeedVersion, body });
193
+ const text = serializePersona({ name, description, tools, model, effort, color, tags, projects: resolvedProjects, action: resolvedAction, actionLabel: resolvedActionLabel, title: resolvedTitle, seedVersion: resolvedSeedVersion, executorOnly: resolvedExecutorOnly, body });
190
194
  await writeTextAtomic(target, text);
191
195
  if (originalName && originalName !== name) {
192
196
  const oldReal = validatePath(path.join(globalDir, `${originalName}.md`));
@@ -332,6 +336,7 @@ async function listPersonas({
332
336
  actionLabel: decodeLine(fm.actionLabel) || null,
333
337
  title: fm.title || null,
334
338
  seedVersion: fm.seedVersion !== undefined && Number.isFinite(Number(fm.seedVersion)) ? Number(fm.seedVersion) : null,
339
+ executorOnly: String(fm.executorOnly).trim() === 'true',
335
340
  path: real,
336
341
  body: body.trim(),
337
342
  overridingProjects,
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.122.1",
3
- "gitSha": "7f6a60c94e69454a3b52ccae59549e1ee33df648",
4
- "gitShortSha": "7f6a60c9",
2
+ "version": "0.123.1",
3
+ "gitSha": "457770adbb40ab9fb1a79929c1c734198a683ba5",
4
+ "gitShortSha": "457770ad",
5
5
  "gitBranch": "HEAD",
6
- "builtAt": "2026-10-24T16:08:59.401Z",
6
+ "builtAt": "2026-10-25T22:46:53.152Z",
7
7
  "dirty": false
8
8
  }