millhouse 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 (88) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +248 -0
  3. package/commands/millhouse.md +223 -0
  4. package/dist/analysis/graph-builder.d.ts +42 -0
  5. package/dist/analysis/graph-builder.d.ts.map +1 -0
  6. package/dist/analysis/graph-builder.js +98 -0
  7. package/dist/analysis/graph-builder.js.map +1 -0
  8. package/dist/analysis/issue-analyzer.d.ts +20 -0
  9. package/dist/analysis/issue-analyzer.d.ts.map +1 -0
  10. package/dist/analysis/issue-analyzer.js +167 -0
  11. package/dist/analysis/issue-analyzer.js.map +1 -0
  12. package/dist/analysis/plan-parser.d.ts +8 -0
  13. package/dist/analysis/plan-parser.d.ts.map +1 -0
  14. package/dist/analysis/plan-parser.js +112 -0
  15. package/dist/analysis/plan-parser.js.map +1 -0
  16. package/dist/cli/cleanup.d.ts +20 -0
  17. package/dist/cli/cleanup.d.ts.map +1 -0
  18. package/dist/cli/cleanup.js +186 -0
  19. package/dist/cli/cleanup.js.map +1 -0
  20. package/dist/cli/commands/clean.d.ts +2 -0
  21. package/dist/cli/commands/clean.d.ts.map +1 -0
  22. package/dist/cli/commands/clean.js +16 -0
  23. package/dist/cli/commands/clean.js.map +1 -0
  24. package/dist/cli/commands/resume.d.ts +2 -0
  25. package/dist/cli/commands/resume.d.ts.map +1 -0
  26. package/dist/cli/commands/resume.js +82 -0
  27. package/dist/cli/commands/resume.js.map +1 -0
  28. package/dist/cli/commands/run.d.ts +10 -0
  29. package/dist/cli/commands/run.d.ts.map +1 -0
  30. package/dist/cli/commands/run.js +352 -0
  31. package/dist/cli/commands/run.js.map +1 -0
  32. package/dist/cli/commands/setup.d.ts +6 -0
  33. package/dist/cli/commands/setup.d.ts.map +1 -0
  34. package/dist/cli/commands/setup.js +74 -0
  35. package/dist/cli/commands/setup.js.map +1 -0
  36. package/dist/cli/commands/status.d.ts +7 -0
  37. package/dist/cli/commands/status.d.ts.map +1 -0
  38. package/dist/cli/commands/status.js +83 -0
  39. package/dist/cli/commands/status.js.map +1 -0
  40. package/dist/cli/progress-display.d.ts +93 -0
  41. package/dist/cli/progress-display.d.ts.map +1 -0
  42. package/dist/cli/progress-display.js +318 -0
  43. package/dist/cli/progress-display.js.map +1 -0
  44. package/dist/core/orchestrator.d.ts +79 -0
  45. package/dist/core/orchestrator.d.ts.map +1 -0
  46. package/dist/core/orchestrator.js +389 -0
  47. package/dist/core/orchestrator.js.map +1 -0
  48. package/dist/core/scheduler.d.ts +72 -0
  49. package/dist/core/scheduler.d.ts.map +1 -0
  50. package/dist/core/scheduler.js +234 -0
  51. package/dist/core/scheduler.js.map +1 -0
  52. package/dist/execution/claude-runner.d.ts +41 -0
  53. package/dist/execution/claude-runner.d.ts.map +1 -0
  54. package/dist/execution/claude-runner.js +241 -0
  55. package/dist/execution/claude-runner.js.map +1 -0
  56. package/dist/execution/worktree-manager.d.ts +52 -0
  57. package/dist/execution/worktree-manager.d.ts.map +1 -0
  58. package/dist/execution/worktree-manager.js +208 -0
  59. package/dist/execution/worktree-manager.js.map +1 -0
  60. package/dist/github/client.d.ts +27 -0
  61. package/dist/github/client.d.ts.map +1 -0
  62. package/dist/github/client.js +178 -0
  63. package/dist/github/client.js.map +1 -0
  64. package/dist/github/issue-discoverer.d.ts +20 -0
  65. package/dist/github/issue-discoverer.d.ts.map +1 -0
  66. package/dist/github/issue-discoverer.js +83 -0
  67. package/dist/github/issue-discoverer.js.map +1 -0
  68. package/dist/github/label-manager.d.ts +44 -0
  69. package/dist/github/label-manager.d.ts.map +1 -0
  70. package/dist/github/label-manager.js +93 -0
  71. package/dist/github/label-manager.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +54 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/storage/config.d.ts +4 -0
  77. package/dist/storage/config.d.ts.map +1 -0
  78. package/dist/storage/config.js +25 -0
  79. package/dist/storage/config.js.map +1 -0
  80. package/dist/storage/json-store.d.ts +20 -0
  81. package/dist/storage/json-store.d.ts.map +1 -0
  82. package/dist/storage/json-store.js +92 -0
  83. package/dist/storage/json-store.js.map +1 -0
  84. package/dist/types.d.ts +142 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +36 -0
  87. package/dist/types.js.map +1 -0
  88. package/package.json +60 -0
@@ -0,0 +1,208 @@
1
+ import { execSync, exec } from 'node:child_process';
2
+ import { promises as fs } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ const execAsync = promisify(exec);
6
+ export class WorktreeManager {
7
+ basePath;
8
+ worktreesDir;
9
+ constructor(basePath = process.cwd()) {
10
+ this.basePath = basePath;
11
+ this.worktreesDir = path.join(basePath, '.millhouse', 'worktrees');
12
+ }
13
+ /**
14
+ * Create the run branch from the base branch.
15
+ */
16
+ async createRunBranch(runId, baseBranch) {
17
+ const runBranch = `millhouse/run-${runId}`;
18
+ // Fetch latest from remote
19
+ try {
20
+ await execAsync('git fetch origin', { cwd: this.basePath });
21
+ }
22
+ catch {
23
+ // Might not have remote, continue anyway
24
+ }
25
+ // Create the run branch from base
26
+ try {
27
+ await execAsync(`git checkout -b ${runBranch} origin/${baseBranch}`, { cwd: this.basePath });
28
+ }
29
+ catch {
30
+ // Try without origin/ prefix
31
+ await execAsync(`git checkout -b ${runBranch} ${baseBranch}`, { cwd: this.basePath });
32
+ }
33
+ // Go back to original branch
34
+ await execAsync(`git checkout -`, { cwd: this.basePath });
35
+ return runBranch;
36
+ }
37
+ /**
38
+ * Create an isolated worktree for an issue.
39
+ * Each issue gets its own branch to allow parallel worktrees.
40
+ */
41
+ async createWorktree(runId, issueNumber, runBranch) {
42
+ await fs.mkdir(this.worktreesDir, { recursive: true });
43
+ const worktreePath = path.join(this.worktreesDir, `run-${runId}-issue-${issueNumber}`);
44
+ // Each issue gets its own branch forked from the run branch
45
+ // Use -issue- instead of /issue- to avoid git ref conflicts
46
+ const issueBranch = `${runBranch}-issue-${issueNumber}`;
47
+ // Remove existing worktree if any
48
+ await this.removeWorktree(worktreePath).catch(() => { });
49
+ // Delete the issue branch if it exists (from a previous failed run)
50
+ await execAsync(`git branch -D ${issueBranch}`, { cwd: this.basePath }).catch(() => { });
51
+ // Create worktree with a new branch forked from run branch
52
+ await execAsync(`git worktree add -b ${issueBranch} "${worktreePath}" ${runBranch}`, { cwd: this.basePath });
53
+ const worktreeInfo = {
54
+ issueNumber,
55
+ runId,
56
+ path: worktreePath,
57
+ branch: issueBranch,
58
+ createdAt: new Date().toISOString(),
59
+ };
60
+ return worktreeInfo;
61
+ }
62
+ /**
63
+ * Get list of commits made in a worktree since it was created.
64
+ */
65
+ async getNewCommits(worktreePath, runBranch) {
66
+ try {
67
+ // Get commits that are in worktree HEAD but not in run branch
68
+ const { stdout } = await execAsync(`git log ${runBranch}..HEAD --format=%H`, { cwd: worktreePath });
69
+ return stdout.trim().split('\n').filter(Boolean);
70
+ }
71
+ catch {
72
+ return [];
73
+ }
74
+ }
75
+ /**
76
+ * Merge changes from a worktree back into the run branch.
77
+ * Returns true if successful, false if there were conflicts.
78
+ */
79
+ async mergeWorktree(worktreePath, runBranch) {
80
+ try {
81
+ // Get the current HEAD of the worktree
82
+ const { stdout: worktreeHead } = await execAsync('git rev-parse HEAD', { cwd: worktreePath });
83
+ const commitHash = worktreeHead.trim();
84
+ // Check if there are any new commits
85
+ const commits = await this.getNewCommits(worktreePath, runBranch);
86
+ if (commits.length === 0) {
87
+ return { success: true };
88
+ }
89
+ // Switch to run branch in main repo and merge
90
+ const originalBranch = execSync('git branch --show-current', {
91
+ cwd: this.basePath,
92
+ encoding: 'utf-8',
93
+ }).trim();
94
+ try {
95
+ await execAsync(`git checkout ${runBranch}`, { cwd: this.basePath });
96
+ await execAsync(`git merge ${commitHash} --no-edit`, { cwd: this.basePath });
97
+ return { success: true };
98
+ }
99
+ finally {
100
+ // Return to original branch if different
101
+ if (originalBranch && originalBranch !== runBranch) {
102
+ await execAsync(`git checkout ${originalBranch}`, { cwd: this.basePath }).catch(() => { });
103
+ }
104
+ }
105
+ }
106
+ catch (error) {
107
+ const errorMessage = error instanceof Error ? error.message : String(error);
108
+ // Check if it's a merge conflict
109
+ if (errorMessage.includes('CONFLICT') || errorMessage.includes('Merge conflict')) {
110
+ // Abort the merge
111
+ await execAsync('git merge --abort', { cwd: this.basePath }).catch(() => { });
112
+ return { success: false, error: 'Merge conflict detected' };
113
+ }
114
+ return { success: false, error: errorMessage };
115
+ }
116
+ }
117
+ /**
118
+ * Remove a worktree and its associated branch.
119
+ */
120
+ async removeWorktree(worktreePath, issueBranch) {
121
+ try {
122
+ await execAsync(`git worktree remove "${worktreePath}" --force`, { cwd: this.basePath });
123
+ }
124
+ catch {
125
+ // Try to remove directory manually if git worktree fails
126
+ await fs.rm(worktreePath, { recursive: true, force: true });
127
+ await execAsync('git worktree prune', { cwd: this.basePath }).catch(() => { });
128
+ }
129
+ // Clean up the issue branch if provided
130
+ if (issueBranch) {
131
+ await execAsync(`git branch -D ${issueBranch}`, { cwd: this.basePath }).catch(() => { });
132
+ }
133
+ }
134
+ /**
135
+ * Clean up all worktrees for a run.
136
+ */
137
+ async cleanupRun(_runId) {
138
+ try {
139
+ const entries = await fs.readdir(this.worktreesDir);
140
+ for (const entry of entries) {
141
+ const worktreePath = path.join(this.worktreesDir, entry);
142
+ await this.removeWorktree(worktreePath);
143
+ }
144
+ }
145
+ catch {
146
+ // Directory might not exist
147
+ }
148
+ }
149
+ /**
150
+ * Push the run branch to remote.
151
+ */
152
+ async pushRunBranch(runBranch) {
153
+ await execAsync(`git push -u origin ${runBranch}`, { cwd: this.basePath });
154
+ }
155
+ /**
156
+ * Check if there are uncommitted changes in the main repo.
157
+ */
158
+ async hasUncommittedChanges() {
159
+ try {
160
+ const { stdout } = await execAsync('git status --porcelain', { cwd: this.basePath });
161
+ return stdout.trim().length > 0;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ /**
168
+ * Get the current branch name.
169
+ */
170
+ async getCurrentBranch() {
171
+ const { stdout } = await execAsync('git branch --show-current', { cwd: this.basePath });
172
+ return stdout.trim();
173
+ }
174
+ /**
175
+ * Restore to a specific branch, aborting any in-progress merge.
176
+ */
177
+ async restoreBranch(branchName) {
178
+ // Abort any in-progress merge
179
+ try {
180
+ await execAsync('git merge --abort', { cwd: this.basePath });
181
+ }
182
+ catch {
183
+ // No merge in progress, that's fine
184
+ }
185
+ // Abort any in-progress rebase
186
+ try {
187
+ await execAsync('git rebase --abort', { cwd: this.basePath });
188
+ }
189
+ catch {
190
+ // No rebase in progress, that's fine
191
+ }
192
+ // Discard any uncommitted changes
193
+ try {
194
+ await execAsync('git checkout -- .', { cwd: this.basePath });
195
+ }
196
+ catch {
197
+ // Might fail if no changes, that's fine
198
+ }
199
+ // Switch back to original branch
200
+ try {
201
+ await execAsync(`git checkout "${branchName}"`, { cwd: this.basePath });
202
+ }
203
+ catch {
204
+ // Branch might not exist or other issue
205
+ }
206
+ }
207
+ }
208
+ //# sourceMappingURL=worktree-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-manager.js","sourceRoot":"","sources":["../../src/execution/worktree-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { GitHubIssue } from '../types.js';
2
+ export declare class GitHubClient {
3
+ private octokit;
4
+ private owner;
5
+ private repo;
6
+ constructor();
7
+ private getGitHubToken;
8
+ private getRepoInfo;
9
+ get repoOwner(): string;
10
+ get repoName(): string;
11
+ getIssue(issueNumber: number): Promise<GitHubIssue>;
12
+ getIssues(issueNumbers: number[]): Promise<GitHubIssue[]>;
13
+ listOpenIssues(): Promise<GitHubIssue[]>;
14
+ addLabels(issueNumber: number, labels: string[]): Promise<void>;
15
+ removeLabel(issueNumber: number, label: string): Promise<void>;
16
+ setLabels(issueNumber: number, labels: string[]): Promise<void>;
17
+ createLabel(name: string, color: string, description?: string): Promise<void>;
18
+ addComment(issueNumber: number, body: string): Promise<void>;
19
+ createPullRequest(options: {
20
+ title: string;
21
+ body: string;
22
+ head: string;
23
+ base: string;
24
+ draft?: boolean;
25
+ }): Promise<string>;
26
+ }
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,IAAI,CAAS;;IAsBrB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,WAAW;IAqBnB,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAEK,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBnD,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAKzD,cAAc,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAoCxC,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9D,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7E,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5D,iBAAiB,CAAC,OAAO,EAAE;QAC/B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,OAAO,CAAC,MAAM,CAAC;CAapB"}
@@ -0,0 +1,178 @@
1
+ import { Octokit } from '@octokit/rest';
2
+ import { execSync } from 'node:child_process';
3
+ export class GitHubClient {
4
+ octokit;
5
+ owner;
6
+ repo;
7
+ constructor() {
8
+ // Get token from gh CLI or environment
9
+ const token = this.getGitHubToken();
10
+ this.octokit = new Octokit({
11
+ auth: token,
12
+ log: {
13
+ // Silence Octokit's built-in logging
14
+ debug: () => { },
15
+ info: () => { },
16
+ warn: () => { },
17
+ error: () => { },
18
+ },
19
+ });
20
+ // Get repo info from git remote
21
+ const { owner, repo } = this.getRepoInfo();
22
+ this.owner = owner;
23
+ this.repo = repo;
24
+ }
25
+ getGitHubToken() {
26
+ // First try environment variable
27
+ if (process.env.GITHUB_TOKEN) {
28
+ return process.env.GITHUB_TOKEN;
29
+ }
30
+ // Try gh CLI
31
+ try {
32
+ const token = execSync('gh auth token', { encoding: 'utf-8' }).trim();
33
+ return token;
34
+ }
35
+ catch {
36
+ throw new Error('No GitHub token found. Run "gh auth login" or set GITHUB_TOKEN');
37
+ }
38
+ }
39
+ getRepoInfo() {
40
+ try {
41
+ const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8' }).trim();
42
+ // Parse GitHub URL (SSH or HTTPS)
43
+ const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
44
+ if (sshMatch) {
45
+ return { owner: sshMatch[1], repo: sshMatch[2] };
46
+ }
47
+ const httpsMatch = remoteUrl.match(/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/);
48
+ if (httpsMatch) {
49
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
50
+ }
51
+ throw new Error(`Cannot parse GitHub URL: ${remoteUrl}`);
52
+ }
53
+ catch (error) {
54
+ throw new Error(`Failed to get repo info: ${error instanceof Error ? error.message : error}`);
55
+ }
56
+ }
57
+ get repoOwner() {
58
+ return this.owner;
59
+ }
60
+ get repoName() {
61
+ return this.repo;
62
+ }
63
+ async getIssue(issueNumber) {
64
+ const response = await this.octokit.issues.get({
65
+ owner: this.owner,
66
+ repo: this.repo,
67
+ issue_number: issueNumber,
68
+ });
69
+ return {
70
+ number: response.data.number,
71
+ title: response.data.title,
72
+ body: response.data.body ?? null,
73
+ state: response.data.state,
74
+ labels: response.data.labels.map(l => (typeof l === 'string' ? l : l.name || '')),
75
+ url: response.data.url,
76
+ htmlUrl: response.data.html_url,
77
+ };
78
+ }
79
+ async getIssues(issueNumbers) {
80
+ const promises = issueNumbers.map(n => this.getIssue(n));
81
+ return Promise.all(promises);
82
+ }
83
+ async listOpenIssues() {
84
+ const issues = [];
85
+ let page = 1;
86
+ while (true) {
87
+ const response = await this.octokit.issues.listForRepo({
88
+ owner: this.owner,
89
+ repo: this.repo,
90
+ state: 'open',
91
+ per_page: 100,
92
+ page,
93
+ });
94
+ if (response.data.length === 0)
95
+ break;
96
+ for (const issue of response.data) {
97
+ // Skip pull requests (GitHub API returns them as issues)
98
+ if (issue.pull_request)
99
+ continue;
100
+ issues.push({
101
+ number: issue.number,
102
+ title: issue.title,
103
+ body: issue.body ?? null,
104
+ state: issue.state,
105
+ labels: issue.labels.map(l => (typeof l === 'string' ? l : l.name || '')),
106
+ url: issue.url,
107
+ htmlUrl: issue.html_url,
108
+ });
109
+ }
110
+ page++;
111
+ }
112
+ return issues;
113
+ }
114
+ async addLabels(issueNumber, labels) {
115
+ await this.octokit.issues.addLabels({
116
+ owner: this.owner,
117
+ repo: this.repo,
118
+ issue_number: issueNumber,
119
+ labels,
120
+ });
121
+ }
122
+ async removeLabel(issueNumber, label) {
123
+ try {
124
+ await this.octokit.issues.removeLabel({
125
+ owner: this.owner,
126
+ repo: this.repo,
127
+ issue_number: issueNumber,
128
+ name: label,
129
+ });
130
+ }
131
+ catch {
132
+ // Label might not exist, ignore
133
+ }
134
+ }
135
+ async setLabels(issueNumber, labels) {
136
+ await this.octokit.issues.setLabels({
137
+ owner: this.owner,
138
+ repo: this.repo,
139
+ issue_number: issueNumber,
140
+ labels,
141
+ });
142
+ }
143
+ async createLabel(name, color, description) {
144
+ try {
145
+ await this.octokit.issues.createLabel({
146
+ owner: this.owner,
147
+ repo: this.repo,
148
+ name,
149
+ color,
150
+ description,
151
+ });
152
+ }
153
+ catch {
154
+ // Label might already exist
155
+ }
156
+ }
157
+ async addComment(issueNumber, body) {
158
+ await this.octokit.issues.createComment({
159
+ owner: this.owner,
160
+ repo: this.repo,
161
+ issue_number: issueNumber,
162
+ body,
163
+ });
164
+ }
165
+ async createPullRequest(options) {
166
+ const response = await this.octokit.pulls.create({
167
+ owner: this.owner,
168
+ repo: this.repo,
169
+ title: options.title,
170
+ body: options.body,
171
+ head: options.head,
172
+ base: options.base,
173
+ draft: options.draft ?? true,
174
+ });
175
+ return response.data.html_url;
176
+ }
177
+ }
178
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { GitHubClient } from './client.js';
2
+ import type { GitHubIssue } from '../types.js';
3
+ export declare class IssueDiscoverer {
4
+ private client;
5
+ constructor(client: GitHubClient);
6
+ /**
7
+ * Discovers all issues starting from the given issue numbers.
8
+ * If recursive is true, follows linked issues in descriptions.
9
+ */
10
+ discover(issueNumbers: number[], recursive: boolean): Promise<GitHubIssue[]>;
11
+ /**
12
+ * Parse issue body for linked issue references.
13
+ * Handles:
14
+ * - Direct references: #123
15
+ * - Task lists: - [ ] #123
16
+ * - URLs: github.com/owner/repo/issues/123
17
+ */
18
+ private parseLinkedIssues;
19
+ }
20
+ //# sourceMappingURL=issue-discoverer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-discoverer.d.ts","sourceRoot":"","sources":["../../src/github/issue-discoverer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,eAAe;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC;;;OAGG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA4ClF;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;CAkC1B"}
@@ -0,0 +1,83 @@
1
+ import chalk from 'chalk';
2
+ export class IssueDiscoverer {
3
+ client;
4
+ constructor(client) {
5
+ this.client = client;
6
+ }
7
+ /**
8
+ * Discovers all issues starting from the given issue numbers.
9
+ * If recursive is true, follows linked issues in descriptions.
10
+ */
11
+ async discover(issueNumbers, recursive) {
12
+ const discovered = new Map();
13
+ const toProcess = new Set(issueNumbers);
14
+ const processed = new Set();
15
+ console.log(chalk.gray(` Starting with issues: ${issueNumbers.map(n => `#${n}`).join(', ')}`));
16
+ if (recursive) {
17
+ console.log(chalk.gray(` Recursive mode: will follow linked issues`));
18
+ }
19
+ while (toProcess.size > 0) {
20
+ const batch = Array.from(toProcess);
21
+ toProcess.clear();
22
+ console.log(chalk.gray(` Fetching: ${batch.map(n => `#${n}`).join(', ')}`));
23
+ // Fetch issues in parallel
24
+ const issues = await this.client.getIssues(batch);
25
+ for (const issue of issues) {
26
+ if (discovered.has(issue.number))
27
+ continue;
28
+ discovered.set(issue.number, issue);
29
+ processed.add(issue.number);
30
+ console.log(chalk.gray(` Found #${issue.number}: ${issue.title}`));
31
+ // If recursive, find linked issues
32
+ if (recursive && issue.body) {
33
+ const linkedIssues = this.parseLinkedIssues(issue.body);
34
+ const newLinked = linkedIssues.filter(n => !processed.has(n) && !discovered.has(n));
35
+ if (newLinked.length > 0) {
36
+ console.log(chalk.gray(` └─ Links to: ${newLinked.map(n => `#${n}`).join(', ')}`));
37
+ }
38
+ for (const linkedNumber of newLinked) {
39
+ toProcess.add(linkedNumber);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ return Array.from(discovered.values());
45
+ }
46
+ /**
47
+ * Parse issue body for linked issue references.
48
+ * Handles:
49
+ * - Direct references: #123
50
+ * - Task lists: - [ ] #123
51
+ * - URLs: github.com/owner/repo/issues/123
52
+ */
53
+ parseLinkedIssues(body) {
54
+ const issueNumbers = new Set();
55
+ // Match #123 patterns (not in URLs)
56
+ const hashPattern = /(?:^|[^/])#(\d+)/g;
57
+ let match;
58
+ while ((match = hashPattern.exec(body)) !== null) {
59
+ const num = parseInt(match[1], 10);
60
+ if (num > 0 && num < 1000000) {
61
+ issueNumbers.add(num);
62
+ }
63
+ }
64
+ // Match task list items: - [ ] #123 or - [x] #123
65
+ const taskListPattern = /- \[[ x]\] #(\d+)/gi;
66
+ while ((match = taskListPattern.exec(body)) !== null) {
67
+ const num = parseInt(match[1], 10);
68
+ if (num > 0 && num < 1000000) {
69
+ issueNumbers.add(num);
70
+ }
71
+ }
72
+ // Match GitHub issue URLs for the same repo
73
+ const urlPattern = /github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/g;
74
+ while ((match = urlPattern.exec(body)) !== null) {
75
+ const num = parseInt(match[1], 10);
76
+ if (num > 0 && num < 1000000) {
77
+ issueNumbers.add(num);
78
+ }
79
+ }
80
+ return Array.from(issueNumbers);
81
+ }
82
+ }
83
+ //# sourceMappingURL=issue-discoverer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-discoverer.js","sourceRoot":"","sources":["../../src/github/issue-discoverer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { GitHubClient } from './client.js';
2
+ import { type TaskStatus } from '../types.js';
3
+ export declare class LabelManager {
4
+ private client;
5
+ constructor(client: GitHubClient);
6
+ /**
7
+ * Ensure all Millhouse labels exist in the repository.
8
+ */
9
+ ensureLabelsExist(): Promise<void>;
10
+ /**
11
+ * Remove all Millhouse labels from an issue.
12
+ */
13
+ clearMillhouseLabels(issueNumber: number): Promise<void>;
14
+ /**
15
+ * Set the Millhouse status label for an issue.
16
+ * Removes any existing Millhouse labels first.
17
+ */
18
+ setStatus(issueNumber: number, status: TaskStatus): Promise<void>;
19
+ /**
20
+ * Set multiple issues to a status.
21
+ */
22
+ setStatusBatch(issueNumbers: number[], status: TaskStatus): Promise<void>;
23
+ /**
24
+ * Mark an issue as queued.
25
+ */
26
+ markQueued(issueNumber: number): Promise<void>;
27
+ /**
28
+ * Mark an issue as in progress.
29
+ */
30
+ markInProgress(issueNumber: number): Promise<void>;
31
+ /**
32
+ * Mark an issue as blocked.
33
+ */
34
+ markBlocked(issueNumber: number): Promise<void>;
35
+ /**
36
+ * Mark an issue as failed.
37
+ */
38
+ markFailed(issueNumber: number): Promise<void>;
39
+ /**
40
+ * Mark an issue as done.
41
+ */
42
+ markDone(issueNumber: number): Promise<void>;
43
+ }
44
+ //# sourceMappingURL=label-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-manager.d.ts","sourceRoot":"","sources":["../../src/github/label-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AA2BrF,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYxC;;OAEG;IACG,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D;;;OAGG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE;;OAEG;IACG,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/E;;OAEG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD;;OAEG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD;;OAEG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD;;OAEG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD;;OAEG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGnD"}