@pi-unipi/kanboard 3.0.0-alpha.30 → 3.0.0-alpha.31

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/index.ts CHANGED
@@ -20,19 +20,17 @@ import { Text } from "@earendil-works/pi-tui";
20
20
  import {
21
21
  MODULES,
22
22
  UNIPI_EVENTS,
23
- emitEvent,
23
+ bus,
24
24
  getPackageVersion,
25
25
  getSettings,
26
- getSharedKanboardStatus,
27
- getSharedOwnerStatus,
28
26
  initUnipiDirs,
29
27
  isChildProcess,
30
28
  registerCommandRunner,
29
+ registerCompactionContext,
31
30
  registerEvidenceContributor,
32
31
  registerMoveHandler,
33
32
  registerNudgeProvider,
34
33
  registerProgressRenderer,
35
- setSharedKanboardStatus,
36
34
  appendProgress,
37
35
  } from "@pi-unipi/core";
38
36
 
@@ -51,6 +49,7 @@ import {
51
49
  } from "./src/commands.js";
52
50
  import { createWriteGuard } from "./src/guard.js";
53
51
  import { NOTICE_ENTRY, flushNotices, NoticeBuffer } from "./src/notice-buffer.js";
52
+ import { kanboardCompactionContext, rearmOnOwnerResume } from "./src/bus-hooks.js";
54
53
  import { createKanboardMonitor } from "./src/monitor.js";
55
54
  import { createDebugLog } from "./src/debug.js";
56
55
  import { createProgressTracker, registerProgressReminders, ANTI_POISONING_SUFFIX } from "./src/reminders.js";
@@ -67,9 +66,11 @@ import {
67
66
  const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
68
67
 
69
68
  /** Utility owns the frozen judged set, so kanboard asks it to reveal the skill. */
70
- export const SKILL_REVEAL_EVENT = "unipi:skills:reveal";
71
69
  export const KANBOARD_SKILL = "kanboard";
72
70
 
71
+ /** Last non-null CLI prefix (`<bin> --actor agent --project <slug>`); survives CLI re-attach gaps (UNI-123d). */
72
+ let lastCliPrefix: string | null = null;
73
+
73
74
  export default function (pi: ExtensionAPI) {
74
75
  registerMoveHandler(kanboardMoveHandler);
75
76
  // One session id shared by the agent's bash calls (refined at session_start
@@ -104,6 +105,12 @@ export default function (pi: ExtensionAPI) {
104
105
  }
105
106
  };
106
107
 
108
+ /** Full CLI prefix for board writes, or null while the CLI/slug is unknown. */
109
+ const currentCliPrefix = (): string | null => {
110
+ const slug = projectSlug();
111
+ return cli && slug ? `${cli.binary.path} --actor agent --project ${slug}` : null;
112
+ };
113
+
107
114
  // ── board reads (lead monitor/evidence/holder/notice) ────────────────
108
115
  const listTasks = async (args: string[] = []): Promise<KanboardTask[]> => {
109
116
  const client = cli;
@@ -133,7 +140,14 @@ export default function (pi: ExtensionAPI) {
133
140
  list: () => listTasks(),
134
141
  listReady: () => listTasks(["--ready"]),
135
142
  session: sessionId,
136
- ownerStatus: () => getSharedOwnerStatus(),
143
+ // The long-horizon owner lifecycle rides the bus's sticky LH_STATE now;
144
+ // the monitor's deps shape is unchanged.
145
+ ownerStatus: () => {
146
+ const s = bus.get(UNIPI_EVENTS.LH_STATE);
147
+ return s
148
+ ? { ...(s.owner ? { owner: s.owner } : {}), ...(s.lastStop ? { lastStop: s.lastStop } : {}) }
149
+ : undefined;
150
+ },
137
151
  now: () => Date.now(),
138
152
  // User-only: only QUEUE here — a sendMessage custom message would be
139
153
  // converted to a user-role LLM message (never what a notice wants).
@@ -148,15 +162,17 @@ export default function (pi: ExtensionAPI) {
148
162
  void refreshHolder();
149
163
  },
150
164
  cliPrefix: () => {
151
- const slug = projectSlug();
152
- return cli && slug ? `${cli.binary.path} --actor agent --project ${slug}` : null;
165
+ // UNI-123d: before the CLI attaches, fall back to the last known prefix
166
+ // so nudge text carries the real --actor/--project invocation.
167
+ const current = currentCliPrefix();
168
+ return current ?? lastCliPrefix;
153
169
  },
154
170
  debug,
155
171
  });
156
172
 
157
173
  const refreshHolder = async (): Promise<void> => {
158
174
  try {
159
- setSharedKanboardStatus({
175
+ bus.emit(UNIPI_EVENTS.KANBOARD_STATUS, {
160
176
  claims: (await ownClaims()).map((task) => task.id),
161
177
  autowork: guard.remaining().autowork,
162
178
  });
@@ -171,6 +187,19 @@ export default function (pi: ExtensionAPI) {
171
187
  } catch {
172
188
  // Registration must never block module load.
173
189
  }
190
+ // UNI-123a: the board contract survives compaction — the reminder rides
191
+ // every summary while claims are open.
192
+ try {
193
+ registerCompactionContext("kanboard", kanboardCompactionContext);
194
+ } catch {
195
+ // Registration must never block module load.
196
+ }
197
+ // UNI-123c: a resumed owner re-arms the settle monitor (pause disarmed it).
198
+ try {
199
+ rearmOnOwnerResume(pi, monitor);
200
+ } catch {
201
+ // Registration must never block module load.
202
+ }
174
203
  try {
175
204
  registerEvidenceContributor("kanboard", async () => ({
176
205
  blocking: (await ownClaims()).map(
@@ -208,7 +237,7 @@ export default function (pi: ExtensionAPI) {
208
237
 
209
238
  const revealSkill = (ctx: ExtensionContext | { cwd?: string }): void => {
210
239
  // Append-only reveal (never the system prompt), so the prefix cache holds.
211
- emitEvent(pi, SKILL_REVEAL_EVENT, { names: [KANBOARD_SKILL], ctx });
240
+ bus.emit(UNIPI_EVENTS.SKILLS_REVEAL, { names: [KANBOARD_SKILL], ctx });
212
241
  debug(`reveal requested for ${KANBOARD_SKILL}`);
213
242
  };
214
243
 
@@ -348,7 +377,7 @@ export default function (pi: ExtensionAPI) {
348
377
  const deps = buildDeps();
349
378
  if (deps.cli) void syncPiRuntime(deps, ctx as unknown as ExtensionContext);
350
379
  }
351
- emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
380
+ bus.emit(UNIPI_EVENTS.MODULE_READY, {
352
381
  name: MODULES.KANBOARD,
353
382
  version: VERSION,
354
383
  commands: ["kanboard"],
@@ -449,7 +478,12 @@ export default function (pi: ExtensionAPI) {
449
478
  .then(() => debug(`released ${task.id} at shutdown`))
450
479
  .catch((error) => debug(`shutdown release ${task.id} failed: ${error instanceof Error ? error.message : String(error)}`));
451
480
  }
452
- setSharedKanboardStatus({ claims: [], autowork: guard.remaining().autowork });
481
+ const shutdownPrefix = currentCliPrefix() ?? lastCliPrefix;
482
+ bus.emit(UNIPI_EVENTS.KANBOARD_STATUS, {
483
+ claims: [],
484
+ autowork: guard.remaining().autowork,
485
+ ...(shutdownPrefix ? { cli: shutdownPrefix } : {}),
486
+ });
453
487
  } catch {
454
488
  // Shutdown must never hang the session teardown.
455
489
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/kanboard",
3
- "version": "3.0.0-alpha.30",
3
+ "version": "3.0.0-alpha.31",
4
4
  "description": "Kanboard v3 — deferred-work board: CLI/daemon bridge, task runner and skill",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@pi-unipi/core": "3.0.0-alpha.30"
34
+ "@pi-unipi/core": "3.0.0-alpha.31"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -48,10 +48,10 @@
48
48
  "themes": []
49
49
  },
50
50
  "optionalDependencies": {
51
- "@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.30",
52
- "@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.30",
53
- "@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.30",
54
- "@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.30",
55
- "@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.30"
51
+ "@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.31",
52
+ "@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.31",
53
+ "@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.31",
54
+ "@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.31",
55
+ "@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.31"
56
56
  }
57
57
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * kanboard ⇄ bus glue for the LEAD process (UNI-123).
3
+ *
4
+ * - kanboardCompactionContext: the board contract (finish / block / show for
5
+ * this session's claims) rides every compaction summary, so the agent keeps
6
+ * its duties after compaction summarizes the /unipi:kanboard-do message away.
7
+ * - rearmOnOwnerResume: a goal pause disarms the settle monitor; when the
8
+ * parked long-horizon owner resumes, the monitor must come back.
9
+ */
10
+
11
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
+ import { bus, kanboardClaimsReminder, UNIPI_EVENTS } from "@pi-unipi/core";
13
+
14
+ /** Compaction-context provider: the board contract for this session's claims. */
15
+ export function kanboardCompactionContext(): string | null {
16
+ return kanboardClaimsReminder(bus.get(UNIPI_EVENTS.KANBOARD_STATUS));
17
+ }
18
+
19
+ interface Armable {
20
+ arm(reason?: string): void;
21
+ }
22
+
23
+ /** Re-arm the settle monitor when a parked owner resumes. Returns unsubscribe. */
24
+ export function rearmOnOwnerResume(pi: Pick<ExtensionAPI, "on">, monitor: Armable): () => void {
25
+ return bus.on(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, (e) => {
26
+ if (e.event === "resumed") monitor.arm("owner resumed");
27
+ });
28
+ }