duocode 0.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 (220) hide show
  1. package/.env.example +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +52 -0
  4. package/dist/ast/context.d.ts +16 -0
  5. package/dist/ast/context.js +37 -0
  6. package/dist/ast/context.js.map +1 -0
  7. package/dist/ast/diff.d.ts +27 -0
  8. package/dist/ast/diff.js +44 -0
  9. package/dist/ast/diff.js.map +1 -0
  10. package/dist/ast/locks.d.ts +47 -0
  11. package/dist/ast/locks.js +88 -0
  12. package/dist/ast/locks.js.map +1 -0
  13. package/dist/ast/merge.d.ts +22 -0
  14. package/dist/ast/merge.js +120 -0
  15. package/dist/ast/merge.js.map +1 -0
  16. package/dist/ast/ownership.d.ts +31 -0
  17. package/dist/ast/ownership.js +111 -0
  18. package/dist/ast/ownership.js.map +1 -0
  19. package/dist/ast/parser.d.ts +44 -0
  20. package/dist/ast/parser.js +134 -0
  21. package/dist/ast/parser.js.map +1 -0
  22. package/dist/cli.d.ts +2 -0
  23. package/dist/cli.js +423 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/commands/doctor.d.ts +5 -0
  26. package/dist/commands/doctor.js +63 -0
  27. package/dist/commands/doctor.js.map +1 -0
  28. package/dist/commands/duo.d.ts +9 -0
  29. package/dist/commands/duo.js +285 -0
  30. package/dist/commands/duo.js.map +1 -0
  31. package/dist/commands/github.d.ts +2 -0
  32. package/dist/commands/github.js +85 -0
  33. package/dist/commands/github.js.map +1 -0
  34. package/dist/commands/init.d.ts +2 -0
  35. package/dist/commands/init.js +33 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/negotiation.d.ts +2 -0
  38. package/dist/commands/negotiation.js +160 -0
  39. package/dist/commands/negotiation.js.map +1 -0
  40. package/dist/commands/repl_commands.d.ts +26 -0
  41. package/dist/commands/repl_commands.js +226 -0
  42. package/dist/commands/repl_commands.js.map +1 -0
  43. package/dist/commands/shell.d.ts +1 -0
  44. package/dist/commands/shell.js +110 -0
  45. package/dist/commands/shell.js.map +1 -0
  46. package/dist/commands/start.d.ts +2 -0
  47. package/dist/commands/start.js +231 -0
  48. package/dist/commands/start.js.map +1 -0
  49. package/dist/commands/task.d.ts +2 -0
  50. package/dist/commands/task.js +215 -0
  51. package/dist/commands/task.js.map +1 -0
  52. package/dist/config/loader.d.ts +193 -0
  53. package/dist/config/loader.js +106 -0
  54. package/dist/config/loader.js.map +1 -0
  55. package/dist/context/project_context.d.ts +79 -0
  56. package/dist/context/project_context.js +292 -0
  57. package/dist/context/project_context.js.map +1 -0
  58. package/dist/context/token_budget.d.ts +35 -0
  59. package/dist/context/token_budget.js +81 -0
  60. package/dist/context/token_budget.js.map +1 -0
  61. package/dist/db/queries.d.ts +121 -0
  62. package/dist/db/queries.js +109 -0
  63. package/dist/db/queries.js.map +1 -0
  64. package/dist/db/schema.d.ts +110 -0
  65. package/dist/db/schema.js +346 -0
  66. package/dist/db/schema.js.map +1 -0
  67. package/dist/duo/duo_orchestrator.d.ts +50 -0
  68. package/dist/duo/duo_orchestrator.js +510 -0
  69. package/dist/duo/duo_orchestrator.js.map +1 -0
  70. package/dist/duo/duo_session.d.ts +47 -0
  71. package/dist/duo/duo_session.js +127 -0
  72. package/dist/duo/duo_session.js.map +1 -0
  73. package/dist/duo/duo_types.d.ts +168 -0
  74. package/dist/duo/duo_types.js +53 -0
  75. package/dist/duo/duo_types.js.map +1 -0
  76. package/dist/duo/session_store.d.ts +71 -0
  77. package/dist/duo/session_store.js +177 -0
  78. package/dist/duo/session_store.js.map +1 -0
  79. package/dist/git/worktree.d.ts +21 -0
  80. package/dist/git/worktree.js +86 -0
  81. package/dist/git/worktree.js.map +1 -0
  82. package/dist/github/cache.d.ts +23 -0
  83. package/dist/github/cache.js +67 -0
  84. package/dist/github/cache.js.map +1 -0
  85. package/dist/github/issues.d.ts +17 -0
  86. package/dist/github/issues.js +93 -0
  87. package/dist/github/issues.js.map +1 -0
  88. package/dist/github/mcp_client.d.ts +57 -0
  89. package/dist/github/mcp_client.js +214 -0
  90. package/dist/github/mcp_client.js.map +1 -0
  91. package/dist/github/sync.d.ts +11 -0
  92. package/dist/github/sync.js +65 -0
  93. package/dist/github/sync.js.map +1 -0
  94. package/dist/github/webhook.d.ts +25 -0
  95. package/dist/github/webhook.js +197 -0
  96. package/dist/github/webhook.js.map +1 -0
  97. package/dist/negotiation/index.d.ts +1 -0
  98. package/dist/negotiation/index.js +2 -0
  99. package/dist/negotiation/index.js.map +1 -0
  100. package/dist/negotiation/protocol.d.ts +62 -0
  101. package/dist/negotiation/protocol.js +188 -0
  102. package/dist/negotiation/protocol.js.map +1 -0
  103. package/dist/orchestrator/complexity_scorer.d.ts +2 -0
  104. package/dist/orchestrator/complexity_scorer.js +79 -0
  105. package/dist/orchestrator/complexity_scorer.js.map +1 -0
  106. package/dist/orchestrator/dependency_graph.d.ts +7 -0
  107. package/dist/orchestrator/dependency_graph.js +73 -0
  108. package/dist/orchestrator/dependency_graph.js.map +1 -0
  109. package/dist/orchestrator/intent_parser.d.ts +11 -0
  110. package/dist/orchestrator/intent_parser.js +116 -0
  111. package/dist/orchestrator/intent_parser.js.map +1 -0
  112. package/dist/orchestrator/task_runner.d.ts +56 -0
  113. package/dist/orchestrator/task_runner.js +181 -0
  114. package/dist/orchestrator/task_runner.js.map +1 -0
  115. package/dist/orchestrator/types.d.ts +44 -0
  116. package/dist/orchestrator/types.js +21 -0
  117. package/dist/orchestrator/types.js.map +1 -0
  118. package/dist/providers/anthropic.d.ts +12 -0
  119. package/dist/providers/anthropic.js +258 -0
  120. package/dist/providers/anthropic.js.map +1 -0
  121. package/dist/providers/auction.d.ts +42 -0
  122. package/dist/providers/auction.js +190 -0
  123. package/dist/providers/auction.js.map +1 -0
  124. package/dist/providers/base.d.ts +103 -0
  125. package/dist/providers/base.js +2 -0
  126. package/dist/providers/base.js.map +1 -0
  127. package/dist/providers/cost_tracker.d.ts +45 -0
  128. package/dist/providers/cost_tracker.js +111 -0
  129. package/dist/providers/cost_tracker.js.map +1 -0
  130. package/dist/providers/duo_pair_router.d.ts +11 -0
  131. package/dist/providers/duo_pair_router.js +67 -0
  132. package/dist/providers/duo_pair_router.js.map +1 -0
  133. package/dist/providers/factory.d.ts +7 -0
  134. package/dist/providers/factory.js +130 -0
  135. package/dist/providers/factory.js.map +1 -0
  136. package/dist/providers/grading_rubric.d.ts +37 -0
  137. package/dist/providers/grading_rubric.js +238 -0
  138. package/dist/providers/grading_rubric.js.map +1 -0
  139. package/dist/providers/openai.d.ts +12 -0
  140. package/dist/providers/openai.js +229 -0
  141. package/dist/providers/openai.js.map +1 -0
  142. package/dist/providers/openrouter.d.ts +14 -0
  143. package/dist/providers/openrouter.js +178 -0
  144. package/dist/providers/openrouter.js.map +1 -0
  145. package/dist/providers/performance_tracker.d.ts +21 -0
  146. package/dist/providers/performance_tracker.js +63 -0
  147. package/dist/providers/performance_tracker.js.map +1 -0
  148. package/dist/providers/registry_loader.d.ts +6 -0
  149. package/dist/providers/registry_loader.js +54 -0
  150. package/dist/providers/registry_loader.js.map +1 -0
  151. package/dist/providers/retry.d.ts +66 -0
  152. package/dist/providers/retry.js +203 -0
  153. package/dist/providers/retry.js.map +1 -0
  154. package/dist/providers/role_scorer.d.ts +16 -0
  155. package/dist/providers/role_scorer.js +16 -0
  156. package/dist/providers/role_scorer.js.map +1 -0
  157. package/dist/providers/router.d.ts +84 -0
  158. package/dist/providers/router.js +542 -0
  159. package/dist/providers/router.js.map +1 -0
  160. package/dist/security/credentials.d.ts +6 -0
  161. package/dist/security/credentials.js +16 -0
  162. package/dist/security/credentials.js.map +1 -0
  163. package/dist/setup/browser.d.ts +1 -0
  164. package/dist/setup/browser.js +12 -0
  165. package/dist/setup/browser.js.map +1 -0
  166. package/dist/setup/global_config.d.ts +14 -0
  167. package/dist/setup/global_config.js +54 -0
  168. package/dist/setup/global_config.js.map +1 -0
  169. package/dist/setup/wizard.d.ts +2 -0
  170. package/dist/setup/wizard.js +206 -0
  171. package/dist/setup/wizard.js.map +1 -0
  172. package/dist/tools/agent_loop.d.ts +38 -0
  173. package/dist/tools/agent_loop.js +72 -0
  174. package/dist/tools/agent_loop.js.map +1 -0
  175. package/dist/tools/approval.d.ts +64 -0
  176. package/dist/tools/approval.js +172 -0
  177. package/dist/tools/approval.js.map +1 -0
  178. package/dist/tools/checkpoint.d.ts +65 -0
  179. package/dist/tools/checkpoint.js +342 -0
  180. package/dist/tools/checkpoint.js.map +1 -0
  181. package/dist/tools/definitions.d.ts +13 -0
  182. package/dist/tools/definitions.js +103 -0
  183. package/dist/tools/definitions.js.map +1 -0
  184. package/dist/tools/diff_display.d.ts +46 -0
  185. package/dist/tools/diff_display.js +298 -0
  186. package/dist/tools/diff_display.js.map +1 -0
  187. package/dist/tools/executor.d.ts +12 -0
  188. package/dist/tools/executor.js +340 -0
  189. package/dist/tools/executor.js.map +1 -0
  190. package/dist/tools/permissions.d.ts +17 -0
  191. package/dist/tools/permissions.js +139 -0
  192. package/dist/tools/permissions.js.map +1 -0
  193. package/dist/tools/tool_types.d.ts +48 -0
  194. package/dist/tools/tool_types.js +7 -0
  195. package/dist/tools/tool_types.js.map +1 -0
  196. package/dist/ui/banner.d.ts +4 -0
  197. package/dist/ui/banner.js +104 -0
  198. package/dist/ui/banner.js.map +1 -0
  199. package/dist/ui/callbacks.d.ts +30 -0
  200. package/dist/ui/callbacks.js +132 -0
  201. package/dist/ui/callbacks.js.map +1 -0
  202. package/dist/ui/colors.d.ts +14 -0
  203. package/dist/ui/colors.js +28 -0
  204. package/dist/ui/colors.js.map +1 -0
  205. package/dist/ui/dashboard.d.ts +51 -0
  206. package/dist/ui/dashboard.js +181 -0
  207. package/dist/ui/dashboard.js.map +1 -0
  208. package/dist/ui/leaderboard.d.ts +16 -0
  209. package/dist/ui/leaderboard.js +43 -0
  210. package/dist/ui/leaderboard.js.map +1 -0
  211. package/dist/ui/logger.d.ts +28 -0
  212. package/dist/ui/logger.js +117 -0
  213. package/dist/ui/logger.js.map +1 -0
  214. package/dist/ui/progress.d.ts +16 -0
  215. package/dist/ui/progress.js +62 -0
  216. package/dist/ui/progress.js.map +1 -0
  217. package/dist/ui/tokenizer.d.ts +5 -0
  218. package/dist/ui/tokenizer.js +54 -0
  219. package/dist/ui/tokenizer.js.map +1 -0
  220. package/package.json +63 -0
@@ -0,0 +1,86 @@
1
+ import { execFileSync } from "node:child_process";
2
+ function runGit(repoPath, args) {
3
+ return execFileSync("git", ["-C", repoPath, ...args], { encoding: "utf8" }).trim();
4
+ }
5
+ export function ensureGitRepository(repoPath) {
6
+ try {
7
+ execFileSync("git", ["-C", repoPath, "rev-parse", "--is-inside-work-tree"], {
8
+ encoding: "utf8",
9
+ stdio: ["pipe", "pipe", "pipe"],
10
+ });
11
+ }
12
+ catch {
13
+ throw new Error(`Not a git repository: ${repoPath}\nRun \`git init\` first, or use \`duocode init\` inside an existing repo.`);
14
+ }
15
+ }
16
+ export function sanitizeBranchName(raw) {
17
+ return raw
18
+ .trim()
19
+ .toLowerCase()
20
+ .replace(/[^a-z0-9/_-]+/g, "-")
21
+ .replace(/^-+|-+$/g, "")
22
+ .replace(/-+/g, "-");
23
+ }
24
+ export function createWorktree(params) {
25
+ const { repoPath, worktreePath, branchName, baseRef = "HEAD" } = params;
26
+ runGit(repoPath, ["worktree", "add", "-b", branchName, worktreePath, baseRef]);
27
+ }
28
+ export function removeWorktree(repoPath, worktreePath, force = false) {
29
+ const args = ["worktree", "remove"];
30
+ if (force) {
31
+ args.push("--force");
32
+ }
33
+ args.push(worktreePath);
34
+ runGit(repoPath, args);
35
+ }
36
+ export function pruneWorktrees(repoPath) {
37
+ runGit(repoPath, ["worktree", "prune"]);
38
+ }
39
+ export function listWorktrees(repoPath) {
40
+ const output = runGit(repoPath, ["worktree", "list", "--porcelain"]);
41
+ if (!output) {
42
+ return [];
43
+ }
44
+ const blocks = output.split("\n\n");
45
+ return blocks.map((block) => {
46
+ const lines = block.split("\n").map((line) => line.trim());
47
+ const entry = {
48
+ path: "",
49
+ head: "",
50
+ branch: null,
51
+ bare: false,
52
+ detached: false,
53
+ locked: false,
54
+ prunable: false,
55
+ };
56
+ for (const line of lines) {
57
+ if (line.startsWith("worktree ")) {
58
+ entry.path = line.slice("worktree ".length);
59
+ }
60
+ else if (line.startsWith("HEAD ")) {
61
+ entry.head = line.slice("HEAD ".length);
62
+ }
63
+ else if (line.startsWith("branch ")) {
64
+ entry.branch = line.slice("branch refs/heads/".length);
65
+ }
66
+ else if (line === "bare") {
67
+ entry.bare = true;
68
+ }
69
+ else if (line === "detached") {
70
+ entry.detached = true;
71
+ }
72
+ else if (line.startsWith("locked")) {
73
+ entry.locked = true;
74
+ }
75
+ else if (line.startsWith("prunable")) {
76
+ entry.prunable = true;
77
+ }
78
+ }
79
+ return entry;
80
+ });
81
+ }
82
+ export function findWorktreeByBranch(repoPath, branchName) {
83
+ const worktrees = listWorktrees(repoPath);
84
+ return worktrees.find((entry) => entry.branch === branchName) ?? null;
85
+ }
86
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/git/worktree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface CacheEntry {
2
+ value: string;
3
+ expiresAt: number;
4
+ lastAccessedAt: number;
5
+ }
6
+ export interface GitHubFileCacheOptions {
7
+ maxEntries?: number;
8
+ ttlMs?: number;
9
+ }
10
+ export declare class GitHubFileCache {
11
+ private readonly maxEntries;
12
+ private readonly ttlMs;
13
+ private readonly entries;
14
+ constructor(options?: GitHubFileCacheOptions);
15
+ private static cacheKey;
16
+ get(owner: string, repo: string, filePath: string, ref?: string, now?: number): string | null;
17
+ set(owner: string, repo: string, filePath: string, value: string, ref?: string, now?: number): void;
18
+ delete(owner: string, repo: string, filePath: string, ref?: string): boolean;
19
+ clearExpired(now?: number): number;
20
+ clear(): void;
21
+ size(): number;
22
+ private evictIfNeeded;
23
+ }
@@ -0,0 +1,67 @@
1
+ export class GitHubFileCache {
2
+ maxEntries;
3
+ ttlMs;
4
+ entries = new Map();
5
+ constructor(options = {}) {
6
+ this.maxEntries = options.maxEntries ?? 512;
7
+ this.ttlMs = options.ttlMs ?? 60000;
8
+ }
9
+ static cacheKey(owner, repo, filePath, ref = "HEAD") {
10
+ return `${owner}/${repo}:${ref}:${filePath}`;
11
+ }
12
+ get(owner, repo, filePath, ref = "HEAD", now = Date.now()) {
13
+ const key = GitHubFileCache.cacheKey(owner, repo, filePath, ref);
14
+ const entry = this.entries.get(key);
15
+ if (!entry) {
16
+ return null;
17
+ }
18
+ if (entry.expiresAt <= now) {
19
+ this.entries.delete(key);
20
+ return null;
21
+ }
22
+ entry.lastAccessedAt = now;
23
+ this.entries.delete(key);
24
+ this.entries.set(key, entry);
25
+ return entry.value;
26
+ }
27
+ set(owner, repo, filePath, value, ref = "HEAD", now = Date.now()) {
28
+ const key = GitHubFileCache.cacheKey(owner, repo, filePath, ref);
29
+ this.entries.delete(key);
30
+ this.entries.set(key, {
31
+ value,
32
+ expiresAt: now + this.ttlMs,
33
+ lastAccessedAt: now,
34
+ });
35
+ this.evictIfNeeded();
36
+ }
37
+ delete(owner, repo, filePath, ref = "HEAD") {
38
+ const key = GitHubFileCache.cacheKey(owner, repo, filePath, ref);
39
+ return this.entries.delete(key);
40
+ }
41
+ clearExpired(now = Date.now()) {
42
+ let removed = 0;
43
+ for (const [key, entry] of this.entries.entries()) {
44
+ if (entry.expiresAt <= now) {
45
+ this.entries.delete(key);
46
+ removed += 1;
47
+ }
48
+ }
49
+ return removed;
50
+ }
51
+ clear() {
52
+ this.entries.clear();
53
+ }
54
+ size() {
55
+ return this.entries.size;
56
+ }
57
+ evictIfNeeded() {
58
+ while (this.entries.size > this.maxEntries) {
59
+ const oldestKey = this.entries.keys().next().value;
60
+ if (!oldestKey) {
61
+ return;
62
+ }
63
+ this.entries.delete(oldestKey);
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/github/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type Database from "better-sqlite3";
2
+ export interface GitHubIssue {
3
+ number: number;
4
+ title: string;
5
+ body?: string | null;
6
+ labels?: Array<{
7
+ name: string;
8
+ } | string>;
9
+ state?: "open" | "closed";
10
+ html_url?: string;
11
+ }
12
+ export interface IssueTaskLink {
13
+ issueNumber: number;
14
+ taskId: string;
15
+ }
16
+ export declare function convertIssueToTask(db: Database.Database, issue: GitHubIssue): IssueTaskLink;
17
+ export declare function convertIssuesToTasks(db: Database.Database, issues: GitHubIssue[]): IssueTaskLink[];
@@ -0,0 +1,93 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { createTask } from "../db/schema.js";
3
+ function inferPriority(issue) {
4
+ const labels = (issue.labels ?? [])
5
+ .map((label) => (typeof label === "string" ? label : label.name).toLowerCase());
6
+ if (labels.some((label) => ["p0", "critical", "sev0", "sev1"].includes(label))) {
7
+ return "critical";
8
+ }
9
+ if (labels.some((label) => ["p1", "high", "sev2"].includes(label))) {
10
+ return "high";
11
+ }
12
+ if (labels.some((label) => ["p3", "low", "chore"].includes(label))) {
13
+ return "low";
14
+ }
15
+ return "medium";
16
+ }
17
+ function inferCapabilities(issue) {
18
+ const labels = (issue.labels ?? [])
19
+ .map((label) => (typeof label === "string" ? label : label.name).toLowerCase());
20
+ const capabilities = new Set();
21
+ for (const label of labels) {
22
+ if (label.includes("bug") || label.includes("fix")) {
23
+ capabilities.add("debugging");
24
+ }
25
+ if (label.includes("test")) {
26
+ capabilities.add("testing");
27
+ }
28
+ if (label.includes("docs")) {
29
+ capabilities.add("documentation");
30
+ }
31
+ if (label.includes("refactor")) {
32
+ capabilities.add("refactoring");
33
+ }
34
+ if (label.includes("security")) {
35
+ capabilities.add("security");
36
+ }
37
+ }
38
+ if (capabilities.size === 0) {
39
+ capabilities.add("code_generation");
40
+ }
41
+ return [...capabilities];
42
+ }
43
+ function ensureIssueMappingTable(db) {
44
+ db.exec(`
45
+ CREATE TABLE IF NOT EXISTS github_issue_tasks (
46
+ issue_number INTEGER PRIMARY KEY,
47
+ task_id TEXT NOT NULL,
48
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
49
+ )
50
+ `);
51
+ }
52
+ export function convertIssueToTask(db, issue) {
53
+ ensureIssueMappingTable(db);
54
+ const existing = db
55
+ .prepare("SELECT task_id FROM github_issue_tasks WHERE issue_number = ?")
56
+ .get(issue.number);
57
+ if (existing) {
58
+ return {
59
+ issueNumber: issue.number,
60
+ taskId: existing.task_id,
61
+ };
62
+ }
63
+ const taskId = randomUUID();
64
+ const descriptionParts = [issue.body ?? ""];
65
+ if (issue.html_url) {
66
+ descriptionParts.push(`Source issue: ${issue.html_url}`);
67
+ }
68
+ createTask(db, {
69
+ id: taskId,
70
+ title: issue.title,
71
+ description: descriptionParts.filter(Boolean).join("\n\n"),
72
+ priority: inferPriority(issue),
73
+ requiredCapabilities: inferCapabilities(issue),
74
+ status: "pending",
75
+ worktreePath: null,
76
+ });
77
+ db.prepare("INSERT INTO github_issue_tasks (issue_number, task_id) VALUES (?, ?)").run(issue.number, taskId);
78
+ return {
79
+ issueNumber: issue.number,
80
+ taskId,
81
+ };
82
+ }
83
+ export function convertIssuesToTasks(db, issues) {
84
+ const links = [];
85
+ for (const issue of issues) {
86
+ if (issue.state && issue.state !== "open") {
87
+ continue;
88
+ }
89
+ links.push(convertIssueToTask(db, issue));
90
+ }
91
+ return links;
92
+ }
93
+ //# sourceMappingURL=issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/github/issues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ interface ToolCallResult {
2
+ content?: Array<{
3
+ type?: string;
4
+ text?: string;
5
+ }>;
6
+ structuredContent?: unknown;
7
+ [key: string]: unknown;
8
+ }
9
+ export interface McpClientOptions {
10
+ command: string;
11
+ args?: string[];
12
+ cwd?: string;
13
+ env?: Record<string, string | undefined>;
14
+ timeoutMs?: number;
15
+ }
16
+ export interface RetryPolicy {
17
+ maxAttempts?: number;
18
+ baseDelayMs?: number;
19
+ }
20
+ export interface PullRequestDraft {
21
+ owner: string;
22
+ repo: string;
23
+ title: string;
24
+ head: string;
25
+ base: string;
26
+ body?: string;
27
+ draft?: boolean;
28
+ }
29
+ export declare class McpClient {
30
+ private readonly proc;
31
+ private readonly timeoutMs;
32
+ private readonly pending;
33
+ private nextId;
34
+ constructor(options: McpClientOptions);
35
+ private handleLine;
36
+ private request;
37
+ initialize(clientName?: string, clientVersion?: string): Promise<void>;
38
+ listTools(): Promise<unknown[]>;
39
+ callTool(name: string, args: Record<string, unknown>): Promise<ToolCallResult>;
40
+ close(): Promise<void>;
41
+ }
42
+ export declare class GitHubMcpClient {
43
+ private readonly mcp;
44
+ private readonly idempotency;
45
+ private readonly retryPolicy;
46
+ constructor(mcp: McpClient, retryPolicy?: RetryPolicy);
47
+ private withRetry;
48
+ validateRequiredTools(requiredTools: string[]): Promise<void>;
49
+ readFile(owner: string, repo: string, path: string, ref?: string): Promise<string>;
50
+ createPullRequest(input: PullRequestDraft & {
51
+ idempotencyKey?: string;
52
+ }): Promise<unknown>;
53
+ commentOnIssue(owner: string, repo: string, issueNumber: number, body: string, idempotencyKey?: string): Promise<unknown>;
54
+ listPullRequests(owner: string, repo: string, state?: "open" | "closed" | "all"): Promise<unknown[]>;
55
+ listIssues(owner: string, repo: string, state?: "open" | "closed" | "all"): Promise<unknown[]>;
56
+ }
57
+ export {};
@@ -0,0 +1,214 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ export class McpClient {
4
+ proc;
5
+ timeoutMs;
6
+ pending = new Map();
7
+ nextId = 1;
8
+ constructor(options) {
9
+ this.timeoutMs = options.timeoutMs ?? 15000;
10
+ this.proc = spawn(options.command, options.args ?? [], {
11
+ cwd: options.cwd,
12
+ env: { ...process.env, ...options.env },
13
+ stdio: ["pipe", "pipe", "pipe"],
14
+ });
15
+ const rl = createInterface({ input: this.proc.stdout });
16
+ rl.on("line", (line) => this.handleLine(line));
17
+ this.proc.stderr.on("data", (_data) => {
18
+ // Keep stderr attached for diagnostics; no-op here.
19
+ });
20
+ this.proc.on("exit", (code) => {
21
+ const message = `MCP process exited with code ${code ?? -1}`;
22
+ for (const [, pending] of this.pending) {
23
+ clearTimeout(pending.timer);
24
+ pending.reject(new Error(message));
25
+ }
26
+ this.pending.clear();
27
+ });
28
+ }
29
+ handleLine(line) {
30
+ if (!line.trim()) {
31
+ return;
32
+ }
33
+ let message;
34
+ try {
35
+ message = JSON.parse(line);
36
+ }
37
+ catch {
38
+ return;
39
+ }
40
+ const pending = this.pending.get(message.id);
41
+ if (!pending) {
42
+ return;
43
+ }
44
+ clearTimeout(pending.timer);
45
+ this.pending.delete(message.id);
46
+ if ("error" in message) {
47
+ pending.reject(new Error(`MCP error ${message.error.code}: ${message.error.message}`));
48
+ return;
49
+ }
50
+ pending.resolve(message.result);
51
+ }
52
+ request(method, params) {
53
+ const id = this.nextId++;
54
+ const payload = {
55
+ jsonrpc: "2.0",
56
+ id,
57
+ method,
58
+ params,
59
+ };
60
+ return new Promise((resolve, reject) => {
61
+ const timer = setTimeout(() => {
62
+ this.pending.delete(id);
63
+ reject(new Error(`MCP request timed out: ${method}`));
64
+ }, this.timeoutMs);
65
+ this.pending.set(id, { resolve, reject, timer });
66
+ this.proc.stdin.write(`${JSON.stringify(payload)}\n`);
67
+ });
68
+ }
69
+ async initialize(clientName = "duocode", clientVersion = "0.1.0") {
70
+ await this.request("initialize", {
71
+ protocolVersion: "2024-11-05",
72
+ capabilities: {},
73
+ clientInfo: { name: clientName, version: clientVersion },
74
+ });
75
+ await this.request("notifications/initialized", {});
76
+ }
77
+ async listTools() {
78
+ const result = await this.request("tools/list", {});
79
+ const typed = result;
80
+ return typed.tools ?? [];
81
+ }
82
+ async callTool(name, args) {
83
+ const result = await this.request("tools/call", {
84
+ name,
85
+ arguments: args,
86
+ });
87
+ return result;
88
+ }
89
+ async close() {
90
+ this.proc.kill();
91
+ }
92
+ }
93
+ export class GitHubMcpClient {
94
+ mcp;
95
+ idempotency = new Map();
96
+ retryPolicy;
97
+ constructor(mcp, retryPolicy) {
98
+ this.mcp = mcp;
99
+ this.retryPolicy = {
100
+ maxAttempts: retryPolicy?.maxAttempts ?? 3,
101
+ baseDelayMs: retryPolicy?.baseDelayMs ?? 250,
102
+ };
103
+ }
104
+ async withRetry(fn) {
105
+ let attempt = 0;
106
+ let lastError = null;
107
+ while (attempt < this.retryPolicy.maxAttempts) {
108
+ attempt += 1;
109
+ try {
110
+ return await fn();
111
+ }
112
+ catch (error) {
113
+ lastError = error instanceof Error ? error : new Error(String(error));
114
+ if (attempt >= this.retryPolicy.maxAttempts) {
115
+ break;
116
+ }
117
+ const delay = this.retryPolicy.baseDelayMs * 2 ** (attempt - 1);
118
+ await new Promise((resolve) => setTimeout(resolve, delay));
119
+ }
120
+ }
121
+ throw lastError ?? new Error("Unknown MCP call failure");
122
+ }
123
+ async validateRequiredTools(requiredTools) {
124
+ const tools = await this.mcp.listTools();
125
+ const available = new Set(tools
126
+ .map((tool) => {
127
+ if (tool && typeof tool === "object" && "name" in tool) {
128
+ return String(tool.name);
129
+ }
130
+ return "";
131
+ })
132
+ .filter(Boolean));
133
+ const missing = requiredTools.filter((tool) => !available.has(tool));
134
+ if (missing.length > 0) {
135
+ throw new Error(`MCP server missing required tools: ${missing.join(", ")}`);
136
+ }
137
+ }
138
+ async readFile(owner, repo, path, ref) {
139
+ const result = await this.withRetry(() => this.mcp.callTool("github_read_file", {
140
+ owner,
141
+ repo,
142
+ path,
143
+ ref,
144
+ }));
145
+ if (result.structuredContent && typeof result.structuredContent === "object") {
146
+ const structured = result.structuredContent;
147
+ if (typeof structured.content === "string") {
148
+ return structured.content;
149
+ }
150
+ }
151
+ const textBlock = result.content?.find((item) => item.type === "text" && typeof item.text === "string");
152
+ return textBlock?.text ?? "";
153
+ }
154
+ async createPullRequest(input) {
155
+ if (input.idempotencyKey && this.idempotency.has(input.idempotencyKey)) {
156
+ return this.idempotency.get(input.idempotencyKey);
157
+ }
158
+ const result = await this.withRetry(() => this.mcp.callTool("github_create_pull_request", {
159
+ owner: input.owner,
160
+ repo: input.repo,
161
+ title: input.title,
162
+ head: input.head,
163
+ base: input.base,
164
+ body: input.body,
165
+ draft: input.draft,
166
+ }));
167
+ const response = result.structuredContent ?? result;
168
+ if (input.idempotencyKey) {
169
+ this.idempotency.set(input.idempotencyKey, response);
170
+ }
171
+ return response;
172
+ }
173
+ async commentOnIssue(owner, repo, issueNumber, body, idempotencyKey) {
174
+ if (idempotencyKey && this.idempotency.has(idempotencyKey)) {
175
+ return this.idempotency.get(idempotencyKey);
176
+ }
177
+ const result = await this.withRetry(() => this.mcp.callTool("github_add_issue_comment", {
178
+ owner,
179
+ repo,
180
+ issue_number: issueNumber,
181
+ body,
182
+ }));
183
+ const response = result.structuredContent ?? result;
184
+ if (idempotencyKey) {
185
+ this.idempotency.set(idempotencyKey, response);
186
+ }
187
+ return response;
188
+ }
189
+ async listPullRequests(owner, repo, state = "open") {
190
+ const result = await this.withRetry(() => this.mcp.callTool("github_list_pull_requests", {
191
+ owner,
192
+ repo,
193
+ state,
194
+ }));
195
+ if (Array.isArray(result.structuredContent)) {
196
+ return result.structuredContent;
197
+ }
198
+ const payload = result.structuredContent;
199
+ return payload?.pull_requests ?? [];
200
+ }
201
+ async listIssues(owner, repo, state = "open") {
202
+ const result = await this.withRetry(() => this.mcp.callTool("github_list_issues", {
203
+ owner,
204
+ repo,
205
+ state,
206
+ }));
207
+ if (Array.isArray(result.structuredContent)) {
208
+ return result.structuredContent;
209
+ }
210
+ const payload = result.structuredContent;
211
+ return payload?.issues ?? [];
212
+ }
213
+ }
214
+ //# sourceMappingURL=mcp_client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp_client.js","sourceRoot":"","sources":["../../src/github/mcp_client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type Database from "better-sqlite3";
2
+ import { GitHubMcpClient } from "./mcp_client.js";
3
+ export interface SyncContext {
4
+ owner: string;
5
+ repo: string;
6
+ baseBranch: string;
7
+ }
8
+ export declare function syncWorktreesWithPullRequests(db: Database.Database, github: GitHubMcpClient, context: SyncContext): Promise<{
9
+ created: number;
10
+ linked: number;
11
+ }>;