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.
- package/CHANGELOG.md +22 -0
- package/README.md +122 -0
- package/harness/e2e/minimax-quota-scraper.ts +4 -4
- package/harness/e2e/openai-quota-scraper.ts +13 -8
- package/index.ts +93 -2
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/dist/src/copy-sync.d.ts +27 -0
- package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -0
- package/packages/clipboard/dist/src/copy-sync.js +170 -0
- package/packages/clipboard/dist/src/copy-sync.js.map +1 -0
- package/packages/clipboard/dist/src/gist-relay.d.ts +48 -0
- package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -0
- package/packages/clipboard/dist/src/gist-relay.js +186 -0
- package/packages/clipboard/dist/src/gist-relay.js.map +1 -0
- package/packages/clipboard/dist/src/github-login.d.ts +16 -0
- package/packages/clipboard/dist/src/github-login.d.ts.map +1 -0
- package/packages/clipboard/dist/src/github-login.js +69 -0
- package/packages/clipboard/dist/src/github-login.js.map +1 -0
- package/packages/clipboard/dist/src/index.d.ts +2 -0
- package/packages/clipboard/dist/src/index.d.ts.map +1 -1
- package/packages/clipboard/dist/src/index.js +2 -1
- package/packages/clipboard/dist/src/index.js.map +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard/src/copy-sync.ts +201 -0
- package/packages/clipboard/src/gist-relay.ts +240 -0
- package/packages/clipboard/src/github-login.ts +107 -0
- package/packages/clipboard/src/index.ts +2 -0
- package/packages/clipboard/test/mojibake.test.mjs +143 -0
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +22 -0
- package/packages/todo-bd-sync/src/detector.ts +150 -0
- package/packages/todo-bd-sync/src/extension.ts +84 -0
- package/packages/todo-bd-sync/src/index.ts +401 -0
- package/packages/todo-bd-sync/src/sync.ts +322 -0
- package/packages/todo-bd-sync/src/todo-reminder.ts +418 -0
- package/packages/todo-bd-sync/src/types.ts +137 -0
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- 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
|
+
}
|