kaizen-agent 1.19.0 → 1.19.2

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/README.md CHANGED
@@ -230,6 +230,8 @@ Anthropic, OpenAI and Requesty are supported. Setup, privacy and settings are in
230
230
 
231
231
  In a terminal: `kaizen` (dashboard), `kaizen web` (board), `kaizen settings`, `kaizen upgrade`.
232
232
 
233
+ Stuck on 1.18.0 or older? If `kaizen upgrade` says *could not pull* and stays on the old version, run this once: `bunx kaizen-agent@latest`. Your old install is kept as `~/kaizen.old`; from then on `kaizen upgrade` works by itself.
234
+
233
235
  ## Learn more
234
236
 
235
237
  - **[The guide](https://github.com/hfadhlullah/kaizen/blob/main/docs/guide.md)**: the board, request sources, notebook, settings, installer flags, uninstall
package/cli/install.ts CHANGED
@@ -434,7 +434,9 @@ if (upgrade) {
434
434
  const out = clean(r.stdout.toString() + r.stderr.toString()).filter((l) => l.trim());
435
435
  for (const l of out) console.log(l.startsWith(" ") ? l : ` ${l}`);
436
436
  const after = versionOf(dest);
437
- if (r.exitCode !== 0 || after !== latest) {
437
+ // The installer exits 1 when it skipped a link (an agent file the user owns), which
438
+ // is not a failed upgrade: the version on disk is what decides.
439
+ if (after !== latest) {
438
440
  console.log(`\n ${c.bold(`could not install kaizen ${latest}`)}`);
439
441
  console.log(` ${c.dim("try:")} ${c.cyan(`bunx kaizen-agent@${latest}`)}\n`);
440
442
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.19.0",
3
+ "version": "1.19.2",
4
4
  "description": "Staged workflow for AI agents — code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
5
5
  "type": "module",
6
6
  "bin": {
package/web/board.html CHANGED
@@ -37,11 +37,12 @@ select.sel,button.sel{cursor:pointer;appearance:none;padding-right:30px;max-widt
37
37
  .combo .menu-pop{left:0;right:auto;top:calc(100% + 4px);width:320px;max-width:calc(100vw - 32px);gap:6px;z-index:9}
38
38
  .combo[hidden],.combo .menu-pop[hidden]{display:none}
39
39
  .combo input.sel{width:100%;max-width:none}
40
- #projs{display:flex;flex-direction:column;max-height:300px;overflow-y:auto}
41
- #projs button{display:block;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
42
- #projs button.on{background:var(--hover)}
40
+ #ndc>.sel{width:100%;max-width:none;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
41
+ #projs,#nds{display:flex;flex-direction:column;max-height:300px;overflow-y:auto}
42
+ #projs button,#nds button{display:block;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
43
+ #projs button.on,#nds button.on{background:var(--hover)}
43
44
  #shows{display:flex;flex-direction:column;max-height:300px;overflow-y:auto}#shows label{display:flex;gap:8px;align-items:center;padding:6px 10px;font-size:13px;border-radius:var(--r-s);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#shows label:hover{background:var(--hover)}#shows .grp{padding:8px 10px 4px;font-size:11px;color:var(--dim)}
44
- #projs .grp{padding:8px 10px 4px;font-size:11px;color:var(--dim)}
45
+ #projs .grp,#nds .grp{padding:8px 10px 4px;font-size:11px;color:var(--dim)}
45
46
  .sp{flex:1}
46
47
  .ib{width:34px;height:34px;display:grid;place-items:center;color:var(--dim);border-radius:var(--r-s);transition:background .12s,color .12s}
47
48
  .ib:hover{color:var(--ink);background:var(--hover)}
@@ -629,9 +630,9 @@ function render(){
629
630
  s.querySelectorAll("[data-p]").forEach(bt=>bt.onclick=async e=>{e.stopPropagation();if(bt.dataset.p==="cancel"){picking=false;picked.clear();render();return}
630
631
  const set=list.filter(c=>picked.has(key(c)));picking=false;picked.clear();for(const c of set)await post("/archive",{dir:c.state,id:c.id});toast("Archived "+set.length);load()});}
631
632
  const ul=document.createElement("ul");
632
- if(ci===0&&mode==="new"){const f=document.createElement("li");f.className="inl";f.innerHTML=`<input id="newidea" placeholder="What would you like done?"><textarea id="newnotes" placeholder="Notes: links, paths, background (optional)"></textarea><div class="row"><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add idea</button></div>`;ul.appendChild(f);
633
- const inp=f.querySelector("input");draft(inp,"f:new:0");draft(f.querySelector("textarea"),"f:new:1");const save=async()=>{const text=inp.value.trim();if(!text)return;const notes=f.querySelector("textarea").value.trim();mode=null;const r=await post("/inbox",{op:"add",text,notes});if(r.ok)toast("Idea added");load()};
634
- f.querySelector("#ns").onclick=save;f.querySelector("#nc").onclick=()=>{mode=null;render()};{const m=mic([inp,f.querySelector("textarea")]);if(m)f.querySelector(".row").prepend(m)}inp.onkeydown=e=>{if(e.key==="Enter")save();if(e.key==="Escape"){mode=null;render()}};setTimeout(()=>inp.focus())}
633
+ if(ci===0&&mode==="new"){const f=document.createElement("li");f.className="inl";f.innerHTML=`${all?`<div class="combo" id="ndc"><button class="sel" id="newdir" aria-label="Project for this idea" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>`:""}<input id="newidea" placeholder="What would you like done?"><textarea id="newnotes" placeholder="Notes: links, paths, background (optional)"></textarea><div class="row"><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add idea</button></div>`;ul.appendChild(f);
634
+ const inp=f.querySelector("#newidea");draft(inp,"f:new:0");draft(f.querySelector("textarea"),"f:new:1");const save=async()=>{const text=inp.value.trim();if(!text)return;const notes=f.querySelector("textarea").value.trim();mode=null;const dir=all?newDir:null;const r=await post("/inbox",dir?{op:"add",dir,text,notes}:{op:"add",text,notes});if(r.ok)toast("Idea added");load()};
635
+ if(all)ideaCombo(f);f.querySelector("#ns").onclick=save;f.querySelector("#nc").onclick=()=>{mode=null;render()};{const m=mic([inp,f.querySelector("textarea")]);if(m)f.querySelector(".row").prepend(m)}inp.onkeydown=e=>{if(e.key==="Enter")save();if(e.key==="Escape"){mode=null;render()}};setTimeout(()=>inp.focus())}
635
636
  const shown=unfolded.has(ci)||list.length<=FOLD+1?list:list.slice(0,FOLD);
636
637
  shown.forEach(c=>{if(modeKey===key(c)&&mode){ul.appendChild(inlineForm(c));return}const li=document.createElement("li");li.appendChild(card(c));ul.appendChild(li)});
637
638
  if(shown.length<list.length){const li=document.createElement("li");li.innerHTML=`<button class="more-btn">Show ${list.length-shown.length} more</button>`;li.firstChild.onclick=()=>{unfolded.add(ci);render()};ul.appendChild(li)}
@@ -1021,6 +1022,18 @@ async function pick(v){
1021
1022
  // The all-projects filter: tick the projects to show; none ticked shows every one. Kept per browser.
1022
1023
  let shown=new Set((()=>{try{return JSON.parse(localStorage.getItem("kz-show"))||[]}catch(e){return[]}})());
1023
1024
  // Known projects, plus any folder a card comes from that is not one (the global inbox).
1025
+ // New idea in All projects: the projects being shown, else every one.
1026
+ let newDir=null;
1027
+ // Same combo as the header's project picker: a button, then a searchable list.
1028
+ function ideaCombo(f){const ps=ideaDirs(),btn=f.querySelector("#newdir"),pp=f.querySelector("#ndc .menu-pop"),q=pp.querySelector("input"),box=f.querySelector("#nds");
1029
+ if(!ps.some(p=>p.dir===newDir))newDir=ps[0]?.dir??null;
1030
+ const name=()=>{btn.textContent=(ps.find(p=>p.dir===newDir)||{label:"Choose project"}).label};
1031
+ const draw=()=>{const t=q.value.trim().toLowerCase();box.innerHTML=ps.filter(p=>p.label.toLowerCase().includes(t)).map(p=>`<button type="button" role="option" data-d="${esc(p.dir)}" title="${esc(p.label)}" class="${p.dir===newDir?"on":""}">${esc(p.label)}</button>`).join("")||`<div class="grp">No match</div>`;
1032
+ box.querySelectorAll("button").forEach(b=>b.onclick=()=>{newDir=b.dataset.d;name();open(false);f.querySelector("#newidea").focus()})};
1033
+ const open=on=>{pp.hidden=!on;btn.setAttribute("aria-expanded",on);if(on){q.value="";draw();q.focus()}};
1034
+ btn.onclick=e=>{e.preventDefault();open(pp.hidden)};q.oninput=draw;q.onkeydown=e=>{if(e.key==="Escape"){e.stopPropagation();open(false)}if(e.key==="Enter"){e.preventDefault();box.querySelector("button")?.click()}};
1035
+ name()}
1036
+ function ideaDirs(){const sp=showable(),pick=sp.filter(p=>shown.has(p.dir));return pick.length?pick:sp}
1024
1037
  function showable(){const l=[...S.projects];for(const c of S.cards)if(!l.some(p=>p.dir===c.state))l.push({dir:c.state,label:c.where});return l}
1025
1038
  let SP=[];
1026
1039
  function showList(){const q=$("showq").value.trim().toLowerCase();SP=showable();