kanbango 3.8.0 → 5.1.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/AGENTS.md +23 -18
- package/API.md +58 -170
- package/CHANGELOG.md +39 -1
- package/LLM_AGENTS.md +21 -502
- package/README.md +18 -16
- package/agent-playbook.js +18 -57
- package/bin/kanban.js +344 -304
- package/config-wizard.js +4 -4
- package/index.html +59 -2
- package/kanban.js +503 -283
- package/mcp-server.js +259 -201
- package/package.json +1 -1
- package/plan.js +19 -24
- package/plugins/tui-kanban-controller.js +117 -0
- package/plugins/tui-kanban.tsx +314 -0
- package/workflow.js +15 -14
- package/.ai/lessons.jsonl +0 -14
- package/.ai/retro/close.json +0 -1
- package/.ai/retro/last-run.json +0 -1
- package/.ait-quality.yml +0 -26
- package/bin/kanban-cmd.js +0 -40
- package/kan2.md +0 -76
- package/kanbango.md +0 -48
- package/planv2.md +0 -317
- package/tests/fixtures/fake-opencode.js +0 -69
- package/tests/index.js +0 -19
- package/tests/kanban-cli.js +0 -118
- package/tests/kanban.js +0 -104
- package/tests/run.js +0 -38
package/config-wizard.js
CHANGED
|
@@ -8,10 +8,10 @@ const path = require('path');
|
|
|
8
8
|
const CONFIG_REL = path.join('backlog', 'kanbango.json');
|
|
9
9
|
|
|
10
10
|
const DEFAULT_ANSWERS = {
|
|
11
|
-
testing:
|
|
12
|
-
testingAgent:
|
|
13
|
-
review:
|
|
14
|
-
reviewAgent:
|
|
11
|
+
testing: false,
|
|
12
|
+
testingAgent: false,
|
|
13
|
+
review: false,
|
|
14
|
+
reviewAgent: false
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
const ITEMS = [
|
package/index.html
CHANGED
|
@@ -238,6 +238,23 @@ header h1 { font-size: 14px; font-weight: 700; letter-spacing: -0.2px; }
|
|
|
238
238
|
border-radius: 10px;
|
|
239
239
|
padding: 1px 7px;
|
|
240
240
|
}
|
|
241
|
+
.now-context {
|
|
242
|
+
font-size: 10px;
|
|
243
|
+
font-weight: 700;
|
|
244
|
+
color: var(--active);
|
|
245
|
+
margin-left: auto;
|
|
246
|
+
letter-spacing: 0;
|
|
247
|
+
text-transform: none;
|
|
248
|
+
}
|
|
249
|
+
.card-blocked {
|
|
250
|
+
font-size: 10px;
|
|
251
|
+
font-weight: 700;
|
|
252
|
+
color: #991b1b;
|
|
253
|
+
background: #fee2e2;
|
|
254
|
+
border-radius: 4px;
|
|
255
|
+
padding: 1px 6px;
|
|
256
|
+
margin-top: 6px;
|
|
257
|
+
}
|
|
241
258
|
.now-cards {
|
|
242
259
|
display: flex;
|
|
243
260
|
flex-wrap: wrap;
|
|
@@ -793,6 +810,7 @@ header h1 { font-size: 14px; font-weight: 700; letter-spacing: -0.2px; }
|
|
|
793
810
|
<div class="now-head">
|
|
794
811
|
<span class="now-title">NOW</span>
|
|
795
812
|
<span class="now-count" id="now-count">0</span>
|
|
813
|
+
<span class="now-context" id="now-context"></span>
|
|
796
814
|
</div>
|
|
797
815
|
<div class="now-cards" id="now-cards"></div>
|
|
798
816
|
</div>
|
|
@@ -831,6 +849,7 @@ let boardConfig = null;
|
|
|
831
849
|
|
|
832
850
|
let allTasks = [];
|
|
833
851
|
let allEpicEntities = [];
|
|
852
|
+
let agentContext = null;
|
|
834
853
|
/** @type {null|string} null=all, "—"=no epic, "E001"=epic id */
|
|
835
854
|
let filterKey = null;
|
|
836
855
|
/** @type {null|{kind:'task'|'epic', id:string}} */
|
|
@@ -890,10 +909,11 @@ function rebuildNewTaskColSelect() {
|
|
|
890
909
|
|
|
891
910
|
async function load() {
|
|
892
911
|
const q = showArchived ? "?include_archived=true" : "";
|
|
893
|
-
const [boardRes, epicsRes, configRes] = await Promise.all([
|
|
912
|
+
const [boardRes, epicsRes, configRes, contextRes] = await Promise.all([
|
|
894
913
|
fetch("/api/board" + q),
|
|
895
914
|
fetch("/api/epics" + q),
|
|
896
|
-
fetch("/api/config")
|
|
915
|
+
fetch("/api/config"),
|
|
916
|
+
fetch("/api/context")
|
|
897
917
|
]);
|
|
898
918
|
if (!configRes.ok) {
|
|
899
919
|
toast('Board config failed to load — using default columns', true);
|
|
@@ -902,6 +922,7 @@ async function load() {
|
|
|
902
922
|
}
|
|
903
923
|
allTasks = await boardRes.json();
|
|
904
924
|
allEpicEntities = epicsRes.ok ? await epicsRes.json() : [];
|
|
925
|
+
agentContext = contextRes.ok ? await contextRes.json() : null;
|
|
905
926
|
refreshEpicDatalist();
|
|
906
927
|
// Drop paper target if entity vanished
|
|
907
928
|
if (paperTarget) {
|
|
@@ -1028,6 +1049,17 @@ function makeRailItem({ key, title, idLabel, count, status, on, entity }) {
|
|
|
1028
1049
|
function renderNow(tasks) {
|
|
1029
1050
|
const active = tasks.filter(t => t.column === "active");
|
|
1030
1051
|
document.getElementById("now-count").textContent = String(active.length);
|
|
1052
|
+
const ctxEl = document.getElementById("now-context");
|
|
1053
|
+
if (ctxEl) {
|
|
1054
|
+
const action = agentContext && agentContext.next_action;
|
|
1055
|
+
if (!action) {
|
|
1056
|
+
ctxEl.textContent = "";
|
|
1057
|
+
} else {
|
|
1058
|
+
const bits = ["next_action: " + action];
|
|
1059
|
+
if (agentContext.task_id) bits.push(agentContext.task_id);
|
|
1060
|
+
ctxEl.textContent = bits.join(" · ");
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1031
1063
|
const wrap = document.getElementById("now-cards");
|
|
1032
1064
|
wrap.innerHTML = "";
|
|
1033
1065
|
if (active.length === 0) {
|
|
@@ -1122,6 +1154,15 @@ function renderCard(task, colId) {
|
|
|
1122
1154
|
titleEl.textContent = displayTitle;
|
|
1123
1155
|
card.appendChild(titleEl);
|
|
1124
1156
|
|
|
1157
|
+
const unmet = Array.isArray(task.unmet_dependencies) ? task.unmet_dependencies : [];
|
|
1158
|
+
if (task.blocked || unmet.length > 0) {
|
|
1159
|
+
const blockedEl = el("div", "card-blocked");
|
|
1160
|
+
blockedEl.textContent = unmet.length
|
|
1161
|
+
? "blocked by " + unmet.join(", ")
|
|
1162
|
+
: "blocked";
|
|
1163
|
+
card.appendChild(blockedEl);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1125
1166
|
if (totalT > 0 && colId !== "icebox") {
|
|
1126
1167
|
const pr = el("div", "prog-row");
|
|
1127
1168
|
pr.innerHTML = `
|
|
@@ -1259,6 +1300,13 @@ function renderTaskPaperView(paper, task) {
|
|
|
1259
1300
|
];
|
|
1260
1301
|
if (task.epic_id) badges.push({ text: task.epic_id, cls: "" });
|
|
1261
1302
|
if (task.plan && task.plan.status) badges.push({ text: "plan:" + task.plan.status, cls: "" });
|
|
1303
|
+
const paperUnmet = Array.isArray(task.unmet_dependencies) ? task.unmet_dependencies : [];
|
|
1304
|
+
if (task.blocked || paperUnmet.length > 0) {
|
|
1305
|
+
badges.push({
|
|
1306
|
+
text: paperUnmet.length ? "blocked by " + paperUnmet.join(", ") : "blocked",
|
|
1307
|
+
cls: ""
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1262
1310
|
|
|
1263
1311
|
const actions = [
|
|
1264
1312
|
makePaperBtn("Edit", "btn-sm btn-confirm", () => { paperEditing = true; render(); }),
|
|
@@ -1291,6 +1339,15 @@ function renderTaskPaperView(paper, task) {
|
|
|
1291
1339
|
body.appendChild(renderDetailListSection("Test Cases", task.test_cases));
|
|
1292
1340
|
}
|
|
1293
1341
|
if (task.notes) body.appendChild(renderDetailSection("Notes", task.notes));
|
|
1342
|
+
if (task.depends_on && task.depends_on.length) {
|
|
1343
|
+
body.appendChild(renderDetailListSection("Depends on", task.depends_on));
|
|
1344
|
+
}
|
|
1345
|
+
if (paperUnmet.length) {
|
|
1346
|
+
body.appendChild(renderDetailListSection("blocked by", paperUnmet));
|
|
1347
|
+
}
|
|
1348
|
+
if (task.files && task.files.length) {
|
|
1349
|
+
body.appendChild(renderDetailListSection("Files", task.files));
|
|
1350
|
+
}
|
|
1294
1351
|
|
|
1295
1352
|
const subtasks = task.subtasks || [];
|
|
1296
1353
|
if (subtasks.length > 0) {
|