kandown 0.12.0 â 0.13.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/bin/kandown.js +584 -0
- package/bin/tui.js +109 -5
- package/dist/android-chrome-192x192.png +0 -0
- package/dist/android-chrome-512x512.png +0 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/favicon-16x16.png +0 -0
- package/dist/favicon-32x32.png +0 -0
- package/dist/favicon-48x48.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +25 -7
- package/dist/index.html +129 -122
- package/dist/kandownlogo.png +0 -0
- package/dist/manifest.json +19 -1
- package/dist/mstile-150x150.png +0 -0
- package/dist/og-image.png +0 -0
- package/package.json +1 -1
- package/templates/AGENT_KANDOWN.md +1 -0
package/bin/tui.js
CHANGED
|
@@ -54431,6 +54431,7 @@ function taskToBoardTask(task) {
|
|
|
54431
54431
|
priority: normalizePriority(frontmatter.priority),
|
|
54432
54432
|
ownerType: normalizeOwnerType(frontmatter.ownerType),
|
|
54433
54433
|
progress: total > 0 ? { done, total } : null,
|
|
54434
|
+
dependsOn: Array.isArray(frontmatter.depends_on) ? frontmatter.depends_on.filter((d) => typeof d === "string" && d.trim().length > 0) : [],
|
|
54434
54435
|
frontmatter: metadata
|
|
54435
54436
|
};
|
|
54436
54437
|
}
|
|
@@ -56684,7 +56685,7 @@ ${taskPrompt}`;
|
|
|
56684
56685
|
---
|
|
56685
56686
|
|
|
56686
56687
|
${taskPrompt}`;
|
|
56687
|
-
return ["gemini", "-
|
|
56688
|
+
return ["gemini", "--prompt-interactive", combined];
|
|
56688
56689
|
}
|
|
56689
56690
|
},
|
|
56690
56691
|
{
|
|
@@ -56723,8 +56724,13 @@ ${taskPrompt}`;
|
|
|
56723
56724
|
bin: "opencode",
|
|
56724
56725
|
description: "SST AI coding TUI",
|
|
56725
56726
|
interactive: true,
|
|
56726
|
-
buildCommand: ({ taskPrompt }) => {
|
|
56727
|
-
|
|
56727
|
+
buildCommand: ({ systemPrompt, taskPrompt }) => {
|
|
56728
|
+
const combined = `${systemPrompt}
|
|
56729
|
+
|
|
56730
|
+
---
|
|
56731
|
+
|
|
56732
|
+
${taskPrompt}`;
|
|
56733
|
+
return ["opencode", "--prompt", combined];
|
|
56728
56734
|
}
|
|
56729
56735
|
}
|
|
56730
56736
|
];
|
|
@@ -56807,7 +56813,12 @@ ${taskPrompt}`, "utf8");
|
|
|
56807
56813
|
}
|
|
56808
56814
|
if (isInTmux()) {
|
|
56809
56815
|
const shellCmd = buildShellCmd(binary, args);
|
|
56810
|
-
|
|
56816
|
+
const envPrefix = [
|
|
56817
|
+
`KANDOWN_CONTEXT_FILE=${shellescape(contextFile)}`,
|
|
56818
|
+
`KANDOWN_TASK_ID=${shellescape(taskId)}`,
|
|
56819
|
+
`KANDOWN_DIR=${shellescape(kandownDir)}`
|
|
56820
|
+
].join(" ");
|
|
56821
|
+
execSync(`tmux split-window -h -p 50 ${shellescape(`env ${envPrefix} ${shellCmd}`)}`, {
|
|
56811
56822
|
stdio: "inherit"
|
|
56812
56823
|
});
|
|
56813
56824
|
} else {
|
|
@@ -57041,6 +57052,14 @@ function SingleTaskRow({ task, focused, dragging, colWidth }) {
|
|
|
57041
57052
|
" "
|
|
57042
57053
|
] }),
|
|
57043
57054
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: dragging ? "yellow" : focused ? "cyan" : "yellow", bold: focused || dragging, children: idStr }),
|
|
57055
|
+
task.dependsOn && task.dependsOn.length > 0 && // ð TUI equivalent of the web card's `âŠN` chip. Surfaces blocked
|
|
57056
|
+
// work inline so the user doesn't try to move a task they can't
|
|
57057
|
+
// unblock without a different upstream task first.
|
|
57058
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "yellow", children: [
|
|
57059
|
+
" ",
|
|
57060
|
+
"\u21AA",
|
|
57061
|
+
task.dependsOn.length
|
|
57062
|
+
] }),
|
|
57044
57063
|
tag && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "white" : "magenta", bold: true, children: [
|
|
57045
57064
|
" ",
|
|
57046
57065
|
tag
|
|
@@ -57155,7 +57174,7 @@ function BoardHeader({ title, inTmux, modeHint, version, daemonStatus, daemonBus
|
|
|
57155
57174
|
const tmuxHint = inTmux ? " tmux" : "";
|
|
57156
57175
|
const versionTag = version ? ` v${version}` : "";
|
|
57157
57176
|
const daemonLabel = daemonBusy ? "\u25CC daemon\u2026" : daemonStatus.running ? `\u25CF web ${daemonStatus.metadata?.port ?? ""}` : "\u25CB web off";
|
|
57158
|
-
const hint = modeHint || "h/l cols \xB7 j/k tasks \xB7 drag tasks \xB7 d daemon \xB7 r reload \xB7 q quit";
|
|
57177
|
+
const hint = modeHint || "h/l cols \xB7 j/k tasks \xB7 drag tasks \xB7 a agent \xB7 g send-hook \xB7 d daemon \xB7 r reload \xB7 q quit";
|
|
57159
57178
|
const width = termWidth();
|
|
57160
57179
|
const leftWidth = Math.min(Math.max(34, Math.floor(width * 0.46)), width);
|
|
57161
57180
|
const daemonWidth = Math.min(16, Math.max(0, width - leftWidth));
|
|
@@ -57211,6 +57230,14 @@ function TaskDetail({ task, taskId, scrollOffset }) {
|
|
|
57211
57230
|
fm.assignee ? ` assignee: ${fm.assignee}` : "",
|
|
57212
57231
|
fm.due ? ` due: ${fm.due}` : ""
|
|
57213
57232
|
] }) }),
|
|
57233
|
+
Array.isArray(fm.depends_on) && fm.depends_on.length > 0 && // ð TUI equivalent of the web Drawer's dependency chips. Lists raw
|
|
57234
|
+
// ids â the user can `cat tasks/<id>.md` to inspect any one. We don't
|
|
57235
|
+
// resolve live status here (read-only context) but the move gate in
|
|
57236
|
+
// the TUI blocks the user from moving into Done until they're clear.
|
|
57237
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, children: [
|
|
57238
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "depends on: " }),
|
|
57239
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: fm.depends_on.join(", ") })
|
|
57240
|
+
] }),
|
|
57214
57241
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "\u2500".repeat(termWidth() - 4) }),
|
|
57215
57242
|
visibleLines.map((line, idx) => {
|
|
57216
57243
|
const isH = RE_HEADER.test(line);
|
|
@@ -57366,6 +57393,61 @@ function Board({ kandownDir, version }) {
|
|
|
57366
57393
|
setTimeout(() => setStatusMsg(""), 2500);
|
|
57367
57394
|
}
|
|
57368
57395
|
}, [daemonBusy, kandownDir, preferredDaemonPort]);
|
|
57396
|
+
const tryMoveWithGate = (0, import_react37.useCallback)((taskId, targetCol) => {
|
|
57397
|
+
if (!board) return false;
|
|
57398
|
+
const cfg = loadConfig(kandownDir);
|
|
57399
|
+
const cols = cfg.board.columns;
|
|
57400
|
+
const terminalLower = (cols[cols.length - 1] || "Done").toLowerCase();
|
|
57401
|
+
const isTerminal = targetCol.toLowerCase() === terminalLower;
|
|
57402
|
+
if (!isTerminal) return true;
|
|
57403
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
57404
|
+
for (const col of board.columns) {
|
|
57405
|
+
for (const t of col.tasks) {
|
|
57406
|
+
const isArch = t.frontmatter && (t.frontmatter.archived === true || t.frontmatter.archived === "true");
|
|
57407
|
+
resolved.set(t.id, isArch || col.name.toLowerCase() === terminalLower);
|
|
57408
|
+
}
|
|
57409
|
+
}
|
|
57410
|
+
const movingTask = board.columns.flatMap((c) => c.tasks).find((t) => t.id === taskId);
|
|
57411
|
+
if (!movingTask) return true;
|
|
57412
|
+
const deps = Array.isArray(movingTask.dependsOn) ? movingTask.dependsOn : [];
|
|
57413
|
+
const blocked = [];
|
|
57414
|
+
for (const dep of deps) {
|
|
57415
|
+
if (typeof dep !== "string" || !dep.trim() || dep === taskId) continue;
|
|
57416
|
+
const r = resolved.get(dep);
|
|
57417
|
+
if (!r) blocked.push(dep);
|
|
57418
|
+
}
|
|
57419
|
+
if (blocked.length > 0) {
|
|
57420
|
+
const list = blocked.length === 1 ? blocked[0] : `${blocked.slice(0, -1).join(", ")} and ${blocked[blocked.length - 1]}`;
|
|
57421
|
+
setStatusMsg(`Blocked: ${taskId} \u2190 ${list}`);
|
|
57422
|
+
setTimeout(() => setStatusMsg(""), 3500);
|
|
57423
|
+
return false;
|
|
57424
|
+
}
|
|
57425
|
+
return true;
|
|
57426
|
+
}, [board, kandownDir]);
|
|
57427
|
+
const sendTaskToAgentHook = (0, import_react37.useCallback)(async (taskId) => {
|
|
57428
|
+
const status = await getDaemonStatus(kandownDir);
|
|
57429
|
+
if (!status.running || !status.metadata) {
|
|
57430
|
+
setStatusMsg("Web daemon not running (press d to start)");
|
|
57431
|
+
setTimeout(() => setStatusMsg(""), 2500);
|
|
57432
|
+
return;
|
|
57433
|
+
}
|
|
57434
|
+
try {
|
|
57435
|
+
const res = await fetch(`http://127.0.0.1:${status.metadata.port}/api/tasks/${encodeURIComponent(taskId)}/agent`, {
|
|
57436
|
+
method: "POST",
|
|
57437
|
+
signal: AbortSignal.timeout(8e3)
|
|
57438
|
+
});
|
|
57439
|
+
if (res.ok) {
|
|
57440
|
+
setStatusMsg(`Sent ${taskId} to agent hook`);
|
|
57441
|
+
} else {
|
|
57442
|
+
const body = await res.text().catch(() => "");
|
|
57443
|
+
setStatusMsg(`Agent hook: ${res.status}${body ? " \u2014 " + body.slice(0, 60) : ""}`);
|
|
57444
|
+
}
|
|
57445
|
+
} catch (error) {
|
|
57446
|
+
setStatusMsg(`Agent hook failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
57447
|
+
} finally {
|
|
57448
|
+
setTimeout(() => setStatusMsg(""), 3e3);
|
|
57449
|
+
}
|
|
57450
|
+
}, [kandownDir]);
|
|
57369
57451
|
const getFocusedTask = (0, import_react37.useCallback)(() => {
|
|
57370
57452
|
if (!board) return null;
|
|
57371
57453
|
const col = board.columns[colIndex];
|
|
@@ -57506,6 +57588,11 @@ function Board({ kandownDir, version }) {
|
|
|
57506
57588
|
if (y === placeholderY) {
|
|
57507
57589
|
const targetColName = col.name;
|
|
57508
57590
|
if (moveTaskId) {
|
|
57591
|
+
if (!tryMoveWithGate(moveTaskId, targetColName)) {
|
|
57592
|
+
setMoveTaskId(null);
|
|
57593
|
+
setMode("browse");
|
|
57594
|
+
return;
|
|
57595
|
+
}
|
|
57509
57596
|
moveTaskToColumn(kandownDir, moveTaskId, targetColName);
|
|
57510
57597
|
const loaded = readBoard(kandownDir);
|
|
57511
57598
|
setBoard(loaded);
|
|
@@ -57576,6 +57663,12 @@ function Board({ kandownDir, version }) {
|
|
|
57576
57663
|
if (targetCol >= 0 && targetCol !== taskDrag.sourceCol) {
|
|
57577
57664
|
const targetColName = board.columns[targetCol]?.name;
|
|
57578
57665
|
if (targetColName) {
|
|
57666
|
+
if (!tryMoveWithGate(taskDrag.taskId, targetColName)) {
|
|
57667
|
+
setTaskDrag(null);
|
|
57668
|
+
setMousePress(null);
|
|
57669
|
+
setMode("browse");
|
|
57670
|
+
return;
|
|
57671
|
+
}
|
|
57579
57672
|
moveTaskToColumn(kandownDir, taskDrag.taskId, targetColName);
|
|
57580
57673
|
const loaded = readBoard(kandownDir);
|
|
57581
57674
|
setBoard(loaded);
|
|
@@ -57664,6 +57757,12 @@ function Board({ kandownDir, version }) {
|
|
|
57664
57757
|
setMode("agent-picker");
|
|
57665
57758
|
return;
|
|
57666
57759
|
}
|
|
57760
|
+
if (input === "g") {
|
|
57761
|
+
const task = getFocusedTask();
|
|
57762
|
+
if (!task) return;
|
|
57763
|
+
void sendTaskToAgentHook(task.id);
|
|
57764
|
+
return;
|
|
57765
|
+
}
|
|
57667
57766
|
}
|
|
57668
57767
|
if (mode === "context-menu") {
|
|
57669
57768
|
if (key.escape || input === "q") {
|
|
@@ -57731,6 +57830,11 @@ function Board({ kandownDir, version }) {
|
|
|
57731
57830
|
if (!board || !moveTaskId) return;
|
|
57732
57831
|
const name = board.columns[moveTargetCol]?.name;
|
|
57733
57832
|
if (name) {
|
|
57833
|
+
if (!tryMoveWithGate(moveTaskId, name)) {
|
|
57834
|
+
setMoveTaskId(null);
|
|
57835
|
+
setMode("browse");
|
|
57836
|
+
return;
|
|
57837
|
+
}
|
|
57734
57838
|
moveTaskToColumn(kandownDir, moveTaskId, name);
|
|
57735
57839
|
const loaded = readBoard(kandownDir);
|
|
57736
57840
|
setBoard(loaded);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/favicon.ico
ADDED
|
Binary file
|
package/dist/favicon.svg
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
<!--
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
@file public/favicon.svg
|
|
4
|
+
@description Kandown brand mark â vector favicon.
|
|
5
|
+
ð Hand-traced from kandownlogo.png. Optimised for 16â256px rendering.
|
|
6
|
+
Colors: dark surface #0a0a0a, K strokes #ffffff, lime accent #cef867.
|
|
7
|
+
The lime slash is a parallelogram that slices from top-right to bottom-left,
|
|
8
|
+
crossing the K. Z-order is: surface â K strokes â lime slash.
|
|
9
|
+
-->
|
|
10
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-label="Kandown">
|
|
11
|
+
<title>Kandown</title>
|
|
12
|
+
<rect width="256" height="256" rx="48" ry="48" fill="#0a0a0a"/>
|
|
13
|
+
|
|
14
|
+
<!-- K vertical bar (left stem) -->
|
|
15
|
+
<rect x="50" y="36" width="34" height="184" rx="3" ry="3" fill="#ffffff"/>
|
|
16
|
+
|
|
17
|
+
<!-- K upper diagonal: middle-left â top-right -->
|
|
18
|
+
<path d="M 84 128 L 200 36 L 222 36 L 84 152 Z" fill="#ffffff"/>
|
|
19
|
+
|
|
20
|
+
<!-- K lower diagonal: middle-left â bottom-right (with the small flag/notch on the right) -->
|
|
21
|
+
<path d="M 84 128 L 200 220 L 222 220 L 188 186 L 200 174 L 84 132 Z" fill="#ffffff"/>
|
|
22
|
+
|
|
23
|
+
<!-- Lime diagonal slash: thick parallelogram from top-right to bottom-left -->
|
|
24
|
+
<path d="M 232 22 L 174 22 L 24 172 L 24 230 Z" fill="#cef867"/>
|
|
25
|
+
</svg>
|