local-kanban 1.8.1 → 1.8.3

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 CHANGED
@@ -3,6 +3,38 @@
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.3]
7
+
8
+ ### Added
9
+
10
+ - **A roadmap at the end of both READMEs.** More interface languages, working with Codex the way it
11
+ works with Claude Code, plugins with declared permissions instead of blind trust, a board a team
12
+ can share over a local network, and attaching a CLI straight to a card. Plans, not promises —
13
+ written down so the direction is visible to anyone deciding whether to build on this.
14
+
15
+ ## [1.8.2]
16
+
17
+ ### Added
18
+
19
+ - **The README now opens with how you actually use the thing.** It described what the board is and
20
+ how to install it, and left the loop itself — the part someone came for — to be inferred: write
21
+ the task in *Backlog*, move it to *To do*, copy that column as a job for Claude, and let it work
22
+ until it stops at *Review* for you to accept. Including the trick that makes it pay off: let
23
+ *Review* pile up, then ask for the whole column to be checked in one pass, from a fresh chat.
24
+
25
+ ### Fixed
26
+
27
+ - **The update line no longer draws over itself.** With both a status and a button on the right,
28
+ the two together were wider than the row had left, and the status — which was not allowed to
29
+ shrink — spilled leftwards across the label: 104 pixels of one text printed on top of another.
30
+ The status and the button now wrap onto their own line, and the command hint next to the button
31
+ is gone, because the button is the action and two instructions for one thing only made the row
32
+ longer.
33
+ - **Pressing the button now visibly does something.** `npm install -g` takes tens of seconds and
34
+ says nothing while it runs, so the board looked frozen and inviting a second click. It counts
35
+ out loud instead — *installing… 12s*, then *restarting… 3s* — and the button reads *Updating…*
36
+ until the page reloads itself.
37
+
6
38
  ## [1.8.1]
7
39
 
8
40
  ### 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
  ![Dashboard](docs/screenshot-dashboard.png)
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.
@@ -145,6 +163,24 @@ The board is MIT-licensed and free for everyone: nothing is switched off and the
145
163
  activate. If it earns you money — inside a company, or on paid client work — $12 a year per person
146
164
  keeps it maintained. A request, not a rule: [docs/COMMERCIAL.md](docs/COMMERCIAL.md).
147
165
 
166
+ ## What's planned
167
+
168
+ Not promises — the direction the project is heading:
169
+
170
+ - **More interface languages.** English and Russian are in. The dictionary is a single file, so a
171
+ translation is a pull request rather than a project.
172
+ - **Working with Codex** the way it works with Claude Code. The `kb` CLI and the status model are not
173
+ Claude-specific; only the skills are.
174
+ - **Plugins, done properly** — a manifest with declared permissions and a sandboxed frame, not a
175
+ checkbox that trusts whatever you installed. A plugin that can read your tasks and reach the
176
+ network has to say so before it runs.
177
+ - **A board a team can share** — over one local network to start with, without turning a local-first
178
+ tool into a service that needs accounts, a server and someone to run it.
179
+ - **Attaching a CLI straight to the board**, so an agent can be driven from the card instead of from
180
+ a terminal window next to it.
181
+
182
+ And the long one: get the dream job at Anthropic and build this for everyone.
183
+
148
184
  ## License
149
185
 
150
186
  [MIT](LICENSE).
package/README.ru.md CHANGED
@@ -10,6 +10,24 @@ English version — [README.md](README.md).
10
10
 
11
11
  ![Дашборд](docs/screenshot-dashboard.png)
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`, работает и останавливается на «Проверяю» — принимает всегда человек.
@@ -144,6 +162,24 @@ node --test # БД изолируется через KB_DATA_DIR
144
162
  зарабатывает тебе деньги — в компании или на платных заказах — $12 в год за человека помогают её
145
163
  поддерживать. Это просьба, а не правило: [docs/COMMERCIAL.md](docs/COMMERCIAL.md).
146
164
 
165
+ ## Планы
166
+
167
+ Не обещания — направление, в котором проект движется:
168
+
169
+ - **Больше языков интерфейса.** Английский и русский уже есть. Словарь лежит в одном файле, так что
170
+ перевод — это пул-реквест, а не проект.
171
+ - **Работа с Codex** так же, как сейчас с Claude Code. CLI `kb` и модель статусов не привязаны к
172
+ Claude — привязаны только скиллы.
173
+ - **Плагины по-честному** — манифест с заявленными правами и изолированный фрейм, а не галочка
174
+ «доверяю». Плагин, который может читать твои задачи и ходить в сеть, обязан сказать об этом до
175
+ запуска, а не после.
176
+ - **Доска на команду** — для начала в пределах одной локальной сети и без превращения локального
177
+ инструмента в сервис, которому нужны аккаунты, сервер и кто-то, кто его поддерживает.
178
+ - **Подключение CLI прямо в доску**, чтобы агентом можно было управлять из карточки, а не из
179
+ соседнего окна терминала.
180
+
181
+ И самое дальнее: устроиться на работу мечты в Anthropic и сделать это для всех пользователей.
182
+
147
183
  ## Лицензия
148
184
 
149
185
  [MIT](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-kanban",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "license": "MIT",
5
5
  "author": "Dannylamao",
6
6
  "repository": {
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': 'Пользуешься доской в работе',
@@ -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} · ${updateHint()}`;
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
- btn.disabled = true; sub.textContent = tr('updating…');
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
- sub.textContent = tr('waiting for the board to come back…');
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);