@pi-unipi/kanboard 3.0.0-alpha.2 → 3.0.0-alpha.21
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/README.md +51 -47
- package/index.ts +293 -55
- package/package.json +8 -8
- package/skills/kanboard/SKILL.md +61 -27
- package/src/badges.ts +93 -0
- package/src/bin.ts +4 -2
- package/src/commands.ts +218 -84
- package/src/debug.ts +20 -0
- package/src/guard.ts +267 -109
- package/src/monitor.ts +330 -0
- package/src/notice-buffer.ts +74 -0
- package/src/progress.ts +27 -0
- package/src/reminders.ts +234 -0
- package/src/settings.ts +69 -15
- package/src/shapes.ts +5 -0
- package/src/runner.ts +0 -817
package/src/settings.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* @pi-unipi/kanboard — settings (hub section "Kanboard").
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { getSettings, globalSettingsPath, projectSettingsPath, registerCommandRunner, registerSettings, setSettings } from "@pi-unipi/core";
|
|
6
7
|
|
|
7
8
|
export type ChainGate = "in_review" | "done";
|
|
8
9
|
|
|
@@ -25,12 +26,20 @@ export interface KanboardSettings {
|
|
|
25
26
|
requireAuth: boolean;
|
|
26
27
|
/** Reuse <home>/token so board links survive daemon restarts. */
|
|
27
28
|
keepToken: boolean;
|
|
28
|
-
/** Tasks one session may queue (0 = unlimited). */
|
|
29
|
-
queueMax: number;
|
|
30
29
|
/** Distinct sessions running tasks per project (min 1). */
|
|
31
30
|
maxSessions: number;
|
|
32
|
-
/** `add` calls allowed per
|
|
31
|
+
/** `add` calls allowed per turn (0 = unlimited). */
|
|
33
32
|
turnAddLimit: number;
|
|
33
|
+
/** Task slots a /unipi:kanboard-do grants (each `start` uses one). */
|
|
34
|
+
doTasks: number;
|
|
35
|
+
/** Board writes a /unipi:kanboard-do grants. */
|
|
36
|
+
doWrites: number;
|
|
37
|
+
/** Whether a blocked-by-confusion task may ask the user (ask) or must
|
|
38
|
+
* assume-and-note (avoid, the default). */
|
|
39
|
+
blocking: "avoid" | "ask";
|
|
40
|
+
/** Progress reminders when the agent works board tasks by hand: `start`
|
|
41
|
+
* before the first edit, `finish` (or block) before the turn ends. */
|
|
42
|
+
reminders: boolean;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
export const DEFAULT_SETTINGS: KanboardSettings = {
|
|
@@ -43,9 +52,12 @@ export const DEFAULT_SETTINGS: KanboardSettings = {
|
|
|
43
52
|
openBrowser: false,
|
|
44
53
|
requireAuth: false,
|
|
45
54
|
keepToken: false,
|
|
46
|
-
queueMax: 10,
|
|
47
55
|
maxSessions: 2,
|
|
48
56
|
turnAddLimit: 20,
|
|
57
|
+
doTasks: 5,
|
|
58
|
+
doWrites: 10,
|
|
59
|
+
blocking: "avoid",
|
|
60
|
+
reminders: true,
|
|
49
61
|
};
|
|
50
62
|
|
|
51
63
|
export const KANBOARD_NAMESPACE = "kanboard";
|
|
@@ -59,11 +71,11 @@ export function registerKanboardSettings(): void {
|
|
|
59
71
|
registerSettings({
|
|
60
72
|
namespace: KANBOARD_NAMESPACE,
|
|
61
73
|
label: "Kanboard",
|
|
62
|
-
defaults: DEFAULT_SETTINGS as unknown as Record<string, unknown>,
|
|
74
|
+
defaults: { ...DEFAULT_SETTINGS } as unknown as Record<string, unknown>,
|
|
63
75
|
schema: [
|
|
64
76
|
{
|
|
65
77
|
title: "Kanboard",
|
|
66
|
-
description: "
|
|
78
|
+
description: "The session's work board: daemon, -do budgets and skill",
|
|
67
79
|
fields: [
|
|
68
80
|
{
|
|
69
81
|
key: "chainGate",
|
|
@@ -100,12 +112,16 @@ export function registerKanboardSettings(): void {
|
|
|
100
112
|
label: "Keep the access token across restarts",
|
|
101
113
|
description: "Reuse one token so board links stay valid; rotate it below.",
|
|
102
114
|
},
|
|
103
|
-
{ key: "queueMax", type: "number", label: "-do queue limit", min: 0, zeroLabel: "unlimited", description: "Tasks a session may queue for the runner" },
|
|
104
115
|
{ key: "maxSessions", type: "number", label: "Sessions working at once (per project)", min: 1, description: "Distinct sessions holding in_progress tasks" },
|
|
105
|
-
{ key: "turnAddLimit", type: "number", label: "New tasks per turn", min: 0, zeroLabel: "unlimited", description: "`add` calls allowed per
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
{ key: "turnAddLimit", type: "number", label: "New tasks per turn", min: 0, zeroLabel: "unlimited", description: "`add` calls allowed per turn" },
|
|
117
|
+
{
|
|
118
|
+
key: "reminders",
|
|
119
|
+
type: "boolean",
|
|
120
|
+
label: "Progress reminders",
|
|
121
|
+
description: "Remind the agent to `start` a mentioned Todo task before editing (text only, never blocks; off in child sessions)",
|
|
122
|
+
},
|
|
123
|
+
{ key: "doTasks", type: "number", label: "-do task slots", min: 0, zeroLabel: "off", description: "Task slots a /unipi:kanboard-do grants; each `start` uses one" },
|
|
124
|
+
{ key: "doWrites", type: "number", label: "-do write budget", min: 0, zeroLabel: "off", description: "Board writes a /unipi:kanboard-do grants (add, edit, link, order, move, note on tasks you don't hold)" },
|
|
109
125
|
],
|
|
110
126
|
},
|
|
111
127
|
],
|
|
@@ -127,9 +143,18 @@ export function readKanboardSettings(cwd: string = process.cwd()): KanboardSetti
|
|
|
127
143
|
openBrowser: raw.openBrowser === true,
|
|
128
144
|
requireAuth: raw.requireAuth === true,
|
|
129
145
|
keepToken: raw.keepToken === true,
|
|
130
|
-
queueMax: typeof raw.queueMax === "number" && raw.queueMax >= 0 ? raw.queueMax : DEFAULT_SETTINGS.queueMax,
|
|
131
146
|
maxSessions: typeof raw.maxSessions === "number" && raw.maxSessions >= 1 ? raw.maxSessions : DEFAULT_SETTINGS.maxSessions,
|
|
132
147
|
turnAddLimit: typeof raw.turnAddLimit === "number" && raw.turnAddLimit >= 0 ? raw.turnAddLimit : DEFAULT_SETTINGS.turnAddLimit,
|
|
148
|
+
// Migration: getSettings merges defaults, so a stored doCredits hides
|
|
149
|
+
// behind the doWrites default. Look at the stored layer for the legacy key
|
|
150
|
+
// (project first, then global) — the next settings write persists doWrites.
|
|
151
|
+
doTasks: typeof raw.doTasks === "number" && raw.doTasks >= 0 ? raw.doTasks : DEFAULT_SETTINGS.doTasks,
|
|
152
|
+
doWrites:
|
|
153
|
+
typeof raw.doWrites === "number" && raw.doWrites >= 0 && storedDoWritesSet(cwd)
|
|
154
|
+
? raw.doWrites
|
|
155
|
+
: (storedLegacyDoCredits(cwd) ?? DEFAULT_SETTINGS.doWrites),
|
|
156
|
+
blocking: raw.blocking === "ask" ? "ask" : "avoid",
|
|
157
|
+
reminders: raw.reminders !== false,
|
|
133
158
|
};
|
|
134
159
|
}
|
|
135
160
|
|
|
@@ -137,6 +162,35 @@ export function writeKanboardSettings(patch: Partial<KanboardSettings>, cwd: str
|
|
|
137
162
|
setSettings(KANBOARD_NAMESPACE, patch as unknown as Record<string, unknown>, "global", cwd);
|
|
138
163
|
}
|
|
139
164
|
|
|
165
|
+
/** Whether any stored layer carries an explicit doWrites (defaults don't count). */
|
|
166
|
+
function storedDoWritesSet(cwd: string): boolean {
|
|
167
|
+
for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
|
|
168
|
+
try {
|
|
169
|
+
if (!existsSync(file)) continue;
|
|
170
|
+
const parsed = JSON.parse(readFileSync(file, "utf-8")) as Record<string, unknown>;
|
|
171
|
+
if (typeof parsed.doWrites === "number") return true;
|
|
172
|
+
} catch {
|
|
173
|
+
// A corrupt file has no legacy value either.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** The pre-slot doCredits value from the stored layers, if any. */
|
|
180
|
+
function storedLegacyDoCredits(cwd: string): number | undefined {
|
|
181
|
+
for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
|
|
182
|
+
try {
|
|
183
|
+
if (!existsSync(file)) continue;
|
|
184
|
+
const parsed = JSON.parse(readFileSync(file, "utf-8")) as Record<string, unknown>;
|
|
185
|
+
const value = parsed.doCredits;
|
|
186
|
+
if (typeof value === "number" && value >= 0) return value;
|
|
187
|
+
} catch {
|
|
188
|
+
// A corrupt file has no legacy value either.
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
140
194
|
/** Re-exported so index.ts and tests share one registration path. */
|
|
141
195
|
export function registerKanboardRunners(runners: {
|
|
142
196
|
open: (ctx: unknown) => void | Promise<void>;
|
|
@@ -150,9 +204,9 @@ export function registerKanboardRunners(runners: {
|
|
|
150
204
|
|
|
151
205
|
/**
|
|
152
206
|
* Limits reach the Rust CLI through the environment — refresh the process-wide
|
|
153
|
-
* values so the agent's bash calls and
|
|
207
|
+
* values so the agent's bash calls and spawned CLI calls both see them.
|
|
208
|
+
* (queueMax died with the runner; the binary's queue limit is untouched.)
|
|
154
209
|
*/
|
|
155
210
|
export function applyLimitEnv(settings: KanboardSettings): void {
|
|
156
|
-
process.env.UNIPI_KANBOARD_QUEUE_MAX = String(settings.queueMax);
|
|
157
211
|
process.env.UNIPI_KANBOARD_MAX_SESSIONS = String(settings.maxSessions);
|
|
158
212
|
}
|
package/src/shapes.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface KanboardRun {
|
|
|
32
32
|
mode?: string;
|
|
33
33
|
goal?: string | null;
|
|
34
34
|
started?: string;
|
|
35
|
+
/** "system" (runner claim) or "agent" (`start` self-claim). */
|
|
36
|
+
owner?: string;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export interface KanboardActivity {
|
|
@@ -43,6 +45,8 @@ export interface KanboardActivity {
|
|
|
43
45
|
export interface KanboardTask {
|
|
44
46
|
id: string;
|
|
45
47
|
title: string;
|
|
48
|
+
/** Title derived from the first body line when `title` is empty (Rust `Task::display_title`). */
|
|
49
|
+
displayTitle?: string;
|
|
46
50
|
body?: string;
|
|
47
51
|
status: string;
|
|
48
52
|
priority?: string;
|
|
@@ -113,6 +117,7 @@ export function asTask(command: string, value: unknown): KanboardTask {
|
|
|
113
117
|
...record,
|
|
114
118
|
id: requireString(command, record, "id"),
|
|
115
119
|
title: requireString(command, record, "title"),
|
|
120
|
+
displayTitle: typeof record.displayTitle === "string" ? record.displayTitle : undefined,
|
|
116
121
|
status: requireString(command, record, "status"),
|
|
117
122
|
deps: optionalArray<string>(record.deps) ?? [],
|
|
118
123
|
activity: optionalArray<KanboardActivity>(record.activity) ?? [],
|