pi-harness-runtime 0.10.0 → 0.10.2

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +122 -0
  3. package/harness/e2e/minimax-quota-scraper.ts +4 -4
  4. package/harness/e2e/openai-quota-scraper.ts +13 -8
  5. package/index.ts +93 -2
  6. package/package.json +1 -1
  7. package/packages/a2a-adapter/package.json +1 -1
  8. package/packages/auth/package.json +1 -1
  9. package/packages/autonomous-refactor/package.json +1 -1
  10. package/packages/autonomous-runtime/package.json +1 -1
  11. package/packages/cache-strategy/package.json +1 -1
  12. package/packages/capability-registry/package.json +1 -1
  13. package/packages/checkpoint/package.json +1 -1
  14. package/packages/cli-plugin-sdk/package.json +1 -1
  15. package/packages/clipboard/dist/src/copy-sync.d.ts +27 -0
  16. package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -0
  17. package/packages/clipboard/dist/src/copy-sync.js +170 -0
  18. package/packages/clipboard/dist/src/copy-sync.js.map +1 -0
  19. package/packages/clipboard/dist/src/gist-relay.d.ts +48 -0
  20. package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -0
  21. package/packages/clipboard/dist/src/gist-relay.js +186 -0
  22. package/packages/clipboard/dist/src/gist-relay.js.map +1 -0
  23. package/packages/clipboard/dist/src/github-login.d.ts +16 -0
  24. package/packages/clipboard/dist/src/github-login.d.ts.map +1 -0
  25. package/packages/clipboard/dist/src/github-login.js +69 -0
  26. package/packages/clipboard/dist/src/github-login.js.map +1 -0
  27. package/packages/clipboard/dist/src/index.d.ts +2 -0
  28. package/packages/clipboard/dist/src/index.d.ts.map +1 -1
  29. package/packages/clipboard/dist/src/index.js +2 -1
  30. package/packages/clipboard/dist/src/index.js.map +1 -1
  31. package/packages/clipboard/package.json +1 -1
  32. package/packages/clipboard/src/copy-sync.ts +201 -0
  33. package/packages/clipboard/src/gist-relay.ts +240 -0
  34. package/packages/clipboard/src/github-login.ts +107 -0
  35. package/packages/clipboard/src/index.ts +2 -0
  36. package/packages/clipboard/test/mojibake.test.mjs +143 -0
  37. package/packages/code-generation/package.json +1 -1
  38. package/packages/code-review/package.json +1 -1
  39. package/packages/codex-adapter/package.json +1 -1
  40. package/packages/context-compiler/package.json +1 -1
  41. package/packages/context-discovery/package.json +1 -1
  42. package/packages/context-manager/package.json +1 -1
  43. package/packages/cookie-sanitizer/package.json +1 -1
  44. package/packages/cost-optimizer/package.json +1 -1
  45. package/packages/dependency-analyzer/package.json +1 -1
  46. package/packages/django-plugin/package.json +1 -1
  47. package/packages/doc-generator/package.json +1 -1
  48. package/packages/evaluation-engine/package.json +1 -1
  49. package/packages/evaluation-runner/package.json +1 -1
  50. package/packages/event-bus/package.json +1 -1
  51. package/packages/event-store/package.json +1 -1
  52. package/packages/experience-replay/package.json +1 -1
  53. package/packages/feedback-collector/package.json +1 -1
  54. package/packages/framework-detector/package.json +1 -1
  55. package/packages/framework-plugin-sdk/package.json +1 -1
  56. package/packages/frappe-plugin/package.json +1 -1
  57. package/packages/generic-web-plugin/package.json +1 -1
  58. package/packages/health-monitor/package.json +1 -1
  59. package/packages/intent-analyzer/package.json +1 -1
  60. package/packages/laravel-plugin/package.json +1 -1
  61. package/packages/learning-engine/package.json +1 -1
  62. package/packages/mcp-adapter/package.json +1 -1
  63. package/packages/memory-engine/package.json +1 -1
  64. package/packages/model-registry/package.json +1 -1
  65. package/packages/nextjs-plugin/package.json +1 -1
  66. package/packages/notification/package.json +1 -1
  67. package/packages/observability/package.json +1 -1
  68. package/packages/performance-optimizer/package.json +1 -1
  69. package/packages/privilege-broker/package.json +1 -1
  70. package/packages/project-analyzer/package.json +1 -1
  71. package/packages/projection-engine/package.json +1 -1
  72. package/packages/prompt-compiler/package.json +1 -1
  73. package/packages/prompt-versioning/package.json +1 -1
  74. package/packages/provider-adapter-sdk/package.json +1 -1
  75. package/packages/provider-router/package.json +1 -1
  76. package/packages/provider-selector/package.json +1 -1
  77. package/packages/providers/package.json +1 -1
  78. package/packages/quota-manager/package.json +1 -1
  79. package/packages/rate-limiter/package.json +1 -1
  80. package/packages/react-vite-plugin/package.json +1 -1
  81. package/packages/release-manager/package.json +1 -1
  82. package/packages/requirement-compiler/package.json +1 -1
  83. package/packages/runtime/package.json +1 -1
  84. package/packages/scheduler/package.json +1 -1
  85. package/packages/scheduler-adapter/package.json +1 -1
  86. package/packages/session/package.json +1 -1
  87. package/packages/session-api/package.json +1 -1
  88. package/packages/session-export/package.json +1 -1
  89. package/packages/shared-context/package.json +1 -1
  90. package/packages/skill-registry/package.json +1 -1
  91. package/packages/sprint-planner/package.json +1 -1
  92. package/packages/subscription-engine/package.json +1 -1
  93. package/packages/task-compiler/package.json +1 -1
  94. package/packages/test-data-generator/package.json +1 -1
  95. package/packages/test-generator/package.json +1 -1
  96. package/packages/todo-bd-sync/package.json +22 -0
  97. package/packages/todo-bd-sync/src/detector.ts +150 -0
  98. package/packages/todo-bd-sync/src/extension.ts +84 -0
  99. package/packages/todo-bd-sync/src/index.ts +401 -0
  100. package/packages/todo-bd-sync/src/sync.ts +322 -0
  101. package/packages/todo-bd-sync/src/todo-reminder.ts +418 -0
  102. package/packages/todo-bd-sync/src/types.ts +137 -0
  103. package/packages/token-estimation/package.json +1 -1
  104. package/packages/token-optimizer/package.json +1 -1
  105. package/packages/tui/package.json +1 -1
  106. package/packages/types/package.json +1 -1
  107. package/packages/workflow-events/package.json +1 -1
  108. package/packages/workspace-scanner/package.json +1 -1
  109. package/packages/worktree/package.json +1 -1
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Detection utilities for checking if rpiv-todo is installed
3
+ */
4
+
5
+ import { execSync } from "node:child_process";
6
+ import { existsSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { homedir } from "node:os";
9
+
10
+ /**
11
+ * Check if @juicesharp/rpiv-todo is installed globally via npm
12
+ */
13
+ export function isRpivTodoInstalled(): boolean {
14
+ try {
15
+ // Check npm global packages
16
+ const output = execSync(
17
+ "npm list -g @juicesharp/rpiv-todo 2>/dev/null || echo 'NOT_FOUND'",
18
+ {
19
+ encoding: "utf8",
20
+ timeout: 5000,
21
+ },
22
+ );
23
+ return (
24
+ output.includes("@juicesharp/rpiv-todo") && !output.includes("NOT_FOUND")
25
+ );
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Check if rpiv-todo is loaded in pi extensions
33
+ * This checks common extension directories
34
+ */
35
+ export function isRpivTodoLoaded(): boolean {
36
+ // Check pi-agent extensions directory
37
+ const extensionsDir = join(homedir(), ".pi", "agent", "extensions");
38
+
39
+ // Check for rpiv-todo related files
40
+ const possiblePaths = [
41
+ join(extensionsDir, "rpiv-todo"),
42
+ join(extensionsDir, "@juicesharp", "rpiv-todo"),
43
+ join(extensionsDir, "node_modules", "@juicesharp", "rpiv-todo"),
44
+ ];
45
+
46
+ for (const path of possiblePaths) {
47
+ if (existsSync(path)) {
48
+ return true;
49
+ }
50
+ }
51
+
52
+ // Also check if the module can be resolved
53
+ try {
54
+ require.resolve("@juicesharp/rpiv-todo");
55
+ return true;
56
+ } catch {
57
+ // Not found via require
58
+ }
59
+
60
+ return false;
61
+ }
62
+
63
+ /**
64
+ * Get the version of rpiv-todo if installed
65
+ */
66
+ export function getRpivTodoVersion(): string | null {
67
+ try {
68
+ const output = execSync(
69
+ "npm list -g @juicesharp/rpiv-todo --json 2>/dev/null | grep '\"version\"' | head -1",
70
+ { encoding: "utf8", timeout: 5000 },
71
+ );
72
+ const match = output.match(/"version":\s*"([^"]+)"/);
73
+ return match ? match[1] : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Check if bd CLI is installed
81
+ */
82
+ export function isBdInstalled(): boolean {
83
+ try {
84
+ const output = execSync("which bd 2>/dev/null || echo 'NOT_FOUND'", {
85
+ encoding: "utf8",
86
+ timeout: 5000,
87
+ });
88
+ return output.includes("bd") && !output.includes("NOT_FOUND");
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Get the version of bd CLI if installed
96
+ */
97
+ export function getBdVersion(): string | null {
98
+ try {
99
+ const output = execSync("bd --version 2>/dev/null || echo 'unknown'", {
100
+ encoding: "utf8",
101
+ timeout: 5000,
102
+ });
103
+ return output.trim().replace("bd version ", "");
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get installation status of both dependencies
111
+ */
112
+ export interface DependencyStatus {
113
+ rpivTodo: {
114
+ installed: boolean;
115
+ version: string | null;
116
+ loaded: boolean;
117
+ };
118
+ bd: {
119
+ installed: boolean;
120
+ version: string | null;
121
+ };
122
+ }
123
+
124
+ export function getDependencyStatus(): DependencyStatus {
125
+ return {
126
+ rpivTodo: {
127
+ installed: isRpivTodoInstalled(),
128
+ version: getRpivTodoVersion(),
129
+ loaded: isRpivTodoLoaded(),
130
+ },
131
+ bd: {
132
+ installed: isBdInstalled(),
133
+ version: getBdVersion(),
134
+ },
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Log dependency status for debugging
140
+ */
141
+ export function logDependencyStatus(): void {
142
+ const status = getDependencyStatus();
143
+ console.log("[DEBUG todo-bd-sync] Dependency check:");
144
+ console.log(
145
+ ` rpiv-todo: installed=${status.rpivTodo.installed}, version=${status.rpivTodo.version}, loaded=${status.rpivTodo.loaded}`,
146
+ );
147
+ console.log(
148
+ ` bd: installed=${status.bd.installed}, version=${status.bd.version}`,
149
+ );
150
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * todo-bd-sync extension entry point
3
+ *
4
+ * This is the pi-coding-agent extension that:
5
+ * 1. Auto-injects "todo-list" to prompt to enable rpiv-todo
6
+ * 2. Sets up two-way sync between rpiv-todo and bd
7
+ * 3. Loads existing bd issues on session start
8
+ *
9
+ * To use this extension:
10
+ * 1. Install dependencies:
11
+ * pi install npm:@juicesharp/rpiv-todo
12
+ * (bd should already be installed)
13
+ *
14
+ * 2. The extension auto-enables when rpiv-todo is detected
15
+ */
16
+
17
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
+ import {
19
+ createTodoBdSync,
20
+ type TodoBdSync,
21
+ type TodoBdSyncConfig,
22
+ } from "./index.js";
23
+ import { getDependencyStatus, logDependencyStatus } from "./detector.js";
24
+ import { createCustomReminder } from "./todo-reminder.js";
25
+ import { getOpenBdIssues } from "./sync.js";
26
+
27
+ /**
28
+ * Register the todo-bd-sync extension
29
+ */
30
+ export function registerTodoBdSync(
31
+ pi: ExtensionAPI,
32
+ config?: TodoBdSyncConfig,
33
+ ): TodoBdSync | null {
34
+ const deps = getDependencyStatus();
35
+
36
+ // Check if bd is installed
37
+ if (!deps.bd.installed) {
38
+ return null;
39
+ }
40
+
41
+ // Log dependency status for debugging (only if debug mode)
42
+ if (config?.debug) {
43
+ logDependencyStatus();
44
+ }
45
+
46
+ // Create and start sync
47
+ const sync = createTodoBdSync(pi, {
48
+ autoInjectPrompt: true,
49
+ syncDirection: "both",
50
+ loadOnSessionStart: true,
51
+ ...config,
52
+ });
53
+
54
+ sync.start();
55
+
56
+ // Start todo reminder (DISABLED by default — Phase 1 emergency containment)
57
+ // Reminder spam was causing transcript growth. Enable only after proper task scoping.
58
+ const reminder = createCustomReminder(
59
+ pi,
60
+ () => {
61
+ // Get remaining todos from bd
62
+ const issues = getOpenBdIssues();
63
+ return issues
64
+ .filter((i) => i.status !== "closed")
65
+ .map((i) => ({
66
+ id: i.id,
67
+ title: i.title,
68
+ status: i.status,
69
+ }));
70
+ },
71
+ {
72
+ autoRemind: false, // DISABLED — fix reminder spam before re-enabling
73
+ minPendingTasks: 1,
74
+ },
75
+ );
76
+ reminder.start();
77
+
78
+ return sync;
79
+ }
80
+
81
+ // Default export for pi extension loading
82
+ export default function todoBdSyncExtension(pi: ExtensionAPI): void {
83
+ registerTodoBdSync(pi, { debug: false });
84
+ }
@@ -0,0 +1,401 @@
1
+ /**
2
+ * todo-bd-sync - Two-way sync between rpiv-todo and bd
3
+ *
4
+ * This package provides:
5
+ * 1. Detection of rpiv-todo installation
6
+ * 2. Two-way sync between rpiv-todo tasks and bd issues
7
+ * 3. Auto-enable functionality via prompt injection
8
+ *
9
+ * Usage:
10
+ * import { createTodoBdSync } from "@moocoding/todo-bd-sync";
11
+ *
12
+ * const sync = createTodoBdSync(pi);
13
+ * sync.start();
14
+ */
15
+
16
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
17
+ import { getDependencyStatus, logDependencyStatus } from "./detector.js";
18
+ import {
19
+ syncTodoToBd,
20
+ isBdCommand,
21
+ extractBdIdFromCommand,
22
+ getMappingRegistry,
23
+ getOpenBdIssues,
24
+ parseBdCreateOutput,
25
+ } from "./sync.js";
26
+ import type { TodoTask } from "./types.js";
27
+
28
+ // Configuration
29
+ export interface TodoBdSyncConfig {
30
+ /** Auto-inject "todo-list" to prompt (default: true) */
31
+ autoInjectPrompt?: boolean;
32
+ /** Sync direction: both, todoOnly, bdOnly (default: both) */
33
+ syncDirection?: "both" | "todoOnly" | "bdOnly";
34
+ /** Load existing bd issues on session start (default: true) */
35
+ loadOnSessionStart?: boolean;
36
+ /** Debug logging (default: false) */
37
+ debug?: boolean;
38
+ }
39
+
40
+ const DEFAULT_CONFIG: Required<TodoBdSyncConfig> = {
41
+ autoInjectPrompt: true,
42
+ syncDirection: "both",
43
+ loadOnSessionStart: true,
44
+ debug: false,
45
+ };
46
+
47
+ interface ToolExecutionEvent {
48
+ toolName: string;
49
+ args?: Record<string, unknown>;
50
+ result?: unknown;
51
+ isError?: boolean;
52
+ }
53
+
54
+ /**
55
+ * Create a todo-bd sync instance
56
+ */
57
+ export function createTodoBdSync(
58
+ pi: ExtensionAPI,
59
+ config: TodoBdSyncConfig = {},
60
+ ): TodoBdSync {
61
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
62
+ return new TodoBdSync(pi, finalConfig);
63
+ }
64
+
65
+ /**
66
+ * TodoBdSync class - handles two-way sync between rpiv-todo and bd
67
+ */
68
+ export class TodoBdSync {
69
+ private pi: ExtensionAPI;
70
+ private config: Required<TodoBdSyncConfig>;
71
+ private enabled: boolean = false;
72
+ private sessionStartLoaded: boolean = false;
73
+
74
+ constructor(pi: ExtensionAPI, config: Required<TodoBdSyncConfig>) {
75
+ this.pi = pi;
76
+ this.config = config;
77
+ }
78
+
79
+ /**
80
+ * Initialize and start the sync
81
+ */
82
+ start(): void {
83
+ // Check dependencies
84
+ const deps = getDependencyStatus();
85
+
86
+ if (!deps.bd.installed) {
87
+ if (this.config.debug) {
88
+ console.log("[DEBUG todo-bd-sync] bd CLI not installed, skipping");
89
+ }
90
+ return;
91
+ }
92
+
93
+ if (!deps.rpivTodo.installed && !this.shouldUseFallback()) {
94
+ if (this.config.debug) {
95
+ console.log(
96
+ "[todo-bd-sync] rpiv-todo not installed, using fallback mode",
97
+ );
98
+ }
99
+ // In fallback mode, we would implement our own todo overlay
100
+ // For now, we'll just note that it's not available
101
+ }
102
+
103
+ this.enabled = true;
104
+
105
+ if (this.config.debug) {
106
+ logDependencyStatus();
107
+ }
108
+
109
+ // Register event handlers
110
+ this.registerHandlers();
111
+
112
+ if (this.config.debug) {
113
+ console.log("[DEBUG todo-bd-sync] Started with config:", this.config);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Register event handlers
119
+ */
120
+ private registerHandlers(): void {
121
+ // Session start - load existing bd issues
122
+ if (this.config.loadOnSessionStart) {
123
+ this.pi.on(
124
+ "session_start",
125
+ async (_event: unknown, ctx: { hasUI?: boolean }) => {
126
+ if (!this.enabled || this.sessionStartLoaded) return;
127
+ await this.handleSessionStart(ctx);
128
+ this.sessionStartLoaded = true;
129
+ },
130
+ );
131
+ }
132
+
133
+ // Tool execution end - sync changes
134
+ this.pi.on("tool_execution_end", async (event: ToolExecutionEvent) => {
135
+ if (!this.enabled) return;
136
+
137
+ if (event.toolName === "todo") {
138
+ // Todo tool was called - sync to bd
139
+ if (
140
+ this.config.syncDirection === "both" ||
141
+ this.config.syncDirection === "todoOnly"
142
+ ) {
143
+ this.handleTodoExecution(event);
144
+ }
145
+ }
146
+
147
+ if (event.toolName === "bash" && event.args?.command) {
148
+ // Bash command executed - check if it's a bd command
149
+ if (
150
+ this.config.syncDirection === "both" ||
151
+ this.config.syncDirection === "bdOnly"
152
+ ) {
153
+ this.handleBashExecution(event);
154
+ }
155
+ }
156
+ });
157
+
158
+ // Register slash command for manual sync
159
+ this.pi.registerCommand("bd-todo-sync", {
160
+ description: "Sync todo list with bd issues",
161
+ handler: async (_args: unknown, ctx) => {
162
+ if (!this.enabled) {
163
+ ctx.ui.notify(
164
+ "[todo-bd-sync] Not enabled - dependencies missing",
165
+ "warning",
166
+ );
167
+ return;
168
+ }
169
+
170
+ // Sync all todos to bd
171
+ const mappings = getMappingRegistry().getAll();
172
+ ctx.ui.notify(
173
+ `[todo-bd-sync] Syncing ${mappings.length} mapped tasks to bd...`,
174
+ "info",
175
+ );
176
+
177
+ // Force refresh bd issues
178
+ const issues = getOpenBdIssues();
179
+ ctx.ui.notify(
180
+ `[todo-bd-sync] Found ${issues.length} open bd issues`,
181
+ "info",
182
+ );
183
+ },
184
+ });
185
+
186
+ // Register slash command to check status
187
+ this.pi.registerCommand("bd-todo-status", {
188
+ description: "Show todo-bd sync status",
189
+ handler: async (_args: unknown, ctx) => {
190
+ const deps = getDependencyStatus();
191
+ const mappings = getMappingRegistry().getAll();
192
+
193
+ const lines = [
194
+ "=== todo-bd-sync Status ===",
195
+ `rpiv-todo: ${deps.rpivTodo.installed ? "installed" : "not installed"}`,
196
+ `bd: ${deps.bd.installed ? "installed (" + deps.bd.version + ")" : "not installed"}`,
197
+ `Mappings: ${mappings.length}`,
198
+ `Enabled: ${this.enabled}`,
199
+ ];
200
+
201
+ ctx.ui.notify(lines.join("\n"), "info");
202
+ },
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Handle todo tool execution
208
+ */
209
+ private handleTodoExecution(event: ToolExecutionEvent): void {
210
+ if (event.isError) return;
211
+
212
+ // Extract action and task from event
213
+ const action = event.args?.action as string | undefined;
214
+ const task = this.extractTaskFromEvent(event);
215
+
216
+ if (!action || !task) {
217
+ if (this.config.debug) {
218
+ console.log("[DEBUG todo-bd-sync] Could not extract action/task from event");
219
+ }
220
+ return;
221
+ }
222
+
223
+ // Sync to bd
224
+ const result = syncTodoToBd(action, task);
225
+
226
+ if (this.config.debug) {
227
+ console.log("[DEBUG todo-bd-sync] Todo sync result:", result);
228
+ }
229
+
230
+ // Notify if sync failed
231
+ if (!result.success && this.config.debug) {
232
+ console.error("[DEBUG todo-bd-sync] Sync failed:", result.error);
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Handle bash command execution
238
+ */
239
+ private handleBashExecution(event: ToolExecutionEvent): void {
240
+ const command = event.args?.command as string;
241
+
242
+ if (!isBdCommand(command)) {
243
+ return;
244
+ }
245
+
246
+ if (this.config.debug) {
247
+ console.log("[DEBUG todo-bd-sync] Detected bd command:", command);
248
+ }
249
+
250
+ // Check for bd create
251
+ if (command.includes("bd create")) {
252
+ this.handleBdCreate(event);
253
+ }
254
+
255
+ // Check for bd update
256
+ if (command.includes("bd update") || command.includes("bd close")) {
257
+ this.handleBdUpdate(event);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Handle bd create command
263
+ */
264
+ private handleBdCreate(event: ToolExecutionEvent): void {
265
+ const result = event.result;
266
+ if (!result || typeof result !== "object") return;
267
+
268
+ // Extract created bd issue info
269
+ const output = JSON.stringify(result);
270
+ const parsed = parseBdCreateOutput(output);
271
+
272
+ if (parsed && parsed.bdId) {
273
+ if (this.config.debug) {
274
+ console.log(`[todo-bd-sync] Created bd issue: ${parsed.bdId}`);
275
+ }
276
+ // Note: At this point we don't have a todo ID yet
277
+ // The sync will happen when the todo tool is called next
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Handle bd update/close command
283
+ */
284
+ private handleBdUpdate(event: ToolExecutionEvent): void {
285
+ const command = event.args?.command as string;
286
+ const bdId = extractBdIdFromCommand(command);
287
+
288
+ if (!bdId) return;
289
+
290
+ const mappings = getMappingRegistry();
291
+ const mapping = mappings.getByBdId(bdId);
292
+
293
+ if (mapping) {
294
+ if (this.config.debug) {
295
+ console.log(
296
+ `[todo-bd-sync] Updated bd issue ${bdId}, mapped to todo ${mapping.todoId}`,
297
+ );
298
+ }
299
+ mappings.updateLastSync(mapping.todoId);
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Handle session start
305
+ */
306
+ private async handleSessionStart(ctx: { hasUI?: boolean }): Promise<void> {
307
+ if (!ctx.hasUI) return;
308
+
309
+ try {
310
+ // Get all open bd issues
311
+ const issues = getOpenBdIssues();
312
+
313
+ if (this.config.debug) {
314
+ console.log(
315
+ `[todo-bd-sync] Session start: found ${issues.length} open bd issues`,
316
+ );
317
+ }
318
+
319
+ // Notify user about sync status
320
+ if (issues.length > 0) {
321
+ ctx.hasUI && this.pi; // Just to use ctx
322
+ }
323
+ } catch (error) {
324
+ if (this.config.debug) {
325
+ console.error("[DEBUG todo-bd-sync] Session start sync failed:", error);
326
+ }
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Extract task from todo tool event
332
+ */
333
+ private extractTaskFromEvent(event: ToolExecutionEvent): TodoTask | null {
334
+ // The event structure depends on how pi-coding-agent exposes tool results
335
+ // This is a simplified extraction
336
+
337
+ const result = event.result as Record<string, unknown> | undefined;
338
+ if (!result) return null;
339
+
340
+ // Try to extract task from result
341
+ const task = result.task as TodoTask | undefined;
342
+ if (task && typeof task.id === "number") {
343
+ return task;
344
+ }
345
+
346
+ // Try to extract from args
347
+ const args = event.args as Record<string, unknown> | undefined;
348
+ if (args) {
349
+ const id = args.id as number | undefined;
350
+ const subject = args.subject as string | undefined;
351
+ const status = args.status as TodoTask["status"] | undefined;
352
+ const description = args.description as string | undefined;
353
+
354
+ if (id !== undefined) {
355
+ return {
356
+ id,
357
+ subject: subject || "",
358
+ status: status || "pending",
359
+ description,
360
+ metadata: {},
361
+ };
362
+ }
363
+ }
364
+
365
+ return null;
366
+ }
367
+
368
+ /**
369
+ * Check if we should use fallback mode (custom todo implementation)
370
+ */
371
+ private shouldUseFallback(): boolean {
372
+ // Check if there's a config option for fallback
373
+ // For now, return false (we don't have a fallback implementation yet)
374
+ return false;
375
+ }
376
+
377
+ /**
378
+ * Get the prompt injection string
379
+ * This is added to the system prompt to trigger rpiv-todo
380
+ */
381
+ static getPromptInjection(): string {
382
+ return "todo-list";
383
+ }
384
+
385
+ /**
386
+ * Check if sync is enabled
387
+ */
388
+ isEnabled(): boolean {
389
+ return this.enabled;
390
+ }
391
+ }
392
+
393
+ // Default export
394
+ export default createTodoBdSync;
395
+
396
+ export type {
397
+ TodoTask,
398
+ BdIssue,
399
+ SyncResult,
400
+ IdMappingRegistry,
401
+ } from "./types.js";