kanbot 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (30) hide show
  1. {kanbot-0.2.2 → kanbot-0.2.3}/PKG-INFO +1 -1
  2. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/__init__.py +1 -1
  3. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/app.py +1 -1
  4. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/db.py +1 -2
  5. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/hub.py +6 -8
  6. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/static/app.js +9 -10
  7. {kanbot-0.2.2 → kanbot-0.2.3}/pyproject.toml +1 -1
  8. {kanbot-0.2.2 → kanbot-0.2.3}/scripts/e2e.py +2 -2
  9. {kanbot-0.2.2 → kanbot-0.2.3}/.gitignore +0 -0
  10. {kanbot-0.2.2 → kanbot-0.2.3}/LICENSE +0 -0
  11. {kanbot-0.2.2 → kanbot-0.2.3}/README.md +0 -0
  12. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/__main__.py +0 -0
  13. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/agents.py +0 -0
  14. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/cli.py +0 -0
  15. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/config.py +0 -0
  16. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/profiles.py +0 -0
  17. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/runner/__init__.py +0 -0
  18. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/runner/agents.py +0 -0
  19. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/runner/discovery.py +0 -0
  20. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/runner/worker.py +0 -0
  21. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/__init__.py +0 -0
  22. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/insights.py +0 -0
  23. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/schemas.py +0 -0
  24. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/static/index.html +0 -0
  25. {kanbot-0.2.2 → kanbot-0.2.3}/kanbot/server/static/styles.css +0 -0
  26. {kanbot-0.2.2 → kanbot-0.2.3}/scripts/deploy.sh +0 -0
  27. {kanbot-0.2.2 → kanbot-0.2.3}/scripts/e2e_live.py +0 -0
  28. {kanbot-0.2.2 → kanbot-0.2.3}/scripts/e2e_loop.py +0 -0
  29. {kanbot-0.2.2 → kanbot-0.2.3}/scripts/e2e_revive.py +0 -0
  30. {kanbot-0.2.2 → kanbot-0.2.3}/vercel.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kanbot
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Kanban board where every card is a task run by your local CLI coding agents (Claude, Codex, Gemini, GLM/ZAI, or any CLI you define).
5
5
  Project-URL: Homepage, https://github.com/publu/kanbot
6
6
  Project-URL: Repository, https://github.com/publu/kanbot
@@ -1,3 +1,3 @@
1
1
  """Deckhand — a Kanban board where every card is a task run by your local CLI coding agents."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.3"
@@ -72,7 +72,7 @@ def create_app(db_path: Optional[str] = None) -> FastAPI:
72
72
  kind = col["kind"]
73
73
  # Dropping into Running means "run it" -> queue for dispatch (unless it's
74
74
  # already executing). Other columns just park the card.
75
- mapping = {"backlog": "idle", "review": "review", "done": "done"}
75
+ mapping = {"backlog": "idle", "done": "done"}
76
76
  if kind == "running":
77
77
  if card["status"] not in ("running", "queued"):
78
78
  db.update_card(card["id"], status="queued")
@@ -114,7 +114,6 @@ CREATE INDEX IF NOT EXISTS idx_events_session ON session_events(session_id);
114
114
  DEFAULT_COLUMNS = [
115
115
  ("Backlog", "backlog"),
116
116
  ("Running", "running"),
117
- ("Review", "review"),
118
117
  ("Done", "done"),
119
118
  ]
120
119
 
@@ -177,7 +176,7 @@ class DB:
177
176
  # Drop deprecated columns from older boards, relocating any stray cards:
178
177
  # info -> backlog (sessions now live inline by recency)
179
178
  # queued -> running (a card is queued via status, not a column)
180
- deprecated = {"info": "backlog", "queued": "running"}
179
+ deprecated = {"info": "backlog", "queued": "running", "review": "done"}
181
180
  for board in self.q("SELECT id FROM boards"):
182
181
  bid = board["id"]
183
182
  changed = False
@@ -208,15 +208,13 @@ class Hub:
208
208
  self.db.update_session(sid, status=status, exit_code=exit_code, ended_at=now())
209
209
  card = self.db.get_card(sess["card_id"])
210
210
  if card:
211
- new_status = "review" if status == "success" else status
211
+ # Any finished run (success or failure) leaves Running and lands in
212
+ # Done, carrying its status so the card shows ✓ done / ✗ failed.
213
+ new_status = "done" if status == "success" else status
212
214
  self.db.update_card(card["id"], status=new_status)
213
- if card.get("auto_advance", 1):
214
- target_kind = "review" if status == "success" else None
215
- if target_kind:
216
- col = self.db.column_by_kind(card["board_id"], target_kind)
217
- if col:
218
- self.db.move_card(card["id"], col["id"],
219
- self.db._next_position(col["id"]))
215
+ col = self.db.column_by_kind(card["board_id"], "done")
216
+ if col:
217
+ self.db.move_card(card["id"], col["id"], self.db._next_position(col["id"]))
220
218
  await self._emit_card(card["id"])
221
219
  # Free the runner slot.
222
220
  runner = self.runners.get(sess["runner_id"])
@@ -423,8 +423,15 @@ function renderColumns() {
423
423
  // (stale sessions ARE the backlog). No dedicated column.
424
424
  const RECENT_DONE_S = 30 * 60;
425
425
  const nowS = Date.now() / 1000;
426
+ // Dedupe: a session adopted as a card (resumed) is represented by that card,
427
+ // so hide its discovered twin. Also hide an active session whose cwd matches a
428
+ // currently-running task card (that's the card's own session writing to disk).
429
+ const adopted = new Set(S.cards.filter(c => c.resume_of).map(c => c.resume_of));
430
+ const runningCwds = new Set(S.cards.filter(c => c.status === 'running' && c.cwd).map(c => c.cwd));
426
431
  const byBucket = { backlog: [], running: [], done: [] };
427
432
  for (const s of S.agentSessions) {
433
+ if (adopted.has(s.session_id)) continue;
434
+ if (s.active && runningCwds.has(s.cwd)) continue;
428
435
  if (s.active) byBucket.running.push(s);
429
436
  else if ((nowS - (s.mtime || 0)) <= RECENT_DONE_S) byBucket.done.push(s);
430
437
  else byBucket.backlog.push(s);
@@ -780,14 +787,6 @@ function renderDrawerBody(card) {
780
787
  (max, until) => patchCard(card.id, { loop_max: max, loop_until: until }));
781
788
  body.appendChild(loop.wrap);
782
789
 
783
- // auto-advance toggle
784
- const tg = el('label', 'toggle');
785
- const cb = el('input'); cb.type = 'checkbox'; cb.checked = !!card.auto_advance;
786
- cb.onchange = () => patchCard(card.id, { auto_advance: cb.checked });
787
- tg.appendChild(cb); tg.appendChild(el('span', 'track'));
788
- tg.appendChild(el('span', null, 'Auto-advance to Review on success'));
789
- body.appendChild(tg);
790
-
791
790
  // tags
792
791
  body.appendChild(renderTagsField(card));
793
792
 
@@ -1084,10 +1083,10 @@ CONTENT TYPE
1084
1083
  CORE MODEL
1085
1084
  Board { id, name, repo_path, created_at }
1086
1085
  Column { id, board_id, name, kind, position }
1087
- kind in: backlog | running | review | done (queue is a status, not a column)
1086
+ kind in: backlog | running | done (queue is a status, not a column)
1088
1087
  Card { id, board_id, column_id, title, prompt, agent, cwd, status,
1089
1088
  position, auto_advance, resume_of, pin_runner, tags[], created_at, updated_at }
1090
- status in: idle | queued | running | review | done | failed | cancelled
1089
+ status in: idle | queued | running | done | failed | cancelled
1091
1090
  agent: "auto" or an agent name (claude, codex, gemini, glm, shell, ...)
1092
1091
  resume_of: an external session id this card resumes (optional)
1093
1092
  pin_runner: restrict execution to one runner id (optional)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kanbot"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A Kanban board where every card is a task run by your local CLI coding agents (Claude, Codex, Gemini, GLM/ZAI, or any CLI you define)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -74,9 +74,9 @@ def main():
74
74
 
75
75
  card_after = next(x for x in c.get(f"/api/boards/{bid}").json()["cards"] if x["id"] == card["id"])
76
76
  print("card status after run:", card_after["status"])
77
- assert card_after["status"] == "review", f"expected review, got {card_after['status']}"
77
+ assert card_after["status"] == "done", f"expected done, got {card_after['status']}"
78
78
 
79
- print("\n✅ E2E PASSED: task queued, executed by runner, streamed logs, auto-advanced to Review.")
79
+ print("\n✅ E2E PASSED: task queued, executed by runner, streamed logs, landed in Done.")
80
80
 
81
81
 
82
82
  if __name__ == "__main__":
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes