@wrongstack/tui 0.303.0 → 0.305.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/dist/app-settings-type.d.ts +1 -1
- package/dist/components/kanban-panel.d.ts +9 -0
- package/dist/components/settings-picker-model.d.ts +2 -3
- package/dist/components/tools-picker.d.ts +1 -0
- package/dist/index.js +234 -100
- package/dist/kanban-audit.d.ts +18 -5
- package/dist/kanban-slash.d.ts +9 -2
- package/dist/ui-contracts.d.ts +1 -0
- package/package.json +7 -7
|
@@ -85,7 +85,7 @@ export type Settings = {
|
|
|
85
85
|
* live-settings coercion always fills in a full map.
|
|
86
86
|
*/
|
|
87
87
|
panelPositions?: Partial<PanelPositionMap>;
|
|
88
|
-
/** Show SAGE Memory Inject
|
|
88
|
+
/** Show SAGE Memory Inject chips in tool results. Default: true (compact). */
|
|
89
89
|
showSageMemoryInject: boolean;
|
|
90
90
|
/** Minimum relation strength for SAGE memory injection. Default: 0.85. */
|
|
91
91
|
sageMemoryInjectThreshold: number;
|
|
@@ -38,6 +38,15 @@ export declare function computeBoardColumnLayout(totalColumns: number, terminalW
|
|
|
38
38
|
columnWidth: number;
|
|
39
39
|
overflow: number;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* The next stage a managed card may advance to, or null when the card is
|
|
43
|
+
* already at the terminal `done` stage (or the current stage is unknown).
|
|
44
|
+
* The domain only allows one-step forward transitions; callers must keep
|
|
45
|
+
* this in lockstep with `KANBAN_AGENT_STAGES` in `@wrongstack/kanban`.
|
|
46
|
+
*
|
|
47
|
+
* Exported so unit tests can pin the stage order without rendering the panel.
|
|
48
|
+
*/
|
|
49
|
+
export declare function nextManagedStage(current: KanbanLifecycleStage): KanbanLifecycleStage | null;
|
|
41
50
|
/**
|
|
42
51
|
* Look up the lifecycle stage that owns the given column under a managed
|
|
43
52
|
* Kanban Agent board. Returns null when the board is not managed, or when
|
|
@@ -58,9 +58,8 @@ export declare const MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS: number[];
|
|
|
58
58
|
export declare const ENHANCE_LANGUAGES: readonly ['original', 'english'];
|
|
59
59
|
export type EnhanceLanguage = (typeof ENHANCE_LANGUAGES)[number];
|
|
60
60
|
/** Token-saving tier options — cyclable via ←/→ in the settings picker.
|
|
61
|
-
* `auto` is the default: it keeps
|
|
62
|
-
*
|
|
63
|
-
* level regardless of window. */
|
|
61
|
+
* `auto` is the default: it keeps the executable catalog, uses the medium
|
|
62
|
+
* direct surface, and adapts prompt trimming to the model window. */
|
|
64
63
|
export declare const TOKEN_SAVING_TIERS: readonly ['auto', 'off', 'minimal', 'light', 'medium', 'aggressive'];
|
|
65
64
|
export type TokenSavingTierTui = (typeof TOKEN_SAVING_TIERS)[number];
|
|
66
65
|
export declare const TOKEN_SAVING_TIER_DESCS: Record<TokenSavingTierTui, string>;
|
package/dist/index.js
CHANGED
|
@@ -5081,7 +5081,8 @@ function buildTodoPreviewRows(todos, width, maxRows = 2) {
|
|
|
5081
5081
|
const shown = ordered.slice(0, maxRows);
|
|
5082
5082
|
const textWidth = Math.max(4, width - 5);
|
|
5083
5083
|
const rows = shown.map((todo) => {
|
|
5084
|
-
const
|
|
5084
|
+
const base = todo.status === "in_progress" && todo.activeForm ? todo.activeForm : todo.content;
|
|
5085
|
+
const label = todo.blockedBy?.[0] ? `${base} \u2014 waiting on ${todo.blockedBy[0]}` : base;
|
|
5085
5086
|
if (todo.status === "completed") {
|
|
5086
5087
|
return {
|
|
5087
5088
|
id: todo.id,
|
|
@@ -6141,6 +6142,7 @@ import {
|
|
|
6141
6142
|
moveTask,
|
|
6142
6143
|
removeTask,
|
|
6143
6144
|
transferTaskToBoard,
|
|
6145
|
+
transitionTask,
|
|
6144
6146
|
updateTask
|
|
6145
6147
|
} from "@wrongstack/kanban";
|
|
6146
6148
|
import { applySessionKanbanTaskToSource } from "@wrongstack/tools/session-kanban";
|
|
@@ -6149,6 +6151,7 @@ import { useEffect as useEffect9, useMemo as useMemo4, useState as useState10 }
|
|
|
6149
6151
|
// src/kanban-audit.ts
|
|
6150
6152
|
var DEFAULT_REVIEW_STALE_AFTER_MS = 72 * 60 * 60 * 1e3;
|
|
6151
6153
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "archived"]);
|
|
6154
|
+
var BOARD_SOFT_MAX_BYTES = 512 * 1024;
|
|
6152
6155
|
function auditKanbanBoard(board, options) {
|
|
6153
6156
|
const now = toEpoch(options.now);
|
|
6154
6157
|
if (now === null) throw new TypeError("Kanban audit requires a valid current time");
|
|
@@ -6160,15 +6163,16 @@ function auditKanbanBoard(board, options) {
|
|
|
6160
6163
|
);
|
|
6161
6164
|
const lifecycleOrder = resolveLifecycleOrder(auditableBoard);
|
|
6162
6165
|
const managed = isManagedBoard(auditableBoard);
|
|
6163
|
-
const requireDueDate =
|
|
6166
|
+
const requireDueDate = options.requireDueDate === true;
|
|
6164
6167
|
const issues = [];
|
|
6165
6168
|
for (const task of board.tasks) {
|
|
6166
|
-
if (lifecycleOrder.length > 1) addLifecycleIssue(issues, task, lifecycleOrder);
|
|
6167
6169
|
if (TERMINAL_STATUSES.has(task.status)) continue;
|
|
6168
6170
|
addRequiredDetailIssues(issues, task, requireDueDate, managed);
|
|
6169
6171
|
addRunningIssue(issues, task, now, liveIdentities);
|
|
6170
6172
|
addReviewIssue(issues, task, now, reviewStaleAfterMs);
|
|
6173
|
+
if (lifecycleOrder.length > 1) addLifecycleIssue(issues, task, lifecycleOrder);
|
|
6171
6174
|
}
|
|
6175
|
+
addBoardSizeIssue(issues, board);
|
|
6172
6176
|
issues.sort(compareIssues);
|
|
6173
6177
|
return {
|
|
6174
6178
|
generatedAt: new Date(now).toISOString(),
|
|
@@ -6209,7 +6213,7 @@ function addRequiredDetailIssues(issues, task, requireDueDate, managed) {
|
|
|
6209
6213
|
if (!hasAssignee) {
|
|
6210
6214
|
pushIssue(issues, task, "missing-assignee", "warning", "Assign an owner");
|
|
6211
6215
|
}
|
|
6212
|
-
if (requireDueDate && (
|
|
6216
|
+
if (requireDueDate && toEpoch(task.dueDate) === null) {
|
|
6213
6217
|
pushIssue(issues, task, "missing-due-date", "warning", "Set a valid due date");
|
|
6214
6218
|
}
|
|
6215
6219
|
if (!hasTextItem(task.labels) && (managed || !hasTextItem(task.tags))) {
|
|
@@ -6234,6 +6238,25 @@ function addRequiredDetailIssues(issues, task, requireDueDate, managed) {
|
|
|
6234
6238
|
pushIssue(issues, task, "missing-success-criteria", "warning", "Add success criteria");
|
|
6235
6239
|
}
|
|
6236
6240
|
}
|
|
6241
|
+
function addBoardSizeIssue(issues, board) {
|
|
6242
|
+
const bytes = boardByteSize(board);
|
|
6243
|
+
if (bytes <= BOARD_SOFT_MAX_BYTES) return;
|
|
6244
|
+
issues.push({
|
|
6245
|
+
id: `${board.id}:board-oversized`,
|
|
6246
|
+
taskId: board.id,
|
|
6247
|
+
taskTitle: board.title,
|
|
6248
|
+
code: "board-oversized",
|
|
6249
|
+
severity: "warning",
|
|
6250
|
+
message: `Board is ~${Math.round(bytes / 1024)} KB across ${board.tasks.length} cards; archive completed cards before it stops syncing to HQ`
|
|
6251
|
+
});
|
|
6252
|
+
}
|
|
6253
|
+
function boardByteSize(board) {
|
|
6254
|
+
try {
|
|
6255
|
+
return JSON.stringify(board)?.length ?? 0;
|
|
6256
|
+
} catch {
|
|
6257
|
+
return 0;
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6237
6260
|
function addRunningIssue(issues, task, now, liveIdentities) {
|
|
6238
6261
|
const assignment = task.assignment;
|
|
6239
6262
|
const running = task.status === "in_progress" || assignment?.status === "running";
|
|
@@ -6390,7 +6413,7 @@ function normalizeIdentities(values) {
|
|
|
6390
6413
|
return normalized;
|
|
6391
6414
|
}
|
|
6392
6415
|
function normalizeIdentity(value) {
|
|
6393
|
-
return value.trim().
|
|
6416
|
+
return value.trim().toLowerCase();
|
|
6394
6417
|
}
|
|
6395
6418
|
function firstText(...values) {
|
|
6396
6419
|
for (const value of values) {
|
|
@@ -6536,6 +6559,25 @@ function KanbanPanel({
|
|
|
6536
6559
|
});
|
|
6537
6560
|
return;
|
|
6538
6561
|
}
|
|
6562
|
+
if (prompt.kind === "transitionTask") {
|
|
6563
|
+
const comment = prompt.buffer.trim();
|
|
6564
|
+
if (!comment) return;
|
|
6565
|
+
const target = prompt.to;
|
|
6566
|
+
const task = prompt.task;
|
|
6567
|
+
setPrompt(null);
|
|
6568
|
+
await runMutation(async () => {
|
|
6569
|
+
if (!board) return null;
|
|
6570
|
+
const actor = sessionId ?? "tui-operator";
|
|
6571
|
+
const result = await transitionTask(projectRoot, board.id, task.id, {
|
|
6572
|
+
to: target,
|
|
6573
|
+
actor,
|
|
6574
|
+
comment
|
|
6575
|
+
});
|
|
6576
|
+
if (result) await syncSource(result.board, task.id, false, task);
|
|
6577
|
+
return result ? `Transitioned "${task.title}" \u2192 ${target}` : "Transition failed (task or board not found)";
|
|
6578
|
+
});
|
|
6579
|
+
return;
|
|
6580
|
+
}
|
|
6539
6581
|
const value = prompt.buffer.trim();
|
|
6540
6582
|
if (!value) return;
|
|
6541
6583
|
const kind = prompt.kind;
|
|
@@ -6635,28 +6677,46 @@ function KanbanPanel({
|
|
|
6635
6677
|
return;
|
|
6636
6678
|
}
|
|
6637
6679
|
if (key.rightArrow && board && activeTask) {
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6680
|
+
if (isManagedBoard2(board)) {
|
|
6681
|
+
const current = stageForColumn(board, activeTask.columnId);
|
|
6682
|
+
const target = current ? nextManagedStage(current) : null;
|
|
6683
|
+
if (target) {
|
|
6684
|
+
setPrompt({ kind: "transitionTask", task: activeTask, to: target, buffer: "" });
|
|
6685
|
+
} else {
|
|
6686
|
+
setNotice("Task is already at the final stage (done).");
|
|
6687
|
+
}
|
|
6688
|
+
} else {
|
|
6689
|
+
const nextColumn = adjacentColumn(sortedColumns, activeTask.columnId, 1);
|
|
6690
|
+
if (nextColumn) {
|
|
6691
|
+
void runMutation(async () => {
|
|
6692
|
+
const nextBoard2 = await moveTask(projectRoot, board.id, activeTask.id, nextColumn.id);
|
|
6693
|
+
await syncSource(nextBoard2, activeTask.id);
|
|
6694
|
+
return `Moved to ${nextColumn.title}`;
|
|
6695
|
+
});
|
|
6696
|
+
}
|
|
6645
6697
|
}
|
|
6646
6698
|
return;
|
|
6647
6699
|
}
|
|
6648
6700
|
if (key.leftArrow && board && activeTask) {
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6701
|
+
if (isManagedBoard2(board)) {
|
|
6702
|
+
setNotice("Managed boards advance forward only; use the kanban tool to repair backward.");
|
|
6703
|
+
} else {
|
|
6704
|
+
const prevColumn = adjacentColumn(sortedColumns, activeTask.columnId, -1);
|
|
6705
|
+
if (prevColumn) {
|
|
6706
|
+
void runMutation(async () => {
|
|
6707
|
+
const nextBoard2 = await moveTask(projectRoot, board.id, activeTask.id, prevColumn.id);
|
|
6708
|
+
await syncSource(nextBoard2, activeTask.id);
|
|
6709
|
+
return `Moved to ${prevColumn.title}`;
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6656
6712
|
}
|
|
6657
6713
|
return;
|
|
6658
6714
|
}
|
|
6659
6715
|
if (shortcutsEnabled && (input === " " || input === "D") && board && activeTask) {
|
|
6716
|
+
if (isManagedBoard2(board)) {
|
|
6717
|
+
setNotice("Managed cards advance via \u2192 or t (transition). Use a non-managed board for direct status edits.");
|
|
6718
|
+
return;
|
|
6719
|
+
}
|
|
6660
6720
|
void runMutation(async () => {
|
|
6661
6721
|
const nextBoard2 = await updateTask(projectRoot, board.id, activeTask.id, {
|
|
6662
6722
|
status: "completed",
|
|
@@ -6668,6 +6728,10 @@ function KanbanPanel({
|
|
|
6668
6728
|
return;
|
|
6669
6729
|
}
|
|
6670
6730
|
if (shortcutsEnabled && input === "b" && board && activeTask) {
|
|
6731
|
+
if (isManagedBoard2(board)) {
|
|
6732
|
+
setNotice('Managed cards advance via \u2192 or t (transition); "blocked" is a status, not a lifecycle stage.');
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6671
6735
|
void runMutation(async () => {
|
|
6672
6736
|
const nextBoard2 = await updateTask(projectRoot, board.id, activeTask.id, {
|
|
6673
6737
|
status: "blocked"
|
|
@@ -6677,6 +6741,16 @@ function KanbanPanel({
|
|
|
6677
6741
|
});
|
|
6678
6742
|
return;
|
|
6679
6743
|
}
|
|
6744
|
+
if (shortcutsEnabled && input === "t" && board && activeTask && isManagedBoard2(board)) {
|
|
6745
|
+
const current = stageForColumn(board, activeTask.columnId);
|
|
6746
|
+
const target = current ? nextManagedStage(current) : null;
|
|
6747
|
+
if (!target) {
|
|
6748
|
+
setNotice("Task is already at the final stage (done).");
|
|
6749
|
+
} else {
|
|
6750
|
+
setPrompt({ kind: "transitionTask", task: activeTask, to: target, buffer: "" });
|
|
6751
|
+
}
|
|
6752
|
+
return;
|
|
6753
|
+
}
|
|
6680
6754
|
if (shortcutsEnabled && input === "x" && activeTask) {
|
|
6681
6755
|
setPrompt({ kind: "confirmDeleteTask", task: activeTask });
|
|
6682
6756
|
return;
|
|
@@ -6766,6 +6840,22 @@ function PromptLine({ prompt }) {
|
|
|
6766
6840
|
'"? y/n'
|
|
6767
6841
|
] });
|
|
6768
6842
|
}
|
|
6843
|
+
if (prompt.kind === "transitionTask") {
|
|
6844
|
+
return /* @__PURE__ */ jsxs14(Text, { children: [
|
|
6845
|
+
/* @__PURE__ */ jsxs14(Text, { color: "magenta", children: [
|
|
6846
|
+
prompt.to.toUpperCase(),
|
|
6847
|
+
' "',
|
|
6848
|
+
prompt.task.title,
|
|
6849
|
+
'":',
|
|
6850
|
+
" "
|
|
6851
|
+
] }),
|
|
6852
|
+
prompt.buffer,
|
|
6853
|
+
/* @__PURE__ */ jsxs14(Text, { dimColor: true, children: [
|
|
6854
|
+
"_ ",
|
|
6855
|
+
/* @__PURE__ */ jsx15(Text, { dimColor: true, children: "(Esc cancel \xB7 Enter confirm \u2014 comment required)" })
|
|
6856
|
+
] })
|
|
6857
|
+
] });
|
|
6858
|
+
}
|
|
6769
6859
|
const label = prompt.kind === "createBoard" ? "New board title" : "New task title";
|
|
6770
6860
|
return /* @__PURE__ */ jsxs14(Text, { children: [
|
|
6771
6861
|
/* @__PURE__ */ jsxs14(Text, { color: "cyan", children: [
|
|
@@ -6882,6 +6972,20 @@ function BoardColumns({
|
|
|
6882
6972
|
] }) : null
|
|
6883
6973
|
] });
|
|
6884
6974
|
}
|
|
6975
|
+
function isManagedBoard2(board) {
|
|
6976
|
+
return board?.lifecycle?.mode === "managed";
|
|
6977
|
+
}
|
|
6978
|
+
var MANAGED_STAGE_ORDER = [
|
|
6979
|
+
"backlog",
|
|
6980
|
+
"todo",
|
|
6981
|
+
"running",
|
|
6982
|
+
"review",
|
|
6983
|
+
"done"
|
|
6984
|
+
];
|
|
6985
|
+
function nextManagedStage(current) {
|
|
6986
|
+
const idx = MANAGED_STAGE_ORDER.indexOf(current);
|
|
6987
|
+
return idx >= 0 && idx < MANAGED_STAGE_ORDER.length - 1 ? MANAGED_STAGE_ORDER[idx + 1] : null;
|
|
6988
|
+
}
|
|
6885
6989
|
function stageForColumn(board, columnId) {
|
|
6886
6990
|
const policy = board.lifecycle;
|
|
6887
6991
|
if (policy?.mode !== "managed") return null;
|
|
@@ -6977,7 +7081,7 @@ function TaskDetail({
|
|
|
6977
7081
|
"C/T target: ",
|
|
6978
7082
|
target.title
|
|
6979
7083
|
] }) : null,
|
|
6980
|
-
/* @__PURE__ */ jsx15(Text, { dimColor: true, children: "space done | b block | x delete" })
|
|
7084
|
+
/* @__PURE__ */ jsx15(Text, { dimColor: true, children: board.lifecycle?.mode === "managed" ? "\u2192/t transition | x delete" : "space done | b block | x delete" })
|
|
6981
7085
|
] })
|
|
6982
7086
|
] });
|
|
6983
7087
|
}
|
|
@@ -17212,22 +17316,6 @@ function estimateTextRows(text, contentWidth, maxRows) {
|
|
|
17212
17316
|
rows += Math.max(1, Math.ceil(displayWidth(safeText.slice(lineStart)) / width));
|
|
17213
17317
|
return Math.min(maxRows, Math.max(1, rows));
|
|
17214
17318
|
}
|
|
17215
|
-
function estimateSageKvRows(cells, contentWidth) {
|
|
17216
|
-
if (cells.length === 0) return 0;
|
|
17217
|
-
const gap = 2;
|
|
17218
|
-
let rows = 1;
|
|
17219
|
-
let usedColumns = 0;
|
|
17220
|
-
for (const cell of cells) {
|
|
17221
|
-
const cellWidth = displayWidth(cell) + gap;
|
|
17222
|
-
if (usedColumns > 0 && usedColumns + cellWidth > contentWidth) {
|
|
17223
|
-
rows += 1;
|
|
17224
|
-
usedColumns = cellWidth;
|
|
17225
|
-
} else {
|
|
17226
|
-
usedColumns += cellWidth;
|
|
17227
|
-
}
|
|
17228
|
-
}
|
|
17229
|
-
return rows;
|
|
17230
|
-
}
|
|
17231
17319
|
function estimateRenderGroupRows(group, contentWidth, showSageMemoryInject) {
|
|
17232
17320
|
if (group.type === "tool-group") return group.data.entries.length + 2;
|
|
17233
17321
|
const { entry } = group;
|
|
@@ -17235,34 +17323,13 @@ function estimateRenderGroupRows(group, contentWidth, showSageMemoryInject) {
|
|
|
17235
17323
|
const { cleanOutput, sageLines } = resolveEntrySage(entry.output, entry.sageLines);
|
|
17236
17324
|
const memoryLines = sageLines.slice(1);
|
|
17237
17325
|
const panelContentWidth = Math.max(1, contentWidth - 4);
|
|
17238
|
-
const sagePanelRows = showSageMemoryInject === false || memoryLines.length === 0 ? 0 :
|
|
17239
|
-
`\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${entry.sageStats ? ` \xB7 ${entry.sageStats}` : ""}`,
|
|
17240
|
-
panelContentWidth,
|
|
17241
|
-
MAX_TEXT_ESTIMATE_ROWS
|
|
17242
|
-
) + memoryLines.reduce((rows, line) => {
|
|
17326
|
+
const sagePanelRows = showSageMemoryInject === false || memoryLines.length === 0 ? 0 : 4 + memoryLines.reduce((sum, line) => {
|
|
17243
17327
|
const parsed = parseSageMemoryLine(line);
|
|
17244
|
-
|
|
17245
|
-
|
|
17246
|
-
|
|
17247
|
-
const
|
|
17248
|
-
|
|
17249
|
-
panelContentWidth,
|
|
17250
|
-
MAX_TEXT_ESTIMATE_ROWS
|
|
17251
|
-
);
|
|
17252
|
-
const bodyRow = estimateTextRows(
|
|
17253
|
-
parsed.text,
|
|
17254
|
-
panelContentWidth,
|
|
17255
|
-
MAX_TEXT_ESTIMATE_ROWS
|
|
17256
|
-
);
|
|
17257
|
-
const kvCells = [];
|
|
17258
|
-
kvCells.push(`id: ${parsed.id}`);
|
|
17259
|
-
if (parsed.anchor) kvCells.push(`anchor: ${parsed.anchor}`);
|
|
17260
|
-
if (parsed.relation) kvCells.push(`relation: ${parsed.relation}`);
|
|
17261
|
-
if (parsed.tags && parsed.tags.length > 0)
|
|
17262
|
-
kvCells.push(`tags: ${parsed.tags.join(", ")}`);
|
|
17263
|
-
const kvRows = estimateSageKvRows(kvCells, panelContentWidth);
|
|
17264
|
-
const gap = rows > 0 ? 1 : 0;
|
|
17265
|
-
return rows + labelRow + bodyRow + kvRows + gap;
|
|
17328
|
+
const bodySource = parsed ? parsed.text : line;
|
|
17329
|
+
const bodyWidth = displayWidth(sanitizeTerminalText(bodySource));
|
|
17330
|
+
const bodyRows = Math.max(1, Math.ceil(bodyWidth / panelContentWidth));
|
|
17331
|
+
const kvRows = panelContentWidth < 48 ? 2 : 1;
|
|
17332
|
+
return sum + 1 + bodyRows + kvRows;
|
|
17266
17333
|
}, 0);
|
|
17267
17334
|
if (entry.resultRenderMode === "simple") return 3 + sagePanelRows;
|
|
17268
17335
|
if (STRUCTURED_DIFF_TOOLS.has(entry.name)) {
|
|
@@ -19025,11 +19092,23 @@ var Entry = React4.memo(function Entry2({
|
|
|
19025
19092
|
function SageMemoryBlock({
|
|
19026
19093
|
sageLines,
|
|
19027
19094
|
toolName,
|
|
19028
|
-
stats
|
|
19095
|
+
stats,
|
|
19096
|
+
compact = false
|
|
19029
19097
|
}) {
|
|
19030
19098
|
if (!sageLines || sageLines.length < 2) return null;
|
|
19031
19099
|
const memoryLines = sageLines.slice(1);
|
|
19032
19100
|
if (memoryLines.length === 0) return null;
|
|
19101
|
+
if (compact) {
|
|
19102
|
+
const first = parseSageMemoryLine(memoryLines[0] ?? "");
|
|
19103
|
+
const preview = first ? sanitizeTerminalText(first.text).slice(0, 72) : sanitizeTerminalText(memoryLines[0] ?? "").slice(0, 72);
|
|
19104
|
+
const labels = first?.labels?.length ? first.labels.map((l) => `[${l}]`).join("") : "";
|
|
19105
|
+
return /* @__PURE__ */ jsx68(Box, { flexDirection: "row", marginTop: 0, children: /* @__PURE__ */ jsxs65(Text, { color: theme.accent, children: [
|
|
19106
|
+
`\u{1F9E0} ${memoryLines.length} SAGE \xB7 ${toolName}`,
|
|
19107
|
+
stats ? ` \xB7 ${stats}` : "",
|
|
19108
|
+
labels ? ` ${labels}` : "",
|
|
19109
|
+
preview ? ` \u2014 ${preview}${preview.length >= 72 ? "\u2026" : ""}` : ""
|
|
19110
|
+
] }) });
|
|
19111
|
+
}
|
|
19033
19112
|
return /* @__PURE__ */ jsxs65(
|
|
19034
19113
|
Box,
|
|
19035
19114
|
{
|
|
@@ -20237,12 +20316,12 @@ var TOKEN_SAVING_TIERS = [
|
|
|
20237
20316
|
"aggressive"
|
|
20238
20317
|
];
|
|
20239
20318
|
var TOKEN_SAVING_TIER_DESCS = {
|
|
20240
|
-
auto: "
|
|
20241
|
-
off: "
|
|
20242
|
-
minimal: "
|
|
20243
|
-
light: "
|
|
20244
|
-
medium: "
|
|
20245
|
-
aggressive: "
|
|
20319
|
+
auto: "Medium direct surface; prompt adapts to the model window",
|
|
20320
|
+
off: "Every enabled catalog tool is sent directly; full prompt",
|
|
20321
|
+
minimal: "Essential direct tools; compact prompt and guidance",
|
|
20322
|
+
light: "Essential direct tools; retains common workflow guidance",
|
|
20323
|
+
medium: "Essential + regular development tools; moderate trimming",
|
|
20324
|
+
aggressive: "Smallest direct surface and most compact prompt"
|
|
20246
20325
|
};
|
|
20247
20326
|
var FLEET_CHAT_MODES = ["off", "full"];
|
|
20248
20327
|
var FLEET_CHAT_MODE_DESCS = {
|
|
@@ -20815,7 +20894,7 @@ var SETTINGS_DEFAULTS = Object.freeze({
|
|
|
20815
20894
|
showAgentSwarmPanel: "bottom",
|
|
20816
20895
|
panelPositions: DEFAULT_PANEL_POSITIONS,
|
|
20817
20896
|
readSymbols: false,
|
|
20818
|
-
showSageMemoryInject:
|
|
20897
|
+
showSageMemoryInject: true,
|
|
20819
20898
|
sageMemoryInjectThreshold: 0.85,
|
|
20820
20899
|
nextStepsTool: false
|
|
20821
20900
|
});
|
|
@@ -21612,11 +21691,15 @@ function buildMissionRows(todos, maxRows) {
|
|
|
21612
21691
|
const rank = (t) => t.status === "in_progress" ? 0 : t.status === "pending" ? 1 : 2;
|
|
21613
21692
|
const ordered = [...list].sort((a, b) => rank(a) - rank(b));
|
|
21614
21693
|
const shown = ordered.slice(0, maxRows);
|
|
21615
|
-
const rows = shown.map((t) =>
|
|
21616
|
-
|
|
21617
|
-
|
|
21618
|
-
|
|
21619
|
-
|
|
21694
|
+
const rows = shown.map((t) => {
|
|
21695
|
+
const base = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
|
|
21696
|
+
const blocker = t.blockedBy?.[0];
|
|
21697
|
+
return {
|
|
21698
|
+
id: t.id,
|
|
21699
|
+
status: t.status,
|
|
21700
|
+
label: blocker ? `${base} \u2014 waiting on ${blocker}` : base
|
|
21701
|
+
};
|
|
21702
|
+
});
|
|
21620
21703
|
return { rows, overflow: ordered.length - shown.length, done, total };
|
|
21621
21704
|
}
|
|
21622
21705
|
function DialRow({
|
|
@@ -22567,7 +22650,8 @@ function TodosPanelSidebar({ todos, width }) {
|
|
|
22567
22650
|
children: /* @__PURE__ */ jsx79(SidebarPanelCard, { innerWidth: inner, marginBottom: 0, children: ordered.length === 0 ? /* @__PURE__ */ jsx79(Text, { color: theme.textMuted, children: "no todos" }) : ordered.map((t) => {
|
|
22568
22651
|
const icon = t.status === "completed" ? glyphs.success : t.status === "in_progress" ? "\u25CF" : "\u25CB";
|
|
22569
22652
|
const color = t.status === "completed" ? theme.success : t.status === "in_progress" ? theme.accent : theme.textMuted;
|
|
22570
|
-
const
|
|
22653
|
+
const base = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
|
|
22654
|
+
const label = t.blockedBy?.[0] ? `${base} \u2014 waiting on ${t.blockedBy[0]}` : base;
|
|
22571
22655
|
return /* @__PURE__ */ jsx79(
|
|
22572
22656
|
SidebarWorklistRow,
|
|
22573
22657
|
{
|
|
@@ -23492,10 +23576,12 @@ import { Fragment as Fragment30, jsx as jsx84, jsxs as jsxs81 } from "react/jsx-
|
|
|
23492
23576
|
var CHROME_ROWS6 = 15;
|
|
23493
23577
|
var MAX_PICKER_ITEMS2 = 15;
|
|
23494
23578
|
function toolActionLabel(enabled) {
|
|
23495
|
-
return enabled ? "Enter
|
|
23579
|
+
return enabled ? "Enter disables this tool" : "Enter re-enables this tool";
|
|
23496
23580
|
}
|
|
23497
|
-
function statusBadge2(
|
|
23498
|
-
|
|
23581
|
+
function statusBadge2(exposure) {
|
|
23582
|
+
if (exposure === "direct") return /* @__PURE__ */ jsx84(Text, { color: "green", children: "\u25CF direct" });
|
|
23583
|
+
if (exposure === "lazy") return /* @__PURE__ */ jsx84(Text, { color: "cyan", children: "\u25C7 lazy" });
|
|
23584
|
+
return /* @__PURE__ */ jsx84(Text, { color: "red", children: "\u25CB disabled" });
|
|
23499
23585
|
}
|
|
23500
23586
|
function rwBadge(mutating) {
|
|
23501
23587
|
return mutating ? /* @__PURE__ */ jsx84(Text, { color: "yellow", children: "mut" }) : /* @__PURE__ */ jsx84(Text, { color: "cyan", children: "ro" });
|
|
@@ -23589,8 +23675,9 @@ function ToolsPicker({
|
|
|
23589
23675
|
visibleSelected = selected;
|
|
23590
23676
|
}
|
|
23591
23677
|
const rows = buildRows2(visibleItems);
|
|
23592
|
-
const
|
|
23593
|
-
const
|
|
23678
|
+
const directCount = items.filter((item) => item.exposure === "direct").length;
|
|
23679
|
+
const lazyCount = items.filter((item) => item.exposure === "lazy").length;
|
|
23680
|
+
const disabledCount = items.filter((item) => item.exposure === "disabled").length;
|
|
23594
23681
|
const selectedItem = visibleItems[visibleSelected];
|
|
23595
23682
|
const win = windowRows3(rows, visibleSelected, maxVisible);
|
|
23596
23683
|
const displayRows = win.rows;
|
|
@@ -23599,8 +23686,8 @@ function ToolsPicker({
|
|
|
23599
23686
|
const hasFilter = filterActive;
|
|
23600
23687
|
return /* @__PURE__ */ jsxs81(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: [
|
|
23601
23688
|
/* @__PURE__ */ jsx84(Text, { bold: true, color: "green", children: "\u2501\u2501 Tools \u2501\u2501" }),
|
|
23602
|
-
/* @__PURE__ */ jsx84(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${visibleItems.length} match${visibleItems.length === 1 ? "" : "es"}) \xB7 Backspace edit \xB7 Esc clear` : `\u2191/\u2193 select \xB7 type to filter \xB7 Enter toggles \xB7 Esc close \xB7 ${
|
|
23603
|
-
/* @__PURE__ */ jsx84(Text, { dimColor: true, children: "
|
|
23689
|
+
/* @__PURE__ */ jsx84(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${visibleItems.length} match${visibleItems.length === 1 ? "" : "es"}) \xB7 Backspace edit \xB7 Esc clear` : `\u2191/\u2193 select \xB7 type to filter \xB7 Enter toggles \xB7 Esc close \xB7 ${directCount} direct / ${lazyCount} lazy / ${disabledCount} disabled` }),
|
|
23690
|
+
/* @__PURE__ */ jsx84(Text, { dimColor: true, children: "Lazy tools stay executable through discovery gateways without sending every schema to the provider." }),
|
|
23604
23691
|
/* @__PURE__ */ jsx84(Box, { marginTop: 1, flexDirection: "column", children: total === 0 ? /* @__PURE__ */ jsx84(Text, { dimColor: true, children: busy ? "Loading tools\u2026" : "No tools registered." }) : visibleItems.length === 0 ? /* @__PURE__ */ jsxs81(Text, { dimColor: true, children: [
|
|
23605
23692
|
'No tools match "',
|
|
23606
23693
|
filter,
|
|
@@ -23617,7 +23704,7 @@ function ToolsPicker({
|
|
|
23617
23704
|
wrap: "truncate-end",
|
|
23618
23705
|
children: [
|
|
23619
23706
|
focused ? "\u203A " : " ",
|
|
23620
|
-
statusBadge2(item.
|
|
23707
|
+
statusBadge2(item.exposure),
|
|
23621
23708
|
" ",
|
|
23622
23709
|
/* @__PURE__ */ jsx84(Text, { bold: true, children: fitCell(item.name, columnWidths.name) }),
|
|
23623
23710
|
" ",
|
|
@@ -23628,7 +23715,6 @@ function ToolsPicker({
|
|
|
23628
23715
|
" ",
|
|
23629
23716
|
rwBadge(item.mutating),
|
|
23630
23717
|
" ",
|
|
23631
|
-
" ",
|
|
23632
23718
|
fitCell(item.permission, columnWidths.permission),
|
|
23633
23719
|
" ",
|
|
23634
23720
|
/* @__PURE__ */ jsxs81(Text, { color: item.descMode === "simple" ? "yellow" : void 0, children: [
|
|
@@ -23646,7 +23732,13 @@ function ToolsPicker({
|
|
|
23646
23732
|
selectedItem ? /* @__PURE__ */ jsxs81(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
23647
23733
|
/* @__PURE__ */ jsxs81(Text, { children: [
|
|
23648
23734
|
/* @__PURE__ */ jsx84(Text, { color: selectedItem.enabled ? "green" : "red", children: toolActionLabel(selectedItem.enabled) }),
|
|
23649
|
-
/* @__PURE__ */ jsx84(
|
|
23735
|
+
/* @__PURE__ */ jsx84(
|
|
23736
|
+
Text,
|
|
23737
|
+
{
|
|
23738
|
+
dimColor: true,
|
|
23739
|
+
children: ` \xB7 ${selectedItem.mutating ? "Mutating" : "Read-only"} \xB7 permission: ${selectedItem.permission} \xB7 descriptions: ${selectedItem.descMode}`
|
|
23740
|
+
}
|
|
23741
|
+
)
|
|
23650
23742
|
] }),
|
|
23651
23743
|
/* @__PURE__ */ jsx84(Text, { dimColor: true, wrap: "truncate-end", children: selectedItem.description || "No description provided." })
|
|
23652
23744
|
] }) : null,
|
|
@@ -26775,7 +26867,9 @@ function createInitialState(options) {
|
|
|
26775
26867
|
showModelReasoning: true,
|
|
26776
26868
|
showAgentSwarmPanel: "bottom",
|
|
26777
26869
|
panelPositions: DEFAULT_PANEL_POSITIONS,
|
|
26778
|
-
|
|
26870
|
+
// Default on: compact one-line chip (see SageMemoryBlock). Full bordered
|
|
26871
|
+
// panel remains available when operators expand inject visibility later.
|
|
26872
|
+
showSageMemoryInject: true,
|
|
26779
26873
|
sageMemoryInjectThreshold: 0.85,
|
|
26780
26874
|
nextStepsTool: false,
|
|
26781
26875
|
readSymbols: false
|
|
@@ -31539,7 +31633,7 @@ import {
|
|
|
31539
31633
|
updateBoard,
|
|
31540
31634
|
updateTask as updateTask2
|
|
31541
31635
|
} from "@wrongstack/kanban";
|
|
31542
|
-
var USAGE2 = "Usage:\n /kanban \u2014 open the project kanban panel\n /kanban create <title> \u2014 create a board, then open the panel\n /kanban add <title> [--column X]
|
|
31636
|
+
var USAGE2 = "Usage:\n /kanban \u2014 open the project kanban panel\n /kanban create <title> \u2014 create a board, then open the panel\n /kanban add <title> [--column X] [--desc <text>] \u2014 add a task to the active board (managed boards require --desc)\n /kanban use <boardId|title|tag> \u2014 open the panel on a specific board\n /kanban boards \u2014 list every board in the project\n /kanban health \u2014 show kanban queue health summary\n /kanban audit [boardId|all] \u2014 show kanban cleaner audit (all boards if omitted)\n /kanban boundary <board> show [--task ID]\n /kanban boundary <board> allow|deny <kind> <access> <path> [--task ID] [--enforcement confirm|block] [--shell allow|confirm|block]\n /kanban boundary <board> clear [--task ID]\n /kanban help \u2014 show this help";
|
|
31543
31637
|
var KANBAN_PANEL_ACTION = "toggleKanbanPanel";
|
|
31544
31638
|
function createKanbanSlashCommand(deps) {
|
|
31545
31639
|
return {
|
|
@@ -31587,15 +31681,26 @@ function createKanbanSlashCommand(deps) {
|
|
|
31587
31681
|
message: "No kanban board found. Use `/kanban create <title>` to make one first."
|
|
31588
31682
|
};
|
|
31589
31683
|
}
|
|
31590
|
-
|
|
31684
|
+
if (target.managed && !parsed.description) {
|
|
31685
|
+
return {
|
|
31686
|
+
message: `"${target.boardTitle}" is a managed board \u2014 every card needs a description. Try: /kanban add "${parsed.title}" --desc "<what the task is>"`
|
|
31687
|
+
};
|
|
31688
|
+
}
|
|
31689
|
+
const created = await addTaskToBoard(
|
|
31690
|
+
deps.projectRoot,
|
|
31691
|
+
target,
|
|
31692
|
+
parsed.title,
|
|
31693
|
+
parsed.description
|
|
31694
|
+
);
|
|
31591
31695
|
if (!created) {
|
|
31592
31696
|
return {
|
|
31593
31697
|
message: `Failed to add task to "${target.boardTitle}" \u2014 no writable column found.`
|
|
31594
31698
|
};
|
|
31595
31699
|
}
|
|
31596
31700
|
deps.onPanelOpen?.current?.(KANBAN_PANEL_ACTION);
|
|
31701
|
+
const columnHint = target.managed ? " (backlog)" : "";
|
|
31597
31702
|
return {
|
|
31598
|
-
message: `Added task "${created.title}" to "${target.boardTitle}" \u2192 ${created.columnId}. Opening panel\u2026`
|
|
31703
|
+
message: `Added task "${created.title}" to "${target.boardTitle}" \u2192 ${created.columnId}${columnHint}. Opening panel\u2026`
|
|
31599
31704
|
};
|
|
31600
31705
|
}
|
|
31601
31706
|
if (parsed.kind === "use") {
|
|
@@ -31661,6 +31766,7 @@ function parseKanbanArgs(raw) {
|
|
|
31661
31766
|
if (head === "add" || head === "add-task" || head === "task") {
|
|
31662
31767
|
let title = "";
|
|
31663
31768
|
let column = null;
|
|
31769
|
+
let description = null;
|
|
31664
31770
|
for (let i = 1; i < tokens.length; i++) {
|
|
31665
31771
|
const tok = tokens[i] ?? "";
|
|
31666
31772
|
if (tok === "--column" || tok === "-c") {
|
|
@@ -31675,11 +31781,28 @@ function parseKanbanArgs(raw) {
|
|
|
31675
31781
|
column = tok.slice("--column=".length);
|
|
31676
31782
|
continue;
|
|
31677
31783
|
}
|
|
31784
|
+
if (tok === "--desc" || tok === "-d") {
|
|
31785
|
+
const next = tokens[i + 1];
|
|
31786
|
+
if (next) {
|
|
31787
|
+
description = next;
|
|
31788
|
+
i++;
|
|
31789
|
+
}
|
|
31790
|
+
continue;
|
|
31791
|
+
}
|
|
31792
|
+
if (tok.startsWith("--desc=")) {
|
|
31793
|
+
description = tok.slice("--desc=".length);
|
|
31794
|
+
continue;
|
|
31795
|
+
}
|
|
31678
31796
|
title = title ? `${title} ${tok}` : tok;
|
|
31679
31797
|
}
|
|
31680
31798
|
title = title.trim();
|
|
31681
31799
|
if (!title) return { kind: "help" };
|
|
31682
|
-
return {
|
|
31800
|
+
return {
|
|
31801
|
+
kind: "add",
|
|
31802
|
+
title,
|
|
31803
|
+
column,
|
|
31804
|
+
...description ? { description } : {}
|
|
31805
|
+
};
|
|
31683
31806
|
}
|
|
31684
31807
|
if (head === "use" || head === "open-board" || head === "focus") {
|
|
31685
31808
|
const query = tokens.slice(1).join(" ").trim();
|
|
@@ -31792,9 +31915,7 @@ function renderBoundaryPolicy(policy) {
|
|
|
31792
31915
|
if (!policy?.enabled) return "unrestricted";
|
|
31793
31916
|
const lines = [
|
|
31794
31917
|
`mode ${policy.enforcement} \xB7 shell ${policy.shellAccess}`,
|
|
31795
|
-
...policy.allow.map(
|
|
31796
|
-
(selector) => `ALLOW ${selector.access} ${selector.kind}:${selector.path}`
|
|
31797
|
-
),
|
|
31918
|
+
...policy.allow.map((selector) => `ALLOW ${selector.access} ${selector.kind}:${selector.path}`),
|
|
31798
31919
|
...(policy.deny ?? []).map(
|
|
31799
31920
|
(selector) => `DENY ${selector.access} ${selector.kind}:${selector.path}`
|
|
31800
31921
|
)
|
|
@@ -31871,13 +31992,17 @@ async function resolveAddTarget(deps, column) {
|
|
|
31871
31992
|
if (!preferred) return null;
|
|
31872
31993
|
const board = await getBoard3(deps.projectRoot, preferred.id);
|
|
31873
31994
|
if (!board) return null;
|
|
31995
|
+
const managed = board.lifecycle?.mode === "managed";
|
|
31874
31996
|
const firstColumn = board.columns[0];
|
|
31875
|
-
const
|
|
31997
|
+
const backlogColumnId = managed ? board.lifecycle?.columns?.backlog : void 0;
|
|
31998
|
+
const backlogColumn = backlogColumnId ? board.columns.find((c) => c.id === backlogColumnId) : void 0;
|
|
31999
|
+
const targetColumn = managed ? backlogColumn ?? firstColumn : resolveColumn(board.columns, column) ?? firstColumn;
|
|
31876
32000
|
if (!targetColumn) return null;
|
|
31877
32001
|
return {
|
|
31878
32002
|
boardId: board.id,
|
|
31879
32003
|
boardTitle: board.title,
|
|
31880
|
-
columnId: targetColumn.id
|
|
32004
|
+
columnId: targetColumn.id,
|
|
32005
|
+
managed
|
|
31881
32006
|
};
|
|
31882
32007
|
}
|
|
31883
32008
|
function resolveColumn(columns, hint) {
|
|
@@ -31912,7 +32037,15 @@ function renderHealthReport(health2) {
|
|
|
31912
32037
|
c.queued > 0 ? ` + ${c.queued} currently queued for assignment (subset)` : "",
|
|
31913
32038
|
"",
|
|
31914
32039
|
" **Per-status counts**",
|
|
31915
|
-
`
|
|
32040
|
+
// `startable`, not `ready`. `counts.ready` tallies the stored status field,
|
|
32041
|
+
// which no dispatcher writes, so it printed a permanent 0 while
|
|
32042
|
+
// `ready_tasks` on the same board returned work. `counts.startable` is the
|
|
32043
|
+
// derived answer and agrees with `listReadyTasks`.
|
|
32044
|
+
//
|
|
32045
|
+
// `lifecycleTotal` above still sums `c.ready` on purpose: that is the
|
|
32046
|
+
// single-count partition, and `startable` overlaps `pending`/`ready`, so
|
|
32047
|
+
// swapping it there would double-count. These two lines are meant to differ.
|
|
32048
|
+
` startable ${c.startable} \xB7 running ${c.running} \xB7 review ${c.review} \xB7 failed ${c.failed} \xB7 completed ${c.completed}`,
|
|
31916
32049
|
` pending ${c.pending} \xB7 archived ${c.archived} \xB7 blocked ${c.blocked}` + (c.queued > 0 ? ` \xB7 queued ${c.queued}` : ""),
|
|
31917
32050
|
""
|
|
31918
32051
|
].filter((line) => line !== "");
|
|
@@ -32038,10 +32171,11 @@ function renderAuditReport(rows, multi) {
|
|
|
32038
32171
|
function severityRank2(severity) {
|
|
32039
32172
|
return severity === "error" ? 0 : 1;
|
|
32040
32173
|
}
|
|
32041
|
-
async function addTaskToBoard(cwd, target, title) {
|
|
32174
|
+
async function addTaskToBoard(cwd, target, title, description) {
|
|
32042
32175
|
const input = {
|
|
32043
32176
|
title,
|
|
32044
|
-
columnId: target.columnId
|
|
32177
|
+
columnId: target.columnId,
|
|
32178
|
+
...description ? { description } : {}
|
|
32045
32179
|
};
|
|
32046
32180
|
const result = await addTask2(cwd, target.boardId, input);
|
|
32047
32181
|
if (!result) return null;
|
package/dist/kanban-audit.d.ts
CHANGED
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Mirrors the WebUI's `packages/webui/src/lib/kanban-cleaner.ts` but
|
|
5
5
|
* lives in the TUI package so we don't have to thread `@wrongstack/webui`
|
|
6
|
-
* (which depends on React) into a server-side TUI pipeline.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* (which depends on React) into a server-side TUI pipeline.
|
|
7
|
+
*
|
|
8
|
+
* The two implementations must produce the same verdicts for the same board.
|
|
9
|
+
* That claim used to be a comment; it is now enforced by
|
|
10
|
+
* `packages/cli/tests/kanban-cleaner-parity.test.ts`, which runs a shared
|
|
11
|
+
* corpus through both and compares the `taskId:code:severity` triples. Only
|
|
12
|
+
* three differences are deliberate, and all three are API shape rather than
|
|
13
|
+
* verdict: this file also accepts `liveAgentIdentities`, returns eight extra
|
|
14
|
+
* summary fields the WebUI's `KanbanCleanerAlert` fills from queue health, and
|
|
15
|
+
* exports `summarizeAuditHeadline` / `topAuditIssues` for the panel badge.
|
|
16
|
+
* Messages may differ; codes and severities may not.
|
|
10
17
|
*
|
|
11
18
|
* The output of `auditKanbanBoard()` feeds the TUI panel header badge so
|
|
12
19
|
* users see Cleaner warnings inline — mirroring the WebUI's
|
|
@@ -14,7 +21,13 @@
|
|
|
14
21
|
*/
|
|
15
22
|
import type { KanbanBoard } from '@wrongstack/kanban';
|
|
16
23
|
export type KanbanAuditSeverity = 'error' | 'warning';
|
|
17
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The audit vocabulary, as a runtime value so the parity test can compare the
|
|
26
|
+
* two implementations' code sets instead of trusting that both type unions
|
|
27
|
+
* were edited together.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ALL_AUDIT_CODES: readonly ['abandoned-running-task', 'board-oversized', 'missing-assignee', 'missing-description', 'missing-due-date', 'missing-labels', 'missing-subtasks', 'missing-success-criteria', 'skipped-lifecycle-state', 'stale-review', 'stale-running-task'];
|
|
30
|
+
export type KanbanAuditIssueCode = (typeof ALL_AUDIT_CODES)[number];
|
|
18
31
|
export interface KanbanAuditIssue {
|
|
19
32
|
id: string;
|
|
20
33
|
taskId: string;
|
package/dist/kanban-slash.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* /kanban — open the project kanban panel
|
|
8
8
|
* /kanban create <title> — create a new board, then open the panel
|
|
9
|
-
* /kanban add <title> [--column X]
|
|
9
|
+
* /kanban add <title> [--column X] [--desc <text>] — add a task to the active board (managed boards require --desc)
|
|
10
10
|
* /kanban use <boardId|title|tag> — open the kanban panel on a specific board
|
|
11
11
|
* /kanban boards — list every board in the project
|
|
12
12
|
* /kanban health — show kanban queue health summary
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* is the text-first equivalent for when the panel is closed.
|
|
20
20
|
*/
|
|
21
21
|
import type { SlashCommand } from '@wrongstack/core/types';
|
|
22
|
-
import type { KanbanBoundaryAccess, KanbanBoundaryPolicy, KanbanBoundarySelectorKind,
|
|
22
|
+
import type { KanbanBoardSummary, KanbanBoundaryAccess, KanbanBoundaryPolicy, KanbanBoundarySelectorKind, KanbanQueueHealth } from '@wrongstack/kanban';
|
|
23
23
|
import { type KanbanAuditSummary } from './kanban-audit.js';
|
|
24
24
|
/**
|
|
25
25
|
* Result of resolving the board that an `add` operation should target.
|
|
@@ -34,6 +34,12 @@ export interface KanbanAddTarget {
|
|
|
34
34
|
boardTitle: string;
|
|
35
35
|
/** Column that the task will be placed in. Defaults to the first column. */
|
|
36
36
|
columnId: string;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the board enforces the managed Kanban Agent lifecycle. Managed
|
|
39
|
+
* boards require new cards to be created in the Backlog column and with a
|
|
40
|
+
* description, so callers route the user accordingly.
|
|
41
|
+
*/
|
|
42
|
+
managed: boolean;
|
|
37
43
|
}
|
|
38
44
|
export interface KanbanSlashDeps {
|
|
39
45
|
/**
|
|
@@ -78,6 +84,7 @@ export type ParsedKanbanArgs = {
|
|
|
78
84
|
kind: 'add';
|
|
79
85
|
title: string;
|
|
80
86
|
column: string | null;
|
|
87
|
+
description?: string | undefined;
|
|
81
88
|
} | {
|
|
82
89
|
kind: 'use';
|
|
83
90
|
query: string;
|
package/dist/ui-contracts.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.305.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack terminal UI: Ink-based interactive renderer with history, pickers, and slash menus.",
|
|
6
6
|
"repository": {
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"ink": "7.1.1",
|
|
30
30
|
"react": "^19.2.8",
|
|
31
31
|
"string-width": "8.2.2",
|
|
32
|
-
"@wrongstack/
|
|
33
|
-
"@wrongstack/
|
|
34
|
-
"@wrongstack/
|
|
35
|
-
"@wrongstack/runtime": "0.
|
|
36
|
-
"@wrongstack/
|
|
37
|
-
"@wrongstack/tools": "0.
|
|
32
|
+
"@wrongstack/core": "0.305.0",
|
|
33
|
+
"@wrongstack/kanban": "0.305.0",
|
|
34
|
+
"@wrongstack/sage": "0.305.0",
|
|
35
|
+
"@wrongstack/runtime": "0.305.0",
|
|
36
|
+
"@wrongstack/sdd": "0.305.0",
|
|
37
|
+
"@wrongstack/tools": "0.305.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@testing-library/dom": "^10.4.1",
|