local-kanban 1.8.1 → 1.8.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/CHANGELOG.md +23 -0
- package/README.md +18 -0
- package/README.ru.md +18 -0
- package/package.json +1 -1
- package/public/js/core.js +4 -0
- package/public/js/settings.js +21 -9
- package/public/style.css +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
Notable changes, newest first. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
4
4
|
and the project uses [semantic versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [1.8.2]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **The README now opens with how you actually use the thing.** It described what the board is and
|
|
11
|
+
how to install it, and left the loop itself — the part someone came for — to be inferred: write
|
|
12
|
+
the task in *Backlog*, move it to *To do*, copy that column as a job for Claude, and let it work
|
|
13
|
+
until it stops at *Review* for you to accept. Including the trick that makes it pay off: let
|
|
14
|
+
*Review* pile up, then ask for the whole column to be checked in one pass, from a fresh chat.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **The update line no longer draws over itself.** With both a status and a button on the right,
|
|
19
|
+
the two together were wider than the row had left, and the status — which was not allowed to
|
|
20
|
+
shrink — spilled leftwards across the label: 104 pixels of one text printed on top of another.
|
|
21
|
+
The status and the button now wrap onto their own line, and the command hint next to the button
|
|
22
|
+
is gone, because the button is the action and two instructions for one thing only made the row
|
|
23
|
+
longer.
|
|
24
|
+
- **Pressing the button now visibly does something.** `npm install -g` takes tens of seconds and
|
|
25
|
+
says nothing while it runs, so the board looked frozen and inviting a second click. It counts
|
|
26
|
+
out loud instead — *installing… 12s*, then *restarting… 3s* — and the button reads *Updating…*
|
|
27
|
+
until the page reloads itself.
|
|
28
|
+
|
|
6
29
|
## [1.8.1]
|
|
7
30
|
|
|
8
31
|
### Changed
|
package/README.md
CHANGED
|
@@ -10,6 +10,24 @@ A local kanban board built for working **in tandem with Claude Code**: the human
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
## How you use it
|
|
14
|
+
|
|
15
|
+
1. **Write the task** in the project's **Backlog** — a title is enough, the description can come later.
|
|
16
|
+
2. **Move it to *To do*** once it is ready to be picked up. That column is the queue.
|
|
17
|
+
3. **Press the copy button on the *To do* column** (*"Copy the tasks as a job for Claude"*) and paste
|
|
18
|
+
what it copied into Claude Code — the chat or the terminal, either works.
|
|
19
|
+
4. **Claude takes it from there.** It reads the task, does the work, leaves comments as it goes,
|
|
20
|
+
commits and pushes, deploys if the project has a deploy skill, ties the commit to a GitHub issue
|
|
21
|
+
if sync is on — and stops at **Review**. It never marks anything done.
|
|
22
|
+
5. **You accept.** Look at what came out and move the card to **Done**, or send it back with a note.
|
|
23
|
+
That note is the first thing Claude reads when it picks the task up again.
|
|
24
|
+
|
|
25
|
+
**A trick worth knowing.** Let *Review* pile up — ten, twenty, thirty cards. Then add one more task to
|
|
26
|
+
the board: *go through everything in Review, try to break it, move what survives to Done*. Claude will
|
|
27
|
+
take the whole column in one pass, and it saves a lot of clicking. Do that one **in a fresh chat**: the
|
|
28
|
+
session that just wrote the code is the worst judge of it, because it remembers what it meant rather
|
|
29
|
+
than what it actually shipped.
|
|
30
|
+
|
|
13
31
|
## Why
|
|
14
32
|
|
|
15
33
|
- **A queue for Claude, not just a board.** Statuses model the real loop: `backlog → todo → prep → doing → deploy → review → done`. Claude pulls the next task with `kb take`, works, and stops at *review* — accepting is always the human's call.
|
package/README.ru.md
CHANGED
|
@@ -10,6 +10,24 @@ English version — [README.md](README.md).
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
## Как этим пользоваться
|
|
14
|
+
|
|
15
|
+
1. **Пишешь задачу** в **Бэклог** проекта — достаточно заголовка, описание можно добавить позже.
|
|
16
|
+
2. **Перекладываешь в «Сделать»**, когда задача готова к работе. Эта колонка и есть очередь.
|
|
17
|
+
3. **Жмёшь кнопку копирования на колонке «Сделать»** (*«Скопировать задачи как задание для Claude»*)
|
|
18
|
+
и отдаёшь скопированное в Claude Code — в чат или в терминал, без разницы.
|
|
19
|
+
4. **Дальше Claude ведёт задачу сам.** Читает её, делает работу, по ходу пишет комментарии, коммитит
|
|
20
|
+
и пушит, деплоит, если у проекта задан деплой-скилл, привязывает коммит к issue на GitHub, если
|
|
21
|
+
включён синк, — и останавливается на **«Проверяю»**. В «Готово» он не переводит ничего и никогда.
|
|
22
|
+
5. **Принимаешь ты.** Смотришь, что получилось, и двигаешь карточку в **«Готово»** — или возвращаешь
|
|
23
|
+
с замечанием. Это замечание Claude прочитает первым, когда возьмёт задачу снова.
|
|
24
|
+
|
|
25
|
+
**Приём, который сильно ускоряет.** Дай «Проверяю» накопиться — десять, двадцать, тридцать карточек.
|
|
26
|
+
Потом заведи на доске одну задачу: *пройди всё в «Проверяю», попробуй сломать, что выдержало — переведи
|
|
27
|
+
в «Готово»*. Claude разберёт всю колонку за один заход. Только запускай её **в новом чате**: сессия,
|
|
28
|
+
которая только что писала этот код, — худший его проверяющий, потому что помнит, что хотела сделать,
|
|
29
|
+
а не то, что получилось.
|
|
30
|
+
|
|
13
31
|
## Зачем
|
|
14
32
|
|
|
15
33
|
- **Очередь для Claude, а не просто доска.** Статусы повторяют реальный цикл: `бэклог → сделать → подготовка → делаю → деплою → проверяю → готово`. Claude берёт следующую задачу через `kb take`, работает и останавливается на «Проверяю» — принимает всегда человек.
|
package/package.json
CHANGED
package/public/js/core.js
CHANGED
|
@@ -285,6 +285,7 @@ const I18N_RU = {
|
|
|
285
285
|
'in the backlog': 'в бэклоге',
|
|
286
286
|
'Install the kb command for Claude like this:': 'Команда kb для Claude ставится так:',
|
|
287
287
|
'Install the update': 'Установить обновление',
|
|
288
|
+
'installing…': 'ставлю обновление…',
|
|
288
289
|
'Interface language': 'Язык интерфейса',
|
|
289
290
|
'is gone from disk. Archive the project? Tasks are kept.': 'удалена с диска. Архивировать проект? Задачи сохранятся.',
|
|
290
291
|
'it has to go through work first': 'сначала через работу',
|
|
@@ -381,7 +382,9 @@ const I18N_RU = {
|
|
|
381
382
|
'Overwrite this file?': 'Перезаписать этот файл?',
|
|
382
383
|
'own skill': 'свой скилл',
|
|
383
384
|
'Paths & deploy': 'Пути и деплой',
|
|
385
|
+
'restarting…': 'перезапускаюсь…',
|
|
384
386
|
'Revert': 'Вернуть как в файле',
|
|
387
|
+
's': 'с',
|
|
385
388
|
'Save to the file': 'Сохранить в файл',
|
|
386
389
|
'saved ✓': 'сохранено ✓',
|
|
387
390
|
'saved ✓ · the previous version is kept as': 'сохранено ✓ · прежняя версия осталась как',
|
|
@@ -598,6 +601,7 @@ const I18N_RU = {
|
|
|
598
601
|
'updated — start the board again to pick it up': 'обновлено — запусти доску заново, чтобы она подхватила новую версию',
|
|
599
602
|
'updated, but the board did not come back — start it yourself': 'обновлено, но доска не поднялась — запусти её сам',
|
|
600
603
|
'updating…': 'обновляю…',
|
|
604
|
+
'Updating…': 'Обновляю…',
|
|
601
605
|
'Upload': 'Загрузить',
|
|
602
606
|
'used by': 'используют',
|
|
603
607
|
'Using the board at work': 'Пользуешься доской в работе',
|
package/public/js/settings.js
CHANGED
|
@@ -409,7 +409,7 @@ async function renderSection(sec) {
|
|
|
409
409
|
} else {
|
|
410
410
|
body.innerHTML = `<div class="sm-h">${tr('About')}</div>`
|
|
411
411
|
+ `<div class="set-row"><span class="set-lab">${tr('Code version')}<small id="about-ver">…</small></span><span class="about-fresh" id="about-fresh"></span></div>`
|
|
412
|
-
+ `<div class="set-row"><span class="set-lab"><span id="about-branch-lab">${tr('Board updates')}</span><small id="about-upd-sub">…</small></span>`
|
|
412
|
+
+ `<div class="set-row upd-row"><span class="set-lab"><span id="about-branch-lab">${tr('Board updates')}</span><small id="about-upd-sub">…</small></span>`
|
|
413
413
|
+ `<span class="about-upd-side"><span class="about-fresh" id="about-upd"></span>`
|
|
414
414
|
+ `<button class="btn-ghost hidden" id="about-do-upd">${tr('Install the update')}</button></span></div>`
|
|
415
415
|
+ `<div class="set-row hidden" id="about-dev-row"><span class="set-lab">${tr('dev branch')}<small id="about-dev-sub">…</small></span><span class="about-fresh" id="about-dev"></span></div>`
|
|
@@ -449,7 +449,7 @@ async function renderSection(sec) {
|
|
|
449
449
|
else if (u.update_available) {
|
|
450
450
|
el.textContent = `⟳ ${tr('update available')} ${u.local} → ${u.remote}`;
|
|
451
451
|
el.classList.add('stale-ver');
|
|
452
|
-
sub.textContent = `${what} ${u.local}${tag}
|
|
452
|
+
sub.textContent = `${what} ${u.local}${tag}`;
|
|
453
453
|
$('about-do-upd')?.classList.remove('hidden');
|
|
454
454
|
}
|
|
455
455
|
else if (u.dev?.ahead) { el.textContent = `${tr('in dev')} ${u.dev.ahead} ${commitsWord(u.dev.ahead)} ${tr('awaiting release')}`; el.classList.add('stale-ver'); sub.textContent = `${tr('commit')} ${u.local}${tag} · ${tr('matches GitHub')}`; }
|
|
@@ -468,19 +468,31 @@ async function renderSection(sec) {
|
|
|
468
468
|
.then(() => api('GET', '/api/update-check')).then(renderUpd)
|
|
469
469
|
.catch(() => { const s = $('about-upd-sub'); if (s) s.textContent = '—'; });
|
|
470
470
|
$('about-do-upd').onclick = async () => {
|
|
471
|
-
const btn = $('about-do-upd'); const sub = $('about-upd-sub');
|
|
471
|
+
const btn = $('about-do-upd'); const sub = $('about-upd-sub'); const el = $('about-upd');
|
|
472
472
|
if (!(await styledConfirm(tr('Update the board now? It will restart.')))) return;
|
|
473
|
-
|
|
473
|
+
const label = btn.textContent;
|
|
474
|
+
let phase = tr('installing…'); let sec = 0;
|
|
475
|
+
const paint = () => { el.textContent = `${phase} ${sec}${tr('s')}`; };
|
|
476
|
+
btn.disabled = true; btn.textContent = tr('Updating…');
|
|
477
|
+
el.classList.add('stale-ver'); paint();
|
|
478
|
+
const timer = setInterval(() => { sec += 1; paint(); }, 1000);
|
|
479
|
+
const stop = (text, keepAccent) => {
|
|
480
|
+
clearInterval(timer);
|
|
481
|
+
el.textContent = text || '';
|
|
482
|
+
el.classList.toggle('stale-ver', !!keepAccent);
|
|
483
|
+
btn.textContent = label;
|
|
484
|
+
};
|
|
474
485
|
let r;
|
|
475
486
|
try { r = await api('POST', '/api/update', null, { quiet: true }); }
|
|
476
|
-
catch (err) { sub.textContent = `${tr('the update did not go through')}: ${err.message || err}`; btn.disabled = false; return; }
|
|
477
|
-
if (!r?.ok) { sub.textContent = `${tr('the update did not go through')}: ${(r?.output || '').split('\n').pop() || '—'}`; btn.disabled = false; return; }
|
|
478
|
-
if (r.restart !== 'pm2') { sub.textContent = tr('updated — start the board again to pick it up'); return; }
|
|
479
|
-
|
|
487
|
+
catch (err) { stop(tr('Did not work out'), true); sub.textContent = `${tr('the update did not go through')}: ${err.message || err}`; btn.disabled = false; return; }
|
|
488
|
+
if (!r?.ok) { stop(tr('Did not work out'), true); sub.textContent = `${tr('the update did not go through')}: ${(r?.output || '').split('\n').pop() || '—'}`; btn.disabled = false; return; }
|
|
489
|
+
if (r.restart !== 'pm2') { stop('', false); sub.textContent = tr('updated — start the board again to pick it up'); return; }
|
|
490
|
+
phase = tr('restarting…'); sec = 0; paint();
|
|
480
491
|
for (let i = 0; i < 60; i++) {
|
|
481
492
|
await new Promise((ok) => setTimeout(ok, 1000));
|
|
482
|
-
try { await api('GET', '/api/about', null, { quiet: true }); location.reload(); return; } catch { }
|
|
493
|
+
try { await api('GET', '/api/about', null, { quiet: true }); clearInterval(timer); location.reload(); return; } catch { }
|
|
483
494
|
}
|
|
495
|
+
stop(tr('Did not work out'), true);
|
|
484
496
|
sub.textContent = tr('updated, but the board did not come back — start it yourself');
|
|
485
497
|
};
|
|
486
498
|
|
package/public/style.css
CHANGED
|
@@ -141,7 +141,10 @@ body {
|
|
|
141
141
|
}
|
|
142
142
|
.btn-ghost:hover { color: var(--text); }
|
|
143
143
|
a.btn-ghost { text-decoration: none; }
|
|
144
|
-
.about-upd-side { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
|
|
144
|
+
.about-upd-side { display: flex; align-items: center; gap: 10px; justify-content: flex-end; flex-shrink: 0; }
|
|
145
|
+
.set-row.upd-row { flex-wrap: wrap; }
|
|
146
|
+
.set-row.upd-row .set-lab { flex: 1 1 200px; min-width: 0; }
|
|
147
|
+
.set-row.upd-row .set-lab small { overflow-wrap: anywhere; }
|
|
145
148
|
.btn-danger {
|
|
146
149
|
background: none; border: 1px solid var(--border); border-radius: 7px;
|
|
147
150
|
padding: 7px 10px; cursor: pointer; color: var(--danger);
|