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,322 @@
1
+ /**
2
+ * Two-way sync between rpiv-todo and bd
3
+ *
4
+ * This module handles:
5
+ * 1. rpiv-todo → bd: When todo tool is called, sync to bd
6
+ * 2. bd → rpiv-todo: When bd commands are executed, sync to todo
7
+ * 3. Session start: Load existing bd issues into todo
8
+ */
9
+
10
+ import { execSync } from "node:child_process";
11
+ import type { TodoTask, BdIssue, SyncResult } from "./types.js";
12
+ import { IdMappingRegistry, STATUS_MAP } from "./types.js";
13
+
14
+ // Mapping registry singleton
15
+ const mappingRegistry = new IdMappingRegistry();
16
+
17
+ /**
18
+ * Execute a bd command and return the output
19
+ */
20
+ export function execBdCommand(command: string): string {
21
+ try {
22
+ return execSync(command, {
23
+ encoding: "utf8",
24
+ timeout: 10000,
25
+ cwd: process.cwd(),
26
+ });
27
+ } catch (error: unknown) {
28
+ const err = error as { message?: string; stderr?: string };
29
+ return err.stderr || err.message || "";
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Extract bd ID from command or output
35
+ */
36
+ export function extractBdId(input: string): string | null {
37
+ // Match patterns like: bd-a1b2, bd-a1b2.1, BD-a1b2, etc.
38
+ const match = input.match(/bd-[a-z0-9]+(\.[0-9]+)?/i);
39
+ return match ? match[0].toLowerCase() : null;
40
+ }
41
+
42
+ /**
43
+ * Get all open bd issues
44
+ */
45
+ export function getOpenBdIssues(): BdIssue[] {
46
+ try {
47
+ const output = execBdCommand("bd ready --json");
48
+ const issues = JSON.parse(output);
49
+ return issues.map((issue: Record<string, unknown>) => ({
50
+ id: issue.id as string,
51
+ title: issue.title as string,
52
+ status: issue.status as BdIssue["status"],
53
+ priority: issue.priority as number | undefined,
54
+ createdAt: issue.created_at as string | undefined,
55
+ updatedAt: issue.updated_at as string | undefined,
56
+ }));
57
+ } catch {
58
+ return [];
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get all bd issues (including closed)
64
+ */
65
+ export function getAllBdIssues(): BdIssue[] {
66
+ try {
67
+ const output = execBdCommand("bd list --json");
68
+ const issues = JSON.parse(output);
69
+ return issues.map((issue: Record<string, unknown>) => ({
70
+ id: issue.id as string,
71
+ title: issue.title as string,
72
+ status: issue.status as BdIssue["status"],
73
+ priority: issue.priority as number | undefined,
74
+ createdAt: issue.created_at as string | undefined,
75
+ updatedAt: issue.updated_at as string | undefined,
76
+ }));
77
+ } catch {
78
+ return [];
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create a bd issue from a todo task
84
+ */
85
+ export function createBdFromTodo(task: TodoTask): SyncResult {
86
+ const priority = inferPriorityFromTask(task);
87
+
88
+ try {
89
+ const command = `bd create "${escapeShell(task.subject)}" -p ${priority} --json`;
90
+ const output = execBdCommand(command);
91
+
92
+ // Try to extract bd ID from output
93
+ let bdId = extractBdId(output);
94
+
95
+ // If not found in output, try to get it from the created issue
96
+ if (!bdId) {
97
+ const issues = getOpenBdIssues();
98
+ // Match by title (not ideal but fallback)
99
+ const match = issues.find((i) => i.title === task.subject);
100
+ bdId = match?.id || null;
101
+ }
102
+
103
+ if (bdId) {
104
+ mappingRegistry.set(task.id, bdId);
105
+ }
106
+
107
+ return {
108
+ success: true,
109
+ direction: "todo_to_bd",
110
+ taskId: task.id,
111
+ bdId: bdId || undefined,
112
+ action: "create",
113
+ };
114
+ } catch (error: unknown) {
115
+ return {
116
+ success: false,
117
+ direction: "todo_to_bd",
118
+ taskId: task.id,
119
+ action: "create",
120
+ error: error instanceof Error ? error.message : String(error),
121
+ };
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Update a bd issue from a todo task update
127
+ */
128
+ export function updateBdFromTodo(task: TodoTask): SyncResult {
129
+ const mapping = mappingRegistry.getByTodoId(task.id);
130
+ if (!mapping) {
131
+ // No mapping exists, create new bd issue
132
+ return createBdFromTodo(task);
133
+ }
134
+
135
+ try {
136
+ const bdStatus = STATUS_MAP[task.status];
137
+ const command = `bd update ${mapping.bdId} --status ${bdStatus} --json`;
138
+ execBdCommand(command);
139
+
140
+ // Update last sync timestamp
141
+ mappingRegistry.updateLastSync(task.id);
142
+
143
+ return {
144
+ success: true,
145
+ direction: "todo_to_bd",
146
+ taskId: task.id,
147
+ bdId: mapping.bdId,
148
+ action: "update",
149
+ };
150
+ } catch (error: unknown) {
151
+ return {
152
+ success: false,
153
+ direction: "todo_to_bd",
154
+ taskId: task.id,
155
+ bdId: mapping.bdId,
156
+ action: "update",
157
+ error: error instanceof Error ? error.message : String(error),
158
+ };
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Close/delete a bd issue when todo is deleted
164
+ */
165
+ export function closeBdFromTodo(task: TodoTask): SyncResult {
166
+ const mapping = mappingRegistry.getByTodoId(task.id);
167
+ if (!mapping) {
168
+ return {
169
+ success: true, // Nothing to close
170
+ direction: "todo_to_bd",
171
+ taskId: task.id,
172
+ action: "delete",
173
+ };
174
+ }
175
+
176
+ try {
177
+ const command = `bd close ${mapping.bdId} --reason "Closed from todo" --json`;
178
+ execBdCommand(command);
179
+
180
+ // Remove mapping
181
+ mappingRegistry.delete(task.id);
182
+
183
+ return {
184
+ success: true,
185
+ direction: "todo_to_bd",
186
+ taskId: task.id,
187
+ bdId: mapping.bdId,
188
+ action: "delete",
189
+ };
190
+ } catch (error: unknown) {
191
+ return {
192
+ success: false,
193
+ direction: "todo_to_bd",
194
+ taskId: task.id,
195
+ bdId: mapping.bdId,
196
+ action: "delete",
197
+ error: error instanceof Error ? error.message : String(error),
198
+ };
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Infer priority from task metadata
204
+ */
205
+ function inferPriorityFromTask(task: TodoTask): number {
206
+ if (task.metadata?.priority !== undefined) {
207
+ return task.metadata.priority as number;
208
+ }
209
+ // Default priority 2 (medium)
210
+ return 2;
211
+ }
212
+
213
+ /**
214
+ * Parse bd create output to extract created issue
215
+ */
216
+ export function parseBdCreateOutput(
217
+ output: string,
218
+ ): { bdId: string; title: string } | null {
219
+ try {
220
+ const json = JSON.parse(output);
221
+ if (json.id) {
222
+ return {
223
+ bdId: json.id,
224
+ title: json.title || "",
225
+ };
226
+ }
227
+ } catch {
228
+ // Not JSON, try text extraction
229
+ }
230
+
231
+ const bdId = extractBdId(output);
232
+ if (bdId) {
233
+ return { bdId, title: "" };
234
+ }
235
+
236
+ return null;
237
+ }
238
+
239
+ /**
240
+ * Detect if a bash command is a bd command
241
+ */
242
+ export function isBdCommand(command: string): boolean {
243
+ const trimmed = command.trim().toLowerCase();
244
+ return (
245
+ trimmed.startsWith("bd ") ||
246
+ trimmed.startsWith("beads ") ||
247
+ trimmed.includes("bd create") ||
248
+ trimmed.includes("bd update") ||
249
+ trimmed.includes("bd close") ||
250
+ trimmed.includes("bd ready") ||
251
+ trimmed.includes("bd list")
252
+ );
253
+ }
254
+
255
+ /**
256
+ * Extract bd ID from a command
257
+ */
258
+ export function extractBdIdFromCommand(command: string): string | null {
259
+ return extractBdId(command);
260
+ }
261
+
262
+ /**
263
+ * Get the mapping registry for external access
264
+ */
265
+ export function getMappingRegistry(): IdMappingRegistry {
266
+ return mappingRegistry;
267
+ }
268
+
269
+ /**
270
+ * Clear all mappings (for testing or reset)
271
+ */
272
+ export function clearMappings(): void {
273
+ mappingRegistry.clear();
274
+ }
275
+
276
+ /**
277
+ * Escape shell arguments
278
+ */
279
+ function escapeShell(str: string): string {
280
+ // Escape double quotes and backslashes
281
+ return str.replace(/["\\]/g, "\\$&");
282
+ }
283
+
284
+ /**
285
+ * Sync from todo action to bd
286
+ * Called when todo tool is executed
287
+ */
288
+ export function syncTodoToBd(action: string, task: TodoTask): SyncResult {
289
+ switch (action) {
290
+ case "create":
291
+ return createBdFromTodo(task);
292
+ case "update":
293
+ return updateBdFromTodo(task);
294
+ case "delete":
295
+ return closeBdFromTodo(task);
296
+ default:
297
+ return {
298
+ success: true,
299
+ direction: "todo_to_bd",
300
+ taskId: task.id,
301
+ action,
302
+ };
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Parse todo task from tool event
308
+ * This is a simplified parser - actual implementation depends on event structure
309
+ */
310
+ export function parseTodoTaskFromEvent(
311
+ event: Record<string, unknown>,
312
+ ): TodoTask | null {
313
+ // The event structure depends on how pi-coding-agent exposes it
314
+ // This is a placeholder - actual implementation needs event inspection
315
+
316
+ const task = event.task as TodoTask | undefined;
317
+ if (!task || typeof task.id !== "number") {
318
+ return null;
319
+ }
320
+
321
+ return task;
322
+ }