@galda/cli 0.10.67 → 0.10.68
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/app/index.html +40 -12
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -5945,6 +5945,15 @@ function openGoalOrCard(goalId){
|
|
|
5945
5945
|
if (g && ['blocked', 'partial', 'failed', 'interrupted'].includes(g.status) && typeof saOpen === 'function') { saOpen(goalId, true); return; }
|
|
5946
5946
|
openGoalDetail(goalId);
|
|
5947
5947
|
}
|
|
5948
|
+
// List / board 共通の詳細導線。Review は review ledger、Needs you は attention card、
|
|
5949
|
+
// それ以外は通常の goal detail を開く。
|
|
5950
|
+
function fsOpenGoalDetail(goalId){
|
|
5951
|
+
const g = state.goals.find((x) => x.id === goalId);
|
|
5952
|
+
if (!g) return;
|
|
5953
|
+
goToGoalInChat(goalId);
|
|
5954
|
+
if (g.status === 'review') openReviewChecklist(goalId);
|
|
5955
|
+
else openGoalOrCard(goalId);
|
|
5956
|
+
}
|
|
5948
5957
|
function closeGoalDetail(){
|
|
5949
5958
|
$('goalDetailOverlay').classList.remove('show');
|
|
5950
5959
|
const goalId = state.goalDetailOpen;
|
|
@@ -6692,12 +6701,22 @@ function renderFsBoard(){
|
|
|
6692
6701
|
h += `<div class="lbc-meter"><span class="bar"><i style="width:${Math.round(done / total * 100)}%"></i></span><span class="frac">${done}/${total}</span></div>`;
|
|
6693
6702
|
}
|
|
6694
6703
|
}
|
|
6695
|
-
return `<div class="lbc${quiet ? ' is-quiet' : ''}">${h}</div>`;
|
|
6704
|
+
return `<div class="lbc${quiet ? ' is-quiet' : ''}" data-fsgoal="${g.id}">${h}</div>`;
|
|
6696
6705
|
};
|
|
6697
6706
|
const no = (g) => goalNo.get(g.id) ?? Infinity;
|
|
6698
6707
|
const bucket = { todo: [], doing: [], needs: [], review: [], done: [], later: [], rejected: [] };
|
|
6699
6708
|
for (const g of pgoals) bucket[fsBoardBucket(g, list)].push(g); // 'rejected' is collected but never rendered as a column below — closed goals disappear from the board
|
|
6700
6709
|
for (const k of Object.keys(bucket)) bucket[k].sort((a, b) => k === 'done' ? no(b) - no(a) : no(a) - no(b));
|
|
6710
|
+
// List と同じ digest を使い、同一 PR にまとまる Review は看板でも1件として扱う。
|
|
6711
|
+
const reviewDigest = buildReviewDigest({ goals: pgoals, tasks: list });
|
|
6712
|
+
const reviewCard = (r) => {
|
|
6713
|
+
const g0 = pgoals.find((g) => g.id === r.goalIds[0]);
|
|
6714
|
+
const n = r.reviewNumber;
|
|
6715
|
+
return `<div class="lbc" data-fsgoal="${r.goalIds[0]}"><div class="lbc-hd"><span class="lbc-dot rev"></span>`
|
|
6716
|
+
+ (n != null ? `<span class="lbc-n">#${n}</span>` : '')
|
|
6717
|
+
+ `</div><div class="lbc-t">${tt(r.plan ? r.checkLine : (g0?.text || g0?.plan?.[0] || r.checkLine), 90)}</div></div>`;
|
|
6718
|
+
};
|
|
6719
|
+
bucket.review = reviewDigest;
|
|
6701
6720
|
// To Do / Doing / Needs you / Review / Done / Later — the original design's columns
|
|
6702
6721
|
// (Masa 2026-07-16). "Needs you" = a clarifying question is waiting on the user —
|
|
6703
6722
|
// distinct from "blocked" (an error needing retry/archive, which stays in Doing, the
|
|
@@ -6706,11 +6725,15 @@ function renderFsBoard(){
|
|
|
6706
6725
|
col('To Do', 'var(--ink3)', bucket.todo.length, bucket.todo.map(goalCard).join(''), 'todo')
|
|
6707
6726
|
+ col('Doing', 'var(--green)', bucket.doing.length, bucket.doing.map(goalCard).join(''), 'doing')
|
|
6708
6727
|
+ (bucket.needs.length ? col('Needs you', '#ffca16', bucket.needs.length, bucket.needs.map(goalCard).join(''), 'needs') : '')
|
|
6709
|
-
+ col('Review', '#8390f2',
|
|
6728
|
+
+ col('Review', '#8390f2', reviewDigest.length, reviewDigest.map(reviewCard).join(''), 'review')
|
|
6710
6729
|
+ col('Done', 'var(--ink3)', bucket.done.length, bucket.done.slice(0, 40).map(goalCard).join(''), 'done')
|
|
6711
6730
|
+ col('Later', 'var(--ink3)', bucket.later.length, bucket.later.map(goalCard).join(''), 'later');
|
|
6712
6731
|
for (const b of el.querySelectorAll('[data-fsnew]')) b.onclick = () => $('input')?.focus(); // every + New / header + focuses the composer
|
|
6713
6732
|
for (const b of el.querySelectorAll('[data-fspause]')) b.onclick = togglePauseActive; // Doing列の一時停止/再生
|
|
6733
|
+
for (const c of el.querySelectorAll('[data-fsgoal]')) c.onclick = (e) => {
|
|
6734
|
+
if (e.target.closest('button, a')) return;
|
|
6735
|
+
fsOpenGoalDetail(Number(c.dataset.fsgoal));
|
|
6736
|
+
};
|
|
6714
6737
|
renderFsWait(bucket);
|
|
6715
6738
|
}
|
|
6716
6739
|
// Doing列の一時停止/再生 (Masa 2026-07-18・Spotify式トグル). 楽観的にローカルの
|
|
@@ -6934,7 +6957,7 @@ function renderFsTodo(){
|
|
|
6934
6957
|
// renders only when they exist. Same endpoints as the old layout's data-pstart/pdel:
|
|
6935
6958
|
// Start = POST /api/goals/:id/activate, Delete = DELETE /api/goals/:id (confirm).
|
|
6936
6959
|
// No mono # — pending goals are never planned, so they carry no task number yet.
|
|
6937
|
-
const laterRows = later.map((g) => `<div class="trow up dim"><span class="st"></span><span class="tt">${tt(g.text, 70)}</span>
|
|
6960
|
+
const laterRows = later.map((g) => `<div class="trow up dim" data-fsgoalrow="${g.id}"><span class="st"></span><span class="tt">${tt(g.text, 70)}</span>
|
|
6938
6961
|
<span class="acts"><button class="ab txt" data-fspstart="${g.id}" title="Queue it now">Start</button><button class="ab txt" data-fspdel="${g.id}" title="Delete">Delete</button></span></div>`).join('');
|
|
6939
6962
|
// reference numbers (HANDOFF-v45 §11): a #NN prefix on every To Do / Review item's name.
|
|
6940
6963
|
// Same goal = same number everywhere (goalReviewNumber = goal.id, unique & stable per
|
|
@@ -6949,11 +6972,11 @@ function renderFsTodo(){
|
|
|
6949
6972
|
running.filter((t) => !pausedGoalIds.has(t.goalId)).map((t) => {
|
|
6950
6973
|
const goal = pgoals.find((g) => g.id === t.goalId);
|
|
6951
6974
|
const title = t.reply ? (goal?.reviewSummary?.check || goal?.plan?.[0] || goal?.text || t.title) : t.title;
|
|
6952
|
-
return `<div class="drow"><div class="trow now"><span class="st run"></span>${noSpan(t.goalId)}<span class="tt">${tt(title)}</span><span class="pct num">${progressFor(t)}%</span>${fsActs(t.goalId)}</div>
|
|
6975
|
+
return `<div class="drow"><div class="trow now" data-fsgoalrow="${t.goalId}"><span class="st run"></span>${noSpan(t.goalId)}<span class="tt">${tt(title)}</span><span class="pct num">${progressFor(t)}%</span>${fsActs(t.goalId)}</div>
|
|
6953
6976
|
<div class="tbar"><i style="width:${progressFor(t)}%"></i></div></div>`;
|
|
6954
6977
|
}).join('')
|
|
6955
6978
|
// 一時停止したゴールは Doing に「Paused」1行(アンバー)で留める+Resume。走行/中断行は上と needsRows で抑止済み。
|
|
6956
|
-
+ pgoals.filter((g) => pausedGoalIds.has(g.id)).map((g) => `<div class="drow"><div class="trow now"><span class="st run int"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword int">Paused</span><button class="ab txt" data-fspause="1" title="Resume — 再開">Resume</button></div></div>`).join('');
|
|
6979
|
+
+ pgoals.filter((g) => pausedGoalIds.has(g.id)).map((g) => `<div class="drow"><div class="trow now" data-fsgoalrow="${g.id}"><span class="st run int"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword int">Paused</span><button class="ab txt" data-fspause="1" title="Resume — 再開">Resume</button></div></div>`).join('');
|
|
6957
6980
|
// NEEDS YOU: stopped/errored work that used to fold into Doing, now under its own caption so
|
|
6958
6981
|
// it no longer reads as "still in flight" (Masa 2026-07-19). Three kinds share it, each
|
|
6959
6982
|
// keeping the affordances it already had — nothing about the failed-task and blocked-goal
|
|
@@ -6978,13 +7001,13 @@ function renderFsTodo(){
|
|
|
6978
7001
|
const reason = String(t.result || '').trim();
|
|
6979
7002
|
const reasonLine = reason && reason.toLowerCase() !== word.toLowerCase()
|
|
6980
7003
|
? `<div class="msg"><span class="x ${isInt ? 'int' : ''}">${icon}</span>${tt(reason, 180)}</div>` : '';
|
|
6981
|
-
return `<div class="drow err${isInt ? ' int' : ''}"><div class="trow now"><span class="st run ${isInt ? 'int' : 'bad'}"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span><span class="stword ${isInt ? 'int' : ''}">${word}</span>${fsActs(t.goalId)}</div>
|
|
7004
|
+
return `<div class="drow err${isInt ? ' int' : ''}"><div class="trow now" data-fsgoalrow="${t.goalId}"><span class="st run ${isInt ? 'int' : 'bad'}"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span><span class="stword ${isInt ? 'int' : ''}">${word}</span>${fsActs(t.goalId)}</div>
|
|
6982
7005
|
<div class="errline">${reasonLine}
|
|
6983
7006
|
<div class="acts2"><button class="errbtn" data-fsretry="${t.id}">${label}</button><button class="errbtn" data-fslog="${t.id}">View log</button></div></div></div>`; }).join('')
|
|
6984
|
-
+ blocked.map((g) => g.blocked?.kind === 'paused' ? '' : `<div class="drow err"><div class="trow now"><span class="st run bad"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword">Failed</span></div>
|
|
7007
|
+
+ blocked.map((g) => g.blocked?.kind === 'paused' ? '' : `<div class="drow err"><div class="trow now" data-fsgoalrow="${g.id}"><span class="st run bad"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword">Failed</span></div>
|
|
6985
7008
|
<div class="errline"><div class="msg"><span class="x">✗</span>${tt(g.blocked?.reason ?? 'blocked', 180)}</div>
|
|
6986
7009
|
<div class="acts2"><button class="errbtn" data-fsreverify="${g.id}">Retry</button><button class="errbtn" data-fsglog="${g.id}">View log</button><button class="errbtn" data-fsarchive="${g.id}">Archive</button></div></div></div>`).join('')
|
|
6987
|
-
+ needsInput.map((g) => `<div class="drow err int"><div class="trow now"><span class="st run int"></span>${noSpan(g.id)}<span class="tt">${tt(g.text || g.plan?.[0], 60)}</span><span class="stword int">Needs input</span></div>
|
|
7010
|
+
+ needsInput.map((g) => `<div class="drow err int"><div class="trow now" data-fsgoalrow="${g.id}"><span class="st run int"></span>${noSpan(g.id)}<span class="tt">${tt(g.text || g.plan?.[0], 60)}</span><span class="stword int">Needs input</span></div>
|
|
6988
7011
|
<div class="errline"><div class="msg"><span class="x int">?</span>${tt(g.question?.text || 'A question is waiting on you', 180)}</div>
|
|
6989
7012
|
<div class="acts2"><button class="errbtn" data-fsglog="${g.id}">Answer</button></div></div></div>`).join('');
|
|
6990
7013
|
// Parked over-cap replies ride at the TOP of To Do (above the ordinary Next-up
|
|
@@ -6993,7 +7016,7 @@ function renderFsTodo(){
|
|
|
6993
7016
|
const canUseCodex = state.auth?.reason === 'rate-limited' && (state.agents || []).includes('codex');
|
|
6994
7017
|
const codexAction = (goalId, agent) => canUseCodex && agent === 'claude-code'
|
|
6995
7018
|
? `<button class="ab txt" data-fscodex="${goalId}" title="Start now with Codex">Run with Codex</button>` : '';
|
|
6996
|
-
const parkedRows = parkedReplyGoals.map((g) => `<div class="trow up" data-goalid="${g.id}"><span class="st"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword" style="color:var(--ink3)">Queued</span>${codexAction(g.id, g.agent)}${fsActs(g.id)}</div>`).join('');
|
|
7019
|
+
const parkedRows = parkedReplyGoals.map((g) => `<div class="trow up" data-goalid="${g.id}" data-fsgoalrow="${g.id}"><span class="st"></span>${noSpan(g.id)}<span class="tt">${tt(g.reviewSummary?.check || g.plan?.[0] || g.text, 60)}</span><span class="stword" style="color:var(--ink3)">Queued</span>${codexAction(g.id, g.agent)}${fsActs(g.id)}</div>`).join('');
|
|
6997
7020
|
const NCAP = 6;
|
|
6998
7021
|
const upShown = fsUI.todoMore ? queued : queued.slice(0, NCAP);
|
|
6999
7022
|
const seenGoals = new Set();
|
|
@@ -7008,7 +7031,7 @@ function renderFsTodo(){
|
|
|
7008
7031
|
<div class="reps">${reps.map((r) => `<div class="rep">${tt(r.detail ?? r.title, 120)}</div>`).join('')}</div></div>`;
|
|
7009
7032
|
}
|
|
7010
7033
|
}
|
|
7011
|
-
return `<div class="trow up${i >= 2 ? ' dim' : ''}" data-goalid="${t.goalId ?? ''}" data-taskid="${t.id}"><span class="st"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span>${codexAction(t.goalId, t.agent)}${fsActs(t.goalId)}</div>${grp}`;
|
|
7034
|
+
return `<div class="trow up${i >= 2 ? ' dim' : ''}" data-goalid="${t.goalId ?? ''}" data-taskid="${t.id}"${t.goalId != null ? ` data-fsgoalrow="${t.goalId}"` : ''}><span class="st"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span>${codexAction(t.goalId, t.agent)}${fsActs(t.goalId)}</div>${grp}`;
|
|
7012
7035
|
}).join('');
|
|
7013
7036
|
// CDO review 2026-07-08 §7: this header count used to be queued.length alone (the
|
|
7014
7037
|
// "Next up" pool only) — so a project sitting in Doing/Later with an empty Next-up
|
|
@@ -7035,20 +7058,25 @@ function renderFsTodo(){
|
|
|
7035
7058
|
<div class="tl">
|
|
7036
7059
|
${(doingRows || paused) ? `<span class="tlcap tlcap-doing" style="padding-top:0">Doing<button class="tlcap-pause" data-fspause="1" aria-pressed="${paused ? 'true' : 'false'}" title="${paused ? 'Resume — 再開' : 'Pause — 一時停止'}">${paused ? '<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>' : '<svg viewBox="0 0 24 24"><rect x="6" y="5" width="4" height="14" rx="1"/><rect x="14" y="5" width="4" height="14" rx="1"/></svg>'}</button></span>${doingRows}` : ''}
|
|
7037
7060
|
${needsRows ? `<span class="tlcap needs"${(doingRows || paused) ? '' : ' style="padding-top:0"'}>Needs you</span>${needsRows}` : ''}
|
|
7038
|
-
${rejected.length ? `<span class="tlcap rejcap"${(doingRows || paused) || needsRows ? '' : ' style="padding-top:0"'}>Dismissed<span class="num">${rejected.length}</span></span>${rejected.map((g) => `<div class="trow up rej"><span class="st rej"></span>${noSpan(g.id)}<span class="tt">${tt(g.text, 70)}</span><span class="acts"><button class="ab" data-fsreopen="${g.id}" title="Reopen — back to Review">${FS_ICONS.reopen}</button><button class="ab" data-fsrejdel="${g.id}" title="Delete — discard (closes the PR)">${FS_ICONS.del}</button></span></div>`).join('')}` : ''}
|
|
7061
|
+
${rejected.length ? `<span class="tlcap rejcap"${(doingRows || paused) || needsRows ? '' : ' style="padding-top:0"'}>Dismissed<span class="num">${rejected.length}</span></span>${rejected.map((g) => `<div class="trow up rej" data-fsgoalrow="${g.id}"><span class="st rej"></span>${noSpan(g.id)}<span class="tt">${tt(g.text, 70)}</span><span class="acts"><button class="ab" data-fsreopen="${g.id}" title="Reopen — back to Review">${FS_ICONS.reopen}</button><button class="ab" data-fsrejdel="${g.id}" title="Delete — discard (closes the PR)">${FS_ICONS.del}</button></span></div>`).join('')}` : ''}
|
|
7039
7062
|
${(parkedRows || upRows) ? `<span class="tlcap"${(doingRows || paused) || needsRows || rejected.length ? '' : ' style="padding-top:0"'}>Next up</span>${parkedRows}${upRows}` : ''}
|
|
7040
7063
|
${queued.length > NCAP ? `<div class="morefold" id="fsTodoMore">${fsUI.todoMore ? 'less' : `+${queued.length - NCAP} more`}<span class="g">›</span></div>` : ''}
|
|
7041
7064
|
${laterRows ? `<span class="tlcap">Later</span>${laterRows}` : ''}
|
|
7042
7065
|
<button class="qadd" data-fsnew="1" title="New goal"><span class="paplus"></span>New</button>
|
|
7043
7066
|
</div>
|
|
7044
7067
|
${doneAll.length ? `<div class="doneline${fsUI.doneOpen ? ' open' : ''}" id="fsDoneLine">✓ Done<span class="num">${doneAll.length}</span><span class="fold">▾</span></div>
|
|
7045
|
-
<div id="fsDonelist"${fsUI.doneOpen ? '' : ' hidden'}>${doneAll.map((t) => `<div class="trow up dim done"><span class="st ok"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span></div>`).join('')}</div>` : ''}`;
|
|
7068
|
+
<div id="fsDonelist"${fsUI.doneOpen ? '' : ' hidden'}>${doneAll.map((t) => `<div class="trow up dim done"${t.goalId != null ? ` data-fsgoalrow="${t.goalId}"` : ''}><span class="st ok"></span>${noSpan(t.goalId)}<span class="tt">${tt(t.title)}</span></div>`).join('')}</div>` : ''}`;
|
|
7046
7069
|
el.classList.toggle('folded', !!fsUI.todoFold);
|
|
7047
7070
|
$('fsTodoHd').onclick = () => { fsUI.todoFold = !fsUI.todoFold; el.classList.toggle('folded', fsUI.todoFold); };
|
|
7048
7071
|
const qadd = el.querySelector('.qadd[data-fsnew]'); // + New → focus the composer (the real create path)
|
|
7049
7072
|
if (qadd) qadd.onclick = () => $('input')?.focus();
|
|
7050
7073
|
const dl = el.querySelector('#fsDoneLine');
|
|
7051
7074
|
if (dl) dl.onclick = () => { fsUI.doneOpen = !fsUI.doneOpen; dl.classList.toggle('open', fsUI.doneOpen); el.querySelector('#fsDonelist').hidden = !fsUI.doneOpen; };
|
|
7075
|
+
for (const r of el.querySelectorAll('[data-fsgoalrow]')) r.onclick = (e) => {
|
|
7076
|
+
if (e.target.closest('button, a, input, .acts, .acts2, .errbtn, .ab, .repfold')) return;
|
|
7077
|
+
const id = Number(r.dataset.fsgoalrow);
|
|
7078
|
+
if (Number.isFinite(id)) fsOpenGoalDetail(id);
|
|
7079
|
+
};
|
|
7052
7080
|
for (const b of el.querySelectorAll('[data-fsreopen]')) b.onclick = async (e) => { e.stopPropagation();
|
|
7053
7081
|
const r = await fetch(withKey(`/api/goals/${b.dataset.fsreopen}/reopen`), { method: 'POST' });
|
|
7054
7082
|
if (!r.ok) showErr('Reopen failed.'); else await refresh(); };
|
package/package.json
CHANGED