@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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: kanboard
3
- description: "Kanboard — the project's deferred-work board. Use when the user asks to note a task for later, to see what is on the board, or while working a board task: read it with `unipi-kanboard show`, add notes, block with a question, or file follow-up work."
3
+ description: "Kanboard — the project's deferred-work board. Use when the user asks to note a task for later, to see what is on the board, or to work board tasks (e.g. do UNI-5): `start` a task before working on it and `finish` it with a summary when done; while working a board task: read it with `unipi-kanboard show`, add notes, block with a question, or file follow-up work."
4
4
  ---
5
5
 
6
6
  # Kanboard
@@ -16,27 +16,35 @@ Always pass the actor and the project, and call it by absolute path (it may not
16
16
  be on `PATH`):
17
17
 
18
18
  ```sh
19
- <binary> --actor agent --project <slug> list [--ready] [--json]
19
+ <binary> --actor agent --project <slug> list [--ready] [--all] [--json] # bare list = todo/in_progress/blocked/in_review/done only; --all adds backlog/cancelled/archived
20
20
  <binary> --actor agent --project <slug> show <ID>
21
- <binary> --actor agent --project <slug> next # what claim-next would pick + why
21
+ <binary> --actor agent --project <slug> next # what would be picked next + why (read-only)
22
22
  <binary> --actor agent --project <slug> chain <ID> # upstream deps + downstream dependents
23
23
  <binary> --actor agent --project <slug> search "<text>" [--all] # id/title/body, archived excluded unless --all
24
24
  <binary> --actor agent --project <slug> add "<title>" [--status todo] [--after <ID>] [--body-file <f>] [--attach <file>]…
25
- <binary> --actor agent --project <slug> note <ID> "<text>"
25
+ <binary> --actor agent --project <slug> note <ID> "<text>" [--attach <file>]…
26
26
  <binary> --actor agent --project <slug> attach <ID> <file> --note "<what it shows>"
27
27
  <binary> --actor agent --project <slug> attachments <ID>
28
28
  <binary> --actor agent --project <slug> edit <ID> --title|--body|--labels … # only tasks you created, while in backlog/todo
29
- <binary> --actor agent --project <slug> move <ID> blocked --comment "<what you need>"
29
+ <binary> --actor agent --project <slug> start <ID> # todo → in progress, claimed for your session (costs a -do slot)
30
+ <binary> --actor agent --project <slug> finish <ID> --comment "<summary>" [--attach <file>]… # in progress → in review, only a task you started (free)
31
+ <binary> --actor agent --project <slug> move <ID> blocked --comment "<what you need>" [--attach <file>]…
30
32
  <binary> --actor agent --project <slug> link <ID> --after <DEP>
31
33
  <binary> --actor agent --project <slug> unlink <ID> --after <DEP>
32
34
  <binary> --actor agent --project <slug> order <ID> --top|--bottom|--before <ID>
33
- <binary> --actor agent --project <slug> queue <ID>… | queue --list # your session's work queue (limit: setting queueMax, default 10)
34
- <binary> --actor agent --project <slug> unqueue [<ID>…] # no ids clears it
35
35
  ```
36
36
 
37
+ There is **no queue and no runner** — the session works tasks itself
38
+ (`/unipi:kanboard-autowork` just keeps offering the next ready task).
39
+ `queue`, `unqueue`, `claim-next`, `set-run` and `edit --strategy/--plan` are
40
+ removed and refused.
41
+
37
42
  `--json` gives machine-readable output for every subcommand (task objects carry
38
43
  `ready`, `waitingFor`, `depsStatus` and `staleness`).
39
44
 
45
+ `list`/`search` show a one-line body excerpt; read a task's full description with
46
+ `show <ID>` before editing or starting it.
47
+
40
48
  `settings show` reads the pi runtime and effective limits; `settings set`
41
49
  and `rotate-token` are user-only — the agent is refused.
42
50
 
@@ -45,10 +53,11 @@ and `rotate-token` are user-only — the agent is refused.
45
53
  | Move | Who |
46
54
  |---|---|
47
55
  | backlog ↔ todo | user, agent |
48
- | todo → in progress | **the runner only** (`claim-next`) |
56
+ | todo → in progress | the agent with `start <ID>` (claims it for its session; costs a `-do` slot) |
49
57
  | edit a task | agent — only tasks it created, and only in backlog/todo |
50
- | in progress → in review | **the runner only** (when your turn ends) |
58
+ | in progress → in review | the agent with `finish <ID> --comment` — only a task its own session `start`ed |
51
59
  | in progress → blocked | agent, system — **comment required** (what you need) |
60
+ | in progress → todo | user or system `release` — comment required (the session died or gave the task up) |
52
61
  | blocked → todo | user only — comment required (the answer) |
53
62
  | in review → done | user only |
54
63
  | in review → todo/backlog | user only — comment required (rework note) |
@@ -65,26 +74,45 @@ lists them — read the files there directly if you need one.
65
74
 
66
75
  1. **Never pass `--actor user`.** That is an honour system: pretending to be the
67
76
  user to cancel a task or mark it done breaks the board's whole contract.
68
- 2. **Never move a task to `in_review` or `done`.** The runner writes those when
69
- your turn ends; claiming them yourself loses the summary and the review step.
77
+ 2. **Work the tasks yourself, in this session** (the user says "do UNI-5"):
78
+ **`start` a task before working on it, `finish` it with a summary when done**
79
+ — `start <ID>` before your first edit, `finish <ID> --comment "<what you
80
+ did>"` before your turn ends (or `move <ID> blocked --comment "<what you
81
+ need>"` if you cannot). `finish` and own-claim `blocked`/`note` are always
82
+ free; **`start` costs one of your `-do` task slots** — count the tasks a
83
+ request needs BEFORE starting anything, and if that is more than your
84
+ remaining slots, start none of them: say what you can do now and ask whether
85
+ to raise `kanboard.doTasks` or work in batches. In Review means "the agent
86
+ did the work, a human reviews" — never leave a task you started In Progress
87
+ (the board monitor will nudge you back to it). **Never move a task to
88
+ `done`.**
70
89
  3. **Never cancel.** If a task should be dropped, block it with
71
90
  `move <ID> blocked --comment "suggest cancel: <why>"` and let the user decide.
72
- 4. **To ask the user something, block the task and stop**: `move <ID> blocked
73
- --comment "<exactly what you need>"`. The answer arrives as a comment the next
74
- time the task is claimed.
91
+ 4. **Follow the blocking rule in your task prompt**: by default work
92
+ autonomously and record assumptions with `note <ID> "assumed: <what/why>"`;
93
+ block (with a comment saying exactly what you need) only when you truly
94
+ cannot continue — `move <ID> blocked --comment "<what you need>"`.
75
95
  5. **Work only on the task you were given.** Follow-up work goes to the board as
76
96
  a new task in Backlog (`add "<title>"`), optionally `link <new> --after <ID>`.
77
- A session holds one claim at a time, and at most `maxSessions` (default 2)
78
- sessions may run tasks in a project — if the board refuses a claim, that is
79
- why. You may
97
+ At most `maxSessions` (default 2) sessions may hold tasks in a project at
98
+ once — if the board refuses a `start`, that is why. You may
80
99
  block only the task your own session is running (`move <ID> blocked` checks
81
100
  `--session`/`UNIPI_KANBOARD_SESSION` against the claim).
82
- 6. **To have tasks worked during a `/unipi:kanboard-do` turn, queue them** with
83
- `queue <IDs>` (Todo tasks, up to the queue limit — setting `queueMax`, default 10) — the runner starts them one by one
84
- after the turn ends. Never claim or start them yourself.
101
+ 6. **Sidekicks and subagents can read the board but never write it** — every
102
+ write is refused with "board writes are the lead's job". Brief them with the
103
+ task, then update the board yourself from their reports. (The lead session
104
+ works tasks itself; `/unipi:kanboard-autowork` just keeps offering the next
105
+ ready task in the same session — there is no separate worker.)
85
106
  7. Use `note <ID> "<text>"` for progress worth remembering (decisions, what you
86
107
  verified, what you left undone) — it is the activity log the next reader sees.
87
- 8. Dependencies form a DAG: a task is ready only when every dep reached the chain
108
+ 8. **Write the `finish` / `blocked` comment as the report the user reads.** It
109
+ is shown as the task's status banner and opens in a reader, rendered as
110
+ markdown. Lead with one plain sentence (what you did, or what stops you),
111
+ then short paragraphs or `-` bullets separated by blank lines; put questions
112
+ for the user as a numbered list under `**Need from you:**`. Name files and
113
+ commands in backticks. Do not repeat a note you already wrote — the report
114
+ replaces it. Attach evidence with `--attach <file>`.
115
+ 9. Dependencies form a DAG: a task is ready only when every dep reached the chain
88
116
  gate (`in_review` by default, `done` when configured). Cancelled deps block
89
117
  forever, so unlink or re-plan instead of waiting. `chain <ID>` shows the
90
118
  whole line, `next` shows what would be picked and why others wait.
@@ -92,15 +120,21 @@ lists them — read the files there directly if you need one.
92
120
  ## Terminal-only execution
93
121
 
94
122
  The web UI can create, edit, reorder, link and move tasks, but it **never runs a
95
- task** — there is no run button. Work starts only from a terminal with
96
- `/unipi:kanboard-autowork start` (or the queue after a `/unipi:kanboard-do`). (The Done column's "Summarize & archive" does call the
97
- agent command set in the board's Settings, but only to write a summary.)
123
+ task** — there is no run button. Work starts only from a terminal: the session
124
+ itself, driven by `/unipi:kanboard-do` (a task-slot + write budget for one
125
+ request) or `/unipi:kanboard-autowork` (the monitor keeps offering the next
126
+ ready task in this session — no separate worker exists). (The Done column's
127
+ "Summarize & archive" does call the agent command set in the board's Settings,
128
+ but only to write a summary.)
98
129
 
99
130
  ## Attachments
100
131
 
101
132
  Users attach screenshots, logs and documents in the board UI; they appear in the
102
133
  text as markdown with `att:<ID>/<name>` references, and `show <ID> --json` lists
103
134
  them under `attachments` with an absolute `path` — read the file from there
104
- (use your image-reading tool for images). To hand back evidence, `attach` a file:
105
- it is stored beside the board and the comment embeds it, so the user sees the
106
- image or file inline.
135
+ (use your image-reading tool for images). To hand back evidence (a screenshot,
136
+ a log, a report), pass `--attach <file>` (repeatable) to `finish`, `move … blocked`
137
+ or `note`, or use `attach <ID> <file> --note "…"`: the file is stored beside the
138
+ board and the comment embeds it, so the user sees the image or file inline. A
139
+ file's path written in the comment is replaced by the embed; otherwise the
140
+ embed is appended. Attaching to a task you started is free.
package/src/badges.ts ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @pi-unipi/kanboard — transcript badges (user-only, never LLM context).
3
+ *
4
+ * A successful lead `start` / `finish` / `move <ID> blocked` appends a custom
5
+ * transcript entry rendered as `▣ UNI-30 started`, `✓ UNI-30 → In Review`,
6
+ * `⊘ UNI-30 blocked: <comment>`. Custom entries persist for the user but are
7
+ * outside the LLM context (same mechanism as core's progress bars).
8
+ */
9
+
10
+ import type { ExtensionAPI, ThemeColor } from "@earendil-works/pi-coding-agent";
11
+ import { Text, truncateToWidth, type Component } from "@earendil-works/pi-tui";
12
+
13
+ import { kanboardInvocations } from "./guard.js";
14
+
15
+ export const BADGE_ENTRY = "unipi:kanboard-badge";
16
+
17
+ export type BadgeKind = "started" | "finished" | "blocked";
18
+
19
+ export interface BadgeData {
20
+ kind: BadgeKind;
21
+ id: string;
22
+ /** Short human text (a blocked task carries what is needed). */
23
+ text?: string;
24
+ }
25
+
26
+ /** The badge kind a kanboard CLI call produces, or null. */
27
+ export function badgeKindFor(sub: string, args: readonly string[]): BadgeKind | null {
28
+ if (sub === "start") return "started";
29
+ if (sub === "finish") return "finished";
30
+ if (sub === "move" && args.includes("blocked")) return "blocked";
31
+ return null;
32
+ }
33
+
34
+ /** Extract a badge from a successful kanboard bash call, or null. */
35
+ export function badgeFromToolCall(toolName: string, input: Record<string, unknown> | undefined, isError: boolean): BadgeData | null {
36
+ if (isError || (toolName !== "bash" && toolName !== "powershell")) return null;
37
+ for (const invocation of kanboardInvocations(String(input?.command ?? ""))) {
38
+ const kind = badgeKindFor(invocation.sub, invocation.args);
39
+ if (!kind) continue;
40
+ const id = invocation.args.find((arg) => !arg.startsWith("-"));
41
+ if (!id) continue;
42
+ let text: string | undefined;
43
+ if (kind === "blocked") {
44
+ const flagIndex = invocation.args.indexOf("--comment");
45
+ text = flagIndex >= 0 ? (invocation.args[flagIndex + 1] ?? "").replace(/^["']|["']$/g, "") : undefined;
46
+ }
47
+ return { kind, id, ...(text !== undefined && text.length > 0 ? { text } : {}) };
48
+ }
49
+ return null;
50
+ }
51
+
52
+ export function badgeText(data: BadgeData): string {
53
+ if (data.kind === "started") return `▣ ${data.id} started`;
54
+ if (data.kind === "finished") return `✓ ${data.id} → In Review`;
55
+ const what = data.text ? `: ${data.text.slice(0, 80)}` : "";
56
+ return `⊘ ${data.id} blocked${what}`;
57
+ }
58
+
59
+ function badgeColor(kind: BadgeKind): ThemeColor {
60
+ if (kind === "started") return "accent";
61
+ if (kind === "finished") return "success";
62
+ return "warning";
63
+ }
64
+
65
+ /** Register the entry renderer (once per extension load; lead only). */
66
+ export function registerBadgeRenderer(pi: ExtensionAPI): void {
67
+ try {
68
+ pi.registerEntryRenderer<BadgeData>(BADGE_ENTRY, (entry, _options, theme) => {
69
+ const data = entry.data;
70
+ if (!data || typeof data.id !== "string") return undefined;
71
+ const t = theme as unknown as {
72
+ fg?: (color: ThemeColor, text: string) => string;
73
+ bold?: (text: string) => string;
74
+ };
75
+ const line = t.fg?.(badgeColor(data.kind), badgeText(data)) ?? badgeText(data);
76
+ return new Text(t.bold ? t.bold(line) : line, 0, 0);
77
+ });
78
+ } catch {
79
+ // Renderer registration is UI-dependent; skip where unavailable.
80
+ }
81
+ }
82
+
83
+ /** Emit a badge for a successful tool result (lead tool_result hook body). */
84
+ export function maybeBadgeToolResult(pi: ExtensionAPI, toolName: string, input: Record<string, unknown> | undefined, isError: boolean, width = 80): Component | null {
85
+ const badge = badgeFromToolCall(toolName, input, isError);
86
+ if (!badge) return null;
87
+ try {
88
+ pi.appendEntry<BadgeData>(BADGE_ENTRY, badge);
89
+ return new Text(truncateToWidth(badgeText(badge), width), 0, 0);
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
package/src/bin.ts CHANGED
@@ -80,10 +80,12 @@ export function resolveBinary(env: NodeJS.ProcessEnv = process.env, from?: strin
80
80
  if (explicit) {
81
81
  return isRunnable(explicit) ? { path: explicit, source: "env" } : null;
82
82
  }
83
- const packaged = platformPackagePath(from);
84
- if (packaged && isRunnable(packaged)) return { path: packaged, source: "platform-package" };
83
+ // In a source checkout the dev build wins — the platform package can ship a
84
+ // stale prebuilt binary (seen on coffee: alpha.0 shadowed the debug build).
85
85
  const dev = devBuildPath();
86
86
  if (dev) return { path: dev, source: "dev-build" };
87
+ const packaged = platformPackagePath(from);
88
+ if (packaged && isRunnable(packaged)) return { path: packaged, source: "platform-package" };
87
89
  return null;
88
90
  }
89
91