claude-code-session-manager 0.121.0 → 0.122.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.
- package/dist/assets/{AgentLibrary-FO2ei2wB.js → AgentLibrary-C2qUX9WU.js} +1 -1
- package/dist/assets/{Button-CeIOXUPU.js → Button-ssBzvFha.js} +1 -1
- package/dist/assets/{DataModel-D0p__Hil.js → DataModel-CENoTvc2.js} +1 -1
- package/dist/assets/History-BRdRp1J_.js +2 -0
- package/dist/assets/{Hooks-DkTnRHyT.js → Hooks--hiGPBQy.js} +1 -1
- package/dist/assets/{Library-l7KS6pkE.js → Library-0XYvkhh2.js} +1 -1
- package/dist/assets/{ListDetail-DDw8fuRT.js → ListDetail-CkKXAM59.js} +1 -1
- package/dist/assets/{McpServers-UUNIxk_3.js → McpServers-CCjHSwFm.js} +1 -1
- package/dist/assets/{Memory-CNYUoQ8C.js → Memory-C7RXoVO4.js} +1 -1
- package/dist/assets/{Panel-Bb54soc2.js → Panel-C92O_aAH.js} +1 -1
- package/dist/assets/{Permissions-D-dNdV3f.js → Permissions-DuTJ1FUb.js} +1 -1
- package/dist/assets/Plugins-DSXsP4hM.js +2 -0
- package/dist/assets/{ProvenanceBadge-DEAkDNjA.js → ProvenanceBadge-CWdDkU1m.js} +1 -1
- package/dist/assets/{RemoteMachines-xCEgvTGU.js → RemoteMachines-BFmbIGpW.js} +1 -1
- package/dist/assets/{SaveBar-BSEs7ecA.js → SaveBar-ob2dJJjH.js} +1 -1
- package/dist/assets/Scheduler-Du4HgPKY.js +16 -0
- package/dist/assets/{ScopeSwitcher-DcaFyAV-.js → ScopeSwitcher-Dv_6M_Ci.js} +1 -1
- package/dist/assets/{Settings-vcdWy_Jx.js → Settings-DMoI0cqB.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-CLrJWTPd.js → SkillReferenceGraph-Dx1Qs8Y0.js} +1 -1
- package/dist/assets/{Skills-CYM4rBEC.js → Skills-DYWQTQB1.js} +1 -1
- package/dist/assets/{SystemPrompt-BWNIu-iY.js → SystemPrompt-Df0IfOuI.js} +1 -1
- package/dist/assets/{TagLibrary-CYdwHK6m.js → TagLibrary-BuMsGx-2.js} +1 -1
- package/dist/assets/{TiptapBody-Kt90lHrD.js → TiptapBody-Cihe5er3.js} +1 -1
- package/dist/assets/{Toggle-D1OXWYOz.js → Toggle-CpYVC8bF.js} +1 -1
- package/dist/assets/{ViewTabs-BeQR3CKh.js → ViewTabs-C3FN6P_C.js} +1 -1
- package/dist/assets/{historyTotals-B0qafrRo.js → historyTotals-C4Rs37Cd.js} +1 -1
- package/dist/assets/{index-Bq9uH08R.js → index-S-F5jp_h.js} +396 -396
- package/dist/assets/{settingsSchema-0ZksSAPB.js → settingsSchema-BZcdzHTQ.js} +1 -1
- package/dist/assets/{skillFrontmatter-C0nh8yfa.js → skillFrontmatter-EfNERt4b.js} +1 -1
- package/dist/assets/{useAgentsDirWatch-DPkoBybS.js → useAgentsDirWatch-BfbrFvB3.js} +1 -1
- package/dist/assets/{useRemotePeers-BDJMUihy.js → useRemotePeers-CKVakQPc.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/develop/SKILL.md +8 -7
- package/plugins/session-manager-dev/skills/develop/standards.md +4 -10
- package/scripts/scheduler-mcp-server.cjs +20 -6
- package/src/main/build-info.json +4 -4
- package/src/main/chatRunner.cjs +13 -37
- package/src/main/docEdit.cjs +1 -0
- package/src/main/index.cjs +12 -13
- package/src/main/ipcSchemas.cjs +2 -2
- package/src/main/lib/classifyPromptTicket.cjs +1 -0
- package/src/main/lib/claude/effectiveModelInfo.cjs +9 -21
- package/src/main/lib/claude/personaToolScope.cjs +7 -1
- package/src/main/lib/claude/runClaudeP.cjs +1 -0
- package/src/main/lib/epic/delegationReadiness.cjs +75 -8
- package/src/main/lib/mcpToolCatalog.cjs +5 -8
- package/src/main/lib/persona-macro/shippedPersonaSeeds.cjs +11 -0
- package/src/main/lib/prd/prdCreate.cjs +48 -1
- package/src/main/lib/prd/prdSizing.cjs +1 -8
- package/src/main/lib/runSeeders.cjs +21 -0
- package/src/main/lib/scheduler/loopEfficiency.cjs +109 -0
- package/src/main/lib/scheduler/queueHistory.cjs +44 -1
- package/src/main/lib/scheduler/queueStore.cjs +4 -72
- package/src/main/lib/scheduler/rcaReport.cjs +1 -1
- package/src/main/lib/scheduler/refireCap.cjs +40 -0
- package/src/main/lib/scheduler/reviewNotice.cjs +1 -1
- package/src/main/lib/scheduler/schedulerConfig.cjs +1 -2
- package/src/main/lib/scheduler/schedulerPaths.cjs +0 -2
- package/src/main/lib/scheduler/sessionSlots.cjs +1 -1
- package/src/main/lib/telemetry/auditLog.cjs +65 -14
- package/src/main/loopEfficiencyHandlers.cjs +160 -0
- package/src/main/queueOps.cjs +4 -9
- package/src/main/runVerify.cjs +22 -454
- package/src/main/scheduler/reviewLadder.cjs +12 -2
- package/src/main/scheduler.cjs +116 -181
- package/src/main/seedDevPlugin.cjs +49 -22
- package/src/main/seedStatus.cjs +3 -2
- package/src/main/supervisor.cjs +1 -0
- package/src/main/templates/PRD_AUTHORING.md +16 -31
- package/src/preload/api.d.ts +26 -6
- package/src/preload/index.cjs +1 -0
- package/src/seed/agents/dev-lead.md +2 -9
- package/dist/assets/History-DVqQ5azN.js +0 -2
- package/dist/assets/Plugins-DhcIXRhQ.js +0 -2
- package/dist/assets/Scheduler-BIKJIU-C.js +0 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aF as b,ap as n,S as N,a_ as O}from"./index-Bq9uH08R.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{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,4 +1,4 @@
|
|
|
1
|
-
import{ap as k,F as E}from"./index-
|
|
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(`
|
|
2
2
|
`)?"":`
|
|
3
3
|
`;return`---
|
|
4
4
|
${u}: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{W as e,b4 as a,a$ as n}from"./index-
|
|
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 +1 @@
|
|
|
1
|
-
import{ap as t,A as f,bf as g,aF as d}from"./index-
|
|
1
|
+
import{ap as t,A as f,bf as g,aF as d}from"./index-S-F5jp_h.js";function j({children:s,right:e}){return t.jsxs("div",{className:"flex items-baseline justify-between mb-2.5",children:[t.jsx("span",{className:"text-[11.5px] font-bold uppercase tracking-[0.8px] text-fg-faint",children:s}),e]})}function y({kind:s="soft",icon:e,children:c,onClick:i,disabled:o,testId:n}){return t.jsxs("button",{type:"button","data-testid":n,onClick:i,disabled:o,className:`shrink-0 inline-flex items-center gap-2 rounded-lg px-3.5 py-2 text-[13px] font-semibold transition-colors disabled:opacity-50 ${s==="soft"?"bg-bg-hi border border-line text-fg hover:bg-bg":"text-fg-dim hover:text-fg"}`,children:[e&&t.jsx(f,{name:e,size:15}),c]})}function N({icon:s,title:e,subtitle:c,badge:i,action:o,testId:n}){return t.jsxs("div",{"data-testid":n,className:"flex items-center gap-3.5 bg-bg-hi border border-line rounded-xl px-[18px] py-3.5",children:[t.jsx("span",{className:"w-[34px] h-[34px] rounded-lg bg-bg border border-line grid place-items-center text-fg-dim shrink-0",children:t.jsx(f,{name:s,size:17})}),t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2.5 mb-0.5",children:[t.jsx("span",{className:"text-sm font-semibold text-fg truncate",children:e}),i]}),t.jsx("div",{className:"font-mono text-[11.5px] text-fg-faint truncate",children:c})]}),o]})}function v({onClick:s,disabled:e,testId:c,children:i="Revoke"}){return t.jsx("button",{"data-testid":c,onClick:s,disabled:e,className:"shrink-0 rounded-lg border border-line bg-bg text-accent px-3.5 py-1.5 text-[12.5px] font-semibold hover:bg-bg-hi disabled:opacity-50 transition-colors",children:i})}function k({code:s,onConfirm:e,testId:c,codeTestId:i,confirmTestId:o,peer:n="browser"}){return t.jsxs("div",{"data-testid":c,className:"flex items-start gap-3.5 px-5 py-4 rounded-2xl border border-accent/40 bg-accent/[0.06]",children:[t.jsx("span",{className:"w-[38px] h-[38px] rounded-[11px] shrink-0 grid place-items-center bg-bg-hi text-accent",children:t.jsx(f,{name:"shield",size:20})}),t.jsxs("div",{className:"flex-1 min-w-0 text-sm text-fg",children:[t.jsx("strong",{className:"font-semibold",children:"Verify E2E session."})," Compare this code with the ",n," — they must match.",t.jsx("div",{"data-testid":i,className:"my-2 text-3xl font-mono tracking-[0.3em] text-center text-accent select-all",children:s}),t.jsx("p",{className:"text-[12.5px] text-fg-faint mb-3",children:"If the codes match, confirm below. Mutating commands are blocked until confirmed."}),t.jsx("button",{"data-testid":o,onClick:e,className:"rounded-lg bg-accent text-white px-4 py-2 text-[13px] font-semibold hover:opacity-90 transition-opacity",children:"Codes match — confirm"})]})]})}function C({children:s,testId:e}){return t.jsx("div",{"data-testid":e,className:"bg-bg-hi border border-line rounded-xl p-5 text-center text-[13.5px] text-fg-faint italic font-serif",children:s})}const m=s=>(s==null?void 0:s.message)||String(s);function S(s){const e=g(r=>r.show),[c,i]=d.useState([]),o=d.useRef(s);o.current=s;const n=d.useCallback(async()=>{try{i(await window.api.remoteClient.list())}catch(r){e("error",m(r))}},[e]);d.useEffect(()=>{n();const r=window.api.remoteClient.onStatus(a=>{var x;(x=o.current)==null||x.call(o,a),i(l=>l.map(b=>b.peerId===a.peerId?{...b,status:a.status,reason:a.reason,sas:a.sas}:b))});return()=>{r()}},[n]);const p=d.useCallback(async r=>{try{const a=await window.api.remoteClient.connect(r);i(x=>x.map(l=>l.peerId===r?{...l,status:a.status,reason:a.reason,sas:a.sas}:l))}catch(a){e("error",m(a))}},[e]),u=d.useCallback(async r=>{try{const a=await window.api.remoteClient.confirmSas(r);a.ok||e("error",a.reason??"SAS confirmation failed"),await n()}catch(a){e("error",m(a))}},[e,n]),h=d.useCallback(async r=>{try{await window.api.remoteClient.remove(r),await n()}catch(a){e("error",m(a))}},[e,n]);return{peers:c,refresh:n,connect:p,confirmSas:u,revoke:h}}export{C as E,y as P,N as R,k as S,v as a,j as b,S as u};
|
package/dist/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" crossorigin src="./assets/index-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-S-F5jp_h.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/monaco-editor-BsIlgG7c.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/monaco-editor-Cu5D__1H.css">
|
|
13
13
|
<link rel="stylesheet" crossorigin href="./assets/index-Dd7UQabV.css">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.122.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",
|
|
@@ -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`) and never re-issue it under a new 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
|
|
@@ -104,9 +105,9 @@ failure modes have different fixes:
|
|
|
104
105
|
its test), `migration` (mechanical change), `doc`, `validate`.
|
|
105
106
|
7. **Keep each PRD small**: at most 3 edited files, 1 new file, 6 criteria. The validate PRD is
|
|
106
107
|
exempt: it has one criterion per PRD, plus the record. A new helper and its first caller are
|
|
107
|
-
two PRDs. Most PRDs take 5–10 executor minutes;
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
two PRDs. Most PRDs take 5–10 executor minutes; split anything expected
|
|
109
|
+
to take over 15. Do not write an estimate. Why: the kill budget floors at 45 minutes, so a
|
|
110
|
+
run without one is never starved.
|
|
110
111
|
8. **Make each PRD self-contained.** The executor sees only the PRD and the project. Notes are
|
|
111
112
|
a recipe: `Read first:` (at most 4 files, with line ranges), `Steps:` (numbered, each naming
|
|
112
113
|
file and function), `Do not touch:` (files a sibling owns). Quote signatures. Say when a PRD
|
|
@@ -114,7 +115,7 @@ failure modes have different fixes:
|
|
|
114
115
|
9. **Write plain, checkable criteria.** Each names a file, a symbol and the result; one names
|
|
115
116
|
the test file and tests. Commands go in `gate`. No open-ended "grep X and update" lines.
|
|
116
117
|
10. **Show the plan once**: the plan goal line, the stage view (for example `Stage 1: A ∥ B →
|
|
117
|
-
Stage 2: C → validate`), and a table (#, PRD, files, gate, dependsOn
|
|
118
|
+
Stage 2: C → validate`), and a table (#, PRD, files, gate, dependsOn) — not PRD
|
|
118
119
|
drafts. Queue at once when the Epic is `feature` or `bug` and scope is clear; otherwise ask
|
|
119
120
|
one approval question, once.
|
|
120
121
|
11. Before the first `scheduler_create_prd` call, run `git -C "$SM_PROJECT_ROOT" rev-parse HEAD`
|
|
@@ -135,7 +136,7 @@ failure modes have different fixes:
|
|
|
135
136
|
| --- | --- |
|
|
136
137
|
| `title`, `goal`, `acceptanceCriteria`, `implementationNotes`, `outOfScope` | Steps 6–9. Goal: 2–4 sentences. |
|
|
137
138
|
| `gate`, `files` | **REQUIRED.** Rules below. The tool refuses a call without them. |
|
|
138
|
-
| `estimateMinutes` |
|
|
139
|
+
| `estimateMinutes` | Omit. Pass one only for a run that legitimately needs more than the 45-minute kill-budget floor. |
|
|
139
140
|
| `sourcePromptId` | Always `<epic-id>`. Never rely on the server's fallback. |
|
|
140
141
|
| `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
142
|
| `dependsOn` | Slugs that must finish first. The only ordering tool. |
|
|
@@ -206,7 +207,7 @@ paths → verdict line.
|
|
|
206
207
|
|
|
207
208
|
End every plan with exactly one validate PRD:
|
|
208
209
|
|
|
209
|
-
1. `agentType: "validator"`, `tag: "build"`,
|
|
210
|
+
1. `agentType: "validator"`, `tag: "build"`, slug
|
|
210
211
|
`validate-<short-plan-name>`. Its title is the plan goal shown on the Scheduler card, so
|
|
211
212
|
write it as `Validate: <plain-language goal>`.
|
|
212
213
|
2. `dependsOn`: every other slug in the plan. Past the cap of 100, only the sinks (PRDs nothing
|
|
@@ -238,7 +239,7 @@ here. Never write the record yourself.
|
|
|
238
239
|
|
|
239
240
|
The scheduler heals most parks itself:
|
|
240
241
|
|
|
241
|
-
1.
|
|
242
|
+
1. Verdict-line parks (verdicts no_verdict_sentinel,
|
|
242
243
|
abandoned_background_task) complete when the gate re-run is green, the landed commit is on
|
|
243
244
|
HEAD and the tracked tree is clean.
|
|
244
245
|
2. Stray-checkout parks (the main checkout was on another branch) are re-landed onto the base
|
|
@@ -71,21 +71,15 @@ a higher-priority source exists:
|
|
|
71
71
|
|
|
72
72
|
## Execution discipline (headless runs)
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Long hangs, not bad code, cause most failed runs. The PRD body carries a one-line pointer to this file, never a copy. The dev-lead persona and the scheduler's finish protocol carry the per-run rules; this file gives the reasons behind them. Run order: orient → red → build → finish protocol (review → verify the `# Gate` commands → commit exact paths → verdict line).
|
|
75
75
|
|
|
76
76
|
- **Bound every command.** Wrap every test, build, dev-server, deploy, or poll command in a hard timeout, e.g. `timeout 300 <typecheck|unit>` or `curl --max-time 15`. Why: a bare `playwright test`, `vite`, `pnpm dev`, full e2e suite, or endpoint-polling publish hangs until the 4-hour watchdog kills it with SIGTERM.
|
|
77
|
-
- **Verify before done.** Run the acceptance test once before declaring success. Why:
|
|
77
|
+
- **Verify before done.** Run the acceptance test once before declaring success. Why: a run that ends on a failing test is not done — fix it or `exit 1` with the failure instead.
|
|
78
78
|
- **Fail loud, fail fast.** On any step failure, print one diagnostic line and `exit 1`. Do not swallow it with `|| true` or retry silently. Exception: a `rateLimited` exit-1 is the scheduler's own benign auto-pause — it resumes next window on its own, so do not engineer around it.
|
|
79
79
|
- **Stay in the AC.** Do not add work past the acceptance checklist — "while we're here" extras are how runs overrun. Keep the body clean UTF-8, with no NUL or control bytes.
|
|
80
80
|
- **You ARE the executor — never re-queue or self-schedule.** Do the acceptance criteria yourself. Why: `/develop` or any queue-authoring skill just writes a *new* PRD and returns — the run exits 0 having done nothing, with no commit and no verdict line (`needs_review` with `no_verdict_sentinel`). The same applies to calling `ScheduleWakeup`, or backgrounding a review agent (`/code-review`, `/security-review`) and ending your turn with something like "I'll wait for the review agents to complete": a headless run has no next turn, so nothing ever resumes it — no verdict line prints, and the job parks in `needs_review` even though the work already landed. Run a required second review pass synchronously, inline, before the finish protocol — call the reviewer and read its result in the same turn. If the work is too big for one run, do not queue more work; see the timeout rule below. (Incidents: PRD 460 invoked `/develop` and exited 0 with no work. PRD 479 landed its commit, then backgrounded `/code-review --fix` + `/security-review` and called `ScheduleWakeup` to "wait" — same failure, different entry point.)
|
|
81
81
|
- **Never `git stash`, `git reset`, `git checkout -- <path>`, or `git clean` against working-tree state you did not create.** There is no polite version of this. You are normally isolated in your own `git worktree` on branch `sm-job/<slug>` (`src/main/lib/git/gitWorktree.cjs`), and the scheduler salvages any uncommitted diff of yours on teardown — so you never need to touch shared state to protect your own work. Confirm isolation with `git rev-parse --git-common-dir` (a worktree's common-dir differs from its own `.git`) or by checking your cwd; `git diff`/`git status` are always safe. If you land in a SHARED tree with pre-existing dirty tracked files anyway, work around them — scope `git add`/commits to only the paths you touched — or end with `SCHEDULER_VERDICT: FAIL <reason>` (`SCHEDULER_VERDICT: BLOCKED_BY_FOREIGN_WIP` only when your gate failed on those files, as the finish protocol says); never discard, stash, or revert someone else's uncommitted changes to get a clean checkout. A shared tree may be feeding a running service that reads its config straight from the working tree, so reverting it is a production change, not a local inconvenience. The scheduler runs a shared-tree stash guard as a backstop (`scheduler.cjs`'s `checkSharedTreeGuard`/`evaluateSharedTreeGuard`), but that guard exists to catch a mistake, not to license one. (Incidents: PRD 477 stashed a sibling job's WIP to get its own checkout and never restored it. 2026-09-01: a headless executor in the trader repo ran a blanket `git stash` against an operator's uncommitted live trading-config edit and exited without restoring it — the third incident of this class.)
|
|
82
|
-
- **`gh pr edit --body` can fail on repos with legacy GitHub Projects (classic) boards.** It queries the deprecated `repository.pullRequest.projectCards` field and errors with a `GraphQL: Projects (classic) is being deprecated` message even though the edit itself would otherwise succeed — a known `gh` CLI quirk, not a defect in your work. Prefer `gh api -X PATCH repos/<owner>/<repo>/pulls/<n> -f body="$(cat body.md)"` instead; it skips that field. If `gh pr edit` fails this way anyway
|
|
83
|
-
-
|
|
84
|
-
- **Negative-assertion checks must exit 0 when clean.** A check for the *absence* of something (a `grep` that should find nothing, a `diff` expecting no change) must exit 0 on the clean case. Why: a bare `grep` exits 1 on no-match, so the success path surfaces as `is_error=true` and the verifier downgrades a perfect run. Invert it: `if <detector>; then echo "HALT: <what was found>"; exit 1; fi; echo clean` — never let the clean path carry a non-zero exit.
|
|
85
|
-
- **Recover or annotate every error — don't strand a Traceback in the transcript.** The verifier's `transcript_errors` heuristic downgrades an otherwise-perfect run when a `Traceback`/`Error` has no visible recovery within ~10 lines — the single most common false-positive on green work. The top cause is a throwaway probe that errors (a quoting/f-string slip in an inline `python -c`, a wrong kwarg, a bad path): when a probe errors, immediately re-run the corrected version, or print `# expected/handled: <why>` right after it so the recovery sits next to the error. Prefer a small temp `.py` file over a fragile multi-quote one-liner. (The timeout rule below is the other cause.)
|
|
86
|
-
- **An *expected* bounded timeout (exit 124) must be annotated, not left bare.** Capping a genuinely long task (a full-universe ingest, a long scan) at its expected limit is correct, but a bare `Exit code 124` reads as a failure to the verifier. Wrap it so the cap reads as success-with-note: `timeout 120 <cmd> || { rc=$?; [ $rc -eq 124 ] && echo "hit time cap — OK" || { echo "HALT: rc=$rc"; exit 1; }; }` — distinguish the expected 124 from a real non-zero. If the work needs longer than a safe cap, the PRD is too big: end with `SCHEDULER_VERDICT: FAIL <reason>` so the planner splits it. Never run it in the background.
|
|
87
|
-
- **Never `sleep N && <cmd>` to poll remote CI/job status.** The harness hard-blocks a `sleep` chained to another command, and that block itself lands in the transcript as a bare error — usually in the last 20% of the run, right where the verifier weighs errors most. Use the tool's own blocking watcher under a hard cap instead: `timeout 600 gh run watch <run-id> --repo <owner>/<repo> --exit-status`. `gh pr checks` is itself negative-assertion-shaped (exits 8 pending, 1 failed/none-reported — see above), so wrap it the same way. (Incident: PRD 745 landed a truthful PASS and a fully green CI run, but an unannotated `sleep 20 && gh pr checks` and a harness-blocked `sleep 90 && gh pr checks` sat at the end of the transcript and the run was flagged anyway.)
|
|
88
|
-
- **Don't leak expected-error text into tool output.** The verifier pattern-matches the transcript for `Traceback`/`FAIL`/`Error:`. When a step is *expected* to error (a TDD red-phase test, an existence probe), capture it and print a clean token instead of letting the raw exception land verbatim — e.g. `if <probe> 2>/dev/null; then echo PROBE_OK; else echo PROBE_ABSENT; fi`, or filter a noisy run down to `RED (expected)` / `GREEN`. When retrying a transient failure, re-run the exact same command with the same description — the self-recovery detector only pairs a failure with a later success when the wording matches.
|
|
89
|
-
- **End green: run the AC gate LAST, and let nothing error after it.** The verifier weighs the final ~20% of the run most heavily — a tool error there trips a downgrade even when everything actually passed. Order the run so any intentionally-failing step (a TDD red test, an expected-nonzero probe) runs early, never after the gate. If you must demonstrate a failure late anyway, capture it (`… 2>&1 | tail` inside a conditional, or assert on the captured text) instead of letting it hit the transcript bare.
|
|
82
|
+
- **`gh pr edit --body` can fail on repos with legacy GitHub Projects (classic) boards.** It queries the deprecated `repository.pullRequest.projectCards` field and errors with a `GraphQL: Projects (classic) is being deprecated` message even though the edit itself would otherwise succeed — a known `gh` CLI quirk, not a defect in your work. Prefer `gh api -X PATCH repos/<owner>/<repo>/pulls/<n> -f body="$(cat body.md)"` instead; it skips that field. If `gh pr edit` fails this way anyway retry immediately with the `gh api` form and note the fallback in your report.
|
|
83
|
+
- **Never `sleep N && <cmd>` to poll remote CI/job status.** The harness hard-blocks a `sleep` chained to another command. Use the tool's own blocking watcher under a hard cap instead: `timeout 600 gh run watch <run-id> --repo <owner>/<repo> --exit-status`. `gh pr checks` exits 8 while checks are pending and 1 on failure — that is not a defect in your work.
|
|
90
84
|
- **The verdict line is your authoritative "I passed" signal — emit it truthfully.** The scheduler's finish protocol ends by printing `SCHEDULER_VERDICT: PASS` once the AC gate is green and the commit has landed (or `SCHEDULER_VERDICT: FAIL <reason>` + `exit 1` otherwise). `PASS` plus a landed commit is authoritative: it overrides incidental transcript markers, so a deliberately reproduced red test or a grep result containing "Error" will not false-trip `needs_review`, as long as the run genuinely ends green and committed. Exiting 0 with uncommitted changes, or with no verdict line at all, is the #1 cause of needless `needs_review`. Never print `PASS` when the gate is red — that turns a safety net into a silent-failure machine.
|
|
91
85
|
- **If your own AC gate failed on a SIBLING job's in-flight file, say so with the right verdict — don't print a lying FAIL.** If the prompt disclosed a `--- FOREIGN WORKING-TREE STATE ---` section and your gate failure is confined to paths that section named as not your work, print `SCHEDULER_VERDICT: BLOCKED_BY_FOREIGN_WIP` (and `exit 1`) plus a `FOREIGN_WIP_PATHS: <path1>, <path2>, ...` line naming exactly the failing paths. The scheduler validates every listed path against the manifest it disclosed to you — list a path it never named, or claim this verdict for your own regression, and it is rejected back to `FAIL`. This is not an escape hatch: it exists so a correctly-diagnosed "not my regression" gets auto-requeued once the sibling clears its WIP, instead of spawning a fix-plan investigation into someone else's unfinished work.
|
|
@@ -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
|
|
@@ -186,7 +200,7 @@ const TOOLS = [
|
|
|
186
200
|
properties: {
|
|
187
201
|
title: { type: 'string', description: 'One-line human-readable title' },
|
|
188
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.' },
|
|
189
|
-
estimateMinutes: { type: 'number', description: '
|
|
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).' },
|
|
190
204
|
goal: { type: 'string', description: '2-4 sentences: what the executor will build and why' },
|
|
191
205
|
acceptanceCriteria: {
|
|
192
206
|
type: 'array', items: { type: 'string' },
|
|
@@ -226,7 +240,7 @@ const TOOLS = [
|
|
|
226
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.',
|
|
227
241
|
},
|
|
228
242
|
},
|
|
229
|
-
required: ['title', '
|
|
243
|
+
required: ['title', 'goal', 'acceptanceCriteria', 'implementationNotes', 'gate', 'files'],
|
|
230
244
|
},
|
|
231
245
|
},
|
|
232
246
|
{
|
|
@@ -870,4 +884,4 @@ if (require.main === module) {
|
|
|
870
884
|
});
|
|
871
885
|
}
|
|
872
886
|
|
|
873
|
-
module.exports = { TOOLS, handleCallTool, HELP_POINTER, NOT_RUNNING_ERROR };
|
|
887
|
+
module.exports = { TOOLS, handleCallTool, HELP_POINTER, NOT_RUNNING_ERROR, busyError };
|
package/src/main/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitShortSha": "
|
|
2
|
+
"version": "0.122.1",
|
|
3
|
+
"gitSha": "7f6a60c94e69454a3b52ccae59549e1ee33df648",
|
|
4
|
+
"gitShortSha": "7f6a60c9",
|
|
5
5
|
"gitBranch": "HEAD",
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-24T16:08:59.401Z",
|
|
7
7
|
"dirty": false
|
|
8
8
|
}
|
package/src/main/chatRunner.cjs
CHANGED
|
@@ -5,19 +5,12 @@
|
|
|
5
5
|
* the terminal chat UI (PRD 319). Each call spawns a fresh process that exits
|
|
6
6
|
* when done; no idle process lives between commands.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* on the scheduler's serialized queue so a burst of probes can't fan out into
|
|
11
|
-
* the parallel-`claude -p` OOM that SIGKILLed a job on 2026-06-26.
|
|
12
|
-
* PRD 493 had manual, user-initiated runs bypass that lane entirely (always
|
|
13
|
-
* immediate, uncapped). A later PRD (per-tab prompt queue) folded manual runs
|
|
14
|
-
* back into the SAME FIFO lane — a per-tab queue in the renderer (chat.ts)
|
|
15
|
-
* now only ever calls run() once per tab at a time, so the cap governs
|
|
16
|
-
* cross-tab fan-out, not per-tab ordering.
|
|
8
|
+
* Runs (silent and manual) go through one FIFO queue gated only by the shared
|
|
9
|
+
* sessionSlots pool; there is no chat-private concurrency cap.
|
|
17
10
|
*
|
|
18
11
|
* Public surface:
|
|
19
12
|
* run({ tabId, sessionId, prompt, cwd, resume, silent }): void — fire-and-forget enqueue.
|
|
20
|
-
* Both silent (PRD 470) and manual runs go through the same
|
|
13
|
+
* Both silent (PRD 470) and manual runs go through the same FIFO queue.
|
|
21
14
|
* Silent runs additionally suppress the six turn-affecting broadcasts + recordExchange.
|
|
22
15
|
* cancel(tabId): Promise<void> — resolves once the cancelled run fully settles
|
|
23
16
|
* parseStopSignal(finalText): { questions: string[] } | null — exported for reuse
|
|
@@ -295,22 +288,10 @@ const CHAT_MODE_TRUTH_INSTRUCTION =
|
|
|
295
288
|
`either a real result or an explicit statement that the user needs to ` +
|
|
296
289
|
`reply for the work to continue.\n\n`;
|
|
297
290
|
|
|
298
|
-
// ───
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
const DEFAULT_CAP = 2;
|
|
304
|
-
// Clamp to [1, 3]; default 2 = "two loops at a time". Read lazily (not
|
|
305
|
-
// captured at module-load time) so tests can stub the env per-case without
|
|
306
|
-
// vi.resetModules(); production behavior is unaffected since the env var
|
|
307
|
-
// never changes mid-process.
|
|
308
|
-
function getConcurrencyCap() {
|
|
309
|
-
return Math.min(
|
|
310
|
-
3,
|
|
311
|
-
Math.max(1, parseInt(process.env.SM_CHAT_CONCURRENCY || String(DEFAULT_CAP), 10) || DEFAULT_CAP),
|
|
312
|
-
);
|
|
313
|
-
}
|
|
291
|
+
// ─── Run queue ──────────────────────────────────────────────────────────────
|
|
292
|
+
// FIFO lane for ALL runs (silent probes and manual sends). There is NO private
|
|
293
|
+
// cap: a queued run starts exactly when lib/scheduler/sessionSlots.cjs grants a
|
|
294
|
+
// slot — the sole machine-wide cap.
|
|
314
295
|
|
|
315
296
|
// tabId → { cancelFn, donePromise } for every ACTIVE run; FIFO list of WAITING
|
|
316
297
|
// runs; live count. `donePromise` resolves once the run's own executeRun()
|
|
@@ -330,7 +311,6 @@ const dispatching = new Set();
|
|
|
330
311
|
// "Session ID … is already in use", so pump() refuses to start a second run
|
|
331
312
|
// for a held sessionId (it waits in `waiting` like any other blocked run).
|
|
332
313
|
const liveSessions = new Set();
|
|
333
|
-
let activeCount = 0;
|
|
334
314
|
|
|
335
315
|
// Indirection so tests can stub the spawn without launching claude.
|
|
336
316
|
let executor = executeRun;
|
|
@@ -347,7 +327,6 @@ function __resetQueueForTests() {
|
|
|
347
327
|
dispatching.clear();
|
|
348
328
|
liveSessions.clear();
|
|
349
329
|
waiting.length = 0;
|
|
350
|
-
activeCount = 0;
|
|
351
330
|
sessionSlots.__resetForTests();
|
|
352
331
|
}
|
|
353
332
|
|
|
@@ -370,7 +349,7 @@ function broadcast(channel, payload) {
|
|
|
370
349
|
/**
|
|
371
350
|
* Enqueue a chat run for a tab. Fire-and-forget — results arrive via IPC.
|
|
372
351
|
* Both silent (automated probe) and manual (user-initiated) runs share the
|
|
373
|
-
*
|
|
352
|
+
* FIFO lane — submits beyond the free slots queue and are
|
|
374
353
|
* announced via chat:run:queued (silent runs stay invisible to the renderer;
|
|
375
354
|
* see pump() below). De-dupes a tab already in the pipeline (the renderer's
|
|
376
355
|
* per-tab prompt queue in chat.ts only ever calls this once per tab at a
|
|
@@ -407,10 +386,10 @@ function run(opts) {
|
|
|
407
386
|
return { accepted: true, queued: tabBusy };
|
|
408
387
|
}
|
|
409
388
|
|
|
410
|
-
//
|
|
389
|
+
// Start queued runs FIFO while sessionSlots grants a slot, then announce queue positions for
|
|
411
390
|
// the remainder. O(n) over the waiting list (bounded by open tabs).
|
|
412
391
|
function pump() {
|
|
413
|
-
while (
|
|
392
|
+
while (waiting.length > 0) {
|
|
414
393
|
// Per-tab exclusivity: skip past any waiting run whose tab already has one
|
|
415
394
|
// live, rather than head-blocking the whole lane on it. `dispatching`
|
|
416
395
|
// covers the gap between picking a job here and executeRun() registering
|
|
@@ -422,14 +401,12 @@ function pump() {
|
|
|
422
401
|
// Everything queued is blocked behind its own tab's live run; the settle()
|
|
423
402
|
// of that run re-pumps.
|
|
424
403
|
if (idx === -1) break;
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
// settle() in either subsystem re-pumps.
|
|
404
|
+
// Capacity comes only from the Session-Manager-wide slot pool shared with
|
|
405
|
+
// the scheduler (lib/scheduler/sessionSlots.cjs). Refusal → stop; the rest
|
|
406
|
+
// stay queued in order and the next release in either subsystem re-pumps.
|
|
429
407
|
const slotToken = sessionSlots.acquire(`chat:${waiting[idx].tabId}`);
|
|
430
408
|
if (!slotToken) break;
|
|
431
409
|
const [job] = waiting.splice(idx, 1);
|
|
432
|
-
activeCount += 1;
|
|
433
410
|
dispatching.add(job.tabId);
|
|
434
411
|
liveSessions.add(job.sessionId);
|
|
435
412
|
Promise.resolve()
|
|
@@ -444,7 +421,6 @@ function pump() {
|
|
|
444
421
|
dispatching.delete(job.tabId);
|
|
445
422
|
liveSessions.delete(job.sessionId);
|
|
446
423
|
sessionSlots.release(slotToken);
|
|
447
|
-
activeCount -= 1;
|
|
448
424
|
pump();
|
|
449
425
|
});
|
|
450
426
|
}
|
package/src/main/docEdit.cjs
CHANGED
|
@@ -97,6 +97,7 @@ async function runClaude(prompt, { model = 'sonnet', timeoutMs = 90_000, systemP
|
|
|
97
97
|
'--model', model,
|
|
98
98
|
'--dangerously-skip-permissions',
|
|
99
99
|
...headlessPermissionArgs(),
|
|
100
|
+
'--strict-mcp-config',
|
|
100
101
|
'--output-format', 'text',
|
|
101
102
|
];
|
|
102
103
|
if (systemPrompt) args.push('--append-system-prompt', systemPrompt);
|
package/src/main/index.cjs
CHANGED
|
@@ -100,12 +100,14 @@ const pluginInstall = require('./pluginInstall.cjs');
|
|
|
100
100
|
const { seedDevPlugin } = require('./seedDevPlugin.cjs');
|
|
101
101
|
const { seedAgentPersonas } = require('./seedAgentPersonas.cjs');
|
|
102
102
|
const { seedSchedulerMcp } = require('./seedSchedulerMcp.cjs');
|
|
103
|
+
const { runSeedersSerially } = require('./lib/runSeeders.cjs');
|
|
103
104
|
const { getSeedStatus } = require('./seedStatus.cjs');
|
|
104
105
|
const otel = require('./otel.cjs');
|
|
105
106
|
const otelSettings = require('./otelSettings.cjs');
|
|
106
107
|
const { registerHistoryAggregatorHandlers, finalizeClosedDays, refreshIntradayToday } = require('./historyAggregator.cjs');
|
|
107
108
|
const runLogRetention = require('./lib/scheduler/runLogRetention.cjs');
|
|
108
109
|
const { registerHistoryDashboardHandlers } = require('./historyDashboard.cjs');
|
|
110
|
+
const { registerLoopEfficiencyHandlers } = require('./loopEfficiencyHandlers.cjs');
|
|
109
111
|
const { tryAcquireLock, releaseLock, DEFAULT_LOCK_PATH } = require('./lib/scheduler/watchdogHelpers.cjs');
|
|
110
112
|
const { createIntradayRefresh } = require('./lib/intradayRefresh.cjs');
|
|
111
113
|
const schedulerConfig = require('./lib/scheduler/schedulerConfig.cjs');
|
|
@@ -1003,6 +1005,7 @@ watchers.registerWatcherHandlers();
|
|
|
1003
1005
|
queueOps.registerQueueOpsHandlers({ retireCompletedSlugs: scheduler.retireCompletedSlugs });
|
|
1004
1006
|
registerHistoryAggregatorHandlers();
|
|
1005
1007
|
registerHistoryDashboardHandlers();
|
|
1008
|
+
registerLoopEfficiencyHandlers();
|
|
1006
1009
|
pluginInstall.registerPluginInstallHandlers();
|
|
1007
1010
|
memoryTool.registerMemoryHandlers();
|
|
1008
1011
|
activeIndexMerge.registerActiveIndexMergeHandlers();
|
|
@@ -1442,9 +1445,6 @@ app.whenReady().then(async () => {
|
|
|
1442
1445
|
// First-boot default: install the bundled session-manager-dev plugin (its 3
|
|
1443
1446
|
// dev skills) from the app's own marketplace. One-shot + idempotent; never
|
|
1444
1447
|
// throws. SM_SEED_DEV_PLUGIN_DISABLE=1 to opt out.
|
|
1445
|
-
seedDevPlugin({ logger: console, writeLog: logs.writeLine }).catch((e) => {
|
|
1446
|
-
logs.writeLine({ scope: 'seed-dev-plugin', level: 'error', message: 'seed failed', meta: { error: e?.message } });
|
|
1447
|
-
});
|
|
1448
1448
|
webRemote.init().catch((e) => {
|
|
1449
1449
|
logs.writeLine({ scope: 'webRemote', level: 'error', message: 'init failed', meta: { error: e?.message } });
|
|
1450
1450
|
});
|
|
@@ -1452,16 +1452,15 @@ app.whenReady().then(async () => {
|
|
|
1452
1452
|
// into ~/.claude/agents/ so the Agent Library isn't empty on a fresh
|
|
1453
1453
|
// install. One-shot + idempotent; never overwrites an existing persona;
|
|
1454
1454
|
// never throws. SM_SEED_AGENT_PERSONAS_DISABLE=1 to opt out.
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
logs.writeLine({ scope: 'seed-scheduler-mcp', level: 'error', message: 'seed failed', meta: { error: e?.message } });
|
|
1455
|
+
// Seeders run through ONE serial chain (runSeeders.cjs): plugin install and
|
|
1456
|
+
// `mcp add --scope user` both write Claude's config files, and concurrent
|
|
1457
|
+
// writes can drop an entry. Fire-and-forget relative to window creation.
|
|
1458
|
+
runSeedersSerially([
|
|
1459
|
+
{ name: 'dev-plugin', run: () => seedDevPlugin({ logger: console, writeLog: logs.writeLine }) },
|
|
1460
|
+
{ name: 'agent-personas', run: () => seedAgentPersonas({ logger: console, writeLog: logs.writeLine }) },
|
|
1461
|
+
{ name: 'scheduler-mcp', run: () => seedSchedulerMcp({ logger: console, writeLog: logs.writeLine }) },
|
|
1462
|
+
]).catch((e) => {
|
|
1463
|
+
logs.writeLine({ scope: 'seed', level: 'error', message: 'seed chain failed', meta: { error: e?.message } });
|
|
1465
1464
|
});
|
|
1466
1465
|
// Rewrite the stable guard-hook shims (~/.claude/session-manager/hooks/) to
|
|
1467
1466
|
// point at THIS running app's root, on every boot. An already-installed
|
package/src/main/ipcSchemas.cjs
CHANGED
|
@@ -365,7 +365,7 @@ const schedulerCreatePrd = z.object({
|
|
|
365
365
|
// sourcePromptId/originProjectRoot. A call with none of those still fails with
|
|
366
366
|
// a clear "cwd is required" error.
|
|
367
367
|
cwd: z.string().min(1).max(4096).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
368
|
-
estimateMinutes: z.number().int().min(1).max(100000),
|
|
368
|
+
estimateMinutes: z.number().int().min(1).max(100000).optional(),
|
|
369
369
|
goal: z.string().min(1).max(20000),
|
|
370
370
|
acceptanceCriteria: z.array(z.string().min(1).max(2000)).min(1).max(100),
|
|
371
371
|
implementationNotes: z.string().min(1).max(20000),
|
|
@@ -596,7 +596,7 @@ const setConfigSchema = z.object({
|
|
|
596
596
|
(s) => s === home || s.startsWith(home + path.sep),
|
|
597
597
|
'defaultCwd must be inside home directory'
|
|
598
598
|
).optional(),
|
|
599
|
-
firePolicy: z.enum(['when-available', '
|
|
599
|
+
firePolicy: z.enum(['when-available', 'manual']).optional(),
|
|
600
600
|
utilizationThreshold: z.number().min(0).max(200).optional(),
|
|
601
601
|
supervisor: z.object({
|
|
602
602
|
enabled: z.boolean().optional(),
|
|
@@ -137,28 +137,16 @@ function readHotJobsForCwd(cwd, deps) {
|
|
|
137
137
|
/** Best-effort read of `<cwd>/session-manager-operations/scheduler/state/history.jsonl`'s archived rows. */
|
|
138
138
|
function readHistoryEntriesForCwd(cwd, deps) {
|
|
139
139
|
const { projectHistoryPath } = deps.queueStore || require('../scheduler/queueStore.cjs');
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
140
|
+
const { readHistoryFileTailSync } = deps.queueHistory || require('../scheduler/queueHistory.cjs');
|
|
141
|
+
// Bounded tail (<=500 rows / <=1 MB): only the most recent matching row matters.
|
|
142
|
+
const rows = readHistoryFileTailSync(projectHistoryPath(cwd), { maxRows: 500, maxBytes: 1024 * 1024 });
|
|
143
|
+
// Tail reader is newest-first; callers sort by timestamp anyway.
|
|
146
144
|
const entries = [];
|
|
147
|
-
for (const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// share history.jsonl with terminal job rows but carry no `agentType` —
|
|
153
|
-
// findLatestSchedulerModel's own `j.agentType === agentType` filter
|
|
154
|
-
// already excludes them, but skip explicitly here too so this reader
|
|
155
|
-
// stays correct even if a future ledger line shape happens to pick up
|
|
156
|
-
// an agentType-shaped field.
|
|
157
|
-
if (j?.kind && j.kind !== 'terminal') continue;
|
|
158
|
-
entries.push(j);
|
|
159
|
-
} catch {
|
|
160
|
-
// corrupt/partial line — skip
|
|
161
|
-
}
|
|
145
|
+
for (const j of rows) {
|
|
146
|
+
// needs_review_entry/needs_review_resolution lines (needsReviewLedger.cjs)
|
|
147
|
+
// share history.jsonl with terminal job rows; skip non-terminal kinds.
|
|
148
|
+
if (j?.kind && j.kind !== 'terminal') continue;
|
|
149
|
+
entries.push(j);
|
|
162
150
|
}
|
|
163
151
|
return entries;
|
|
164
152
|
}
|
|
@@ -54,10 +54,16 @@ function personaToolScopeArgs(scope) {
|
|
|
54
54
|
prompt: 'Tool scope is enforced by the scheduler.',
|
|
55
55
|
tools: scope.tools,
|
|
56
56
|
};
|
|
57
|
-
|
|
57
|
+
const args = [
|
|
58
58
|
'--agents', JSON.stringify({ [HEADLESS_SCOPE_AGENT_NAME]: def }),
|
|
59
59
|
'--agent', HEADLESS_SCOPE_AGENT_NAME,
|
|
60
60
|
];
|
|
61
|
+
// A scope naming no MCP tool never uses a server: with --strict-mcp-config and
|
|
62
|
+
// no --mcp-config (variadic; would swallow later args) the CLI boots none.
|
|
63
|
+
if (!scope.tools.some((t) => String(t).startsWith('mcp__'))) {
|
|
64
|
+
args.push('--strict-mcp-config');
|
|
65
|
+
}
|
|
66
|
+
return args;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
module.exports = {
|
|
@@ -32,6 +32,7 @@ async function runClaudeP(prompt, { model = 'sonnet', timeoutMs = 180_000, syste
|
|
|
32
32
|
'--model', model,
|
|
33
33
|
'--dangerously-skip-permissions',
|
|
34
34
|
...headlessPermissionArgs(),
|
|
35
|
+
'--strict-mcp-config',
|
|
35
36
|
'--output-format', 'text',
|
|
36
37
|
];
|
|
37
38
|
if (systemPrompt) args.push('--append-system-prompt', systemPrompt);
|