pi-harness-runtime 0.9.25 → 1.1.0

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 (350) hide show
  1. package/CHANGELOG.md +49 -7
  2. package/README.md +173 -51
  3. package/harness/auto-compact.ts +1 -1
  4. package/harness/autonomous-review.ts +88 -0
  5. package/harness/context-compact-orchestrator.ts +23 -23
  6. package/harness/context-window-manager.ts +5 -5
  7. package/harness/continue-prompt.ts +3 -3
  8. package/harness/e2e/minimax-quota-scraper.ts +5 -5
  9. package/harness/e2e/openai-quota-scraper.ts +13 -8
  10. package/harness/forked-summarizer.ts +4 -4
  11. package/harness/herdr-agents.ts +91 -0
  12. package/harness/index.ts +5 -5
  13. package/harness/loop-code-agent.ts +284 -0
  14. package/harness/loop-controller.ts +201 -0
  15. package/harness/loop-review-agent.ts +295 -0
  16. package/harness/loop-runtime.ts +10 -10
  17. package/harness/loop-types.ts +118 -0
  18. package/harness/notification-events.ts +3 -3
  19. package/harness/output-limit-handler.ts +1 -1
  20. package/harness/partial-recovery.ts +3 -3
  21. package/harness/session-memory.ts +5 -5
  22. package/index.ts +139 -34
  23. package/package.json +1 -1
  24. package/packages/a2a-adapter/package.json +1 -1
  25. package/packages/a2a-adapter/src/types.ts +5 -5
  26. package/packages/auth/package.json +1 -1
  27. package/packages/autonomous-refactor/package.json +1 -1
  28. package/packages/autonomous-runtime/dist/inbox.d.ts +3 -3
  29. package/packages/autonomous-runtime/dist/inbox.js +12 -12
  30. package/packages/autonomous-runtime/dist/lease.js +14 -14
  31. package/packages/autonomous-runtime/dist/types.js +1 -1
  32. package/packages/autonomous-runtime/package.json +1 -1
  33. package/packages/autonomous-runtime/src/inbox.ts +12 -12
  34. package/packages/autonomous-runtime/src/lease.ts +15 -15
  35. package/packages/autonomous-runtime/src/types.ts +13 -13
  36. package/packages/autonomous-runtime/tsconfig.tsbuildinfo +1 -1
  37. package/packages/cache-strategy/package.json +1 -1
  38. package/packages/capability-registry/dist/types.js +1 -1
  39. package/packages/capability-registry/package.json +1 -1
  40. package/packages/capability-registry/src/types.ts +5 -5
  41. package/packages/checkpoint/README.md +6 -6
  42. package/packages/checkpoint/dist/differ.js +2 -2
  43. package/packages/checkpoint/dist/engine.js +3 -3
  44. package/packages/checkpoint/dist/index.js +5 -5
  45. package/packages/checkpoint/dist/storage.js +3 -3
  46. package/packages/checkpoint/dist/types.js +1 -1
  47. package/packages/checkpoint/package.json +1 -1
  48. package/packages/checkpoint/src/differ.ts +2 -2
  49. package/packages/checkpoint/src/engine.ts +3 -3
  50. package/packages/checkpoint/src/index.ts +5 -5
  51. package/packages/checkpoint/src/storage.ts +3 -3
  52. package/packages/checkpoint/src/types.ts +10 -10
  53. package/packages/checkpoint/tsconfig.tsbuildinfo +1 -1
  54. package/packages/cli-plugin-sdk/package.json +1 -1
  55. package/packages/clipboard/dist/src/clipboard.d.ts.map +1 -1
  56. package/packages/clipboard/dist/src/clipboard.js +15 -7
  57. package/packages/clipboard/dist/src/clipboard.js.map +1 -1
  58. package/packages/clipboard/dist/src/copy-sync.d.ts +27 -0
  59. package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -0
  60. package/packages/clipboard/dist/src/copy-sync.js +170 -0
  61. package/packages/clipboard/dist/src/copy-sync.js.map +1 -0
  62. package/packages/clipboard/dist/src/gist-relay.d.ts +48 -0
  63. package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -0
  64. package/packages/clipboard/dist/src/gist-relay.js +186 -0
  65. package/packages/clipboard/dist/src/gist-relay.js.map +1 -0
  66. package/packages/clipboard/dist/src/github-login.d.ts +16 -0
  67. package/packages/clipboard/dist/src/github-login.d.ts.map +1 -0
  68. package/packages/clipboard/dist/src/github-login.js +69 -0
  69. package/packages/clipboard/dist/src/github-login.js.map +1 -0
  70. package/packages/clipboard/dist/src/index.d.ts +2 -0
  71. package/packages/clipboard/dist/src/index.d.ts.map +1 -1
  72. package/packages/clipboard/dist/src/index.js +2 -1
  73. package/packages/clipboard/dist/src/index.js.map +1 -1
  74. package/packages/clipboard/package.json +1 -1
  75. package/packages/clipboard/src/clipboard.ts +16 -9
  76. package/packages/clipboard/src/copy-sync.ts +201 -0
  77. package/packages/clipboard/src/gist-relay.ts +240 -0
  78. package/packages/clipboard/src/github-login.ts +107 -0
  79. package/packages/clipboard/src/index.ts +2 -0
  80. package/packages/clipboard/test/mojibake.test.mjs +143 -0
  81. package/packages/code-generation/README.md +13 -13
  82. package/packages/code-generation/dist/faker.js +4 -4
  83. package/packages/code-generation/dist/generator.js +3 -3
  84. package/packages/code-generation/dist/index.js +3 -3
  85. package/packages/code-generation/dist/templates/registry.js +1 -1
  86. package/packages/code-generation/dist/types.js +1 -1
  87. package/packages/code-generation/package.json +1 -1
  88. package/packages/code-generation/src/faker.ts +5 -5
  89. package/packages/code-generation/src/generator.ts +4 -4
  90. package/packages/code-generation/src/index.ts +3 -3
  91. package/packages/code-generation/src/templates/registry.ts +1 -1
  92. package/packages/code-generation/src/types.ts +10 -10
  93. package/packages/code-generation/tsconfig.tsbuildinfo +1 -1
  94. package/packages/code-review/README.md +13 -13
  95. package/packages/code-review/dist/engine.js +3 -3
  96. package/packages/code-review/dist/index.js +3 -3
  97. package/packages/code-review/dist/types.js +1 -1
  98. package/packages/code-review/package.json +1 -1
  99. package/packages/code-review/src/engine.ts +3 -3
  100. package/packages/code-review/src/index.ts +3 -3
  101. package/packages/code-review/src/types.ts +10 -10
  102. package/packages/code-review/tsconfig.tsbuildinfo +1 -1
  103. package/packages/codex-adapter/package.json +1 -1
  104. package/packages/context-compiler/dist/compiler.js +1 -1
  105. package/packages/context-compiler/package.json +1 -1
  106. package/packages/context-compiler/src/compiler.ts +1 -1
  107. package/packages/context-compiler/src/types.ts +11 -11
  108. package/packages/context-compiler/test/context-compiler.test.ts +11 -11
  109. package/packages/context-discovery/package.json +1 -1
  110. package/packages/context-manager/package.json +1 -1
  111. package/packages/cookie-sanitizer/README.md +27 -27
  112. package/packages/cookie-sanitizer/package.json +1 -1
  113. package/packages/cost-optimizer/package.json +1 -1
  114. package/packages/cost-optimizer/src/types.ts +5 -5
  115. package/packages/dependency-analyzer/package.json +1 -1
  116. package/packages/django-plugin/dist/analyzer.js +7 -7
  117. package/packages/django-plugin/package.json +1 -1
  118. package/packages/django-plugin/src/analyzer.ts +7 -7
  119. package/packages/doc-generator/package.json +1 -1
  120. package/packages/evaluation-engine/dist/engine.js +9 -9
  121. package/packages/evaluation-engine/package.json +1 -1
  122. package/packages/evaluation-engine/src/engine.ts +9 -9
  123. package/packages/evaluation-engine/src/types.ts +3 -3
  124. package/packages/evaluation-runner/package.json +1 -1
  125. package/packages/event-bus/dist/src/herdr-bus.d.ts +121 -0
  126. package/packages/event-bus/dist/src/herdr-bus.d.ts.map +1 -0
  127. package/packages/event-bus/dist/src/herdr-bus.js +322 -0
  128. package/packages/event-bus/dist/src/herdr-bus.js.map +1 -0
  129. package/packages/event-bus/dist/src/index.d.ts +2 -0
  130. package/packages/event-bus/dist/src/index.d.ts.map +1 -1
  131. package/packages/event-bus/dist/src/index.js +1 -0
  132. package/packages/event-bus/dist/src/index.js.map +1 -1
  133. package/packages/event-bus/package.json +1 -1
  134. package/packages/event-bus/src/herdr-bus.ts +525 -0
  135. package/packages/event-bus/src/index.ts +29 -0
  136. package/packages/event-store/package.json +1 -1
  137. package/packages/experience-replay/dist/replay.js +7 -7
  138. package/packages/experience-replay/package.json +1 -1
  139. package/packages/experience-replay/src/replay.ts +7 -7
  140. package/packages/experience-replay/src/types.ts +5 -5
  141. package/packages/feedback-collector/package.json +1 -1
  142. package/packages/framework-detector/README.md +18 -18
  143. package/packages/framework-detector/dist/detector.js +3 -3
  144. package/packages/framework-detector/dist/index.js +3 -3
  145. package/packages/framework-detector/dist/signatures/registry.js +24 -24
  146. package/packages/framework-detector/dist/types.js +1 -1
  147. package/packages/framework-detector/package.json +1 -1
  148. package/packages/framework-detector/src/detector.ts +3 -3
  149. package/packages/framework-detector/src/index.ts +3 -3
  150. package/packages/framework-detector/src/signatures/registry.ts +24 -24
  151. package/packages/framework-detector/src/types.ts +7 -7
  152. package/packages/framework-detector/tsconfig.tsbuildinfo +1 -1
  153. package/packages/framework-plugin-sdk/README.md +23 -23
  154. package/packages/framework-plugin-sdk/dist/index.js +2 -2
  155. package/packages/framework-plugin-sdk/dist/manager.js +3 -3
  156. package/packages/framework-plugin-sdk/dist/types.js +2 -2
  157. package/packages/framework-plugin-sdk/package.json +1 -1
  158. package/packages/framework-plugin-sdk/src/index.ts +2 -2
  159. package/packages/framework-plugin-sdk/src/manager.ts +3 -3
  160. package/packages/framework-plugin-sdk/src/types.ts +10 -10
  161. package/packages/framework-plugin-sdk/tsconfig.tsbuildinfo +1 -1
  162. package/packages/frappe-plugin/dist/analyzer.js +8 -8
  163. package/packages/frappe-plugin/dist/types.js +1 -1
  164. package/packages/frappe-plugin/package.json +1 -1
  165. package/packages/frappe-plugin/src/analyzer.ts +8 -8
  166. package/packages/frappe-plugin/src/types.ts +10 -10
  167. package/packages/generic-web-plugin/dist/analyzer.js +5 -5
  168. package/packages/generic-web-plugin/package.json +1 -1
  169. package/packages/generic-web-plugin/src/analyzer.ts +5 -5
  170. package/packages/health-monitor/package.json +1 -1
  171. package/packages/health-monitor/src/monitor.ts +1 -1
  172. package/packages/intent-analyzer/package.json +1 -1
  173. package/packages/laravel-plugin/dist/analyzer.js +11 -11
  174. package/packages/laravel-plugin/package.json +1 -1
  175. package/packages/laravel-plugin/src/analyzer.ts +11 -11
  176. package/packages/learning-engine/dist/engine.js +5 -5
  177. package/packages/learning-engine/dist/types.js +1 -1
  178. package/packages/learning-engine/package.json +1 -1
  179. package/packages/learning-engine/src/engine.ts +5 -5
  180. package/packages/learning-engine/src/types.ts +6 -6
  181. package/packages/mcp-adapter/package.json +1 -1
  182. package/packages/memory-engine/dist/engine.js +8 -8
  183. package/packages/memory-engine/dist/types.js +2 -2
  184. package/packages/memory-engine/package.json +1 -1
  185. package/packages/memory-engine/src/engine.ts +8 -8
  186. package/packages/memory-engine/src/types.ts +9 -9
  187. package/packages/model-registry/package.json +1 -1
  188. package/packages/model-registry/src/types.ts +4 -4
  189. package/packages/nextjs-plugin/dist/analyzer.js +9 -9
  190. package/packages/nextjs-plugin/package.json +1 -1
  191. package/packages/nextjs-plugin/src/analyzer.ts +9 -9
  192. package/packages/notification/dist/notification-center.js +1 -1
  193. package/packages/notification/package.json +1 -1
  194. package/packages/notification/src/notification-center.ts +1 -1
  195. package/packages/observability/dist/alerts.js +2 -2
  196. package/packages/observability/dist/health.js +4 -4
  197. package/packages/observability/dist/index.js +6 -6
  198. package/packages/observability/dist/logger.js +7 -7
  199. package/packages/observability/dist/metrics.js +11 -11
  200. package/packages/observability/dist/tracer.js +3 -3
  201. package/packages/observability/dist/types.js +1 -1
  202. package/packages/observability/package.json +1 -1
  203. package/packages/observability/src/alerts.ts +2 -2
  204. package/packages/observability/src/health.ts +4 -4
  205. package/packages/observability/src/index.ts +6 -6
  206. package/packages/observability/src/logger.ts +7 -7
  207. package/packages/observability/src/metrics.ts +11 -11
  208. package/packages/observability/src/tracer.ts +3 -3
  209. package/packages/observability/src/types.ts +7 -7
  210. package/packages/observability/test/observability.test.ts +4 -4
  211. package/packages/observability/tsconfig.tsbuildinfo +1 -1
  212. package/packages/performance-optimizer/dist/optimizer.js +1 -1
  213. package/packages/performance-optimizer/package.json +1 -1
  214. package/packages/performance-optimizer/src/optimizer.ts +1 -1
  215. package/packages/performance-optimizer/src/types.ts +4 -4
  216. package/packages/privilege-broker/package.json +1 -1
  217. package/packages/project-analyzer/dist/analyzer.js +4 -4
  218. package/packages/project-analyzer/dist/index.js +8 -8
  219. package/packages/project-analyzer/dist/signals.js +3 -3
  220. package/packages/project-analyzer/dist/types.js +1 -1
  221. package/packages/project-analyzer/package.json +1 -1
  222. package/packages/project-analyzer/src/analyzer.ts +4 -4
  223. package/packages/project-analyzer/src/index.ts +9 -9
  224. package/packages/project-analyzer/src/signals.ts +4 -4
  225. package/packages/project-analyzer/src/types.ts +15 -15
  226. package/packages/project-analyzer/test/index.test.ts +17 -17
  227. package/packages/project-analyzer/tsconfig.tsbuildinfo +1 -1
  228. package/packages/projection-engine/package.json +1 -1
  229. package/packages/prompt-compiler/dist/section-builder.js +1 -1
  230. package/packages/prompt-compiler/dist/types.js +3 -3
  231. package/packages/prompt-compiler/package.json +1 -1
  232. package/packages/prompt-compiler/src/section-builder.ts +1 -1
  233. package/packages/prompt-compiler/src/types.ts +12 -12
  234. package/packages/prompt-compiler/test/prompt-compiler.test.ts +12 -12
  235. package/packages/prompt-versioning/package.json +1 -1
  236. package/packages/provider-adapter-sdk/dist/builder.js +1 -1
  237. package/packages/provider-adapter-sdk/dist/index.js +5 -5
  238. package/packages/provider-adapter-sdk/dist/registry.js +1 -1
  239. package/packages/provider-adapter-sdk/dist/tester.js +1 -1
  240. package/packages/provider-adapter-sdk/dist/types.js +1 -1
  241. package/packages/provider-adapter-sdk/package.json +1 -1
  242. package/packages/provider-adapter-sdk/src/builder.ts +1 -1
  243. package/packages/provider-adapter-sdk/src/index.ts +6 -6
  244. package/packages/provider-adapter-sdk/src/registry.ts +1 -1
  245. package/packages/provider-adapter-sdk/src/tester.ts +1 -1
  246. package/packages/provider-adapter-sdk/src/types.ts +5 -5
  247. package/packages/provider-adapter-sdk/tsconfig.tsbuildinfo +1 -1
  248. package/packages/provider-router/package.json +1 -1
  249. package/packages/provider-router/src/types.ts +4 -4
  250. package/packages/provider-selector/package.json +1 -1
  251. package/packages/providers/dist/deepagents-adapter.js +1 -1
  252. package/packages/providers/package.json +1 -1
  253. package/packages/providers/src/deepagents-adapter.ts +1 -1
  254. package/packages/quota-manager/package.json +1 -1
  255. package/packages/rate-limiter/package.json +1 -1
  256. package/packages/react-vite-plugin/dist/analyzer.js +7 -7
  257. package/packages/react-vite-plugin/package.json +1 -1
  258. package/packages/react-vite-plugin/src/analyzer.ts +7 -7
  259. package/packages/release-manager/package.json +1 -1
  260. package/packages/requirement-compiler/dist/compiler.js +17 -17
  261. package/packages/requirement-compiler/dist/types.js +2 -2
  262. package/packages/requirement-compiler/package.json +1 -1
  263. package/packages/requirement-compiler/src/compiler.ts +17 -17
  264. package/packages/requirement-compiler/src/types.ts +21 -21
  265. package/packages/requirement-compiler/test/requirement-compiler.test.ts +17 -17
  266. package/packages/runtime/dist/command-executor.js +1 -1
  267. package/packages/runtime/dist/local-runtime-agent.js +1 -1
  268. package/packages/runtime/dist/policy-engine.js +1 -1
  269. package/packages/runtime/dist/runtime-api.js +1 -1
  270. package/packages/runtime/dist/workspace-manager.js +1 -1
  271. package/packages/runtime/package.json +1 -1
  272. package/packages/runtime/src/command-executor.ts +1 -1
  273. package/packages/runtime/src/local-runtime-agent.ts +1 -1
  274. package/packages/runtime/src/policy-engine.ts +1 -1
  275. package/packages/runtime/src/runtime-api.ts +1 -1
  276. package/packages/runtime/src/workspace-manager.ts +1 -1
  277. package/packages/scheduler/package.json +1 -1
  278. package/packages/scheduler-adapter/package.json +1 -1
  279. package/packages/session/README.md +18 -18
  280. package/packages/session/dist/history.js +3 -3
  281. package/packages/session/dist/index.js +5 -5
  282. package/packages/session/dist/manager.js +3 -3
  283. package/packages/session/dist/policy.js +2 -2
  284. package/packages/session/dist/store.js +2 -2
  285. package/packages/session/dist/types.js +1 -1
  286. package/packages/session/package.json +1 -1
  287. package/packages/session/src/history.ts +3 -3
  288. package/packages/session/src/index.ts +5 -5
  289. package/packages/session/src/manager.ts +3 -3
  290. package/packages/session/src/policy.ts +4 -4
  291. package/packages/session/src/store.ts +3 -3
  292. package/packages/session/src/types.ts +10 -10
  293. package/packages/session/tsconfig.tsbuildinfo +1 -1
  294. package/packages/session-api/package.json +1 -1
  295. package/packages/session-export/package.json +1 -1
  296. package/packages/shared-context/package.json +1 -1
  297. package/packages/skill-registry/package.json +1 -1
  298. package/packages/skill-registry/src/types.ts +4 -4
  299. package/packages/sprint-planner/package.json +1 -1
  300. package/packages/subscription-engine/package.json +1 -1
  301. package/packages/task-compiler/dist/compiler.js +7 -7
  302. package/packages/task-compiler/dist/decompose.js +11 -11
  303. package/packages/task-compiler/dist/file-scope.js +6 -6
  304. package/packages/task-compiler/dist/graph.js +6 -6
  305. package/packages/task-compiler/dist/index.js +2 -2
  306. package/packages/task-compiler/dist/types.js +5 -5
  307. package/packages/task-compiler/dist/validate.js +2 -2
  308. package/packages/task-compiler/dist/verification.js +2 -2
  309. package/packages/task-compiler/package.json +1 -1
  310. package/packages/task-compiler/src/command-policy.ts +1 -1
  311. package/packages/task-compiler/src/compiler.ts +7 -7
  312. package/packages/task-compiler/src/decompose.ts +11 -11
  313. package/packages/task-compiler/src/file-scope.ts +6 -6
  314. package/packages/task-compiler/src/graph.ts +6 -6
  315. package/packages/task-compiler/src/index.ts +3 -3
  316. package/packages/task-compiler/src/types.ts +14 -14
  317. package/packages/task-compiler/src/validate.ts +2 -2
  318. package/packages/task-compiler/src/verification.ts +2 -2
  319. package/packages/task-compiler/test/task-compiler.test.ts +10 -10
  320. package/packages/test-data-generator/README.md +13 -13
  321. package/packages/test-data-generator/dist/faker-adapter.js +4 -4
  322. package/packages/test-data-generator/dist/generator.js +3 -3
  323. package/packages/test-data-generator/dist/index.js +3 -3
  324. package/packages/test-data-generator/dist/types.js +1 -1
  325. package/packages/test-data-generator/package.json +1 -1
  326. package/packages/test-data-generator/src/faker-adapter.ts +4 -4
  327. package/packages/test-data-generator/src/generator.ts +3 -3
  328. package/packages/test-data-generator/src/index.ts +3 -3
  329. package/packages/test-data-generator/src/types.ts +9 -9
  330. package/packages/test-data-generator/tsconfig.tsbuildinfo +1 -1
  331. package/packages/test-generator/package.json +1 -1
  332. package/packages/todo-bd-sync/package.json +22 -0
  333. package/packages/todo-bd-sync/src/detector.ts +150 -0
  334. package/packages/todo-bd-sync/src/extension.ts +101 -0
  335. package/packages/todo-bd-sync/src/index.ts +401 -0
  336. package/packages/todo-bd-sync/src/sync.ts +322 -0
  337. package/packages/todo-bd-sync/src/todo-reminder.ts +418 -0
  338. package/packages/todo-bd-sync/src/types.ts +137 -0
  339. package/packages/token-estimation/package.json +1 -1
  340. package/packages/token-optimizer/package.json +1 -1
  341. package/packages/tui/package.json +1 -1
  342. package/packages/types/package.json +1 -1
  343. package/packages/types/src/agent-worker-types.ts +5 -5
  344. package/packages/types/src/runtime-types.ts +12 -12
  345. package/packages/types/tsconfig.tsbuildinfo +1 -1
  346. package/packages/workflow-events/package.json +1 -1
  347. package/packages/workspace-scanner/package.json +1 -1
  348. package/packages/worktree/package.json +1 -1
  349. package/renderer.ts +7 -7
  350. package/skills/harness-runtime/SKILL.md +15 -15
@@ -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";
@@ -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
+ }