claude-code-session-manager 0.122.0 → 0.123.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AgentLibrary-BmJ8hIPw.js → AgentLibrary-DxWN3FSN.js} +2 -2
- package/dist/assets/{Button-BS4kmz4p.js → Button-CISLFSKb.js} +1 -1
- package/dist/assets/DataModel-CYPbhGrx.js +1 -0
- package/dist/assets/{History-pg00b2Gd.js → History-Fhouektw.js} +2 -2
- package/dist/assets/{Hooks-BTEs0UZY.js → Hooks-COfIcyYV.js} +3 -3
- package/dist/assets/{Library-DuaJ-VSn.js → Library-Bt5WOyyu.js} +7 -7
- package/dist/assets/ListDetail-DH7uzYd5.js +1 -0
- package/dist/assets/{McpServers-Dy-wnG_X.js → McpServers-CwyVEzMN.js} +2 -2
- package/dist/assets/{Memory-Bs1T1Jo7.js → Memory-k9TWT9N4.js} +8 -8
- package/dist/assets/{Panel-8UhLXEOW.js → Panel-Di8JqCYN.js} +1 -1
- package/dist/assets/{Permissions-BfwzvmBS.js → Permissions-FFs6Rq1H.js} +3 -3
- package/dist/assets/{Plugins-McjyvvKh.js → Plugins-uK5vl_RE.js} +2 -2
- package/dist/assets/{ProvenanceBadge-D0Ov_6WJ.js → ProvenanceBadge-CGDyGDoN.js} +1 -1
- package/dist/assets/{SaveBar-Cd6hJR6K.js → SaveBar-Cnwc_XfF.js} +1 -1
- package/dist/assets/{Scheduler-BZXKqYJP.js → Scheduler-aACa6_mC.js} +7 -7
- package/dist/assets/{ScopeSwitcher-C24ysV1K.js → ScopeSwitcher-CoG3CSlC.js} +1 -1
- package/dist/assets/Settings-BG0RM6Di.js +4 -0
- package/dist/assets/{SkillReferenceGraph-BsWuiTbw.js → SkillReferenceGraph-COffGu_A.js} +1 -1
- package/dist/assets/Skills--qQpFjjf.js +2 -0
- package/dist/assets/SystemPrompt-Cr9RRSiZ.js +1 -0
- package/dist/assets/TagLibrary-D_ZbXwBv.js +1 -0
- package/dist/assets/{TiptapBody-C4kIvIMH.js → TiptapBody-CCHsa8Jv.js} +1 -1
- package/dist/assets/{Toggle-pP19zlyk.js → Toggle-BUOj4pws.js} +1 -1
- package/dist/assets/{ViewTabs-CpzN8e5I.js → ViewTabs-BdpsL7yr.js} +1 -1
- package/dist/assets/{historyTotals-Caq78bb5.js → historyTotals-Dx6ca881.js} +1 -1
- package/dist/assets/{index-Dd7UQabV.css → index-BnKaYERU.css} +1 -1
- package/dist/assets/{index-Bjtguv6I.js → index-CkNGj8FA.js} +151 -151
- package/dist/assets/{settingsSchema-CC2ySu3d.js → settingsSchema-BZhkpr9e.js} +1 -1
- package/dist/assets/{skillFrontmatter-hDhNo8jE.js → skillFrontmatter-N13KGqbs.js} +1 -1
- package/dist/assets/{useAgentsDirWatch-BuOtY25U.js → useAgentsDirWatch-De9UsbDt.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/develop/SKILL.md +29 -24
- package/scripts/scheduler-mcp-server.cjs +142 -78
- package/src/main/agentLibrary.cjs +8 -3
- package/src/main/build-info.json +4 -4
- package/src/main/crashDiagnostics.cjs +24 -0
- package/src/main/historyAggregator.cjs +41 -7
- package/src/main/index.cjs +13 -3
- package/src/main/ipcSchemas.cjs +30 -22
- package/src/main/lib/activeSessions.cjs +93 -32
- package/src/main/lib/epic/delegationReadiness.cjs +2 -0
- package/src/main/lib/epic/delegationSelfHeal.cjs +77 -0
- package/src/main/lib/epic/epicMint.cjs +1 -1
- package/src/main/lib/epic/epicStandingInstructions.cjs +73 -0
- package/src/main/lib/epic/guardSpawnSettings.cjs +76 -0
- package/src/main/lib/feedback-remote/crossProjectFeedback.cjs +9 -2
- package/src/main/lib/feedback-remote/remoteClientSessions.cjs +3 -11
- package/src/main/lib/feedback-remote/remoteE2eCrypto.cjs +1 -1
- package/src/main/lib/git/gitWorktree.cjs +9 -4
- package/src/main/lib/git/worktreeDeps.cjs +13 -2
- package/src/main/lib/git/worktreeSetup.cjs +212 -0
- package/src/main/lib/mcpToolCatalog.cjs +36 -9
- package/src/main/lib/persona-macro/agentPersonaSchema.cjs +4 -0
- package/src/main/lib/persona-macro/shippedPersonaSeeds.cjs +23 -0
- package/src/main/lib/prd/prdAgentType.cjs +28 -0
- package/src/main/lib/prd/prdBodyPatch.cjs +138 -0
- package/src/main/lib/prd/prdCreate.cjs +245 -27
- package/src/main/lib/prd/prdGateFiles.cjs +31 -0
- package/src/main/lib/scheduler/definitionOfDone.cjs +24 -3
- package/src/main/lib/scheduler/queueStore.cjs +5 -4
- package/src/main/lib/scheduler/scheduleJobSchema.cjs +1 -0
- package/src/main/lib/scheduler/schedulerBatch.cjs +43 -5
- package/src/main/lib/scheduler/schedulerPaths.cjs +2 -0
- package/src/main/lib/scheduler/watchdogHelpers.cjs +114 -22
- package/src/main/lib/telemetry/mainHeapSnapshot.cjs +69 -0
- package/src/main/lib/telemetry/telemetryBoot.cjs +25 -3
- package/src/main/lib/telemetry/telemetryClient.cjs +12 -0
- package/src/main/lib/telemetry/telemetryCounters.cjs +25 -2
- package/src/main/pty.cjs +0 -53
- package/src/main/runVerify.cjs +38 -11
- package/src/main/scheduler/prdParser.cjs +3 -0
- package/src/main/scheduler/prdPaths.cjs +48 -2
- package/src/main/scheduler.cjs +135 -31
- package/src/main/seedDevPlugin.cjs +1 -1
- package/src/main/seedSchedulerMcp.cjs +1 -1
- package/src/main/templates/PRD_AUTHORING.md +8 -4
- package/src/main/webRemote.cjs +38 -132
- package/src/preload/api.d.ts +4 -52
- package/src/preload/index.cjs +0 -24
- package/src/seed/agents/dev-lead.md +2 -1
- package/src/seed/agents/validator.md +2 -1
- package/dist/assets/DataModel-DZHBZrfL.js +0 -1
- package/dist/assets/ListDetail-Cud72s-O.js +0 -1
- package/dist/assets/RemoteMachines-gOlFTvWq.js +0 -3
- package/dist/assets/Settings-DWnN2qxn.js +0 -4
- package/dist/assets/Skills-6pHn6glu.js +0 -2
- package/dist/assets/SystemPrompt-DT0LBH-K.js +0 -1
- package/dist/assets/TagLibrary-DbJ0zbYL.js +0 -1
- package/dist/assets/useRemotePeers-CNQmRBpt.js +0 -1
- package/src/main/lib/feedback-remote/relayClientAuth.cjs +0 -133
- package/src/main/remoteClient.cjs +0 -334
- 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-Bjtguv6I.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-CkNGj8FA.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{
|
|
1
|
+
import{al as k,F as E}from"./index-CkNGj8FA.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{
|
|
1
|
+
import{V as e,b0 as a,aX as n}from"./index-CkNGj8FA.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-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-CkNGj8FA.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/monaco-editor-BsIlgG7c.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/monaco-editor-Cu5D__1H.css">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="./assets/index-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.
|
|
3
|
+
"version": "0.123.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",
|
|
@@ -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 `
|
|
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,6 +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`, or per entry `applied`) and never re-issue it under a new slug. `scheduler_get_prd` accepts the bare slug.
|
|
72
73
|
- **(b) Tool ABSENT from your tool list.** The `session-manager-scheduler` MCP server is not
|
|
73
74
|
registered: a **misconfiguration**, not an offline app.
|
|
74
75
|
**STOP. Do not write any PRD file.** Tell the human. The app registers the server at user
|
|
@@ -88,10 +89,11 @@ failure modes have different fixes:
|
|
|
88
89
|
(empty, max, concurrent, malformed, permission, failure); interaction effects (what depends
|
|
89
90
|
on your change); integration (reuse the existing schema, store, API, primitives);
|
|
90
91
|
validation (for UI: which screenshot, light and dark, proves which criterion).
|
|
91
|
-
4. **Run a completeness pass** if the ask exceeds one or two PRDs or spans subsystems:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
95
97
|
5. **Tests and security go inside the feature's own PRD** as criteria (security when it
|
|
96
98
|
touches input, auth or data), never as follow-ups. Why: TDD needs the test with the code;
|
|
97
99
|
security is decided while writing it. Also check quality (performance, error handling).
|
|
@@ -102,7 +104,8 @@ failure modes have different fixes:
|
|
|
102
104
|
pass. Each goal's first sentence names its type, in chain order: `primitive` (new helper
|
|
103
105
|
and its test), `wire` (adopt it at named call sites), `behavior` (one function's logic and
|
|
104
106
|
its test), `migration` (mechanical change), `doc`, `validate`.
|
|
105
|
-
7. **Keep each PRD small**: at most 3 edited files, 1 new file, 6 criteria.
|
|
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
|
|
106
109
|
exempt: it has one criterion per PRD, plus the record. A new helper and its first caller are
|
|
107
110
|
two PRDs. Most PRDs take 5–10 executor minutes; split anything expected
|
|
108
111
|
to take over 15. Do not write an estimate. Why: the kill budget floors at 45 minutes, so a
|
|
@@ -115,15 +118,16 @@ failure modes have different fixes:
|
|
|
115
118
|
the test file and tests. Commands go in `gate`. No open-ended "grep X and update" lines.
|
|
116
119
|
10. **Show the plan once**: the plan goal line, the stage view (for example `Stage 1: A ∥ B →
|
|
117
120
|
Stage 2: C → validate`), and a table (#, PRD, files, gate, dependsOn) — not PRD
|
|
118
|
-
drafts. Queue at once when the Epic is `feature` or `
|
|
121
|
+
drafts. Queue at once when the Epic is `feature`, `bug` or `art` and scope is clear; otherwise ask
|
|
119
122
|
one approval question, once.
|
|
120
|
-
11. Before
|
|
121
|
-
|
|
122
|
-
|
|
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.
|
|
123
127
|
12. **Read the warnings each call returns before the next call.** Warnings do not block. If a
|
|
124
|
-
gate or files warning shows a real mistake,
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
127
131
|
change a PRD only to silence a warning. The validate PRD's none-gate warning and its size
|
|
128
132
|
warning (8 or more criteria) are expected. Then post one short message: each PRD's number and slug, the validate slug, the
|
|
129
133
|
warnings you kept, and that no per-PRD check will come from this session.
|
|
@@ -135,7 +139,7 @@ failure modes have different fixes:
|
|
|
135
139
|
| --- | --- |
|
|
136
140
|
| `title`, `goal`, `acceptanceCriteria`, `implementationNotes`, `outOfScope` | Steps 6–9. Goal: 2–4 sentences. |
|
|
137
141
|
| `gate`, `files` | **REQUIRED.** Rules below. The tool refuses a call without them. |
|
|
138
|
-
| `estimateMinutes` | Omit. Pass one only for a run
|
|
142
|
+
| `estimateMinutes` | Omit (a persona's `defaultEstimateMinutes` applies). Pass one only for a run needing more than the 45-minute floor. |
|
|
139
143
|
| `sourcePromptId` | Always `<epic-id>`. Never rely on the server's fallback. |
|
|
140
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. |
|
|
141
145
|
| `dependsOn` | Slugs that must finish first. The only ordering tool. |
|
|
@@ -173,7 +177,9 @@ the API rejects that.
|
|
|
173
177
|
is red, scope the gate to the PRD's own test files, never a suite that is already red, and
|
|
174
178
|
say in the PRD notes which suite was red and why it was scoped out. Why: a gate that is red
|
|
175
179
|
on the base parks the job as `sentinel_fail` whatever the executor does.
|
|
176
|
-
11.
|
|
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,
|
|
177
183
|
waiting for a live post, a live payment, an account dashboard action) in a gate or
|
|
178
184
|
criterion. The executor ends with `SCHEDULER_VERDICT: NEEDS_HUMAN <question>` for those, and
|
|
179
185
|
the PRD notes say so. Why: no executor can pass such a gate, so the job parks as
|
|
@@ -186,8 +192,8 @@ the API rejects that.
|
|
|
186
192
|
1. 1–50 repo-relative paths. A folder ends with `/`.
|
|
187
193
|
2. No absolute paths, no `~` at the start, no `..` or `.` segments, no `*` or `?`, no `\` or
|
|
188
194
|
backticks, no leading `-`. Use `/` between folders.
|
|
189
|
-
3.
|
|
190
|
-
|
|
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.
|
|
191
197
|
|
|
192
198
|
### What a headless run cannot do
|
|
193
199
|
|
|
@@ -212,10 +218,9 @@ End every plan with exactly one validate PRD:
|
|
|
212
218
|
2. `dependsOn`: every other slug in the plan. Past the cap of 100, only the sinks (PRDs nothing
|
|
213
219
|
depends on). Why: `dependsOn` is walked transitively.
|
|
214
220
|
3. Goal: the plan's slugs and titles.
|
|
215
|
-
4. Criteria: one per PRD,
|
|
216
|
-
(`$SM_PROJECT_ROOT/session-manager-operations/scheduler/epics/<epic-id
|
|
217
|
-
|
|
218
|
-
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
|
|
219
224
|
the validator's job worktree. The last: write and commit
|
|
220
225
|
`session-manager-operations/reviews/validation/<epic-id>/<validate-slug>.md`.
|
|
221
226
|
5. Notes: "Work as the validator persona — the procedure is your system prompt." and a line
|
|
@@ -224,7 +229,7 @@ End every plan with exactly one validate PRD:
|
|
|
224
229
|
itself, and a REFUTED PRD is still a successful validation.
|
|
225
230
|
|
|
226
231
|
The validator prints `VALIDATION: <slug> VERIFIED` or `VALIDATION: <slug> REFUTED — <reason>`
|
|
227
|
-
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
|
|
228
233
|
scheduler sends no per-PRD validation prompt. When the Epic has no pending or running PRD for
|
|
229
234
|
`SM_EPIC_SETTLE_SECONDS` (default 120), it queues one `validate-sweep-*` validator for completed
|
|
230
235
|
PRDs no validator checked, then sends one PLAN SETTLED digest. Several validators in one Epic
|
|
@@ -259,8 +264,8 @@ Rules:
|
|
|
259
264
|
reports it, and the fix wave covers it.
|
|
260
265
|
2. Its spec is wrong: fix it with `scheduler_update_prd`, then call `scheduler_reset_job`
|
|
261
266
|
(add `force: true` if it is skipped). Always edit first. Why: a reset job can start on
|
|
262
|
-
the next tick.
|
|
263
|
-
|
|
267
|
+
the next tick. Fix `gate`, `files` and `acceptanceCriteria` through their validated,
|
|
268
|
+
re-rendered fields; use a full body replacement only for prose.
|
|
264
269
|
3. Its spec is right: call `scheduler_reset_job` (add `force: true` if it is skipped).
|
|
265
270
|
4. Another PRD or a human already did the work: confirm it in the tree, then call
|
|
266
271
|
`scheduler_archive_prd`. Archiving marks it completed and frees the PRDs that depend on
|
|
@@ -80,6 +80,16 @@ function errorResult(text) {
|
|
|
80
80
|
const NOT_RUNNING_ERROR =
|
|
81
81
|
`session-manager app is not running (admin API unreachable) — start it first${HELP_POINTER}`;
|
|
82
82
|
|
|
83
|
+
// The app accepted (or may have accepted) the request but did not answer: the
|
|
84
|
+
// write may already be applied, so the advice is "check, then same slug" —
|
|
85
|
+
// never a new slug.
|
|
86
|
+
function busyError(code) {
|
|
87
|
+
return `session-manager admin API did not answer (${code}) — the app is running but busy; the request may still be applied. `
|
|
88
|
+
+ 'Before retrying a create, call scheduler_list_prds for this Epic with no status filter '
|
|
89
|
+
+ '(a just-written PRD has status null), or retry the same call with the same slug; '
|
|
90
|
+
+ `never re-issue it under a new slug.${HELP_POINTER}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
83
93
|
// Same resolution fetchReadiness() below uses: a project-home-builder session
|
|
84
94
|
// running inside an Epic worktree still targets the real project cwd, not
|
|
85
95
|
// the worktree's own pwd.
|
|
@@ -114,11 +124,15 @@ async function adminRequest(method, urlPath, body) {
|
|
|
114
124
|
},
|
|
115
125
|
body: body ? JSON.stringify(body) : undefined,
|
|
116
126
|
});
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (err?.cause?.code === 'ECONNREFUSED') throw new Error(NOT_RUNNING_ERROR);
|
|
129
|
+
throw new Error(busyError(err?.cause?.code || err?.name || 'unknown'));
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
return await res.json();
|
|
117
133
|
} catch {
|
|
118
|
-
throw new Error(
|
|
134
|
+
throw new Error(`admin API answered HTTP ${res.status} with a non-JSON body`);
|
|
119
135
|
}
|
|
120
|
-
const json = await res.json();
|
|
121
|
-
return json;
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
// session_manager_help's live half. The catalog/recipes below are static and
|
|
@@ -144,6 +158,56 @@ async function fetchReadiness() {
|
|
|
144
158
|
}
|
|
145
159
|
}
|
|
146
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
|
+
|
|
147
211
|
const TOOLS = [
|
|
148
212
|
{
|
|
149
213
|
name: 'scheduler_reset_job',
|
|
@@ -181,52 +245,23 @@ const TOOLS = [
|
|
|
181
245
|
{
|
|
182
246
|
name: 'scheduler_create_prd',
|
|
183
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'),
|
|
184
253
|
inputSchema: {
|
|
185
254
|
type: 'object',
|
|
186
255
|
properties: {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
description: 'Each entry is one verifiable checklist line',
|
|
194
|
-
},
|
|
195
|
-
implementationNotes: { type: 'string', description: 'File paths, patterns, and constraints the executor needs' },
|
|
196
|
-
gate: {
|
|
197
|
-
type: 'array', items: { type: 'string' },
|
|
198
|
-
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.',
|
|
199
|
-
},
|
|
200
|
-
files: {
|
|
201
|
-
type: 'array', items: { type: 'string' },
|
|
202
|
-
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.',
|
|
203
|
-
},
|
|
204
|
-
outOfScope: { type: 'array', items: { type: 'string' }, description: 'Optional: what NOT to build' },
|
|
205
|
-
slug: { type: 'string', description: 'Optional kebab-case slug; derived from title if omitted' },
|
|
206
|
-
parallelGroup: { type: 'number', description: 'DEPRECATED and ignored (PRD 832): numbers are strictly unique per project; use dependsOn for ordering' },
|
|
207
|
-
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)' },
|
|
208
|
-
disposition: {
|
|
209
|
-
type: 'string',
|
|
210
|
-
enum: ['append', 'new-head'],
|
|
211
|
-
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.',
|
|
212
|
-
},
|
|
213
|
-
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." },
|
|
214
|
-
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' },
|
|
215
|
-
tag: {
|
|
216
|
-
type: 'string',
|
|
217
|
-
enum: PRD_WORK_TYPES,
|
|
218
|
-
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.',
|
|
219
|
-
},
|
|
220
|
-
agentType: {
|
|
221
|
-
type: 'string',
|
|
222
|
-
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.',
|
|
223
|
-
},
|
|
224
|
-
quietMachine: {
|
|
225
|
-
type: 'boolean',
|
|
226
|
-
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.`,
|
|
227
262
|
},
|
|
228
263
|
},
|
|
229
|
-
required: ['
|
|
264
|
+
required: ['prds'],
|
|
230
265
|
},
|
|
231
266
|
},
|
|
232
267
|
{
|
|
@@ -281,7 +316,10 @@ const TOOLS = [
|
|
|
281
316
|
disposition: { type: 'string', enum: ['append', 'new-head'], description: 'Optional: patches the recorded wave-authoring decision — see scheduler_create_prd' },
|
|
282
317
|
},
|
|
283
318
|
},
|
|
284
|
-
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.' },
|
|
285
323
|
},
|
|
286
324
|
required: ['slug'],
|
|
287
325
|
},
|
|
@@ -538,6 +576,36 @@ const TOOLS = [
|
|
|
538
576
|
// sibling can exercise session_manager_help's argument handling directly —
|
|
539
577
|
// mocking node:fs/promises + global.fetch to drive adminRequest — without
|
|
540
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
|
+
|
|
541
609
|
async function handleCallTool(request) {
|
|
542
610
|
const { name, arguments: args } = request.params;
|
|
543
611
|
try {
|
|
@@ -629,21 +697,8 @@ async function handleCallTool(request) {
|
|
|
629
697
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
630
698
|
}
|
|
631
699
|
if (name === 'scheduler_create_prd') {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
// never queues follow-on work — PRD 460 called /develop from inside its
|
|
635
|
-
// run, spawned a duplicate PRD, and exited 0 having done nothing. The
|
|
636
|
-
// standards prose already says so; this makes the harness enforce it.
|
|
637
|
-
// scheduler.cjs stamps SM_SCHEDULER_JOB_SLUG on every job spawn and
|
|
638
|
-
// SM_SCHEDULER_JOB_MAY_QUEUE=1 only for personas whose whole job is
|
|
639
|
-
// decomposition (architect), so a planning PRD still can.
|
|
640
|
-
if (process.env.SM_SCHEDULER_JOB_SLUG && process.env.SM_SCHEDULER_JOB_MAY_QUEUE !== '1') {
|
|
641
|
-
return errorResult(
|
|
642
|
-
`refused: scheduler_create_prd was called from inside headless scheduled job '${process.env.SM_SCHEDULER_JOB_SLUG}'. `
|
|
643
|
-
+ 'A PRD executor performs its own acceptance criteria directly and never queues follow-on work (self-queue failure class). '
|
|
644
|
-
+ 'If more work is genuinely needed, say so in your final result text — the authoring Epic decides whether to queue it.',
|
|
645
|
-
);
|
|
646
|
-
}
|
|
700
|
+
const refusal = headlessQueueRefusal('scheduler_create_prd');
|
|
701
|
+
if (refusal) return refusal;
|
|
647
702
|
// Required-fields refusal (gate-and-files-in-PRD-body unit): checked
|
|
648
703
|
// right after the headless-refusal check above, before any payload
|
|
649
704
|
// forwarding — 74% of parked jobs had no gate the scheduler could
|
|
@@ -656,23 +711,9 @@ async function handleCallTool(request) {
|
|
|
656
711
|
+ 'Add both and call again.',
|
|
657
712
|
);
|
|
658
713
|
}
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
// the admin route can auto-resolve it to the calling Epic and join
|
|
663
|
-
// that Epic instead of silently minting an unrelated sibling one — see
|
|
664
|
-
// prdCreate.cjs's resolveSourcePromptIdFromClaudeSession.
|
|
665
|
-
const payload = { ...args };
|
|
666
|
-
if (!payload.sourcePromptId && process.env.SM_CHAT_SESSION_ID) {
|
|
667
|
-
payload.originClaudeSessionId = process.env.SM_CHAT_SESSION_ID;
|
|
668
|
-
}
|
|
669
|
-
// SM_PROJECT_ROOT (chatRunner.cjs/pty.cjs/scheduler.cjs's job spawn) —
|
|
670
|
-
// a trusted hint for the real project cwd, forwarded so the admin route
|
|
671
|
-
// can resolve it even when the caller omitted cwd or passed a worktree
|
|
672
|
-
// pwd. See projectRootResolve.cjs's resolveProjectContext.
|
|
673
|
-
if (process.env.SM_PROJECT_ROOT) {
|
|
674
|
-
payload.originProjectRoot = process.env.SM_PROJECT_ROOT;
|
|
675
|
-
}
|
|
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 });
|
|
676
717
|
const result = await adminRequest('POST', '/admin/scheduler/create-prd', payload);
|
|
677
718
|
// Never say "queued" — this tool only writes the PRD file; the queue
|
|
678
719
|
// row is derived by the scheduler's next reconcile pass, not by this
|
|
@@ -698,6 +739,29 @@ async function handleCallTool(request) {
|
|
|
698
739
|
}
|
|
699
740
|
return { content: [{ type: 'text', text }] };
|
|
700
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
|
+
}
|
|
701
765
|
if (name === 'feedback_list_projects') {
|
|
702
766
|
const result = await adminRequest('GET', '/admin/feedback/targets');
|
|
703
767
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
@@ -870,4 +934,4 @@ if (require.main === module) {
|
|
|
870
934
|
});
|
|
871
935
|
}
|
|
872
936
|
|
|
873
|
-
module.exports = { TOOLS, handleCallTool, HELP_POINTER, NOT_RUNNING_ERROR };
|
|
937
|
+
module.exports = { TOOLS, handleCallTool, HELP_POINTER, NOT_RUNNING_ERROR, busyError };
|
|
@@ -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
|
-
|
|
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,
|
package/src/main/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitShortSha": "
|
|
2
|
+
"version": "0.123.0",
|
|
3
|
+
"gitSha": "b001e9be68651668b2b5c0b27dd7f75879375e10",
|
|
4
|
+
"gitShortSha": "b001e9be",
|
|
5
5
|
"gitBranch": "HEAD",
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-25T22:03:48.120Z",
|
|
7
7
|
"dirty": false
|
|
8
8
|
}
|