kaizen-agent 1.8.0 → 1.9.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/README.md +10 -0
- package/cli/build-web.ts +27 -0
- package/cli/notes.test.ts +221 -0
- package/cli/notes.ts +139 -0
- package/cli/release.sh +2 -2
- package/cli/state.test.ts +6 -1
- package/cli/state.ts +32 -8
- package/cli/web.ts +50 -3
- package/package.json +12 -2
- package/web/board.html +34 -4
- package/web/notebook.js +36 -0
- package/web/notebook.ts +696 -0
- package/web/notes-lib.ts +135 -0
- package/web/notes.html +214 -0
package/web/board.html
CHANGED
|
@@ -106,6 +106,7 @@ section h2 .clear{margin-left:auto;min-height:22px;padding:0 8px}
|
|
|
106
106
|
.chip svg{width:11px;height:11px;stroke-width:1.7}
|
|
107
107
|
.chip.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.chip.warn i{background:var(--warn)}
|
|
108
108
|
.chip.accent{color:var(--accent);background:color-mix(in srgb,var(--accent) 14%,transparent)}.chip.accent i{background:var(--accent)}
|
|
109
|
+
.chip.ok{color:color-mix(in srgb,var(--ok) 80%,var(--ink));background:color-mix(in srgb,var(--ok) 14%,transparent)}
|
|
109
110
|
.chip.run{color:var(--run);background:color-mix(in srgb,var(--run) 14%,transparent)}.chip.run i{background:var(--run)}
|
|
110
111
|
.st.d-idea{color:var(--dim)}.st.d-run{color:var(--run)}.st.d-stall{color:var(--dim)}.st.d-wait{color:var(--warn)}.st.d-done{color:var(--ok)}.st.d-aband{color:var(--dim)}
|
|
111
112
|
.d-run{color:var(--run)}.d-wait{color:var(--warn)}.d-done{color:var(--ok)}
|
|
@@ -165,6 +166,8 @@ aside .foot{padding:16px 24px;border-top:1px solid var(--line);display:flex;gap:
|
|
|
165
166
|
aside .foot:empty{display:none}
|
|
166
167
|
.md h3{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin:20px 0 8px}.md h3:first-child{margin-top:0}
|
|
167
168
|
.md p{margin:0 0 10px;max-width:62ch}.md ol,.md ul{display:block;padding:0 0 0 1.4em;margin:0 0 10px;list-style:revert}.md li{margin:3px 0}
|
|
169
|
+
.md .lnk a{color:var(--accent);text-underline-offset:3px}
|
|
170
|
+
.md .hit{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--r-s);transition:outline-color .6s}
|
|
168
171
|
.md li.tk{list-style:none;margin-left:-1.4em}.md li.tk label{display:flex;gap:8px;align-items:baseline}.md li.tk input{flex:none;margin:0;accent-color:var(--accent);position:relative;top:2px;cursor:default}.md li.rej{color:var(--dim);text-decoration:line-through}
|
|
169
172
|
.md pre{background:var(--bg);border:1px solid var(--line);border-radius:var(--r-s);padding:10px 12px;overflow-x:auto;margin:0 0 10px;font:12px/1.5 var(--mono)}
|
|
170
173
|
.md h1,.md h2{font-size:15px;font-weight:600;margin:20px 0 8px}.md h1:first-child,.md h2:first-child{margin-top:0}.md code{font:12px var(--mono);background:var(--bg);padding:1px 5px;border-radius:var(--r-s)}.md table{border-collapse:collapse;font-size:12px;margin:0 0 10px}.md td,.md th{border:1px solid var(--line);padding:4px 8px;text-align:left}.md hr{border:0;border-top:1px solid var(--line);margin:14px 0}
|
|
@@ -206,6 +209,7 @@ aside .foot:empty{display:none}
|
|
|
206
209
|
<symbol id="i-mic" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3M9 21h6"/></symbol>
|
|
207
210
|
<symbol id="i-pen" viewBox="0 0 24 24"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/></symbol>
|
|
208
211
|
<symbol id="i-x" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></symbol>
|
|
212
|
+
<symbol id="i-note" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6z"/><path d="M14 3v5h5M9 13h6M9 17h6"/></symbol>
|
|
209
213
|
<symbol id="i-trash" viewBox="0 0 24 24"><path d="M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14M10 11v6M14 11v6"/></symbol>
|
|
210
214
|
<symbol id="i-close" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></symbol>
|
|
211
215
|
<symbol id="i-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.2" fill="currentColor"/><circle cx="12" cy="12" r="1.2" fill="currentColor"/><circle cx="19" cy="12" r="1.2" fill="currentColor"/></symbol>
|
|
@@ -224,6 +228,7 @@ aside .foot:empty{display:none}
|
|
|
224
228
|
<symbol id="c-cursor" viewBox="0 0 16 16"><path d="M4 3l9 5-4 1-1 4z"/></symbol>
|
|
225
229
|
<symbol id="c-full" viewBox="0 0 16 16"><circle cx="5.5" cy="8" r="3"/><circle cx="11" cy="8" r="3"/></symbol>
|
|
226
230
|
<symbol id="c-lite" viewBox="0 0 16 16"><circle cx="8" cy="8" r="3"/></symbol>
|
|
231
|
+
<symbol id="c-git" viewBox="0 0 16 16"><circle cx="4.5" cy="3.5" r="1.5"/><circle cx="4.5" cy="12.5" r="1.5"/><circle cx="11.5" cy="5.5" r="1.5"/><path d="M4.5 5v6M11.5 7c0 2.5-7 1.5-7 4"/></symbol>
|
|
227
232
|
</defs></svg>
|
|
228
233
|
|
|
229
234
|
<div class="hdr">
|
|
@@ -233,6 +238,7 @@ aside .foot:empty{display:none}
|
|
|
233
238
|
<div class="combo" id="combo"><button class="sel" id="proj" aria-label="project" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" id="projpop" hidden><input class="sel" id="projq" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="projs" role="listbox"></div></div></div>
|
|
234
239
|
<span class="sp"></span>
|
|
235
240
|
<button class="btn pri" id="newtop">New idea</button>
|
|
241
|
+
<a class="ib" href="/notebook" title="Notebook" aria-label="Notebook"><svg><use href="#i-note"/></svg></a>
|
|
236
242
|
<button class="ib" id="sources" title="Request sources" aria-label="Request sources"><svg><use href="#i-link"/></svg></button>
|
|
237
243
|
<button class="ib" id="settings" title="Settings" aria-label="Settings"><svg><use href="#i-gear"/></svg></button>
|
|
238
244
|
</div>
|
|
@@ -387,6 +393,8 @@ function card(c){
|
|
|
387
393
|
if(all)chips.push(`<span class="chip">${esc(c.where)}</span>`);
|
|
388
394
|
chips.push(...(c.tags||[]).map(t=>{const mark=MARKS[t]?`<svg><use href="#c-${t}"/></svg>`:"";return `<span class="chip${t==="full"||t==="lite"?" accent":""}">${mark}${esc(NAMES[t]||t)}</span>`}));
|
|
389
395
|
if(c.notes)chips.push(`<span class="chip">notes</span>`);
|
|
396
|
+
// Information only: the panel's button is where it is committed and pushed.
|
|
397
|
+
if(c.git){const t=c.git==="pushed"?"Committed & pushed":"Not committed & pushed yet";chips.push(`<span class="chip ${c.git==="pushed"?"ok":"warn"}" role="img" title="${t}" aria-label="${t}"><svg><use href="#c-git"/></svg></span>`)}
|
|
390
398
|
const pick=picking&&!archive&&c.column===4;
|
|
391
399
|
el.innerHTML=(pick?`<input type="checkbox" class="pick" ${picked.has(k)?"checked":""} aria-label="Select">`:"")+`<div class="c-id"><span class="st d-${st}"><svg><use href="#s-${st}"/></svg></span><span class="idt">${esc(idt)}</span><span class="age">${c.kind==="run"?age(c.moved||S.now,S.now):""}</span><button class="ib sm more" data-a="menu" title="Actions" aria-label="Actions">${ico("more")}</button></div>
|
|
392
400
|
<div class="c-title">${esc(c.kind==="idea"?c.text:(c.title||c.id))}</div>
|
|
@@ -530,6 +538,10 @@ async function openRun(c,refresh,again){
|
|
|
530
538
|
chat(c,doc,ro);}
|
|
531
539
|
else $("pbody").innerHTML=doc?md(doc,k==="backlog"):`<p style="color:var(--dim)">No ${({request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"})[k]} yet.</p>`;
|
|
532
540
|
if(k==="review")fixForm(c);
|
|
541
|
+
// The notebook's notes that link here, each opening that note: above the run's own notes.
|
|
542
|
+
if(k==="notes"&&runDoc.linkedFrom&&runDoc.linkedFrom.length)$("pbody").insertAdjacentHTML("afterbegin",`<h3>Linked from notes</h3><ul class="lnk">${runDoc.linkedFrom.map(n=>`<li><a href="/notebook?dir=${encodeURIComponent(c.state)}&note=${encodeURIComponent(n)}">${esc(n)}</a></li>`).join("")}</ul>`);
|
|
543
|
+
// Only the panel the link asked for: another card opened meanwhile drops the mark.
|
|
544
|
+
if(hit&&!refresh){if(hit.k===key(c))mark(hit);else hit=null}
|
|
533
545
|
const foot=$("pfoot");
|
|
534
546
|
// A refresh while the commit is being confirmed would wipe the message.
|
|
535
547
|
if(refresh&&foot.querySelector(".inl,[data-busy]"))return;
|
|
@@ -539,6 +551,24 @@ async function openRun(c,refresh,again){
|
|
|
539
551
|
else if(c.status==="done"||c.status==="abandoned")foot.innerHTML=arch;
|
|
540
552
|
else foot.innerHTML=`${arch}<button class="btn ghost" data-a="ab">Abandon run</button>`;
|
|
541
553
|
foot.querySelectorAll("[data-a]").forEach(b=>b.onclick=()=>{const a=b.dataset.a;if(a==="archive"||a==="restore"){toggleArchive(c,a==="archive");return}if(a==="push"){commitPush(c,"",b);return}if(a==="commit"){confirmCommit(c);return}close();mode="reject";modeKey=key(c);render()})}
|
|
554
|
+
// A link from a note (?run= or ?idea=, with dir, tab, item) opens that card's panel;
|
|
555
|
+
// a finding or backlog item named is scrolled to and marked. Found by its text, since
|
|
556
|
+
// the rendered list does not carry the numbers.
|
|
557
|
+
let hit=null;
|
|
558
|
+
function deep(){const p=new URLSearchParams(location.search),d=p.get("dir"),id=p.get("run"),idea=p.get("idea");if(!id&&idea===null)return;
|
|
559
|
+
history.replaceState(null,"",location.pathname+"?all=1");
|
|
560
|
+
const c=S.cards.find(x=>x.state===d&&(id?x.kind==="run"&&x.id===id:x.kind==="idea"&&x.text===idea));
|
|
561
|
+
if(!c){toast(id?"That run is not on this board":"That idea is not on this board");return}
|
|
562
|
+
const m=/^(finding|backlog)-(\d+)$/.exec(p.get("item")||"");
|
|
563
|
+
// An item's tab is its own when the link does not name one.
|
|
564
|
+
const t=["request","notes","plan","work","preview","review","backlog"].indexOf(p.get("tab")||(m?(m[1]==="finding"?"review":"backlog"):""));tab=t<0?0:t;hit=m?{type:m[1],n:+m[2],k:key(c)}:null;open(c)}
|
|
565
|
+
// What md() leaves of a line as text: no backticks or stars, a link reduced to its words.
|
|
566
|
+
function mark(h){hit=null;const flat=t=>String(t).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim();
|
|
567
|
+
const b=[...(runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)];
|
|
568
|
+
const text=h.type==="finding"?(runDoc.findings||[]).find(f=>f.n===h.n)?.text:b[h.n-1]?.[1];
|
|
569
|
+
if(!text){toast("That item is not in this run");return}
|
|
570
|
+
const want=flat(text).slice(0,60),el=[...$("pbody").querySelectorAll("li,p")].find(e=>flat(e.textContent).includes(want));
|
|
571
|
+
if(!el)return;el.scrollIntoView({block:"center"});el.classList.add("hit");setTimeout(()=>el.classList.remove("hit"),2400)}
|
|
542
572
|
// Fix picked findings, under the review: open ones ticked, closed ones listed but settled.
|
|
543
573
|
// Unticked numbers live outside the form, which every refresh rebuilds. A finding is one
|
|
544
574
|
// line, so md() gives one paragraph; its wrapper is dropped to sit inline in the label.
|
|
@@ -563,9 +593,9 @@ function gitBtn(c){const g=runDoc.git,k=runDoc.commit;
|
|
|
563
593
|
if(k&&k.pushed)return `<button class="btn" disabled title="${esc(k.sha)}">Committed & pushed</button>`+(g.files.length?`<button class="btn" data-a="commit">Commit new changes</button>`:g.ahead>0?`<button class="btn" data-a="push">Push</button>`:"");
|
|
564
594
|
if(g.files.length)return `<button class="btn pri" data-a="commit">Commit & push</button>`;
|
|
565
595
|
if(g.ahead>0)return `<button class="btn pri" data-a="push">Push</button>`;
|
|
566
|
-
//
|
|
567
|
-
//
|
|
568
|
-
return k
|
|
596
|
+
// None of its files changed and level with the remote: it was committed and pushed
|
|
597
|
+
// somewhere else. With no record and no report, nothing says it was ever committed.
|
|
598
|
+
return k||runDoc.impl?`<button class="btn" disabled>Committed & pushed</button>`:""}
|
|
569
599
|
function confirmCommit(c,msg){const g=runDoc.git,foot=$("pfoot");
|
|
570
600
|
foot.innerHTML=`<div class="inl" style="flex:1"><div class="q">Commit ${g.files.length} changed file${g.files.length===1?"":"s"} on ${esc(g.branch||"this branch")} and push to ${esc(g.remote)}?</div><div class="q dim">Only the files this run's report names.${g.others?` ${g.others} other changed file${g.others===1?" is":"s are"} left alone.`:""}</div><pre class="files">${esc(g.files.join("\n"))}</pre>${g.shared&&g.shared.length?`<div class="q">These files also hold other runs' changes, and a file is committed whole:</div><pre class="files">${esc(g.shared.join("\n"))}</pre>`:""}<textarea aria-label="Commit message" placeholder="feat(scope): what changed">${esc(msg||commitMsg(runDoc.impl)||c.title||c.text)}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Commit & push</button></div></div>`;
|
|
571
601
|
const inp=foot.querySelector("textarea"),go=foot.querySelector("[data-a=s]");inp.focus();
|
|
@@ -709,7 +739,7 @@ function listen(){const es=new EventSource("/events");es.onmessage=ev=>{if(ev.da
|
|
|
709
739
|
(async()=>{let d;try{d=localStorage.getItem("kz-proj")}catch(e){}
|
|
710
740
|
if(d&&!urlAll)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:d})})).ok)all=false;else keep()}catch(e){}
|
|
711
741
|
try{const f=JSON.parse(sessionStorage.getItem("kz-form"));if(f)[mode,modeKey]=f}catch(e){}
|
|
712
|
-
load();listen()})();
|
|
742
|
+
load().then(deep);listen()})();
|
|
713
743
|
</script>
|
|
714
744
|
</body>
|
|
715
745
|
</html>
|